GBP Endpoint Updates 09/14709/8
authorNeale Ranns <neale.ranns@cisco.com>
Wed, 5 Sep 2018 22:42:26 +0000 (15:42 -0700)
committerNeale Ranns <nranns@cisco.com>
Tue, 11 Sep 2018 16:00:29 +0000 (16:00 +0000)
- common types on the API
- endpoints keyed in various ways for DP lookup
- conparison functions for VPP IP address types

Change-Id: If7ec0bbc5cea71fd0983fe78987d147ec1bd7ec8
Signed-off-by: Neale Ranns <neale.ranns@cisco.com>
39 files changed:
extras/vom/vom/gbp_endpoint.cpp
extras/vom/vom/gbp_endpoint.hpp
extras/vom/vom/gbp_endpoint_cmds.cpp
extras/vom/vom/gbp_endpoint_cmds.hpp
extras/vom/vom/gbp_subnet.cpp
extras/vom/vom/gbp_subnet_cmds.cpp
extras/vom/vom/prefix.hpp
src/plugins/gbp/gbp.api
src/plugins/gbp/gbp_api.c
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_policy.c
src/plugins/gbp/gbp_recirc.c
src/plugins/gbp/gbp_recirc.h
src/vnet/ip/ip6_packet.h
test/test_abf.py
test/test_bfd.py
test/test_bier.py
test/test_gbp.py
test/test_gre.py
test/test_ip6.py
test/test_ip_mcast.py
test/test_ipip.py
test/test_map.py
test/test_mpls.py
test/test_mtu.py
test/test_p2p_ethernet.py
test/test_qos.py
test/test_reassembly.py
test/test_sixrd.py
test/test_srmpls.py
test/test_srv6_ad.py
test/vpp_ip.py
test/vpp_ip_route.py
test/vpp_mac.py [new file with mode: 0644]
test/vpp_papi_provider.py
test/vpp_udp_encap.py

index 9762a91..236a961 100644 (file)
@@ -14,6 +14,7 @@
  */
 
 #include "vom/gbp_endpoint.hpp"
+#include "vom/api_types.hpp"
 #include "vom/gbp_endpoint_cmds.hpp"
 #include "vom/singular_db_funcs.hpp"
 
@@ -23,22 +24,23 @@ singular_db<gbp_endpoint::key_t, gbp_endpoint> gbp_endpoint::m_db;
 
 gbp_endpoint::event_handler gbp_endpoint::m_evh;
 
-gbp_endpoint::gbp_endpoint(const interface& itf,
-                           const boost::asio::ip::address& ip_addr,
-                           const mac_address_t& mac,
-                           const gbp_endpoint_group& epg)
-  : m_hw(false)
+gbp_endpoint::gbp_endpoint(
+  const interface& itf,
+  const std::vector<boost::asio::ip::address>& ip_addrs,
+  const mac_address_t& mac,
+  const gbp_endpoint_group& epg)
+  : m_hdl(handle_t::INVALID)
   , m_itf(itf.singular())
-  , m_ip(ip_addr)
+  , m_ips(ip_addrs)
   , m_mac(mac)
   , m_epg(epg.singular())
 {
 }
 
 gbp_endpoint::gbp_endpoint(const gbp_endpoint& gbpe)
-  : m_hw(gbpe.m_hw)
+  : m_hdl(gbpe.m_hdl)
   , m_itf(gbpe.m_itf)
-  , m_ip(gbpe.m_ip)
+  , m_ips(gbpe.m_ips)
   , m_mac(gbpe.m_mac)
   , m_epg(gbpe.m_epg)
 {
@@ -53,7 +55,7 @@ gbp_endpoint::~gbp_endpoint()
 const gbp_endpoint::key_t
 gbp_endpoint::key() const
 {
-  return (std::make_pair(m_itf->key(), m_ip));
+  return (std::make_pair(m_itf->key(), m_mac));
 }
 
 bool
@@ -65,8 +67,8 @@ gbp_endpoint::operator==(const gbp_endpoint& gbpe) const
 void
 gbp_endpoint::sweep()
 {
-  if (m_hw) {
-    HW::enqueue(new gbp_endpoint_cmds::delete_cmd(m_hw, m_itf->handle(), m_ip));
+  if (m_hdl) {
+    HW::enqueue(new gbp_endpoint_cmds::delete_cmd(m_hdl));
   }
   HW::write();
 }
@@ -74,8 +76,8 @@ gbp_endpoint::sweep()
 void
 gbp_endpoint::replay()
 {
-  if (m_hw) {
-    HW::enqueue(new gbp_endpoint_cmds::create_cmd(m_hw, m_itf->handle(), m_ip,
+  if (m_hdl) {
+    HW::enqueue(new gbp_endpoint_cmds::create_cmd(m_hdl, m_itf->handle(), m_ips,
                                                   m_mac, m_epg->id()));
   }
 }
@@ -84,8 +86,12 @@ std::string
 gbp_endpoint::to_string() const
 {
   std::ostringstream s;
-  s << "gbp-endpoint:[" << m_itf->to_string() << ", " << m_ip.to_string()
-    << ", " << m_mac.to_string() << ", epg:" << m_epg->to_string() << "]";
+  s << "gbp-endpoint:[" << m_itf->to_string() << ", ips:[";
+
+  for (auto ip : m_ips)
+    s << ip.to_string();
+
+  s << "], " << m_mac.to_string() << ", epg:" << m_epg->to_string() << "]";
 
   return (s.str());
 }
@@ -93,8 +99,8 @@ gbp_endpoint::to_string() const
 void
 gbp_endpoint::update(const gbp_endpoint& r)
 {
-  if (rc_t::OK != m_hw.rc()) {
-    HW::enqueue(new gbp_endpoint_cmds::create_cmd(m_hw, m_itf->handle(), m_ip,
+  if (rc_t::OK != m_hdl.rc()) {
+    HW::enqueue(new gbp_endpoint_cmds::create_cmd(m_hdl, m_itf->handle(), m_ips,
                                                   m_mac, m_epg->id()));
   }
 }
@@ -147,18 +153,20 @@ gbp_endpoint::event_handler::handle_populate(const client_db::key_t& key)
   for (auto& record : *cmd) {
     auto& payload = record.get_payload();
 
-    boost::asio::ip::address address =
-      from_bytes(payload.endpoint.is_ip6, payload.endpoint.address);
+    std::vector<boost::asio::ip::address> addresses;
+
+    for (uint8_t n = 0; n < payload.endpoint.n_ips; n++)
+      addresses.push_back(from_api(payload.endpoint.ips[n]));
     std::shared_ptr<interface> itf =
       interface::find(payload.endpoint.sw_if_index);
     std::shared_ptr<gbp_endpoint_group> epg =
       gbp_endpoint_group::find(payload.endpoint.epg_id);
-    mac_address_t mac(payload.endpoint.mac);
+    mac_address_t mac = from_api(payload.endpoint.mac);
 
     VOM_LOG(log_level_t::DEBUG) << "data: " << payload.endpoint.sw_if_index;
 
     if (itf && epg) {
-      gbp_endpoint gbpe(*itf, address, mac, *epg);
+      gbp_endpoint gbpe(*itf, addresses, mac, *epg);
       OM::commit(key, gbpe);
 
       VOM_LOG(log_level_t::DEBUG) << "read: " << gbpe.to_string();
@@ -177,6 +185,15 @@ gbp_endpoint::event_handler::show(std::ostream& os)
 {
   db_dump(m_db, os);
 }
+
+std::ostream&
+operator<<(std::ostream& os, const gbp_endpoint::key_t& key)
+{
+  os << key.first << "," << key.second;
+
+  return os;
+}
+
 } // namespace VOM
 
 /*
index f6466a6..d27ad90 100644 (file)
@@ -17,6 +17,7 @@
 #define __VOM_GBP_ENDPOINT_H__
 
 #include <ostream>
+#include <vector>
 
 #include "vom/gbp_endpoint_group.hpp"
 #include "vom/interface.hpp"
@@ -32,13 +33,13 @@ public:
   /**
    * The key for a GBP endpoint; interface and IP
    */
-  typedef std::pair<interface::key_t, boost::asio::ip::address> key_t;
+  typedef std::pair<interface::key_t, mac_address_t> key_t;
 
   /**
    * Construct a GBP endpoint
    */
   gbp_endpoint(const interface& itf,
-               const boost::asio::ip::address& ip_addr,
+               const std::vector<boost::asio::ip::address>& ip_addr,
                const mac_address_t& mac,
                const gbp_endpoint_group& epg);
 
@@ -151,7 +152,7 @@ private:
   /**
    * HW configuration for the result of creating the endpoint
    */
-  HW::item<bool> m_hw;
+  HW::item<handle_t> m_hdl;
 
   /**
    * The interface the endpoint is attached to.
@@ -161,7 +162,7 @@ private:
   /**
    * The IP address of the endpoint
    */
-  boost::asio::ip::address m_ip;
+  std::vector<boost::asio::ip::address> m_ips;
 
   /**
    * The MAC address of the endpoint
index 1f9078f..8d44c91 100644 (file)
  */
 
 #include "vom/gbp_endpoint_cmds.hpp"
+#include "vom/api_types.hpp"
 
 DEFINE_VAPI_MSG_IDS_GBP_API_JSON;
 
 namespace VOM {
 namespace gbp_endpoint_cmds {
 
-create_cmd::create_cmd(HW::item<bool>& item,
+create_cmd::create_cmd(HW::item<handle_t>& item,
                        const handle_t& itf,
-                       const boost::asio::ip::address& ip_addr,
+                       const std::vector<boost::asio::ip::address>& ip_addrs,
                        const mac_address_t& mac,
                        epg_id_t epg_id)
   : rpc_cmd(item)
   , m_itf(itf)
-  , m_ip_addr(ip_addr)
+  , m_ip_addrs(ip_addrs)
   , m_mac(mac)
   , m_epg_id(epg_id)
 {
@@ -36,50 +37,76 @@ create_cmd::create_cmd(HW::item<bool>& item,
 bool
 create_cmd::operator==(const create_cmd& other) const
 {
-  return ((m_itf == other.m_itf) && (m_ip_addr == other.m_ip_addr) &&
+  return ((m_itf == other.m_itf) && (m_ip_addrs == other.m_ip_addrs) &&
           (m_mac == other.m_mac) && (m_epg_id == other.m_epg_id));
 }
 
 rc_t
 create_cmd::issue(connection& con)
 {
-  msg_t req(con.ctx(), std::ref(*this));
+  msg_t req(con.ctx(), m_ip_addrs.size() * sizeof(vapi_type_address),
+            std::ref(*this));
+  uint8_t n;
 
   auto& payload = req.get_request().get_payload();
-  payload.is_add = 1;
   payload.endpoint.sw_if_index = m_itf.value();
   payload.endpoint.epg_id = m_epg_id;
-  to_bytes(m_ip_addr, &payload.endpoint.is_ip6, payload.endpoint.address);
-  m_mac.to_bytes(payload.endpoint.mac, 6);
+  payload.endpoint.n_ips = m_ip_addrs.size();
+
+  for (n = 0; n < payload.endpoint.n_ips; n++) {
+    payload.endpoint.ips[n] = to_api(m_ip_addrs[n]);
+  }
+  payload.endpoint.mac = to_api(m_mac);
 
   VAPI_CALL(req.execute());
 
   return (wait());
 }
 
+vapi_error_e
+create_cmd::operator()(vapi::Gbp_endpoint_add& reply)
+{
+  int handle = reply.get_response().get_payload().handle;
+  int retval = reply.get_response().get_payload().retval;
+
+  VOM_LOG(log_level_t::DEBUG) << this->to_string() << " " << retval;
+
+  rc_t rc = rc_t::from_vpp_retval(retval);
+  handle_t hdl = handle_t::INVALID;
+
+  if (rc_t::OK == rc) {
+    hdl = handle;
+  }
+
+  this->fulfill(HW::item<handle_t>(hdl, rc));
+
+  return (VAPI_OK);
+}
+
 std::string
 create_cmd::to_string() const
 {
   std::ostringstream s;
   s << "gbp-endpoint-create: " << m_hw_item.to_string() << " itf:" << m_itf
-    << " ip:" << m_ip_addr.to_string() << " epg-id:" << m_epg_id;
+    << " ips:[";
+  for (auto ip : m_ip_addrs)
+    s << ip.to_string();
+
+  s << "] mac:" << m_mac;
+  s << " epg-id:" << m_epg_id;
 
   return (s.str());
 }
 
-delete_cmd::delete_cmd(HW::item<bool>& item,
-                       const handle_t& itf,
-                       const boost::asio::ip::address& ip_addr)
+delete_cmd::delete_cmd(HW::item<handle_t>& item)
   : rpc_cmd(item)
-  , m_itf(itf)
-  , m_ip_addr(ip_addr)
 {
 }
 
 bool
 delete_cmd::operator==(const delete_cmd& other) const
 {
-  return ((m_itf == other.m_itf) && (m_ip_addr == other.m_ip_addr));
+  return (m_hw_item == other.m_hw_item);
 }
 
 rc_t
@@ -88,10 +115,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.endpoint.sw_if_index = m_itf.value();
-  payload.endpoint.epg_id = ~0;
-  to_bytes(m_ip_addr, &payload.endpoint.is_ip6, payload.endpoint.address);
+  payload.handle = m_hw_item.data().value();
 
   VAPI_CALL(req.execute());
 
@@ -102,8 +126,7 @@ std::string
 delete_cmd::to_string() const
 {
   std::ostringstream s;
-  s << "gbp-endpoint-delete: " << m_hw_item.to_string() << " itf:" << m_itf
-    << " ip:" << m_ip_addr.to_string();
+  s << "gbp-endpoint-delete: " << m_hw_item.to_string();
 
   return (s.str());
 }
index dea4c2c..e90fb66 100644 (file)
@@ -27,15 +27,15 @@ namespace gbp_endpoint_cmds {
 /**
 * A command class that creates or updates the GBP endpoint
 */
-class create_cmd : public rpc_cmd<HW::item<bool>, vapi::Gbp_endpoint_add_del>
+class create_cmd : public rpc_cmd<HW::item<handle_t>, vapi::Gbp_endpoint_add>
 {
 public:
   /**
    * Constructor
    */
-  create_cmd(HW::item<bool>& item,
+  create_cmd(HW::item<handle_t>& item,
              const handle_t& itf,
-             const boost::asio::ip::address& ip_addr,
+             const std::vector<boost::asio::ip::address>& ip_addrs,
              const mac_address_t& mac,
              epg_id_t epg_id);
 
@@ -49,6 +49,8 @@ public:
    */
   std::string to_string() const;
 
+  virtual vapi_error_e operator()(vapi::Gbp_endpoint_add& reply);
+
   /**
    * Comparison operator - only used for UT
    */
@@ -56,7 +58,7 @@ public:
 
 private:
   const handle_t m_itf;
-  const boost::asio::ip::address m_ip_addr;
+  const std::vector<boost::asio::ip::address> m_ip_addrs;
   const mac_address_t m_mac;
   const epg_id_t m_epg_id;
 };
@@ -64,15 +66,13 @@ private:
 /**
  * A cmd class that deletes a GBP endpoint
  */
-class delete_cmd : public rpc_cmd<HW::item<bool>, vapi::Gbp_endpoint_add_del>
+class delete_cmd : public rpc_cmd<HW::item<handle_t>, vapi::Gbp_endpoint_del>
 {
 public:
   /**
    * Constructor
    */
-  delete_cmd(HW::item<bool>& item,
-             const handle_t& itf,
-             const boost::asio::ip::address& ip_addr);
+  delete_cmd(HW::item<handle_t>& item);
 
   /**
    * Issue the command to VPP/HW
@@ -90,8 +90,7 @@ public:
   bool operator==(const delete_cmd& i) const;
 
 private:
-  const handle_t m_itf;
-  const boost::asio::ip::address m_ip_addr;
+  const handle_t m_hdl;
 };
 
 /**
index 1a9ee86..1bc024d 100644 (file)
@@ -14,6 +14,7 @@
  */
 
 #include "vom/gbp_subnet.hpp"
+#include "vom/api_types.hpp"
 #include "vom/gbp_subnet_cmds.hpp"
 #include "vom/singular_db_funcs.hpp"
 
@@ -190,8 +191,7 @@ gbp_subnet::event_handler::handle_populate(const client_db::key_t& key)
   for (auto& record : *cmd) {
     auto& payload = record.get_payload();
 
-    route::prefix_t pfx(payload.subnet.is_ip6, payload.subnet.address,
-                        payload.subnet.address_length);
+    route::prefix_t pfx = from_api(payload.subnet.prefix);
     std::shared_ptr<route_domain> rd =
       route_domain::find(payload.subnet.table_id);
 
index 3816a59..79fdf17 100644 (file)
@@ -14,6 +14,7 @@
  */
 
 #include "vom/gbp_subnet_cmds.hpp"
+#include "vom/api_types.hpp"
 
 namespace VOM {
 namespace gbp_subnet_cmds {
@@ -52,8 +53,7 @@ create_cmd::issue(connection& con)
   payload.subnet.table_id = m_rd;
   payload.subnet.sw_if_index = m_itf.value();
   payload.subnet.epg_id = m_epg_id;
-  m_prefix.to_vpp(&payload.subnet.is_ip6, payload.subnet.address,
-                  &payload.subnet.address_length);
+  payload.subnet.prefix = to_api(m_prefix);
 
   VAPI_CALL(req.execute());
 
@@ -94,8 +94,7 @@ delete_cmd::issue(connection& con)
   auto& payload = req.get_request().get_payload();
   payload.is_add = 0;
   payload.subnet.table_id = m_rd;
-  m_prefix.to_vpp(&payload.subnet.is_ip6, payload.subnet.address,
-                  &payload.subnet.address_length);
+  payload.subnet.prefix = to_api(m_prefix);
 
   payload.subnet.is_internal = 0;
   payload.subnet.sw_if_index = ~0;
index 3950f6f..8365541 100644 (file)
@@ -16,9 +16,8 @@
 #ifndef __VOM_PREFIX_H__
 #define __VOM_PREFIX_H__
 
-#include <boost/asio/ip/address.hpp>
-
 #include "vom/enum_base.hpp"
+#include <boost/asio/ip/address.hpp>
 
 namespace VOM {
 /**
@@ -111,10 +110,12 @@ public:
    * Constructor with string and length
    */
   prefix_t(const std::string& s, uint8_t len);
+
   /**
    * Copy Constructor
    */
   prefix_t(const prefix_t&);
+
   /**
    * Constructor with VPP API prefix representation
    */
index 3e27abb..d7c6d83 100644 (file)
  * limitations under the License.
  */
 
-option version = "1.0.0";
+option version = "2.0.0";
+
+import "vnet/ip/ip_types.api";
+import "vnet/ethernet/ethernet_types.api";
 
 /** \brief Endpoint
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
 */
 
-typeonly define gbp_endpoint
+typedef gbp_endpoint
 {
   u32 sw_if_index;
   u16 epg_id;
-  u8  is_ip6;
-  u8  address[16];
-  u8  mac[6];
+  vl_api_mac_address_t mac;
+  u8 n_ips;
+  vl_api_address_t ips[n_ips];
 };
 
-autoreply define gbp_endpoint_add_del
+define gbp_endpoint_add
 {
   u32 client_index;
   u32 context;
-  u8  is_add;
   vl_api_gbp_endpoint_t endpoint;
 };
 
+define gbp_endpoint_add_reply
+{
+  u32 context;
+  i32 retval;
+  u32 handle;
+};
+
+autoreply define gbp_endpoint_del
+{
+  u32 client_index;
+  u32 context;
+  u32 handle;
+};
+
 define gbp_endpoint_dump
 {
   u32 client_index;
@@ -111,10 +127,8 @@ typeonly define gbp_subnet
   u32 table_id;
   u32 sw_if_index;
   u16 epg_id;
-  u8  is_ip6;
   u8  is_internal;
-  u8  address_length;
-  u8  address[16];
+  vl_api_prefix_t prefix;
 };
 
 autoreply define gbp_subnet_add_del
index f487695..a541d96 100644 (file)
@@ -20,6 +20,8 @@
 
 #include <vnet/interface.h>
 #include <vnet/api_errno.h>
+#include <vnet/ip/ip_types_api.h>
+#include <vnet/ethernet/ethernet_types_api.h>
 #include <vpp/app/version.h>
 
 #include <gbp/gbp.h>
@@ -52,7 +54,8 @@
 #include <vlibapi/api_helper_macros.h>
 
 #define foreach_gbp_api_msg                                 \
-  _(GBP_ENDPOINT_ADD_DEL, gbp_endpoint_add_del)             \
+  _(GBP_ENDPOINT_ADD, gbp_endpoint_add)                     \
+  _(GBP_ENDPOINT_DEL, gbp_endpoint_del)                     \
   _(GBP_ENDPOINT_DUMP, gbp_endpoint_dump)                   \
   _(GBP_SUBNET_ADD_DEL, gbp_subnet_add_del)                 \
   _(GBP_SUBNET_DUMP, gbp_subnet_dump)                       \
@@ -70,39 +73,55 @@ static u16 msg_id_base;
 #define GBP_MSG_BASE msg_id_base
 
 static void
-vl_api_gbp_endpoint_add_del_t_handler (vl_api_gbp_endpoint_add_del_t * mp)
+vl_api_gbp_endpoint_add_t_handler (vl_api_gbp_endpoint_add_t * mp)
 {
-  vl_api_gbp_endpoint_add_del_reply_t *rmp;
-  ip46_address_t ip = { };
-  u32 sw_if_index;
-  int rv = 0;
+  vl_api_gbp_endpoint_add_reply_t *rmp;
+  u32 sw_if_index, handle;
+  ip46_address_t *ips;
+  mac_address_t mac;
+  int rv = 0, ii;
+
+  VALIDATE_SW_IF_INDEX (&(mp->endpoint));
 
   sw_if_index = ntohl (mp->endpoint.sw_if_index);
-  if (!vnet_sw_if_index_is_api_valid (sw_if_index))
-    goto bad_sw_if_index;
 
-  if (mp->endpoint.is_ip6)
-    {
-      clib_memcpy (&ip.ip6, mp->endpoint.address, sizeof (ip.ip6));
-    }
-  else
-    {
-      clib_memcpy (&ip.ip4, mp->endpoint.address, sizeof (ip.ip4));
-    }
+  ips = NULL;
 
-  if (mp->is_add)
-    {
-      rv =
-       gbp_endpoint_update (sw_if_index, &ip, ntohs (mp->endpoint.epg_id));
-    }
-  else
+  if (mp->endpoint.n_ips)
     {
-      gbp_endpoint_delete (sw_if_index, &ip);
+      vec_validate (ips, mp->endpoint.n_ips - 1);
+
+      vec_foreach_index (ii, ips)
+      {
+       ip_address_decode (&mp->endpoint.ips[ii], &ips[ii]);
+      }
     }
+  mac_address_decode (&mp->endpoint.mac, &mac);
+
+  rv = gbp_endpoint_update (sw_if_index, ips, &mac,
+                           ntohs (mp->endpoint.epg_id), &handle);
+
+  vec_free (ips);
 
   BAD_SW_IF_INDEX_LABEL;
 
-  REPLY_MACRO (VL_API_GBP_ENDPOINT_ADD_DEL_REPLY + GBP_MSG_BASE);
+  /* *INDENT-OFF* */
+  REPLY_MACRO2 (VL_API_GBP_ENDPOINT_ADD_REPLY + GBP_MSG_BASE,
+  ({
+    rmp->handle = htonl (handle);
+  }));
+  /* *INDENT-ON* */
+}
+
+static void
+vl_api_gbp_endpoint_del_t_handler (vl_api_gbp_endpoint_del_t * mp)
+{
+  vl_api_gbp_endpoint_del_reply_t *rmp;
+  int rv = 0;
+
+  gbp_endpoint_delete (ntohl (mp->handle));
+
+  REPLY_MACRO (VL_API_GBP_ENDPOINT_DEL_REPLY + GBP_MSG_BASE);
 }
 
 typedef struct gbp_walk_ctx_t_
@@ -111,14 +130,16 @@ typedef struct gbp_walk_ctx_t_
   u32 context;
 } gbp_walk_ctx_t;
 
-static int
+static walk_rc_t
 gbp_endpoint_send_details (gbp_endpoint_t * gbpe, void *args)
 {
   vl_api_gbp_endpoint_details_t *mp;
   gbp_walk_ctx_t *ctx;
+  u8 n_ips, ii;
 
   ctx = args;
-  mp = vl_msg_api_alloc (sizeof (*mp));
+  n_ips = vec_len (gbpe->ge_ips);
+  mp = vl_msg_api_alloc (sizeof (*mp) + (sizeof (*mp->endpoint.ips) * n_ips));
   if (!mp)
     return 1;
 
@@ -126,22 +147,20 @@ 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_key->gek_sw_if_index);
-  mp->endpoint.is_ip6 = !ip46_address_is_ip4 (&gbpe->ge_key->gek_ip);
-  if (mp->endpoint.is_ip6)
-    clib_memcpy (&mp->endpoint.address,
-                &gbpe->ge_key->gek_ip.ip6,
-                sizeof (gbpe->ge_key->gek_ip.ip6));
-  else
-    clib_memcpy (&mp->endpoint.address,
-                &gbpe->ge_key->gek_ip.ip4,
-                sizeof (gbpe->ge_key->gek_ip.ip4));
-
+  mp->endpoint.sw_if_index = ntohl (gbpe->ge_sw_if_index);
   mp->endpoint.epg_id = ntohs (gbpe->ge_epg_id);
+  mp->endpoint.n_ips = n_ips;
+  mac_address_encode (&gbpe->ge_mac, &mp->endpoint.mac);
+
+  vec_foreach_index (ii, gbpe->ge_ips)
+  {
+    ip_address_encode (&gbpe->ge_ips[ii], IP46_TYPE_ANY,
+                      &mp->endpoint.ips[ii]);
+  }
 
   vl_api_send_msg (ctx->reg, (u8 *) mp);
 
-  return (1);
+  return (WALK_CONTINUE);
 }
 
 static void
@@ -195,18 +214,10 @@ 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;
+  fib_prefix_t pfx;
   int rv = 0;
-  fib_prefix_t pfx = {
-    .fp_len = mp->subnet.address_length,
-    .fp_proto = (mp->subnet.is_ip6 ? FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4),
-  };
 
-  if (mp->subnet.is_ip6)
-    clib_memcpy (&pfx.fp_addr.ip6, mp->subnet.address,
-                sizeof (pfx.fp_addr.ip6));
-  else
-    clib_memcpy (&pfx.fp_addr.ip4, mp->subnet.address,
-                sizeof (pfx.fp_addr.ip4));
+  ip_prefix_decode (&mp->subnet.prefix, &pfx);
 
   rv = gbp_subnet_add_del (ntohl (mp->subnet.table_id),
                           &pfx,
@@ -238,16 +249,8 @@ gbp_subnet_send_details (u32 table_id,
   mp->subnet.is_internal = is_internal;
   mp->subnet.sw_if_index = ntohl (sw_if_index);
   mp->subnet.epg_id = ntohs (epg);
-  mp->subnet.is_ip6 = (pfx->fp_proto == FIB_PROTOCOL_IP6);
-  mp->subnet.address_length = pfx->fp_len;
   mp->subnet.table_id = ntohl (table_id);
-  if (mp->subnet.is_ip6)
-    clib_memcpy (&mp->subnet.address,
-                &pfx->fp_addr.ip6, sizeof (pfx->fp_addr.ip6));
-  else
-    clib_memcpy (&mp->subnet.address,
-                &pfx->fp_addr.ip4, sizeof (pfx->fp_addr.ip4));
-
+  ip_prefix_encode (pfx, &mp->subnet.prefix);
 
   vl_api_send_msg (ctx->reg, (u8 *) mp);
 
index fdb1e9f..53389f5 100644 (file)
@@ -75,6 +75,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;
          vlib_buffer_t *b0;
 
          bi0 = from[0];
@@ -97,7 +98,8 @@ gbp_classify_inline (vlib_main_t * vm,
            }
          else
            {
-             src_epg = gbp_port_to_epg (sw_if_index0);
+             gep0 = gbp_endpoint_get_itf (sw_if_index0);
+             src_epg = gep0->ge_epg_id;
              if (is_l3)
                {
                  /*
index 0522f61..a261527 100644 (file)
 #include <vnet/l2/l2_output.h>
 #include <vnet/l2/feat_bitmap.h>
 
-/**
- * IP4 destintion address to destination EPG mapping table
- */
-typedef struct gbp_ip4_to_epg_db_t_
-{
-  /**
-   * use a simple hash table
-   */
-  uword *g4ie_hash;
-} gbp_ip4_to_epg_db_t;
-
-static gbp_ip4_to_epg_db_t gbp_ip4_to_epg_db;
+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;
 
 /**
- * IP6 destintion address to destination EPG mapping table
+ * Pool of GBP endpoints
  */
-typedef struct gbp_ip6_to_epg_db_t_
+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)
 {
-  /**
-   * use a memroy hash table
-   */
-  uword *g6ie_hash;
-} gbp_ip6_to_epg_db_t;
+  key->key[0] = mac_address_as_u64 (mac);
+  key->key[1] = sw_if_index;
+}
 
-static gbp_ip6_to_epg_db_t gbp_ip6_to_epg_db;
+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];
+}
 
+gbp_endpoint_t *
+gbp_endpoint_find_mac_itf (const mac_address_t * mac, u32 sw_if_index)
+{
+  clib_bihash_kv_16_8_t key, value;
+  int rv;
 
-const static gbp_itf_t ITF_INVALID = {
-  .gi_epg = EPG_INVALID,
-  .gi_ref_count = 0,
-};
+  gbp_endpoint_mk_key_mac_itf (mac, sw_if_index, &key);
 
-gbp_itf_to_epg_db_t gbp_itf_to_epg_db;
+  rv =
+    clib_bihash_search_16_8 (&gbp_ep_by_mac_itf_db.gte_table, &key, &value);
 
-/**
- * Pool of GBP endpoints
- */
-static gbp_endpoint_t *gbp_endpoint_pool;
+  if (0 != rv)
+    return NULL;
 
-/**
- * DB of endpoints
- */
-static uword *gbp_endpoint_db;
+  return (gbp_endpoint_get (value.value));
+}
 
 static void
-gbp_ip_epg_update (const ip46_address_t * ip, epg_id_t epg_id)
+gbp_endpoint_mk_key_ip_itf (const ip46_address_t * ip,
+                           u32 sw_if_index, clib_bihash_kv_24_8_t * key)
 {
-  /*
-   * we are dealing only with addresses here so this limited
-   * is_ip4 check is ok
-   */
-  if (ip46_address_is_ip4 (ip))
-    {
-      hash_set (gbp_ip4_to_epg_db.g4ie_hash, ip->ip4.as_u32, epg_id);
-    }
-  else
-    {
-      hash_set_mem (gbp_ip6_to_epg_db.g6ie_hash, &ip->ip6, epg_id);
-    }
+  key->key[0] = ip->as_u64[0];
+  key->key[1] = ip->as_u64[1];
+  key->key[2] = sw_if_index;
 }
 
 static void
-gbp_ip_epg_delete (const ip46_address_t * ip)
+gbp_endpoint_extract_key_ip_itf (const clib_bihash_kv_24_8_t * key,
+                                ip46_address_t * ip, u32 * sw_if_index)
 {
-  if (ip46_address_is_ip4 (ip))
-    {
-      hash_unset (gbp_ip4_to_epg_db.g4ie_hash, ip->ip4.as_u32);
-    }
-  else
-    {
-      hash_unset_mem (gbp_ip6_to_epg_db.g6ie_hash, &ip->ip6);
-    }
+  ip->as_u64[0] = key->key[0];
+  ip->as_u64[1] = key->key[1];
+  *sw_if_index = key->key[2];
 }
 
-void
-gbp_itf_epg_update (u32 sw_if_index, epg_id_t src_epg, u8 do_policy)
+gbp_endpoint_t *
+gbp_endpoint_find_ip_itf (const ip46_address_t * ip, u32 sw_if_index)
 {
-  vec_validate_init_empty (gbp_itf_to_epg_db.gte_vec,
-                          sw_if_index, ITF_INVALID);
+  clib_bihash_kv_24_8_t key, value;
+  int rv;
+
+  gbp_endpoint_mk_key_ip_itf (ip, sw_if_index, &key);
+
+  rv = clib_bihash_search_24_8 (&gbp_ep_by_ip_itf_db.gte_table, &key, &value);
+
+  if (0 != rv)
+    return NULL;
+
+  return (gbp_endpoint_get (value.value));
+}
+
+gbp_endpoint_t *
+gbp_endpoint_find_itf (u32 sw_if_index)
+{
+  /* if (vec_len(gbp_ep_by_itf_db.gte_vec) >= sw_if_index) */
+  /*   return NULL; */
+
+  /* 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);
+}
+
+static bool
+gbp_endpoint_add_mac_itf (const mac_address_t * mac,
+                         u32 sw_if_index, index_t gbpei)
+{
+  clib_bihash_kv_16_8_t key;
+  int rv;
+
+  gbp_endpoint_mk_key_mac_itf (mac, sw_if_index, &key);
+  key.value = gbpei;
 
-  if (0 == gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count)
+  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)
+{
+  clib_bihash_kv_24_8_t key;
+  int rv;
+
+  gbp_endpoint_mk_key_ip_itf (ip, sw_if_index, &key);
+  key.value = gbpei;
+
+  rv = clib_bihash_add_del_24_8 (&gbp_ep_by_ip_itf_db.gte_table, &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);
-      if (do_policy)
-       l2output_intf_bitmap_enable (sw_if_index, L2OUTPUT_FEAT_GBP_POLICY,
-                                    1);
+      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++;
+  gbp_ep_by_itf_db.gte_vec[sw_if_index] = gbpei;
 }
 
-void
-gbp_itf_epg_delete (u32 sw_if_index)
+static void
+gbp_endpoint_del_mac_itf (const mac_address_t * mac, u32 sw_if_index)
+{
+  clib_bihash_kv_16_8_t key;
+
+  gbp_endpoint_mk_key_mac_itf (mac, sw_if_index, &key);
+
+  clib_bihash_add_del_16_8 (&gbp_ep_by_mac_itf_db.gte_table, &key, 0);
+}
+
+static void
+gbp_endpoint_del_ip_itf (const ip46_address_t * ip, u32 sw_if_index)
+{
+  clib_bihash_kv_24_8_t key;
+
+  gbp_endpoint_mk_key_ip_itf (ip, sw_if_index, &key);
+
+  clib_bihash_add_del_24_8 (&gbp_ep_by_ip_itf_db.gte_table, &key, 0);
+}
+
+static void
+gbp_endpoint_del_itf (u32 sw_if_index)
 {
-  if (vec_len (gbp_itf_to_epg_db.gte_vec) <= sw_if_index)
+  if (vec_len (gbp_ep_by_itf_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);
 
-      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--;
+  gbp_ep_by_itf_db.gte_vec[sw_if_index] = INDEX_INVALID;
+}
+
+static index_t
+gbp_endpoint_index (const gbp_endpoint_t * gbpe)
+{
+  return (gbpe - gbp_endpoint_pool);
 }
 
 int
 gbp_endpoint_update (u32 sw_if_index,
-                    const ip46_address_t * ip, epg_id_t epg_id)
+                    const ip46_address_t * ips,
+                    const mac_address_t * mac, epg_id_t epg_id, u32 * handle)
 {
-  gbp_endpoint_key_t key = {
-    .gek_ip = *ip,
-    .gek_sw_if_index = sw_if_index,
-  };
   gbp_endpoint_group_t *gepg;
+  const ip46_address_t *ip;
   gbp_endpoint_t *gbpe;
-  uword *p;
 
+  gbpe = NULL;
   gepg = gbp_endpoint_group_find (epg_id);
 
   if (NULL == gepg)
     return (VNET_API_ERROR_NO_SUCH_ENTRY);
 
-  p = hash_get_mem (gbp_endpoint_db, &key);
-
-  if (p)
+  /*
+   * find an existing endpoint matching one of the key types
+   */
+  if (NULL != mac)
     {
-      gbpe = pool_elt_at_index (gbp_endpoint_pool, p[0]);
+      gbpe = gbp_endpoint_find_mac_itf (mac, sw_if_index);
     }
-  else
+  if (NULL == gbpe && NULL != ips)
     {
-      pool_get (gbp_endpoint_pool, gbpe);
-
-      gbpe->ge_key = clib_mem_alloc (sizeof (gbp_endpoint_key_t));
-      clib_memcpy (gbpe->ge_key, &key, sizeof (gbp_endpoint_key_t));
+      vec_foreach (ip, ips)
+      {
+       gbpe = gbp_endpoint_find_ip_itf (ip, sw_if_index);
 
-      hash_set_mem (gbp_endpoint_db, gbpe->ge_key, gbpe - gbp_endpoint_pool);
+       if (NULL != gbpe)
+         break;
+      }
+    }
+  if (NULL == gbpe)
+    {
+      gbpe = gbp_endpoint_find_itf (sw_if_index);
     }
 
-  gbpe->ge_epg_id = epg_id;
-
-  gbp_itf_epg_update (gbpe->ge_key->gek_sw_if_index, gbpe->ge_epg_id, 1);
-
-  if (!ip46_address_is_zero (&gbpe->ge_key->gek_ip))
-    gbp_ip_epg_update (&gbpe->ge_key->gek_ip, gbpe->ge_epg_id);
-
-  /*
-   * 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 (ip46_address_is_ip4 (&gbpe->ge_key->gek_ip))
-    send_ip4_garp_w_addr (vlib_get_main (),
-                         &gbpe->ge_key->gek_ip.ip4,
-                         gepg->gepg_uplink_sw_if_index);
+  if (NULL == gbpe)
+    {
+      index_t gbpei;
+      u32 ii;
+      /*
+       * new entry
+       */
+      pool_get (gbp_endpoint_pool, gbpe);
+      gbpei = gbp_endpoint_index (gbpe);
+
+      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)
+       {
+         gbpe->ge_mac = *mac;
+
+         // FIXME ERROR
+         gbp_endpoint_add_mac_itf (mac, sw_if_index, gbpei);
+       }
+
+      if (NULL != ips)
+       {
+         vec_validate (gbpe->ge_ips, vec_len (ips) - 1);
+         vec_foreach_index (ii, ips)
+         {
+           ip46_address_copy (&gbpe->ge_ips[ii], &ips[ii]);
+
+           // FIXME ERROR
+           gbp_endpoint_add_ip_itf (&ips[ii], sw_if_index, gbpei);
+
+           /*
+            * 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 (ip46_address_is_ip4 (&ips[ii]))
+             send_ip4_garp_w_addr (vlib_get_main (),
+                                   &ips[ii].ip4,
+                                   gepg->gepg_uplink_sw_if_index);
+           else
+             send_ip6_na_w_addr (vlib_get_main (),
+                                 &ips[ii].ip6,
+                                 gepg->gepg_uplink_sw_if_index);
+         }
+       }
+    }
   else
-    send_ip6_na_w_addr (vlib_get_main (),
-                       &gbpe->ge_key->gek_ip.ip6,
-                       gepg->gepg_uplink_sw_if_index);
+    {
+      /*
+       * update existing entry..
+       */
+      ASSERT (0);
+    }
+
+  *handle = (gbpe - gbp_endpoint_pool);
 
   return (0);
 }
 
 void
-gbp_endpoint_delete (u32 sw_if_index, const ip46_address_t * ip)
+gbp_endpoint_delete (u32 handle)
 {
-  gbp_endpoint_key_t key = {
-    .gek_ip = *ip,
-    .gek_sw_if_index = sw_if_index,
-  };
   gbp_endpoint_t *gbpe;
-  uword *p;
 
-  p = hash_get_mem (gbp_endpoint_db, &key);
+  if (pool_is_free_index (gbp_endpoint_pool, handle))
+    return;
 
-  if (p)
-    {
-      gbpe = pool_elt_at_index (gbp_endpoint_pool, p[0]);
+  gbpe = pool_elt_at_index (gbp_endpoint_pool, handle);
 
-      hash_unset_mem (gbp_endpoint_db, gbpe->ge_key);
+  gbp_endpoint_del_itf (gbpe->ge_sw_if_index);
 
-      gbp_itf_epg_delete (gbpe->ge_key->gek_sw_if_index);
-      if (!ip46_address_is_zero (&gbpe->ge_key->gek_ip))
-       gbp_ip_epg_delete (&gbpe->ge_key->gek_ip);
+  if (!mac_address_is_zero (&gbpe->ge_mac))
+    {
+      gbp_endpoint_del_mac_itf (&gbpe->ge_mac, gbpe->ge_sw_if_index);
+    }
 
-      clib_mem_free (gbpe->ge_key);
+  if (NULL != gbpe->ge_ips)
+    {
+      const ip46_address_t *ip;
 
-      pool_put (gbp_endpoint_pool, gbpe);
+      vec_foreach (ip, gbpe->ge_ips)
+      {
+       gbp_endpoint_del_ip_itf (ip, gbpe->ge_sw_if_index);
+      }
     }
+  pool_put (gbp_endpoint_pool, gbpe);
 }
 
 void
@@ -237,14 +377,19 @@ static clib_error_t *
 gbp_endpoint_cli (vlib_main_t * vm,
                  unformat_input_t * input, vlib_cli_command_t * cmd)
 {
+  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;
-  ip46_address_t ip = { };
+  u32 handle = INDEX_INVALID;
   u32 sw_if_index = ~0;
   u8 add = 1;
+  int rv;
 
   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     {
+      ip46_address_reset (&ip);
+
       if (unformat (input, "%U", unformat_vnet_sw_interface,
                    vnm, &sw_if_index))
        ;
@@ -254,25 +399,41 @@ gbp_endpoint_cli (vlib_main_t * vm,
        add = 0;
       else if (unformat (input, "epg %d", &epg_id))
        ;
-      else if (unformat (input, "ip %U", unformat_ip4_address, &ip.ip4))
+      else if (unformat (input, "handle %d", &handle))
        ;
+      else if (unformat (input, "ip %U", unformat_ip4_address, &ip.ip4))
+       vec_add1 (ips, ip);
       else if (unformat (input, "ip %U", unformat_ip6_address, &ip.ip6))
+       vec_add1 (ips, ip);
+      else if (unformat (input, "mac %U", unformat_mac_address, &mac))
        ;
       else
        break;
     }
 
-  if (~0 == sw_if_index)
-    return clib_error_return (0, "interface must be specified");
-  if (EPG_INVALID == epg_id)
-    return clib_error_return (0, "EPG-ID must be specified");
-  if (ip46_address_is_zero (&ip))
-    return clib_error_return (0, "IP address must be specified");
-
   if (add)
-    gbp_endpoint_update (sw_if_index, &ip, epg_id);
+    {
+      if (~0 == sw_if_index)
+       return clib_error_return (0, "interface must be specified");
+      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);
+
+      if (rv)
+       return clib_error_return (0, "GBP Endpoint update returned %d", rv);
+      else
+       vlib_cli_output (vm, "handle %d\n", handle);
+    }
   else
-    gbp_endpoint_delete (sw_if_index, &ip);
+    {
+      if (INDEX_INVALID == handle)
+       return clib_error_return (0, "handle must be specified");
+
+      gbp_endpoint_delete (handle);
+    }
+
+  vec_free (ips);
 
   return (NULL);
 }
@@ -288,75 +449,132 @@ gbp_endpoint_cli (vlib_main_t * vm,
 /* *INDENT-OFF* */
 VLIB_CLI_COMMAND (gbp_endpoint_cli_node, static) = {
   .path = "gbp endpoint",
-  .short_help = "gbp endpoint [del] <interface> epg <ID> ip <IP>",
+  .short_help = "gbp endpoint [del] <interface> epg <ID> ip <IP> mac <MAC>",
   .function = gbp_endpoint_cli,
 };
 /* *INDENT-ON* */
 
-static int
-gbp_endpoint_show_one (gbp_endpoint_t * gbpe, void *ctx)
+u8 *
+format_gbp_endpoint (u8 * s, va_list * args)
 {
+  index_t gbpei = va_arg (*args, index_t);
   vnet_main_t *vnm = vnet_get_main ();
+  const ip46_address_t *ip;
+  gbp_endpoint_t *gbpe;
+
+  gbpe = gbp_endpoint_get (gbpei);
+
+  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:[");
+
+  vec_foreach (ip, gbpe->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);
+
+  return s;
+}
+
+static walk_rc_t
+gbp_endpoint_show_one (gbp_endpoint_t * gbpe, void *ctx)
+{
   vlib_main_t *vm;
 
   vm = ctx;
-  vlib_cli_output (vm, "  {%U, %U} -> %d",
-                  format_vnet_sw_if_index_name, vnm,
-                  gbpe->ge_key->gek_sw_if_index,
-                  format_ip46_address, &gbpe->ge_key->gek_ip, IP46_TYPE_ANY,
-                  gbpe->ge_epg_id);
+  vlib_cli_output (vm, " %U", format_gbp_endpoint, gbp_endpoint_index (gbpe));
 
-  return (1);
+  return (WALK_CONTINUE);
 }
 
-static clib_error_t *
-gbp_endpoint_show (vlib_main_t * vm,
-                  unformat_input_t * input, vlib_cli_command_t * cmd)
+static void
+gbp_endpoint_walk_ip_itf (const clib_bihash_kv_24_8_t * kvp, void *arg)
 {
-  vnet_main_t *vnm = vnet_get_main ();
-  ip46_address_t ip, *ipp;
-  epg_id_t epg_id;
+  ip46_address_t ip;
+  vlib_main_t *vm;
   u32 sw_if_index;
 
-  vlib_cli_output (vm, "Endpoints:");
-  gbp_endpoint_walk (gbp_endpoint_show_one, vm);
+  vm = arg;
 
-  vlib_cli_output (vm, "\nSource interface to EPG:");
+  gbp_endpoint_extract_key_ip_itf (kvp, &ip, &sw_if_index);
 
-  vec_foreach_index (sw_if_index, gbp_itf_to_epg_db.gte_vec)
-  {
-    if (EPG_INVALID != gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg)
-      {
-       vlib_cli_output (vm, "  %U -> %d",
-                        format_vnet_sw_if_index_name, vnm, sw_if_index,
-                        gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg);
-      }
-  }
+  vlib_cli_output (vm, " {%U, %U} -> %d",
+                  format_ip46_address, &ip, IP46_TYPE_ANY,
+                  format_vnet_sw_if_index_name, vnet_get_main (),
+                  sw_if_index, kvp->value);
+}
 
-  vlib_cli_output (vm, "\nDestination IP4 to EPG:");
+static void
+gbp_endpoint_walk_mac_itf (const clib_bihash_kv_16_8_t * kvp, void *arg)
+{
+  mac_address_t mac;
+  vlib_main_t *vm;
+  u32 sw_if_index;
 
-  /* *INDENT-OFF* */
-  hash_foreach (ip.ip4.as_u32, epg_id, gbp_ip4_to_epg_db.g4ie_hash,
-  {
-    vlib_cli_output (vm, "  %U -> %d", format_ip46_address, &ip,
-                     IP46_TYPE_IP4, epg_id);
-  });
-  /* *INDENT-ON* */
+  vm = arg;
 
-  vlib_cli_output (vm, "\nDestination IP6 to EPG:");
+  gbp_endpoint_extract_key_mac_itf (kvp, &mac, &sw_if_index);
 
-  /* *INDENT-OFF* */
-  hash_foreach_mem (ipp, epg_id, gbp_ip6_to_epg_db.g6ie_hash,
-  {
-    vlib_cli_output (vm, "  %U -> %d", format_ip46_address, ipp,
-                     IP46_TYPE_IP6, epg_id);
-  });
-  /* *INDENT-ON* */
+  vlib_cli_output (vm, " {%U, %U} -> %d",
+                  format_mac_address_t, &mac,
+                  format_vnet_sw_if_index_name, vnet_get_main (),
+                  sw_if_index, kvp->value);
+}
+
+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;
+
+  handle = INDEX_INVALID;
+  show_dbs = 0;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "%d", &handle))
+       ;
+      else if (unformat (input, "db", &handle))
+       show_dbs = 1;
+      else
+       break;
+    }
+
+  if (INDEX_INVALID != handle)
+    {
+      vlib_cli_output (vm, "%U", format_gbp_endpoint, handle);
+    }
+  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,
+                                              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]);
+      }
+    }
+  else
+    {
+      vlib_cli_output (vm, "Endpoints:");
+      gbp_endpoint_walk (gbp_endpoint_show_one, vm);
+    }
 
   return (NULL);
 }
 
-
 /*?
  * Show Group Based Policy Endpoints and derived information
  *
@@ -372,15 +590,21 @@ VLIB_CLI_COMMAND (gbp_endpoint_show_node, static) = {
 };
 /* *INDENT-ON* */
 
+#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)
 {
-  gbp_endpoint_db = hash_create_mem (0,
-                                    sizeof (gbp_endpoint_key_t),
-                                    sizeof (u32));
-  gbp_ip6_to_epg_db.g6ie_hash =
-    hash_create_mem (0, sizeof (ip6_address_t), sizeof (u32));
-  return 0;
+  clib_bihash_init_24_8 (&gbp_ep_by_ip_itf_db.gte_table,
+                        "GBP Endpoints - IP/Interface",
+                        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",
+                        GBP_EP_HASH_NUM_BUCKETS, GBP_EP_HASH_MEMORY_SIZE);
+
+  return (NULL);
 }
 
 VLIB_INIT_FUNCTION (gbp_endpoint_init);
index 000c211..275897d 100644 (file)
 
 #include <plugins/gbp/gbp_types.h>
 #include <vnet/ip/ip.h>
+#include <vnet/ethernet/mac_address.h>
+
+#include <vppinfra/bihash_16_8.h>
+#include <vppinfra/bihash_template.h>
+#include <vppinfra/bihash_24_8.h>
+#include <vppinfra/bihash_template.h>
 
 /**
- * The key for an Endpoint
+ * Flags for each endpoint
  */
-typedef struct gbp_endpoint_key_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_flags_t;
+
+/**
+ * A Group Based Policy Endpoint.
+ * This is typcially a VM or container. If the endpoint is local (i.e. on
+ * the smae compute node as VPP) then there is one interface per-endpoint.
+ * If the EP is remote,e.g. reachable onver a [vxlan] tunnel, then there
+ * will be multiple EPs reachable over the tunnel and they can be distingusihed
+ * via either their MAC or IP Address[es].
+ */
+typedef struct gbp_endpoint_t_
 {
   /**
    * The interface on which the EP is connected
    */
-  u32 gek_sw_if_index;
+  u32 ge_sw_if_index;
 
   /**
-   * The IP[46] address of the endpoint
+   * A vector of ip addresses that below to the endpoint
    */
-  ip46_address_t gek_ip;
-} gbp_endpoint_key_t;
+  ip46_address_t *ge_ips;
 
-/**
- * A Group Based Policy Endpoint.
- * This is typcially a VM on the local compute node for which policy must be
- * locally applied
- */
-typedef struct gbp_endpoint_t_
-{
   /**
-   * The endpoint's interface and IP address
+   * MAC address of the endpoint
    */
-  gbp_endpoint_key_t *ge_key;
+  mac_address_t ge_mac;
 
   /**
    * The endpoint's designated EPG
    */
   epg_id_t ge_epg_id;
+
+  /**
+   * Endpoint flags
+   */
+  gbp_endpoint_flags_t ge_flags;
 } gbp_endpoint_t;
 
-/**
- * Result of a interface to EPG mapping.
- * multiple Endpoints can occur on the same interface, so this
- * mapping needs to be reference counted.
- */
-typedef struct gbp_itf_t_
-{
-  epg_id_t gi_epg;
-  u32 gi_ref_count;
-} gbp_itf_t;
+extern u8 *format_gbp_endpoint (u8 * s, va_list * args);
 
 /**
  * Interface to source EPG DB - a per-interface vector
  */
-typedef struct gbp_itf_to_epg_db_t_
+typedef struct gbp_ep_by_itf_db_t_
 {
-  gbp_itf_t *gte_vec;
-} gbp_itf_to_epg_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;
 
 extern int gbp_endpoint_update (u32 sw_if_index,
-                               const ip46_address_t * ip, epg_id_t epg_id);
-extern void gbp_endpoint_delete (u32 sw_if_index, const ip46_address_t * ip);
+                               const ip46_address_t * ip,
+                               const mac_address_t * mac,
+                               epg_id_t epg_id, u32 * handle);
+extern void gbp_endpoint_delete (u32 handle);
 
-typedef int (*gbp_endpoint_cb_t) (gbp_endpoint_t * gbpe, void *ctx);
+typedef walk_rc_t (*gbp_endpoint_cb_t) (gbp_endpoint_t * gbpe, void *ctx);
 extern void gbp_endpoint_walk (gbp_endpoint_cb_t cb, void *ctx);
 
-/**
- * Port to EPG mapping management
- */
-extern void gbp_itf_epg_update (u32 sw_if_index, epg_id_t src_epg,
-                               u8 do_policy);
-extern void gbp_itf_epg_delete (u32 sw_if_index);
 
 /**
  * DP functions and databases
  */
-extern gbp_itf_to_epg_db_t gbp_itf_to_epg_db;
+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_endpoint_t *gbp_endpoint_pool;
 
 /**
- * Get the source EPG for a port/interface
+ * Get the endpoint from a port/interface
  */
-always_inline u32
-gbp_port_to_epg (u32 sw_if_index)
+always_inline gbp_endpoint_t *
+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)
 {
-  return (gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg);
+  return (gbp_endpoint_get (gbp_ep_by_itf_db.gte_vec[sw_if_index]));
 }
 
 #endif
index c5c0666..27d404e 100644 (file)
@@ -209,7 +209,7 @@ gbp_endpoint_group_cli (vlib_main_t * vm,
 /* *INDENT-OFF* */
 VLIB_CLI_COMMAND (gbp_endpoint_group_cli_node, static) = {
   .path = "gbp endpoint-group",
-  .short_help = "gbp endpoint-group [del] epg <ID> bd <ID> <interface>",
+  .short_help = "gbp endpoint-group [del] epg <ID> bd <ID> rd <ID> <interface>",
   .function = gbp_endpoint_group_cli,
 };
 
index c0a8866..f57aa07 100644 (file)
@@ -85,6 +85,7 @@ gbp_policy (vlib_main_t * vm,
 
       while (n_left_from > 0 && n_left_to_next > 0)
        {
+         const gbp_endpoint_t *gep0;
          gbp_policy_next_t next0;
          gbp_contract_key_t key0;
          gbp_contract_value_t value0 = {
@@ -107,7 +108,8 @@ gbp_policy (vlib_main_t * vm,
           * determine the src and dst EPG
           */
          sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX];
-         key0.gck_dst = gbp_port_to_epg (sw_if_index0);
+         gep0 = gbp_endpoint_get_itf (sw_if_index0);
+         key0.gck_dst = gep0->ge_epg_id;
          key0.gck_src = vnet_buffer2 (b0)->gbp.src_epg;
 
          if (EPG_INVALID != key0.gck_src)
index 1debbe4..c44fc21 100644 (file)
@@ -85,7 +85,8 @@ gbp_recirc_add (u32 sw_if_index, epg_id_t epg_id, u8 is_ext)
           * the external EPG, these are classified to the NAT EPG
           * based on its port
           */
-         gbp_itf_epg_update (gr->gr_sw_if_index, gr->gr_epg, 0);
+         gbp_endpoint_update (gr->gr_sw_if_index,
+                              NULL, NULL, gr->gr_epg, &gr->gr_ep);
          vnet_feature_enable_disable ("ip4-unicast",
                                       "ip4-gbp-src-classify",
                                       gr->gr_sw_if_index, 1, 0, 0);
@@ -128,7 +129,7 @@ gbp_recirc_delete (u32 sw_if_index)
 
       if (gr->gr_is_ext)
        {
-         gbp_itf_epg_delete (gr->gr_sw_if_index);
+         gbp_endpoint_delete (gr->gr_ep);
          vnet_feature_enable_disable ("ip4-unicast",
                                       "ip4-gbp-src-classify",
                                       gr->gr_sw_if_index, 0, 0, 0);
index 10f3da6..04a2378 100644 (file)
@@ -44,6 +44,10 @@ typedef struct gpb_recirc_t_
    */
   u32 gr_sw_if_index;
 
+  /**
+   * The endpoint created to represent the reric interface
+   */
+  index_t gr_ep;
 } gbp_recirc_t;
 
 extern int gbp_recirc_add (u32 sw_if_index, epg_id_t epg_id, u8 is_ext);
index b1c9be4..6207400 100644 (file)
@@ -93,6 +93,13 @@ typedef CLIB_PACKED (union {
 #define ip46_address_is_zero(ip46)     (((ip46)->as_u64[0] == 0) && ((ip46)->as_u64[1] == 0))
 #define ip46_address_is_equal(a1, a2)  (((a1)->as_u64[0] == (a2)->as_u64[0]) \
                                          && ((a1)->as_u64[1] == (a2)->as_u64[1]))
+static_always_inline void
+ip46_address_copy (ip46_address_t * dst, const ip46_address_t * src)
+{
+  dst->as_u64[0] = src->as_u64[0];
+  dst->as_u64[1] = src->as_u64[1];
+}
+
 #define ip46_address_initializer {{{ 0 }}}
 
 always_inline ip46_address_t
index ce53e53..fb30fc3 100644 (file)
@@ -2,7 +2,8 @@
 
 from framework import VppTestCase, VppTestRunner
 from vpp_udp_encap import *
-from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable, DpoProto
+from vpp_ip import DpoProto
+from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable
 
 from scapy.packet import Raw
 from scapy.layers.l2 import Ether, ARP
index b3ac635..f14ff71 100644 (file)
@@ -20,7 +20,8 @@ from vpp_pg_interface import CaptureTimeoutError, is_ipv6_misc
 from vpp_lo_interface import VppLoInterface
 from util import ppp
 from vpp_papi_provider import UnexpectedApiReturnValueError
-from vpp_ip_route import VppIpRoute, VppRoutePath, DpoProto
+from vpp_ip import DpoProto
+from vpp_ip_route import VppIpRoute, VppRoutePath
 
 USEC_IN_SEC = 1000000
 
index c4f64bd..0276f95 100644 (file)
@@ -4,9 +4,10 @@ import unittest
 import socket
 
 from framework import VppTestCase, VppTestRunner, running_extended_tests
+from vpp_ip import DpoProto
 from vpp_ip_route import VppIpRoute, VppRoutePath, VppMplsRoute, \
     VppMplsTable, VppIpMRoute, VppMRoutePath, VppIpTable, \
-    MRouteEntryFlags, MRouteItfFlags, MPLS_LABEL_INVALID, DpoProto, \
+    MRouteEntryFlags, MRouteItfFlags, MPLS_LABEL_INVALID, \
     VppMplsLabel
 from vpp_bier import *
 from vpp_udp_encap import *
index 894690b..0d5dd15 100644 (file)
@@ -5,7 +5,10 @@ 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, DpoProto
+from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable
+
+from vpp_ip import *
+from vpp_mac import *
 
 from scapy.packet import Raw
 from scapy.layers.l2 import Ether, ARP
@@ -19,6 +22,19 @@ 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)
+
+    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:
+                return True
+    return False
+
+
 class VppGbpEndpoint(VppObject):
     """
     GBP Endpoint
@@ -32,64 +48,67 @@ class VppGbpEndpoint(VppObject):
     def mac(self):
         return self.itf.remote_mac
 
-    def __init__(self, test, itf, epg, recirc, ip, fip, is_ip6=False):
+    @property
+    def ip4(self):
+        return self._ip4
+
+    @property
+    def fip4(self):
+        return self._fip4
+
+    @property
+    def ip6(self):
+        return self._ip6
+
+    @property
+    def fip6(self):
+        return self._fip6
+
+    @property
+    def ips(self):
+        return [self.ip4, self.ip6]
+
+    @property
+    def fips(self):
+        return [self.fip4, self.fip6]
+
+    def __init__(self, test, itf, epg, recirc, ip4, fip4, ip6, fip6):
         self._test = test
         self.itf = itf
         self.epg = epg
         self.recirc = recirc
-        self.ip = ip
-        self.floating_ip = fip
-        self.is_ip6 = is_ip6
-        if is_ip6:
-            self.proto = DpoProto.DPO_PROTO_IP6
-            self.af = AF_INET6
-            self.is_ip6 = True
-            self.ip_len = 128
-        else:
-            self.proto = DpoProto.DPO_PROTO_IP4
-            self.af = AF_INET
-            self.is_ip6 = False
-            self.ip_len = 32
-        self.ip_n = inet_pton(self.af, ip)
-        self.floating_ip_n = inet_pton(self.af, fip)
+
+        self._ip4 = VppIpAddress(ip4)
+        self._fip4 = VppIpAddress(fip4)
+        self._ip6 = VppIpAddress(ip6)
+        self._fip6 = VppIpAddress(fip6)
+
+        self.vmac = VppMacAddress(self.itf.remote_mac)
 
     def add_vpp_config(self):
-        self._test.vapi.gbp_endpoint_add_del(
-            1,
+        res = self._test.vapi.gbp_endpoint_add(
             self.itf.sw_if_index,
-            self.ip_n,
-            self.is_ip6,
+            [self.ip4.encode(), self.ip6.encode()],
+            self.vmac.encode(),
             self.epg.epg)
+        self.handle = res.handle
         self._test.registry.register(self, self._test.logger)
 
     def remove_vpp_config(self):
-        self._test.vapi.gbp_endpoint_add_del(
-            0,
-            self.itf.sw_if_index,
-            self.ip_n,
-            self.is_ip6,
-            self.epg.epg)
+        self._test.vapi.gbp_endpoint_del(self.handle)
 
     def __str__(self):
         return self.object_id()
 
     def object_id(self):
         return "gbp-endpoint;[%d:%s:%d]" % (self.itf.sw_if_index,
-                                            self.ip,
+                                            self.ip4.address,
                                             self.epg.epg)
 
     def query_vpp_config(self):
-        eps = self._test.vapi.gbp_endpoint_dump()
-        for ep in eps:
-            if self.is_ip6:
-                if ep.endpoint.address == self.ip_n \
-                   and ep.endpoint.sw_if_index == self.itf.sw_if_index:
-                    return True
-            else:
-                if ep.endpoint.address[:4] == self.ip_n \
-                   and ep.endpoint.sw_if_index == self.itf.sw_if_index:
-                    return True
-        return False
+        return find_gbp_endpoint(self._test,
+                                 self.itf.sw_if_index,
+                                 self.ip4.address)
 
 
 class VppGbpRecirc(VppObject):
@@ -138,17 +157,11 @@ class VppGbpSubnet(VppObject):
     """
 
     def __init__(self, test, table_id, address, address_len,
-                 is_internal=True, is_ip6=False,
+                 is_internal=True,
                  sw_if_index=None, epg=None):
         self._test = test
         self.table_id = table_id
-        self.address = address
-        self.address_len = address_len
-        self.is_ip6 = is_ip6
-        if is_ip6:
-            self.address_n = inet_pton(AF_INET6, address)
-        else:
-            self.address_n = inet_pton(AF_INET, address)
+        self.prefix = VppIpPrefix(address, address_len)
         self.is_internal = is_internal
         self.sw_if_index = sw_if_index
         self.epg = epg
@@ -158,11 +171,9 @@ class VppGbpSubnet(VppObject):
             1,
             self.table_id,
             self.is_internal,
-            self.address_n,
-            self.address_len,
+            self.prefix.encode(),
             sw_if_index=self.sw_if_index if self.sw_if_index else 0xffffffff,
-            epg_id=self.epg if self.epg else 0xffff,
-            is_ip6=self.is_ip6)
+            epg_id=self.epg if self.epg else 0xffff)
         self._test.registry.register(self, self._test.logger)
 
     def remove_vpp_config(self):
@@ -170,30 +181,21 @@ class VppGbpSubnet(VppObject):
             0,
             self.table_id,
             self.is_internal,
-            self.address_n,
-            self.address_len,
-            is_ip6=self.is_ip6)
+            self.prefix.encode())
 
     def __str__(self):
         return self.object_id()
 
     def object_id(self):
-        return "gbp-subnet;[%d:%s/%d]" % (self.table_id,
-                                          self.address,
-                                          self.address_len)
+        return "gbp-subnet;[%d-%s]" % (self.table_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 \
-               s.subnet.address_length == self.address_len and \
-               s.subnet.is_ip6 == self.is_ip6:
-                if self.is_ip6:
-                    if s.subnet.address == self.address_n:
-                        return True
-                else:
-                    if s.subnet.address[:4] == self.address_n:
-                        return True
+               s.subnet.prefix == self.prefix:
+                return True
         return False
 
 
@@ -481,43 +483,38 @@ class TestGBP(VppTestCase):
 
         #
         # 3 EPGs, 2 of which share a BD.
-        #
-        epgs = []
-        recircs = []
-        epgs.append(VppGbpEndpointGroup(self, 220, 0, 1, self.pg4,
-                                        self.loop0,
-                                        "10.0.0.128",
-                                        "2001:10::128"))
-        recircs.append(VppGbpRecirc(self, epgs[0],
-                                    self.loop3))
-        epgs.append(VppGbpEndpointGroup(self, 221, 0, 1, self.pg5,
-                                        self.loop0,
-                                        "10.0.1.128",
-                                        "2001:10:1::128"))
-        recircs.append(VppGbpRecirc(self, epgs[1],
-                                    self.loop4))
-        epgs.append(VppGbpEndpointGroup(self, 222, 0, 2, self.pg6,
-                                        self.loop1,
-                                        "10.0.2.128",
-                                        "2001:10:2::128"))
-        recircs.append(VppGbpRecirc(self, epgs[2],
-                                    self.loop5))
-
-        #
         # 2 NAT EPGs, one for floating-IP subnets, the other for internet
         #
-        epgs.append(VppGbpEndpointGroup(self, 333, 20, 20, self.pg7,
-                                        self.loop2,
-                                        "11.0.0.128",
-                                        "3001::128"))
-        recircs.append(VppGbpRecirc(self, epgs[3],
-                                    self.loop6, is_ext=True))
-        epgs.append(VppGbpEndpointGroup(self, 444, 20, 20, self.pg8,
-                                        self.loop2,
-                                        "11.0.0.129",
-                                        "3001::129"))
-        recircs.append(VppGbpRecirc(self, epgs[4],
-                                    self.loop8, is_ext=True))
+        epgs = [VppGbpEndpointGroup(self, 220, 0, 1, self.pg4,
+                                    self.loop0,
+                                    "10.0.0.128",
+                                    "2001:10::128"),
+                VppGbpEndpointGroup(self, 221, 0, 1, self.pg5,
+                                    self.loop0,
+                                    "10.0.1.128",
+                                    "2001:10:1::128"),
+                VppGbpEndpointGroup(self, 222, 0, 2, self.pg6,
+                                    self.loop1,
+                                    "10.0.2.128",
+                                    "2001:10:2::128"),
+                VppGbpEndpointGroup(self, 333, 20, 20, self.pg7,
+                                    self.loop2,
+                                    "11.0.0.128",
+                                    "3001::128"),
+                VppGbpEndpointGroup(self, 444, 20, 20, self.pg8,
+                                    self.loop2,
+                                    "11.0.0.129",
+                                    "3001::129")]
+        recircs = [VppGbpRecirc(self, epgs[0],
+                                self.loop3),
+                   VppGbpRecirc(self, epgs[1],
+                                self.loop4),
+                   VppGbpRecirc(self, epgs[2],
+                                self.loop5),
+                   VppGbpRecirc(self, epgs[3],
+                                self.loop6, is_ext=True),
+                   VppGbpRecirc(self, epgs[4],
+                                self.loop8, is_ext=True)]
 
         epg_nat = epgs[3]
         recirc_nat = recircs[3]
@@ -525,43 +522,22 @@ class TestGBP(VppTestCase):
         #
         # 4 end-points, 2 in the same subnet, 3 in the same BD
         #
-        eps = []
-        eps.append(VppGbpEndpoint(self, self.pg0,
-                                  epgs[0], recircs[0],
-                                  "10.0.0.1",
-                                  "11.0.0.1"))
-        eps.append(VppGbpEndpoint(self, self.pg1,
-                                  epgs[0], recircs[0],
-                                  "10.0.0.2",
-                                  "11.0.0.2"))
-        eps.append(VppGbpEndpoint(self, self.pg2,
-                                  epgs[1], recircs[1],
-                                  "10.0.1.1",
-                                  "11.0.0.3"))
-        eps.append(VppGbpEndpoint(self, self.pg3,
-                                  epgs[2], recircs[2],
-                                  "10.0.2.1",
-                                  "11.0.0.4"))
-        eps.append(VppGbpEndpoint(self, self.pg0,
-                                  epgs[0], recircs[0],
-                                  "2001:10::1",
-                                  "3001::1",
-                                  is_ip6=True))
-        eps.append(VppGbpEndpoint(self, self.pg1,
-                                  epgs[0], recircs[0],
-                                  "2001:10::2",
-                                  "3001::2",
-                                  is_ip6=True))
-        eps.append(VppGbpEndpoint(self, self.pg2,
-                                  epgs[1], recircs[1],
-                                  "2001:10:1::1",
-                                  "3001::3",
-                                  is_ip6=True))
-        eps.append(VppGbpEndpoint(self, self.pg3,
-                                  epgs[2], recircs[2],
-                                  "2001:10:2::1",
-                                  "3001::4",
-                                  is_ip6=True))
+        eps = [VppGbpEndpoint(self, self.pg0,
+                              epgs[0], recircs[0],
+                              "10.0.0.1", "11.0.0.1",
+                              "2001:10::1", "3001::1"),
+               VppGbpEndpoint(self, self.pg1,
+                              epgs[0], recircs[0],
+                              "10.0.0.2", "11.0.0.2",
+                              "2001:10::2", "3001::2"),
+               VppGbpEndpoint(self, self.pg2,
+                              epgs[1], recircs[1],
+                              "10.0.1.1", "11.0.0.3",
+                              "2001:10:1::1", "3001::3"),
+               VppGbpEndpoint(self, self.pg3,
+                              epgs[2], recircs[2],
+                              "10.0.2.1", "11.0.0.4",
+                              "2001:10:2::1", "3001::4")]
 
         #
         # Config related to each of the EPGs
@@ -653,83 +629,91 @@ class TestGBP(VppTestCase):
             # adj-fibs due to the fact the the BVI address has /32 and
             # the subnet is not attached.
             #
-            r = VppIpRoute(self, ep.ip, ep.ip_len,
-                           [VppRoutePath(ep.ip,
-                                         ep.epg.bvi.sw_if_index,
-                                         proto=ep.proto)],
-                           is_ip6=ep.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,
-                            ep.ip, af=ep.af)
-            a.add_vpp_config()
-            ep_arps.append(a)
+            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.bin_mac,
+                                            ip=ip.bytes,
+                                            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,
+                                                           fip.bytes,
+                                                           vrf_id=0,
+                                                           addr_only=1)
+                else:
+                    self.vapi.nat66_add_del_static_mapping(ip.bytes,
+                                                           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)
 
-            # add the BD ARP termination entry
-            self.vapi.bd_ip_mac_add_del(bd_id=ep.epg.bd,
-                                        mac=ep.bin_mac,
-                                        ip=ep.ip_n,
-                                        is_ipv6=0,
-                                        is_add=1)
-
             # L2 FIB entry
             self.vapi.l2fib_add_del(ep.mac,
                                     ep.epg.bd,
                                     ep.itf.sw_if_index,
                                     is_add=1)
 
-            # Add static mappings for each EP from the 10/8 to 11/8 network
-            if ep.af == AF_INET:
-                self.vapi.nat44_add_del_static_mapping(ep.ip_n,
-                                                       ep.floating_ip_n,
-                                                       vrf_id=0,
-                                                       addr_only=1)
-            else:
-                self.vapi.nat66_add_del_static_mapping(ep.ip_n,
-                                                       ep.floating_ip_n,
-                                                       vrf_id=0)
-
             # VPP EP create ...
             ep.add_vpp_config()
 
+            self.logger.info(self.vapi.cli("sh gbp endpoint"))
+
             # ... results in a Gratuitous ARP/ND on the EPG's uplink
-            rx = ep.epg.uplink.get_capture(1, timeout=0.2)
+            rx = ep.epg.uplink.get_capture(len(ep.ips), timeout=0.2)
 
-            if ep.is_ip6:
-                self.assertTrue(rx[0].haslayer(ICMPv6ND_NA))
-                self.assertEqual(rx[0][ICMPv6ND_NA].tgt, ep.ip)
-            else:
-                self.assertTrue(rx[0].haslayer(ARP))
-                self.assertEqual(rx[0][ARP].psrc, ep.ip)
-                self.assertEqual(rx[0][ARP].pdst, ep.ip)
+            for ii, ip in enumerate(ep.ips):
+                p = rx[ii]
 
-            # add the BD ARP termination entry for floating IP
-            self.vapi.bd_ip_mac_add_del(bd_id=epg_nat.bd,
-                                        mac=ep.bin_mac,
-                                        ip=ep.floating_ip_n,
-                                        is_ipv6=ep.is_ip6,
-                                        is_add=1)
+                if ip.is_ip6:
+                    self.assertTrue(p.haslayer(ICMPv6ND_NA))
+                    self.assertEqual(p[ICMPv6ND_NA].tgt, ip.address)
+                else:
+                    self.assertTrue(p.haslayer(ARP))
+                    self.assertEqual(p[ARP].psrc, ip.address)
+                    self.assertEqual(p[ARP].pdst, ip.address)
 
-            # floating IPs route via EPG recirc
-            r = VppIpRoute(self, ep.floating_ip, ep.ip_len,
-                           [VppRoutePath(ep.floating_ip,
-                                         ep.recirc.recirc.sw_if_index,
-                                         is_dvr=1,
-                                         proto=ep.proto)],
-                           table_id=20,
-                           is_ip6=ep.is_ip6)
-            r.add_vpp_config()
-            ep_routes.append(r)
+            # 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.bin_mac,
+                                            ip=fip.bytes,
+                                            is_ipv6=fip.is_ip6,
+                                            is_add=1)
+
+                # floating IPs route via EPG recirc
+                r = VppIpRoute(self, fip.address, fip.length,
+                               [VppRoutePath(fip.address,
+                                             ep.recirc.recirc.sw_if_index,
+                                             is_dvr=1,
+                                             proto=fip.dpo_proto)],
+                               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
@@ -760,14 +744,14 @@ class TestGBP(VppTestCase):
                        hwdst="ff:ff:ff:ff:ff:ff",
                        hwsrc=self.pg0.remote_mac,
                        pdst=epgs[0].bvi_ip4,
-                       psrc=eps[0].ip))
+                       psrc=eps[0].ip4.address))
 
         self.send_and_expect(self.pg0, [pkt_arp], self.pg0)
 
-        nsma = in6_getnsma(inet_pton(AF_INET6, eps[4].ip))
+        nsma = in6_getnsma(inet_pton(AF_INET6, eps[0].ip6.address))
         d = inet_ntop(AF_INET6, nsma)
         pkt_nd = (Ether(dst=in6_getnsmac(nsma)) /
-                  IPv6(dst=d, src=eps[4].ip) /
+                  IPv6(dst=d, src=eps[0].ip6.address) /
                   ICMPv6ND_NS(tgt=epgs[0].bvi_ip6) /
                   ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac))
         self.send_and_expect(self.pg0, [pkt_nd], self.pg0)
@@ -777,7 +761,7 @@ class TestGBP(VppTestCase):
         #
         pkt_bcast = (Ether(dst="ff:ff:ff:ff:ff:ff",
                            src=self.pg0.remote_mac) /
-                     IP(src=eps[0].ip, dst="232.1.1.1") /
+                     IP(src=eps[0].ip4.address, dst="232.1.1.1") /
                      UDP(sport=1234, dport=1234) /
                      Raw('\xa5' * 100))
 
@@ -797,12 +781,14 @@ class TestGBP(VppTestCase):
         #
         pkt_intra_epg_220_ip4 = (Ether(src=self.pg0.remote_mac,
                                        dst=self.router_mac) /
-                                 IP(src=eps[0].ip, dst="10.0.0.99") /
+                                 IP(src=eps[0].ip4.address,
+                                    dst="10.0.0.99") /
                                  UDP(sport=1234, dport=1234) /
                                  Raw('\xa5' * 100))
         pkt_inter_epg_222_ip4 = (Ether(src=self.pg0.remote_mac,
                                        dst=self.router_mac) /
-                                 IP(src=eps[0].ip, dst="10.0.1.99") /
+                                 IP(src=eps[0].ip4.address,
+                                    dst="10.0.1.99") /
                                  UDP(sport=1234, dport=1234) /
                                  Raw('\xa5' * 100))
 
@@ -810,7 +796,8 @@ class TestGBP(VppTestCase):
 
         pkt_inter_epg_222_ip6 = (Ether(src=self.pg0.remote_mac,
                                        dst=self.router_mac) /
-                                 IPv6(src=eps[4].ip, dst="2001:10::99") /
+                                 IPv6(src=eps[0].ip6.address,
+                                      dst="2001:10::99") /
                                  UDP(sport=1234, dport=1234) /
                                  Raw('\xa5' * 100))
         self.send_and_assert_no_replies(self.pg0, pkt_inter_epg_222_ip6 * 65)
@@ -824,9 +811,9 @@ class TestGBP(VppTestCase):
         s41.add_vpp_config()
         s42.add_vpp_config()
         s43.add_vpp_config()
-        s61 = VppGbpSubnet(self, 0, "2001:10::1", 64, is_ip6=True)
-        s62 = VppGbpSubnet(self, 0, "2001:10:1::1", 64, is_ip6=True)
-        s63 = VppGbpSubnet(self, 0, "2001:10:2::1", 64, is_ip6=True)
+        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()
@@ -856,7 +843,8 @@ class TestGBP(VppTestCase):
         #
         pkt_intra_epg_220_to_uplink = (Ether(src=self.pg0.remote_mac,
                                              dst="00:00:00:33:44:55") /
-                                       IP(src=eps[0].ip, dst="10.0.0.99") /
+                                       IP(src=eps[0].ip4.address,
+                                          dst="10.0.0.99") /
                                        UDP(sport=1234, dport=1234) /
                                        Raw('\xa5' * 100))
 
@@ -869,7 +857,8 @@ class TestGBP(VppTestCase):
 
         pkt_intra_epg_221_to_uplink = (Ether(src=self.pg2.remote_mac,
                                              dst="00:00:00:33:44:66") /
-                                       IP(src=eps[0].ip, dst="10.0.0.99") /
+                                       IP(src=eps[0].ip4.address,
+                                          dst="10.0.0.99") /
                                        UDP(sport=1234, dport=1234) /
                                        Raw('\xa5' * 100))
 
@@ -882,7 +871,8 @@ class TestGBP(VppTestCase):
         #
         pkt_intra_epg_220_from_uplink = (Ether(src="00:00:00:33:44:55",
                                                dst=self.pg0.remote_mac) /
-                                         IP(src=eps[0].ip, dst="10.0.0.99") /
+                                         IP(src=eps[0].ip4.address,
+                                            dst="10.0.0.99") /
                                          UDP(sport=1234, dport=1234) /
                                          Raw('\xa5' * 100))
 
@@ -896,7 +886,8 @@ class TestGBP(VppTestCase):
         #
         pkt_intra_epg = (Ether(src=self.pg0.remote_mac,
                                dst=self.pg1.remote_mac) /
-                         IP(src=eps[0].ip, dst=eps[1].ip) /
+                         IP(src=eps[0].ip4.address,
+                            dst=eps[1].ip4.address) /
                          UDP(sport=1234, dport=1234) /
                          Raw('\xa5' * 100))
 
@@ -908,17 +899,20 @@ class TestGBP(VppTestCase):
         #
         pkt_inter_epg_220_to_221 = (Ether(src=self.pg0.remote_mac,
                                           dst=self.pg2.remote_mac) /
-                                    IP(src=eps[0].ip, dst=eps[2].ip) /
+                                    IP(src=eps[0].ip4.address,
+                                       dst=eps[2].ip4.address) /
                                     UDP(sport=1234, dport=1234) /
                                     Raw('\xa5' * 100))
         pkt_inter_epg_221_to_220 = (Ether(src=self.pg2.remote_mac,
                                           dst=self.pg0.remote_mac) /
-                                    IP(src=eps[2].ip, dst=eps[0].ip) /
+                                    IP(src=eps[2].ip4.address,
+                                       dst=eps[0].ip4.address) /
                                     UDP(sport=1234, dport=1234) /
                                     Raw('\xa5' * 100))
         pkt_inter_epg_220_to_222 = (Ether(src=self.pg0.remote_mac,
                                           dst=self.router_mac) /
-                                    IP(src=eps[0].ip, dst=eps[3].ip) /
+                                    IP(src=eps[0].ip4.address,
+                                       dst=eps[3].ip4.address) /
                                     UDP(sport=1234, dport=1234) /
                                     Raw('\xa5' * 100))
 
@@ -1008,8 +1002,7 @@ class TestGBP(VppTestCase):
         se16 = VppGbpSubnet(self, 0, "::", 0,
                             is_internal=False,
                             sw_if_index=recirc_nat.recirc.sw_if_index,
-                            epg=epg_nat.epg,
-                            is_ip6=True)
+                            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,
@@ -1019,8 +1012,7 @@ class TestGBP(VppTestCase):
         se36 = VppGbpSubnet(self, 20, "::", 0,
                             is_internal=False,
                             sw_if_index=epg_nat.uplink.sw_if_index,
-                            epg=epg_nat.epg,
-                            is_ip6=True)
+                            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,
@@ -1033,14 +1025,15 @@ class TestGBP(VppTestCase):
         self.logger.info(self.vapi.cli("sh ip fib 11.0.0.1"))
         self.logger.info(self.vapi.cli("sh ip6 fib ::/0"))
         self.logger.info(self.vapi.cli("sh ip6 fib %s" %
-                                       eps[4].floating_ip))
+                                       eps[0].fip6))
 
         #
         # From an EP to an outside addess: IN2OUT
         #
         pkt_inter_epg_220_to_global = (Ether(src=self.pg0.remote_mac,
                                              dst=self.router_mac) /
-                                       IP(src=eps[0].ip, dst="1.1.1.1") /
+                                       IP(src=eps[0].ip4.address,
+                                          dst="1.1.1.1") /
                                        UDP(sport=1234, dport=1234) /
                                        Raw('\xa5' * 100))
 
@@ -1062,25 +1055,26 @@ class TestGBP(VppTestCase):
         self.send_and_expect_natted(self.pg0,
                                     pkt_inter_epg_220_to_global * 65,
                                     self.pg7,
-                                    eps[0].floating_ip)
+                                    eps[0].fip4.address)
 
         pkt_inter_epg_220_to_global = (Ether(src=self.pg0.remote_mac,
                                              dst=self.router_mac) /
-                                       IPv6(src=eps[4].ip, dst="6001::1") /
+                                       IPv6(src=eps[0].ip6.address,
+                                            dst="6001::1") /
                                        UDP(sport=1234, dport=1234) /
                                        Raw('\xa5' * 100))
 
         self.send_and_expect_natted6(self.pg0,
                                      pkt_inter_epg_220_to_global * 65,
                                      self.pg7,
-                                     eps[4].floating_ip)
+                                     eps[0].fip6.address)
 
         #
         # From a global address to an EP: OUT2IN
         #
         pkt_inter_epg_220_from_global = (Ether(src=self.router_mac,
                                                dst=self.pg0.remote_mac) /
-                                         IP(dst=eps[0].floating_ip,
+                                         IP(dst=eps[0].fip4.address,
                                             src="1.1.1.1") /
                                          UDP(sport=1234, dport=1234) /
                                          Raw('\xa5' * 100))
@@ -1094,19 +1088,19 @@ class TestGBP(VppTestCase):
         self.send_and_expect_unnatted(self.pg7,
                                       pkt_inter_epg_220_from_global * 65,
                                       eps[0].itf,
-                                      eps[0].ip)
+                                      eps[0].ip4.address)
 
         pkt_inter_epg_220_from_global = (Ether(src=self.router_mac,
                                                dst=self.pg0.remote_mac) /
-                                         IPv6(dst=eps[4].floating_ip,
+                                         IPv6(dst=eps[0].fip6.address,
                                               src="6001::1") /
                                          UDP(sport=1234, dport=1234) /
                                          Raw('\xa5' * 100))
 
         self.send_and_expect_unnatted6(self.pg7,
                                        pkt_inter_epg_220_from_global * 65,
-                                       eps[4].itf,
-                                       eps[4].ip)
+                                       eps[0].itf,
+                                       eps[0].ip6.address)
 
         #
         # From a local VM to another local VM using resp. public addresses:
@@ -1114,46 +1108,44 @@ class TestGBP(VppTestCase):
         #
         pkt_intra_epg_220_global = (Ether(src=self.pg0.remote_mac,
                                           dst=self.router_mac) /
-                                    IP(src=eps[0].ip,
-                                       dst=eps[1].floating_ip) /
+                                    IP(src=eps[0].ip4.address,
+                                       dst=eps[1].fip4.address) /
                                     UDP(sport=1234, dport=1234) /
                                     Raw('\xa5' * 100))
 
         self.send_and_expect_double_natted(eps[0].itf,
                                            pkt_intra_epg_220_global * 65,
                                            eps[1].itf,
-                                           eps[0].floating_ip,
-                                           eps[1].ip)
+                                           eps[0].fip4.address,
+                                           eps[1].ip4.address)
 
-        pkt_intra_epg_220_global = (Ether(src=self.pg4.remote_mac,
+        pkt_intra_epg_220_global = (Ether(src=self.pg0.remote_mac,
                                           dst=self.router_mac) /
-                                    IPv6(src=eps[4].ip,
-                                         dst=eps[5].floating_ip) /
+                                    IPv6(src=eps[0].ip6.address,
+                                         dst=eps[1].fip6.address) /
                                     UDP(sport=1234, dport=1234) /
                                     Raw('\xa5' * 100))
 
-        self.send_and_expect_double_natted6(eps[4].itf,
+        self.send_and_expect_double_natted6(eps[0].itf,
                                             pkt_intra_epg_220_global * 65,
-                                            eps[5].itf,
-                                            eps[4].floating_ip,
-                                            eps[5].ip)
+                                            eps[1].itf,
+                                            eps[0].fip6.address,
+                                            eps[1].ip6.address)
 
         #
         # cleanup
         #
         for ep in eps:
             # del static mappings for each EP from the 10/8 to 11/8 network
-            if ep.af == AF_INET:
-                self.vapi.nat44_add_del_static_mapping(ep.ip_n,
-                                                       ep.floating_ip_n,
-                                                       vrf_id=0,
-                                                       addr_only=1,
-                                                       is_add=0)
-            else:
-                self.vapi.nat66_add_del_static_mapping(ep.ip_n,
-                                                       ep.floating_ip_n,
-                                                       vrf_id=0,
-                                                       is_add=0)
+            self.vapi.nat44_add_del_static_mapping(ep.ip4.bytes,
+                                                   ep.fip4.bytes,
+                                                   vrf_id=0,
+                                                   addr_only=1,
+                                                   is_add=0)
+            self.vapi.nat66_add_del_static_mapping(ep.ip6.bytes,
+                                                   ep.fip6.bytes,
+                                                   vrf_id=0,
+                                                   is_add=0)
 
         for epg in epgs:
             # IP config on the BVI interfaces
index 2559381..4d40b3b 100644 (file)
@@ -6,7 +6,8 @@ from logging import *
 from framework import VppTestCase, VppTestRunner
 from vpp_sub_interface import VppDot1QSubint
 from vpp_gre_interface import VppGreInterface, VppGre6Interface
-from vpp_ip_route import VppIpRoute, VppRoutePath, DpoProto, VppIpTable
+from vpp_ip import DpoProto
+from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable
 from vpp_papi_provider import L2_VTR_OP
 
 from scapy.packet import Raw
index 9a0c752..95f9229 100644 (file)
@@ -7,9 +7,10 @@ from framework import VppTestCase, VppTestRunner
 from util import ppp, ip6_normalize
 from vpp_sub_interface import VppSubInterface, VppDot1QSubint
 from vpp_pg_interface import is_ipv6_misc
+from vpp_ip import DpoProto
 from vpp_ip_route import VppIpRoute, VppRoutePath, find_route, VppIpMRoute, \
     VppMRoutePath, MRouteItfFlags, MRouteEntryFlags, VppMplsIpBind, \
-    VppMplsRoute, DpoProto, VppMplsTable, VppIpTable
+    VppMplsRoute, VppMplsTable, VppIpTable
 from vpp_neighbor import find_nbr, VppNeighbor
 
 from scapy.packet import Raw
index 9c216d5..64eb304 100644 (file)
@@ -3,8 +3,9 @@
 import unittest
 
 from framework import VppTestCase, VppTestRunner
+from vpp_ip import DpoProto
 from vpp_ip_route import VppIpMRoute, VppMRoutePath, VppMFibSignal, \
-    MRouteItfFlags, MRouteEntryFlags, VppIpTable, DpoProto
+    MRouteItfFlags, MRouteEntryFlags, VppIpTable
 
 from scapy.packet import Raw
 from scapy.layers.l2 import Ether
index 5754bd0..350eae0 100644 (file)
@@ -5,7 +5,8 @@ import unittest
 from scapy.layers.inet6 import IPv6, Ether, IP, UDP
 from scapy.all import fragment, RandShort
 from framework import VppTestCase, VppTestRunner
-from vpp_ip_route import VppIpRoute, VppRoutePath, DpoProto, VppIpTable
+from vpp_ip import DpoProto
+from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable
 from socket import AF_INET, AF_INET6, inet_pton
 import StringIO
 
index c5e193a..1a53492 100644 (file)
@@ -4,7 +4,8 @@ import unittest
 import socket
 
 from framework import VppTestCase, VppTestRunner
-from vpp_ip_route import VppIpRoute, VppRoutePath, DpoProto
+from vpp_ip import DpoProto
+from vpp_ip_route import VppIpRoute, VppRoutePath
 
 from scapy.layers.l2 import Ether, Raw
 from scapy.layers.inet import IP, UDP, ICMP
index 33fed68..d943f82 100644 (file)
@@ -4,9 +4,10 @@ import unittest
 import socket
 
 from framework import VppTestCase, VppTestRunner
+from vpp_ip import DpoProto
 from vpp_ip_route import VppIpRoute, VppRoutePath, VppMplsRoute, \
     VppMplsIpBind, VppIpMRoute, VppMRoutePath, \
-    MRouteItfFlags, MRouteEntryFlags, DpoProto, VppIpTable, VppMplsTable, \
+    MRouteItfFlags, MRouteEntryFlags, VppIpTable, VppMplsTable, \
     VppMplsLabel, MplsLspMode
 from vpp_mpls_tunnel_interface import VppMPLSTunnelInterface
 
index d6be2e2..a70d513 100644 (file)
@@ -12,7 +12,8 @@ import unittest
 from scapy.layers.inet6 import IPv6, Ether, IP, UDP, ICMPv6PacketTooBig
 from scapy.layers.inet import ICMP
 from framework import VppTestCase, VppTestRunner
-from vpp_ip_route import VppIpRoute, VppRoutePath, DpoProto
+from vpp_ip import DpoProto
+from vpp_ip_route import VppIpRoute, VppRoutePath
 from socket import AF_INET, AF_INET6, inet_pton
 import StringIO
 
index d6d5c50..c901eea 100644 (file)
@@ -11,7 +11,8 @@ from scapy.layers.inet6 import IPv6
 
 from framework import VppTestCase, VppTestRunner
 from vpp_sub_interface import VppP2PSubint
-from vpp_ip_route import VppIpRoute, VppRoutePath, DpoProto
+from vpp_ip import DpoProto
+from vpp_ip_route import VppIpRoute, VppRoutePath
 from util import mactobinary
 
 
index 76fa8a1..4916550 100644 (file)
@@ -5,8 +5,9 @@ import unittest
 from framework import VppTestCase, VppTestRunner
 from vpp_papi_provider import QOS_SOURCE
 from vpp_sub_interface import VppDot1QSubint
+from vpp_ip import DpoProto
 from vpp_ip_route import VppIpRoute, VppRoutePath, VppMplsRoute, \
-    VppMplsLabel, VppMplsTable, DpoProto
+    VppMplsLabel, VppMplsTable
 
 from scapy.packet import Raw
 from scapy.layers.l2 import Ether, Dot1Q
index 76aabcb..b2c271d 100644 (file)
@@ -11,7 +11,8 @@ from util import ppp, fragment_rfc791, fragment_rfc8200
 from scapy.layers.inet6 import IPv6, IPv6ExtHdrFragment, ICMPv6ParamProblem,\
     ICMPv6TimeExceeded
 from vpp_gre_interface import VppGreInterface, VppGre6Interface
-from vpp_ip_route import VppIpRoute, VppRoutePath, DpoProto
+from vpp_ip import DpoProto
+from vpp_ip_route import VppIpRoute, VppRoutePath
 
 test_packet_count = 257
 
index 7656c3f..a469ac5 100644 (file)
@@ -6,7 +6,8 @@ from scapy.layers.inet import IP, UDP, Ether
 from scapy.layers.inet6 import IPv6
 from scapy.packet import Raw
 from framework import VppTestCase, VppTestRunner
-from vpp_ip_route import VppIpRoute, VppRoutePath, DpoProto, VppIpTable
+from vpp_ip import DpoProto
+from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable
 from socket import AF_INET, AF_INET6, inet_pton
 
 """ Test6rd is a subclass of  VPPTestCase classes.
index ded4a71..756405a 100644 (file)
@@ -4,10 +4,9 @@ import unittest
 import socket
 
 from framework import VppTestCase, VppTestRunner
+from vpp_ip import DpoProto
 from vpp_ip_route import VppIpRoute, VppRoutePath, VppMplsRoute, \
-    VppMplsIpBind, VppIpMRoute, VppMRoutePath, \
-    MRouteItfFlags, MRouteEntryFlags, DpoProto, VppIpTable, VppMplsTable, \
-    VppMplsLabel, MplsLspMode
+    VppIpTable, VppMplsTable, VppMplsLabel
 from vpp_mpls_tunnel_interface import VppMPLSTunnelInterface
 
 from scapy.packet import Raw
index 75d9d12..299f343 100644 (file)
@@ -5,7 +5,8 @@ import binascii
 from socket import AF_INET6
 
 from framework import VppTestCase, VppTestRunner
-from vpp_ip_route import VppIpRoute, VppRoutePath, DpoProto, VppIpTable
+from vpp_ip import DpoProto
+from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable
 from vpp_srv6 import SRv6LocalSIDBehaviors, VppSRv6LocalSID, VppSRv6Policy, \
     SRv6PolicyType, VppSRv6Steering, SRv6PolicySteeringTypes
 
index 912d843..e44e6b5 100644 (file)
@@ -4,6 +4,7 @@
 """
 
 from ipaddress import ip_address
+from socket import AF_INET, AF_INET6
 
 
 class IpAddressFamily:
@@ -11,17 +12,16 @@ class IpAddressFamily:
     ADDRESS_IP6 = 1
 
 
-INVALID_INDEX = 0xffffffff
+class DpoProto:
+    DPO_PROTO_IP4 = 0
+    DPO_PROTO_IP6 = 1
+    DPO_PROTO_MPLS = 2
+    DPO_PROTO_ETHERNET = 3
+    DPO_PROTO_BIER = 4
+    DPO_PROTO_NSH = 5
 
 
-def compare_ip_address(api_address, py_address):
-    if 4 is py_address.version:
-        if py_address.packed == api_address.ip4.address:
-            return True
-    else:
-        if py_address.packed == api_address.ip6.address:
-            return True
-    return False
+INVALID_INDEX = 0xffffffff
 
 
 class VppIpAddressUnion():
@@ -29,16 +29,8 @@ class VppIpAddressUnion():
         self.addr = addr
         self.ip_addr = ip_address(unicode(self.addr))
 
-    @property
-    def version(self):
-        return self.ip_addr.version
-
-    @property
-    def address(self):
-        return self.addr
-
     def encode(self):
-        if self.ip_addr.version is 6:
+        if self.version is 6:
             return {
                 'ip6': {
                     'address': self.ip_addr.packed
@@ -51,6 +43,41 @@ class VppIpAddressUnion():
                 },
             }
 
+    @property
+    def version(self):
+        return self.ip_addr.version
+
+    @property
+    def address(self):
+        return self.addr
+
+    @property
+    def length(self):
+        if self.version is 6:
+            return 128
+        else:
+            return 32
+
+    @property
+    def bytes(self):
+        return self.ip_addr.packed
+
+    def __eq__(self, other):
+        if isinstance(other, self.__class__):
+            return self.ip_addr == other.ip_addr
+        elif hasattr(other, "ip4") and hasattr(other, "ip6"):
+            # vl_api_address_union_t
+            if 4 is self.version:
+                return self.ip_addr.packed == other.ip4.address
+            else:
+                return self.ip_addr.packed == other.ip6.address
+        else:
+            raise Exception("Comparing VppIpAddresUnions:%s"
+                            " with unknown type: %s" %
+                            (self, other))
+
+        return False
+
 
 class VppIpAddress():
     def __init__(self, addr):
@@ -68,10 +95,62 @@ class VppIpAddress():
                 'un': self.addr.encode()
             }
 
+    def __eq__(self, other):
+        if isinstance(other, self.__class__):
+            return self.addr == other.addr
+        elif hasattr(other, "af") and hasattr(other, "un"):
+            # a vp_api_address_t
+            if 4 is self.version:
+                return other.af == IpAddressFamily.ADDRESS_IP4 and \
+                    other.un == self.addr
+            else:
+                return other.af == IpAddressFamily.ADDRESS_IP6 and \
+                    other.un == self.addr
+        else:
+            raise Exception("Comparing VppIpAddress:%s with unknown type: %s" %
+                            (self, other))
+        return False
+
+    def __ne__(self, other):
+        return not (self == other)
+
+    def __str__(self):
+        return self.address
+
+    @property
+    def bytes(self):
+        return self.addr.bytes
+
     @property
     def address(self):
         return self.addr.address
 
+    @property
+    def length(self):
+        return self.addr.length
+
+    @property
+    def version(self):
+        return self.addr.version
+
+    @property
+    def is_ip6(self):
+        return (self.version == 6)
+
+    @property
+    def af(self):
+        if self.version == 6:
+            return AF_INET6
+        else:
+            return AF_INET
+
+    @property
+    def dpo_proto(self):
+        if self.version is 6:
+            return DpoProto.DPO_PROTO_IP6
+        else:
+            return DpoProto.DPO_PROTO_IP4
+
 
 class VppIpPrefix():
     def __init__(self, addr, len):
@@ -91,6 +170,25 @@ class VppIpPrefix():
     def address(self):
         return self.addr.address
 
+    @property
+    def length(self):
+        return self.len
+
+    def __str__(self):
+        return "%s/%d" % (self.address, self.length)
+
+    def __eq__(self, other):
+        if isinstance(other, self.__class__):
+            return (self.len == other.len and self.addr == other.addr)
+        elif hasattr(other, "address") and hasattr(other, "address_length"):
+            # vl_api_prefix_t
+            return self.len == other.address_length and \
+                self.addr == other.address
+        else:
+            raise Exception("Comparing VppIpPrefix:%s with unknown type: %s" %
+                            (self, other))
+        return False
+
 
 class VppIpMPrefix():
     def __init__(self, saddr, gaddr, len):
index 216f5c7..9d6bfb7 100644 (file)
@@ -6,6 +6,7 @@
 
 from vpp_object import *
 from socket import inet_pton, inet_ntop, AF_INET, AF_INET6
+from vpp_ip import *
 
 # from vnet/vnet/mpls/mpls_types.h
 MPLS_IETF_MAX_LABEL = 0xfffff
@@ -29,15 +30,6 @@ class MRouteEntryFlags:
     MFIB_ENTRY_FLAG_INHERIT_ACCEPT = 8
 
 
-class DpoProto:
-    DPO_PROTO_IP4 = 0
-    DPO_PROTO_IP6 = 1
-    DPO_PROTO_MPLS = 2
-    DPO_PROTO_ETHERNET = 3
-    DPO_PROTO_BIER = 4
-    DPO_PROTO_NSH = 5
-
-
 class MplsLspMode:
     PIPE = 0
     UNIFORM = 1
diff --git a/test/vpp_mac.py b/test/vpp_mac.py
new file mode 100644 (file)
index 0000000..c9ee11e
--- /dev/null
@@ -0,0 +1,24 @@
+"""
+  MAC Types
+
+"""
+
+from util import mactobinary
+
+
+class VppMacAddress():
+    def __init__(self, addr):
+        self.address = addr
+
+    def encode(self):
+        return {
+            'bytes': self.bytes
+        }
+
+    @property
+    def bytes(self):
+        return mactobinary(self.address)
+
+    @property
+    def address(self):
+        return self.addr.address
index b575b8a..3028a25 100644 (file)
@@ -3432,15 +3432,20 @@ class VppPapiProvider(object):
                          'enable_ip6': 1 if enable_ip6 else 0,
                          })
 
-    def gbp_endpoint_add_del(self, is_add, sw_if_index, addr, is_ip6, epg):
-        """ GBP endpoint Add/Del """
-        return self.api(self.papi.gbp_endpoint_add_del,
-                        {'is_add': is_add,
-                         'endpoint': {
-                             'is_ip6': is_ip6,
-                             'sw_if_index': sw_if_index,
-                             'address': addr,
-                             'epg_id': epg}})
+    def gbp_endpoint_add(self, sw_if_index, ips, mac, epg):
+        """ GBP endpoint Add """
+        return self.api(self.papi.gbp_endpoint_add,
+                        {'endpoint': {
+                            'sw_if_index': sw_if_index,
+                            'ips': ips,
+                            'n_ips': len(ips),
+                            'mac': mac,
+                            'epg_id': epg}})
+
+    def gbp_endpoint_del(self, handle):
+        """ GBP endpoint Del """
+        return self.api(self.papi.gbp_endpoint_del,
+                        {'handle': handle})
 
     def gbp_endpoint_dump(self):
         """ GBP endpoint Dump """
@@ -3479,7 +3484,7 @@ class VppPapiProvider(object):
 
     def gbp_subnet_add_del(self, is_add, table_id,
                            is_internal,
-                           addr, addr_len,
+                           prefix,
                            sw_if_index=0xffffffff,
                            epg_id=0xffff,
                            is_ip6=False):
@@ -3491,8 +3496,7 @@ class VppPapiProvider(object):
                              'is_ip6': is_ip6,
                              'sw_if_index': sw_if_index,
                              'epg_id': epg_id,
-                             'address': addr,
-                             'address_length': addr_len,
+                             'prefix': prefix,
                              'table_id': table_id}})
 
     def gbp_subnet_dump(self):
index 002f9f4..826378b 100644 (file)
@@ -12,10 +12,8 @@ def find_udp_encap(test, ue):
     encaps = test.vapi.udp_encap_dump()
     for e in encaps:
         if ue.id == e.udp_encap.id \
-           and compare_ip_address(e.udp_encap.src_ip.un,
-                                  ue.src_ip.addr.ip_addr) \
-           and compare_ip_address(e.udp_encap.dst_ip.un,
-                                  ue.dst_ip.addr.ip_addr) \
+           and ue.src_ip == e.udp_encap.src_ip \
+           and ue.dst_ip == e.udp_encap.dst_ip \
            and e.udp_encap.dst_port == ue.dst_port \
            and e.udp_encap.src_port == ue.src_port:
             return True