GBP V2 47/11547/10
authorNeale Ranns <neale.ranns@cisco.com>
Wed, 4 Apr 2018 16:34:50 +0000 (09:34 -0700)
committerDamjan Marion <dmarion.lists@gmail.com>
Fri, 13 Apr 2018 08:51:21 +0000 (08:51 +0000)
update the GBP plugin to implement the full NAT feature set of opflex agent

Change-Id: Ic06a039c889445ed0b9087fa1f292634192b0f8d
Signed-off-by: Neale Ranns <neale.ranns@cisco.com>
74 files changed:
src/plugins/gbp.am
src/plugins/gbp/gbp.api
src/plugins/gbp/gbp.c [deleted file]
src/plugins/gbp/gbp.h
src/plugins/gbp/gbp_all_api_h.h
src/plugins/gbp/gbp_api.c
src/plugins/gbp/gbp_classify.c [new file with mode: 0644]
src/plugins/gbp/gbp_contract.c [new file with mode: 0644]
src/plugins/gbp/gbp_contract.h [new file with mode: 0644]
src/plugins/gbp/gbp_endpoint.c [new file with mode: 0644]
src/plugins/gbp/gbp_endpoint.h [new file with mode: 0644]
src/plugins/gbp/gbp_endpoint_group.c [new file with mode: 0644]
src/plugins/gbp/gbp_endpoint_group.h [new file with mode: 0644]
src/plugins/gbp/gbp_fwd.c [new file with mode: 0644]
src/plugins/gbp/gbp_fwd_dpo.c [new file with mode: 0644]
src/plugins/gbp/gbp_fwd_dpo.h [new file with mode: 0644]
src/plugins/gbp/gbp_msg_enum.h
src/plugins/gbp/gbp_policy.c [new file with mode: 0644]
src/plugins/gbp/gbp_policy_dpo.c [new file with mode: 0644]
src/plugins/gbp/gbp_policy_dpo.h [new file with mode: 0644]
src/plugins/gbp/gbp_recirc.c [new file with mode: 0644]
src/plugins/gbp/gbp_recirc.h [new file with mode: 0644]
src/plugins/gbp/gbp_subnet.c [new file with mode: 0644]
src/plugins/gbp/gbp_subnet.h [new file with mode: 0644]
src/plugins/gbp/gbp_types.h [new file with mode: 0644]
src/vnet/buffer.h
src/vnet/ethernet/arp.c
src/vnet/ethernet/arp_packet.h
src/vnet/fib/fib_table.c
src/vnet/fib/fib_table.h
src/vnet/ip/ip6.h
src/vnet/ip/ip6_neighbor.c
src/vnet/l2/l2_input.c
src/vnet/l2/l2_input.h
src/vnet/l2/l2_output.h
src/vpp-api/vom/Makefile.am
src/vpp-api/vom/acl_l2_rule.cpp
src/vpp-api/vom/acl_l2_rule.hpp
src/vpp-api/vom/acl_l3_rule.cpp
src/vpp-api/vom/acl_l3_rule.hpp
src/vpp-api/vom/acl_types.hpp
src/vpp-api/vom/bridge_domain.cpp
src/vpp-api/vom/bridge_domain.hpp
src/vpp-api/vom/bridge_domain_cmds.cpp
src/vpp-api/vom/bridge_domain_cmds.hpp
src/vpp-api/vom/gbp_endpoint.cpp
src/vpp-api/vom/gbp_endpoint.hpp
src/vpp-api/vom/gbp_endpoint_cmds.cpp
src/vpp-api/vom/gbp_endpoint_cmds.hpp
src/vpp-api/vom/gbp_endpoint_group.cpp [new file with mode: 0644]
src/vpp-api/vom/gbp_endpoint_group.hpp [new file with mode: 0644]
src/vpp-api/vom/gbp_endpoint_group_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/gbp_endpoint_group_cmds.hpp [new file with mode: 0644]
src/vpp-api/vom/gbp_recirc.cpp [new file with mode: 0644]
src/vpp-api/vom/gbp_recirc.hpp [new file with mode: 0644]
src/vpp-api/vom/gbp_recirc_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/gbp_recirc_cmds.hpp [new file with mode: 0644]
src/vpp-api/vom/gbp_subnet.cpp [new file with mode: 0644]
src/vpp-api/vom/gbp_subnet.hpp [new file with mode: 0644]
src/vpp-api/vom/gbp_subnet_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/gbp_subnet_cmds.hpp [new file with mode: 0644]
src/vpp-api/vom/interface.cpp
src/vpp-api/vom/interface_types.cpp
src/vpp-api/vom/nat_binding_cmds.hpp
src/vpp-api/vom/nat_static.cpp
src/vpp-api/vom/nat_static.hpp
src/vpp-api/vom/nat_static_cmds.cpp
src/vpp-api/vom/nat_static_cmds.hpp
src/vpp-api/vom/route.cpp
src/vpp-api/vom/types.cpp
src/vpp-api/vom/types.hpp
test/ext/vom_test.cpp
test/test_gbp.py
test/vpp_papi_provider.py

index 644d08c..7271a0d 100644 (file)
 
 vppplugins_LTLIBRARIES += gbp_plugin.la
 
-gbp_plugin_la_SOURCES =        \
-       gbp/gbp.c               \
+gbp_plugin_la_SOURCES =                \
+       gbp/gbp_subnet.c                \
+       gbp/gbp_contract.c              \
+       gbp/gbp_endpoint.c              \
+       gbp/gbp_endpoint_group.c        \
+       gbp/gbp_classify.c              \
+       gbp/gbp_recirc.c                \
+       gbp/gbp_policy.c                \
+       gbp/gbp_policy_dpo.c            \
+       gbp/gbp_fwd.c                   \
+       gbp/gbp_fwd_dpo.c               \
        gbp/gbp_api.c
 
 API_FILES += gbp/gbp.api
index 491ad36..b2b32e9 100644 (file)
@@ -1,6 +1,6 @@
 /* Hey Emacs use -*- mode: C -*- */
 /*
- * Copyright (c) 2016 Cisco and/or its affiliates.
+ * Copyright (c) 2018 Cisco and/or its affiliates.
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at:
@@ -27,6 +27,7 @@ typeonly define gbp_endpoint
   u32 epg_id;
   u8  is_ip6;
   u8  address[16];
+  u8  mac[6];
 };
 
 autoreply define gbp_endpoint_add_del
@@ -49,6 +50,93 @@ define gbp_endpoint_details
   vl_api_gbp_endpoint_t endpoint;
 };
 
+typeonly define gbp_endpoint_group
+{
+  u32 epg_id;
+  u32 bd_id;
+  u32 ip4_table_id;
+  u32 ip6_table_id;
+  u32 uplink_sw_if_index;
+};
+
+autoreply define gbp_endpoint_group_add_del
+{
+  u32 client_index;
+  u32 context;
+  u8  is_add;
+  vl_api_gbp_endpoint_group_t epg;
+};
+
+define gbp_endpoint_group_dump
+{
+  u32 client_index;
+  u32 context;
+};
+
+define gbp_endpoint_group_details
+{
+  u32 context;
+  vl_api_gbp_endpoint_group_t epg;
+};
+
+typeonly define gbp_recirc
+{
+  u32 epg_id;
+  u32 sw_if_index;
+  u8  is_ext;
+};
+
+autoreply define gbp_recirc_add_del
+{
+  u32 client_index;
+  u32 context;
+  u8  is_add;
+  vl_api_gbp_recirc_t recirc;
+};
+
+define gbp_recirc_dump
+{
+  u32 client_index;
+  u32 context;
+};
+
+define gbp_recirc_details
+{
+  u32 context;
+  vl_api_gbp_recirc_t recirc;
+};
+
+typeonly define gbp_subnet
+{
+  u32 table_id;
+  u32 sw_if_index;
+  u32 epg_id;
+  u8  is_ip6;
+  u8  is_internal;
+  u8  address_length;
+  u8  address[16];
+};
+
+autoreply define gbp_subnet_add_del
+{
+  u32 client_index;
+  u32 context;
+  u8  is_add;
+  vl_api_gbp_subnet_t subnet;
+};
+
+define gbp_subnet_dump
+{
+  u32 client_index;
+  u32 context;
+};
+
+define gbp_subnet_details
+{
+  u32 context;
+  vl_api_gbp_subnet_t subnet;
+};
+
 typeonly define gbp_contract
 {
   u32 src_epg;
diff --git a/src/plugins/gbp/gbp.c b/src/plugins/gbp/gbp.c
deleted file mode 100644 (file)
index 0b87d0b..0000000
+++ /dev/null
@@ -1,782 +0,0 @@
-/*
- * gbp.h : Group Based Policy
- *
- * Copyright (c) 2013 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <plugins/gbp/gbp.h>
-
-/**
- * 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;
-
-/**
- * IP6 destintion address to destination EPG mapping table
- */
-typedef struct gbp_ip6_to_epg_db_t_
-{
-  /**
-   * use a memroy hash table
-   */
-  uword *g6ie_hash;
-} gbp_ip6_to_epg_db_t;
-
-static gbp_ip6_to_epg_db_t gbp_ip6_to_epg_db;
-
-/**
- * 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;
-
-const static gbp_itf_t ITF_INVALID = {
-  .gi_epg = EPG_INVALID,
-  .gi_ref_count = 0,
-};
-
-/**
- * Interface to source EPG DB - a per-interface vector
- */
-typedef struct gbp_itf_to_epg_db_t_
-{
-  gbp_itf_t *gte_vec;
-} gbp_itf_to_epg_db_t;
-
-static gbp_itf_to_epg_db_t gbp_itf_to_epg_db;
-
-/**
- * Pool of GBP endpoints
- */
-static gbp_endpoint_t *gbp_endpoint_pool;
-
-/**
- * DB of endpoints
- */
-static uword *gbp_endpoint_db;
-
-/**
- * EPG src,dst pair to ACL mapping table, aka contract DB
- */
-typedef struct gbp_contract_db_t_
-{
-  /**
-   * We can form a u64 key from the pair, so use a simple hash table
-   */
-  uword *gc_hash;
-} gbp_contract_db_t;
-
-/**
- * Since contract DB instance
- */
-static gbp_contract_db_t gbp_contract_db;
-
-static void
-gbp_ip_epg_update (const ip46_address_t * ip, epg_id_t epg_id)
-{
-  /*
-   * 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);
-    }
-}
-
-static void
-gbp_ip_epg_delete (const ip46_address_t * ip)
-{
-  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);
-    }
-}
-
-static void
-gbp_itf_epg_update (u32 sw_if_index, epg_id_t src_epg)
-{
-  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)
-    {
-      vnet_feature_enable_disable ("ip4-unicast", "gbp4",
-                                  sw_if_index, 1, NULL, 0);
-      vnet_feature_enable_disable ("ip6-unicast", "gbp6",
-                                  sw_if_index, 1, NULL, 0);
-    }
-  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++;
-}
-
-static 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;
-
-      vnet_feature_enable_disable ("ip4-unicast", "gbp4",
-                                  sw_if_index, 0, NULL, 0);
-      vnet_feature_enable_disable ("ip6-unicast", "gbp6",
-                                  sw_if_index, 0, NULL, 0);
-    }
-  gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count--;
-}
-
-void
-gbp_endpoint_update (u32 sw_if_index,
-                    const ip46_address_t * ip, epg_id_t epg_id)
-{
-  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 (p)
-    {
-      gbpe = pool_elt_at_index (gbp_endpoint_pool, p[0]);
-    }
-  else
-    {
-      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));
-
-      hash_set_mem (gbp_endpoint_db, gbpe->ge_key, gbpe - gbp_endpoint_pool);
-    }
-
-  gbpe->ge_epg_id = epg_id;
-
-  gbp_itf_epg_update (gbpe->ge_key->gek_sw_if_index, gbpe->ge_epg_id);
-  gbp_ip_epg_update (&gbpe->ge_key->gek_ip, gbpe->ge_epg_id);
-}
-
-void
-gbp_endpoint_delete (u32 sw_if_index, const ip46_address_t * ip)
-{
-  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 (p)
-    {
-      gbpe = pool_elt_at_index (gbp_endpoint_pool, p[0]);
-
-      hash_unset_mem (gbp_endpoint_db, gbpe->ge_key);
-
-      gbp_itf_epg_delete (gbpe->ge_key->gek_sw_if_index);
-      gbp_ip_epg_delete (&gbpe->ge_key->gek_ip);
-
-      clib_mem_free (gbpe->ge_key);
-
-      pool_put (gbp_endpoint_pool, gbpe);
-    }
-}
-
-void
-gbp_endpoint_walk (gbp_endpoint_cb_t cb, void *ctx)
-{
-  gbp_endpoint_t *gbpe;
-
-  /* *INDENT-OFF* */
-  pool_foreach(gbpe, gbp_endpoint_pool,
-  {
-    if (!cb(gbpe, ctx))
-      break;
-  });
-  /* *INDENT-ON* */
-}
-
-void
-gbp_contract_update (epg_id_t src_epg, epg_id_t dst_epg, u32 acl_index)
-{
-  gbp_contract_key_t key = {
-    .gck_src = src_epg,
-    .gck_dst = dst_epg,
-  };
-
-  hash_set (gbp_contract_db.gc_hash, key.as_u64, acl_index);
-}
-
-void
-gbp_contract_delete (epg_id_t src_epg, epg_id_t dst_epg)
-{
-  gbp_contract_key_t key = {
-    .gck_src = src_epg,
-    .gck_dst = dst_epg,
-  };
-
-  hash_unset (gbp_contract_db.gc_hash, key.as_u64);
-}
-
-void
-gbp_contract_walk (gbp_contract_cb_t cb, void *ctx)
-{
-  gbp_contract_key_t key;
-  u32 acl_index;
-
-  /* *INDENT-OFF* */
-  hash_foreach(key.as_u64, acl_index, gbp_contract_db.gc_hash,
-  ({
-    gbp_contract_t gbpc = {
-      .gc_key = key,
-      .gc_acl_index = acl_index,
-    };
-
-    if (!cb(&gbpc, ctx))
-      break;
-  }));
-  /* *INDENT-ON* */
-}
-
-static clib_error_t *
-gbp_endpoint_cli (vlib_main_t * vm,
-                 unformat_input_t * input, vlib_cli_command_t * cmd)
-{
-  vnet_main_t *vnm = vnet_get_main ();
-  epg_id_t epg_id = EPG_INVALID;
-  ip46_address_t ip = { };
-  u32 sw_if_index = ~0;
-  u8 add = 1;
-
-  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
-    {
-      if (unformat (input, "%U", unformat_vnet_sw_interface,
-                   vnm, &sw_if_index))
-       ;
-      else if (unformat (input, "add"))
-       add = 1;
-      else if (unformat (input, "del"))
-       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, "ip %U", unformat_ip6_address, &ip.ip6))
-       ;
-      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);
-  else
-    gbp_endpoint_delete (sw_if_index, &ip);
-
-  return (NULL);
-}
-
-static clib_error_t *
-gbp_contract_cli (vlib_main_t * vm,
-                 unformat_input_t * input, vlib_cli_command_t * cmd)
-{
-  epg_id_t src_epg_id = EPG_INVALID, dst_epg_id = EPG_INVALID;
-  u32 acl_index = ~0;
-  u8 add = 1;
-
-  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
-    {
-      if (unformat (input, "add"))
-       add = 1;
-      else if (unformat (input, "del"))
-       add = 0;
-      else if (unformat (input, "src-epg %d", &src_epg_id))
-       ;
-      else if (unformat (input, "dst-epg %d", &dst_epg_id))
-       ;
-      else if (unformat (input, "acl-index %d", &acl_index))
-       ;
-      else
-       break;
-    }
-
-  if (EPG_INVALID == src_epg_id)
-    return clib_error_return (0, "Source EPG-ID must be specified");
-  if (EPG_INVALID == dst_epg_id)
-    return clib_error_return (0, "Destination EPG-ID must be specified");
-
-  if (add)
-    {
-      gbp_contract_update (src_epg_id, dst_epg_id, acl_index);
-    }
-  else
-    {
-      gbp_contract_delete (src_epg_id, dst_epg_id);
-    }
-
-  return (NULL);
-}
-
-/*?
- * Configure a GBP Endpoint
- *
- * @cliexpar
- * @cliexstart{set gbp endpoint [del] <interface> epg <ID> ip <IP>}
- * @cliexend
- ?*/
-/* *INDENT-OFF* */
-VLIB_CLI_COMMAND (gbp_endpoint_cli_node, static) = {
-  .path = "gbp endpoint",
-  .short_help = "gbp endpoint [del] <interface> epg <ID> ip <IP>",
-  .function = gbp_endpoint_cli,
-};
-
-/*?
- * Configure a GBP Contract
- *
- * @cliexpar
- * @cliexstart{set gbp contract [del] src-epg <ID> dst-epg <ID> acl-index <ACL>}
- * @cliexend
- ?*/
-VLIB_CLI_COMMAND (gbp_contract_cli_node, static) = {
-  .path = "gbp contract",
-  .short_help = "gbp contract [del] src-epg <ID> dst-epg <ID> acl-index <ACL>",
-  .function = gbp_contract_cli,
-};
-/* *INDENT-ON* */
-
-static int
-gbp_endpoint_show_one (gbp_endpoint_t * gbpe, void *ctx)
-{
-  vnet_main_t *vnm = vnet_get_main ();
-  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);
-
-  return (1);
-}
-
-static clib_error_t *
-gbp_endpoint_show (vlib_main_t * vm,
-                  unformat_input_t * input, vlib_cli_command_t * cmd)
-{
-  vnet_main_t *vnm = vnet_get_main ();
-  ip46_address_t ip, *ipp;
-  epg_id_t epg_id;
-  u32 sw_if_index;
-
-  vlib_cli_output (vm, "Endpoints:");
-  gbp_endpoint_walk (gbp_endpoint_show_one, vm);
-
-  vlib_cli_output (vm, "\nSource interface to EPG:");
-
-  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, "\nDestination IP4 to EPG:");
-
-  /* *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* */
-
-  vlib_cli_output (vm, "\nDestination IP6 to EPG:");
-
-  /* *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* */
-
-  return (NULL);
-}
-
-static clib_error_t *
-gbp_contract_show (vlib_main_t * vm,
-                  unformat_input_t * input, vlib_cli_command_t * cmd)
-{
-  gbp_contract_key_t key;
-  epg_id_t epg_id;
-
-  vlib_cli_output (vm, "Contracts:");
-
-  /* *INDENT-OFF* */
-  hash_foreach (key.as_u64, epg_id, gbp_contract_db.gc_hash,
-  {
-    vlib_cli_output (vm, "  {%d,%d} -> %d", key.gck_src,
-                     key.gck_dst, epg_id);
-  });
-  /* *INDENT-ON* */
-
-  return (NULL);
-}
-
-/*?
- * Show Group Based Policy Endpoints and derived information
- *
- * @cliexpar
- * @cliexstart{show gbp endpoint}
- * @cliexend
- ?*/
-/* *INDENT-OFF* */
-VLIB_CLI_COMMAND (gbp_endpoint_show_node, static) = {
-  .path = "show gbp endpoint",
-  .short_help = "show gbp endpoint\n",
-  .function = gbp_endpoint_show,
-};
-/* *INDENT-ON* */
-
-/*?
- * Show Group Based Policy Contracts
- *
- * @cliexpar
- * @cliexstart{show gbp contract}
- * @cliexend
- ?*/
-/* *INDENT-OFF* */
-VLIB_CLI_COMMAND (gbp_contract_show_node, static) = {
-  .path = "show gbp contract",
-  .short_help = "show gbp contract\n",
-  .function = gbp_contract_show,
-};
-/* *INDENT-ON* */
-
-#define foreach_gbp                    \
-  _(DENY,    "deny")
-
-typedef enum
-{
-#define _(sym,str) GBP_ERROR_##sym,
-  foreach_gbp
-#undef _
-    GBP_N_ERROR,
-} gbp_error_t;
-
-static char *gbp_error_strings[] = {
-#define _(sym,string) string,
-  foreach_gbp
-#undef _
-};
-
-typedef enum
-{
-#define _(sym,str) GBP_NEXT_##sym,
-  foreach_gbp
-#undef _
-    GBP_N_NEXT,
-} gbp_next_t;
-
-/**
- * per-packet trace data
- */
-typedef struct gbp_trace_t_
-{
-  /* per-pkt trace data */
-  epg_id_t src_epg;
-  epg_id_t dst_epg;
-  u32 acl_index;
-} gbp_trace_t;
-
-static inline uword
-gbp_inline (vlib_main_t * vm,
-           vlib_node_runtime_t * node, vlib_frame_t * frame, int is_ip6)
-{
-  u32 n_left_from, *from, *to_next;
-  gbp_next_t next_index;
-
-  next_index = 0;
-  n_left_from = frame->n_vectors;
-  from = vlib_frame_vector_args (frame);
-
-  while (n_left_from > 0)
-    {
-      u32 n_left_to_next;
-
-      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
-
-      while (n_left_from > 0 && n_left_to_next > 0)
-       {
-         vlib_buffer_t *b0;
-         u32 sw_if_index0;
-         gbp_next_t next0;
-         u32 bi0;
-         ip4_header_t *ip4_0;
-         ip6_header_t *ip6_0;
-         gbp_contract_key_t key0;
-         u32 acl_index0;
-         uword *p;
-
-         bi0 = from[0];
-         to_next[0] = bi0;
-         from += 1;
-         to_next += 1;
-         n_left_from -= 1;
-         n_left_to_next -= 1;
-
-         /* deny by default */
-         next0 = GBP_NEXT_DENY;
-
-         b0 = vlib_get_buffer (vm, bi0);
-         if (is_ip6)
-           ip6_0 = vlib_buffer_get_current (b0);
-         else
-           ip4_0 = vlib_buffer_get_current (b0);
-         sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
-
-         /*
-          * determine the src and dst EPG
-          */
-         key0.gck_src = gbp_itf_to_epg_db.gte_vec[sw_if_index0].gi_epg;
-
-         if (is_ip6)
-           p = hash_get_mem (gbp_ip6_to_epg_db.g6ie_hash,
-                             &ip6_0->dst_address);
-         else
-           p = hash_get (gbp_ip4_to_epg_db.g4ie_hash,
-                         ip4_0->dst_address.as_u32);
-
-         if (NULL != p)
-           {
-             key0.gck_dst = p[0];
-
-             /*
-              * If the src and dst are the same, then let it through
-              */
-             if (key0.gck_dst == key0.gck_src)
-               {
-                 vnet_feature_next (sw_if_index0, &next0, b0);
-                 acl_index0 = ~0;
-               }
-             else
-               {
-                 /*
-                  * find this src,dst pair in the egp->acl DB
-                  */
-                 p = hash_get (gbp_contract_db.gc_hash, key0.as_u64);
-
-                 if (NULL != p)
-                   {
-                     acl_index0 = p[0];
-
-                     /*
-                      * the ACL index stored is NULL, this means any-any so let it pass
-                      */
-                     if (~0 == acl_index0)
-                       {
-                         vnet_feature_next (sw_if_index0, &next0, b0);
-                       }
-                     else
-                       {
-                         /*
-                          * TODO tests against the ACL
-                          */
-                         /*
-                          * ACL tables are not available outside of ACL plugin
-                          * until then bypass the ACL to next node
-                          */
-                         vnet_feature_next (sw_if_index0, &next0, b0);
-                       }
-                   }
-                 else
-                   {
-                     /*
-                      * no ACL to apply for packets between these two EPGs.
-                      * GBP is a whitelist model, so no ACL implies deny, which
-                      * is the default result
-                      */
-                     acl_index0 = ~0;
-                   }
-               }
-           }
-         else
-           {
-             /*
-              * cannot determine the destinaiotn EPG, so we cannot enforce policy
-              * on this node. permit.
-              */
-             vnet_feature_next (sw_if_index0, &next0, b0);
-
-             key0.gck_dst = ~0;
-             acl_index0 = ~0;
-           }
-
-         if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
-                            (b0->flags & VLIB_BUFFER_IS_TRACED)))
-           {
-             gbp_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
-             t->src_epg = key0.gck_src;
-             t->dst_epg = key0.gck_dst;
-             t->acl_index = acl_index0;
-           }
-
-         /* verify speculative enqueue, maybe switch current next frame */
-         vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
-                                          to_next, n_left_to_next,
-                                          bi0, next0);
-       }
-
-      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
-    }
-
-  return frame->n_vectors;
-}
-
-/* packet trace format function */
-static u8 *
-format_gbp_trace (u8 * s, va_list * args)
-{
-  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
-  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
-  gbp_trace_t *t = va_arg (*args, gbp_trace_t *);
-
-  s = format (s, "gbp: src:%d dst:%d acl:%d",
-             t->src_epg, t->dst_epg, t->acl_index);
-
-  return s;
-}
-
-static inline uword
-gbp_4 (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
-{
-  return (gbp_inline (vm, node, frame, 0));
-}
-
-static inline uword
-gbp_6 (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
-{
-  return (gbp_inline (vm, node, frame, 1));
-}
-
-/* *INDENT-OFF* */
-VLIB_REGISTER_NODE (gbp_4_node) = {
-  .function = gbp_4,
-  .name = "gbp4",
-  .vector_size = sizeof (u32),
-  .format_trace = format_gbp_trace,
-  .type = VLIB_NODE_TYPE_INTERNAL,
-
-  .n_errors = ARRAY_LEN(gbp_error_strings),
-  .error_strings = gbp_error_strings,
-
-  .n_next_nodes = GBP_N_NEXT,
-
-  .next_nodes = {
-    [GBP_NEXT_DENY] = "ip4-drop",
-  },
-};
-
-VLIB_NODE_FUNCTION_MULTIARCH (gbp_4_node, gbp_4);
-
-VNET_FEATURE_INIT (gbp_4_node, static) = {
-    .arc_name = "ip4-unicast",
-    .node_name = "gbp4",
-    .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
-};
-
-VLIB_REGISTER_NODE (gbp_6_node) = {
-  .function = gbp_6,
-  .name = "gbp6",
-  .vector_size = sizeof (u32),
-  .format_trace = format_gbp_trace,
-  .type = VLIB_NODE_TYPE_INTERNAL,
-
-  .n_errors = ARRAY_LEN(gbp_error_strings),
-  .error_strings = gbp_error_strings,
-
-  .n_next_nodes = GBP_N_NEXT,
-
-  .next_nodes = {
-    [GBP_NEXT_DENY] = "ip6-drop",
-  },
-};
-
-VLIB_NODE_FUNCTION_MULTIARCH (gbp_6_node, gbp_6);
-
-VNET_FEATURE_INIT (gbp_6_node, static) = {
-    .arc_name = "ip6-unicast",
-    .node_name = "gbp6",
-    .runs_after = VNET_FEATURES ("acl-plugin-in-ip6-fa"),
-};
-/* *INDENT-ON* */
-
-static clib_error_t *
-gbp_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;
-}
-
-VLIB_INIT_FUNCTION (gbp_init);
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
index 334a743..8672fd3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013 Cisco and/or its affiliates.
+ * Copyright (c) 2018 Cisco and/or its affiliates.
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at:
  *
  */
 
-#ifndef included_vnet_gbp_h
-#define included_vnet_gbp_h
+#ifndef __GBP_H__
+#define __GBP_H__
 
-#include <vlib/vlib.h>
-#include <vnet/vnet.h>
-#include <vnet/ip/ip.h>
-
-typedef u32 epg_id_t;
-#define EPG_INVALID (~0)
-
-/**
- * The key for an Endpoint
- */
-typedef struct gbp_endpoint_key_t_
-{
-  /**
-   * The interface on which the EP is connected
-   */
-  u32 gek_sw_if_index;
-
-  /**
-   * The IP[46] address of the endpoint
-   */
-  ip46_address_t gek_ip;
-} gbp_endpoint_key_t;
-
-/**
- * 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
-   */
-  gbp_endpoint_key_t *ge_key;
-
-  /**
-   * The endpoint's designated EPG
-   */
-  epg_id_t ge_epg_id;
-} gbp_endpoint_t;
-
-extern void 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);
-
-typedef int (*gbp_endpoint_cb_t) (gbp_endpoint_t * gbpe, void *ctx);
-extern void gbp_endpoint_walk (gbp_endpoint_cb_t bgpe, void *ctx);
-
-
-/**
- * The key for an Contract
- */
-typedef struct gbp_contract_key_t_
-{
-  union
-  {
-    struct
-    {
-      /**
-       * source and destination EPGs for which the ACL applies
-       */
-      epg_id_t gck_src;
-      epg_id_t gck_dst;
-    };
-    u64 as_u64;
-  };
-} gbp_contract_key_t;
-
-/**
- * A Group Based Policy Contract.
- *  Determines the ACL that applies to traffic pass between two endpoint groups
- */
-typedef struct gbp_contract_t_
-{
-  /**
-   * source and destination EPGs
-   */
-  gbp_contract_key_t gc_key;
-
-  /**
-   * The ACL to apply for packets from the source to the destination EPG
-   */
-  u32 gc_acl_index;;
-} gbp_contract_t;
-
-
-extern void gbp_contract_update (epg_id_t src_epg,
-                                epg_id_t dst_epg, u32 acl_index);
-extern void gbp_contract_delete (epg_id_t src_epg, epg_id_t dst_epg);
-
-typedef int (*gbp_contract_cb_t) (gbp_contract_t * gbpe, void *ctx);
-extern void gbp_contract_walk (gbp_contract_cb_t bgpe, void *ctx);
+#include <plugins/gbp/gbp_types.h>
+#include <plugins/gbp/gbp_endpoint.h>
+#include <plugins/gbp/gbp_endpoint_group.h>
+#include <plugins/gbp/gbp_contract.h>
+#include <plugins/gbp/gbp_subnet.h>
+#include <plugins/gbp/gbp_recirc.h>
 
 #endif
 
index a65d9fc..3093309 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016 Cisco and/or its affiliates.
+ * Copyright (c) 2018 Cisco and/or its affiliates.
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at:
index 998d5d9..88b2cd1 100644 (file)
@@ -1,8 +1,6 @@
 /*
  *------------------------------------------------------------------
- * gbp_api.c - layer 2 emulation api
- *
- * Copyright (c) 2016 Cisco and/or its affiliates.
+ * Copyright (c) 2018 Cisco and/or its affiliates.
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at:
 #define foreach_gbp_api_msg                                 \
   _(GBP_ENDPOINT_ADD_DEL, gbp_endpoint_add_del)             \
   _(GBP_ENDPOINT_DUMP, gbp_endpoint_dump)                   \
+  _(GBP_SUBNET_ADD_DEL, gbp_subnet_add_del)                 \
+  _(GBP_SUBNET_DUMP, gbp_subnet_dump)                       \
+  _(GBP_ENDPOINT_GROUP_ADD_DEL, gbp_endpoint_group_add_del) \
+  _(GBP_ENDPOINT_GROUP_DUMP, gbp_endpoint_group_dump)       \
+  _(GBP_RECIRC_ADD_DEL, gbp_recirc_add_del)                 \
+  _(GBP_RECIRC_DUMP, gbp_recirc_dump)                       \
   _(GBP_CONTRACT_ADD_DEL, gbp_contract_add_del)             \
   _(GBP_CONTRACT_DUMP, gbp_contract_dump)
 
@@ -94,7 +98,8 @@ vl_api_gbp_endpoint_add_del_t_handler (vl_api_gbp_endpoint_add_del_t * mp)
 
   if (mp->is_add)
     {
-      gbp_endpoint_update (sw_if_index, &ip, ntohl (mp->endpoint.epg_id));
+      rv =
+       gbp_endpoint_update (sw_if_index, &ip, ntohl (mp->endpoint.epg_id));
     }
   else
     {
@@ -162,6 +167,223 @@ vl_api_gbp_endpoint_dump_t_handler (vl_api_gbp_endpoint_dump_t * mp)
   gbp_endpoint_walk (gbp_endpoint_send_details, &ctx);
 }
 
+static void
+  vl_api_gbp_endpoint_group_add_del_t_handler
+  (vl_api_gbp_endpoint_group_add_del_t * mp)
+{
+  vl_api_gbp_endpoint_group_add_del_reply_t *rmp;
+  u32 uplink_sw_if_index;
+  int rv = 0;
+
+  uplink_sw_if_index = ntohl (mp->epg.uplink_sw_if_index);
+  if (!vnet_sw_if_index_is_api_valid (uplink_sw_if_index))
+    goto bad_sw_if_index;
+
+  if (mp->is_add)
+    {
+      rv = gbp_endpoint_group_add (ntohl (mp->epg.epg_id),
+                                  ntohl (mp->epg.bd_id),
+                                  ntohl (mp->epg.ip4_table_id),
+                                  ntohl (mp->epg.ip6_table_id),
+                                  uplink_sw_if_index);
+    }
+  else
+    {
+      gbp_endpoint_group_delete (ntohl (mp->epg.epg_id));
+    }
+
+  BAD_SW_IF_INDEX_LABEL;
+
+  REPLY_MACRO (VL_API_GBP_ENDPOINT_GROUP_ADD_DEL_REPLY + GBP_MSG_BASE);
+}
+
+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;
+  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));
+
+  rv = gbp_subnet_add_del (ntohl (mp->subnet.table_id),
+                          &pfx,
+                          ntohl (mp->subnet.sw_if_index),
+                          ntohl (mp->subnet.epg_id),
+                          mp->is_add, mp->subnet.is_internal);
+
+  REPLY_MACRO (VL_API_GBP_SUBNET_ADD_DEL_REPLY + GBP_MSG_BASE);
+}
+
+static int
+gbp_subnet_send_details (u32 table_id,
+                        const fib_prefix_t * pfx,
+                        u32 sw_if_index,
+                        epg_id_t epg, u8 is_internal, void *args)
+{
+  vl_api_gbp_subnet_details_t *mp;
+  gbp_walk_ctx_t *ctx;
+
+  ctx = args;
+  mp = vl_msg_api_alloc (sizeof (*mp));
+  if (!mp)
+    return 1;
+
+  memset (mp, 0, sizeof (*mp));
+  mp->_vl_msg_id = ntohs (VL_API_GBP_SUBNET_DETAILS + GBP_MSG_BASE);
+  mp->context = ctx->context;
+
+  mp->subnet.is_internal = is_internal;
+  mp->subnet.sw_if_index = ntohl (sw_if_index);
+  mp->subnet.epg_id = ntohl (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));
+
+
+  vl_api_send_msg (ctx->reg, (u8 *) mp);
+
+  return (1);
+}
+
+static void
+vl_api_gbp_subnet_dump_t_handler (vl_api_gbp_subnet_dump_t * mp)
+{
+  vl_api_registration_t *reg;
+
+  reg = vl_api_client_index_to_registration (mp->client_index);
+  if (!reg)
+    return;
+
+  gbp_walk_ctx_t ctx = {
+    .reg = reg,
+    .context = mp->context,
+  };
+
+  gbp_subnet_walk (gbp_subnet_send_details, &ctx);
+}
+
+static int
+gbp_endpoint_group_send_details (gbp_endpoint_group_t * gepg, void *args)
+{
+  vl_api_gbp_endpoint_group_details_t *mp;
+  gbp_walk_ctx_t *ctx;
+
+  ctx = args;
+  mp = vl_msg_api_alloc (sizeof (*mp));
+  if (!mp)
+    return 1;
+
+  memset (mp, 0, sizeof (*mp));
+  mp->_vl_msg_id = ntohs (VL_API_GBP_ENDPOINT_GROUP_DETAILS + GBP_MSG_BASE);
+  mp->context = ctx->context;
+
+  mp->epg.uplink_sw_if_index = ntohl (gepg->gepg_uplink_sw_if_index);
+  mp->epg.epg_id = ntohl (gepg->gepg_id);
+  mp->epg.bd_id = ntohl (gepg->gepg_bd);
+  mp->epg.ip4_table_id = ntohl (gepg->gepg_rd[FIB_PROTOCOL_IP4]);
+  mp->epg.ip6_table_id = ntohl (gepg->gepg_rd[FIB_PROTOCOL_IP6]);
+
+  vl_api_send_msg (ctx->reg, (u8 *) mp);
+
+  return (1);
+}
+
+static void
+vl_api_gbp_endpoint_group_dump_t_handler (vl_api_gbp_endpoint_group_dump_t *
+                                         mp)
+{
+  vl_api_registration_t *reg;
+
+  reg = vl_api_client_index_to_registration (mp->client_index);
+  if (!reg)
+    return;
+
+  gbp_walk_ctx_t ctx = {
+    .reg = reg,
+    .context = mp->context,
+  };
+
+  gbp_endpoint_group_walk (gbp_endpoint_group_send_details, &ctx);
+}
+
+static void
+vl_api_gbp_recirc_add_del_t_handler (vl_api_gbp_recirc_add_del_t * mp)
+{
+  vl_api_gbp_recirc_add_del_reply_t *rmp;
+  u32 sw_if_index;
+  int rv = 0;
+
+  sw_if_index = ntohl (mp->recirc.sw_if_index);
+  if (!vnet_sw_if_index_is_api_valid (sw_if_index))
+    goto bad_sw_if_index;
+
+  if (mp->is_add)
+    gbp_recirc_add (sw_if_index,
+                   ntohl (mp->recirc.epg_id), mp->recirc.is_ext);
+  else
+    gbp_recirc_delete (sw_if_index);
+
+  BAD_SW_IF_INDEX_LABEL;
+
+  REPLY_MACRO (VL_API_GBP_RECIRC_ADD_DEL_REPLY + GBP_MSG_BASE);
+}
+
+static int
+gbp_recirc_send_details (gbp_recirc_t * gr, void *args)
+{
+  vl_api_gbp_recirc_details_t *mp;
+  gbp_walk_ctx_t *ctx;
+
+  ctx = args;
+  mp = vl_msg_api_alloc (sizeof (*mp));
+  if (!mp)
+    return 1;
+
+  memset (mp, 0, sizeof (*mp));
+  mp->_vl_msg_id = ntohs (VL_API_GBP_RECIRC_DETAILS + GBP_MSG_BASE);
+  mp->context = ctx->context;
+
+  mp->recirc.epg_id = ntohl (gr->gr_epg);
+  mp->recirc.sw_if_index = ntohl (gr->gr_sw_if_index);
+  mp->recirc.is_ext = ntohl (gr->gr_is_ext);
+
+  vl_api_send_msg (ctx->reg, (u8 *) mp);
+
+  return (1);
+}
+
+static void
+vl_api_gbp_recirc_dump_t_handler (vl_api_gbp_recirc_dump_t * mp)
+{
+  vl_api_registration_t *reg;
+
+  reg = vl_api_client_index_to_registration (mp->client_index);
+  if (!reg)
+    return;
+
+  gbp_walk_ctx_t ctx = {
+    .reg = reg,
+    .context = mp->context,
+  };
+
+  gbp_recirc_walk (gbp_recirc_send_details, &ctx);
+}
+
 static void
 vl_api_gbp_contract_add_del_t_handler (vl_api_gbp_contract_add_del_t * mp)
 {
diff --git a/src/plugins/gbp/gbp_classify.c b/src/plugins/gbp/gbp_classify.c
new file mode 100644 (file)
index 0000000..859d4f9
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * gbp.h : Group Based Policy
+ *
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <plugins/gbp/gbp.h>
+
+typedef enum gbp_src_classify_type_t_
+{
+  GBP_SRC_CLASSIFY_NULL,
+  GBP_SRC_CLASSIFY_PORT,
+} gbp_src_classify_type_t;
+
+#define GBP_SRC_N_CLASSIFY (GBP_SRC_CLASSIFY_PORT + 1)
+
+/**
+ * Grouping of global data for the GBP source EPG classification feature
+ */
+typedef struct gbp_src_classify_main_t_
+{
+  /**
+   * Next nodes for L2 output features
+   */
+  u32 l2_input_feat_next[GBP_SRC_N_CLASSIFY][32];
+} gbp_src_classify_main_t;
+
+static gbp_src_classify_main_t gbp_src_classify_main;
+
+/**
+ * per-packet trace data
+ */
+typedef struct gbp_classify_trace_t_
+{
+  /* per-pkt trace data */
+  epg_id_t src_epg;
+} gbp_classify_trace_t;
+
+/*
+ * determine the SRC EPG form the input port
+ */
+always_inline uword
+gbp_classify_inline (vlib_main_t * vm,
+                    vlib_node_runtime_t * node,
+                    vlib_frame_t * frame,
+                    gbp_src_classify_type_t type, u8 is_l3)
+{
+  gbp_src_classify_main_t *gscm = &gbp_src_classify_main;
+  u32 n_left_from, *from, *to_next;
+  u32 next_index;
+
+  next_index = 0;
+  n_left_from = frame->n_vectors;
+  from = vlib_frame_vector_args (frame);
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         u32 next0, bi0, src_epg, sw_if_index0;
+         vlib_buffer_t *b0;
+
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+
+         b0 = vlib_get_buffer (vm, bi0);
+
+         sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+
+         if (GBP_SRC_CLASSIFY_NULL == type)
+           {
+             src_epg = ~0;
+             next0 =
+               vnet_l2_feature_next (b0, gscm->l2_input_feat_next[type],
+                                     L2INPUT_FEAT_GBP_NULL_CLASSIFY);
+           }
+         else
+           {
+             src_epg = gbp_port_to_epg (sw_if_index0);
+             if (is_l3)
+               {
+                 /*
+                  * Go straight to looukp, do not pass go, do not collect $200
+                  */
+                 next0 = 0;
+               }
+             else
+               {
+                 next0 =
+                   vnet_l2_feature_next (b0, gscm->l2_input_feat_next[type],
+                                         L2INPUT_FEAT_GBP_SRC_CLASSIFY);
+               }
+           }
+
+         vnet_buffer2 (b0)->gbp.src_epg = src_epg;
+
+         if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
+           {
+             gbp_classify_trace_t *t =
+               vlib_add_trace (vm, node, b0, sizeof (*t));
+             t->src_epg = src_epg;
+           }
+
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+                                          to_next, n_left_to_next,
+                                          bi0, next0);
+       }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+  return frame->n_vectors;
+}
+
+static uword
+gbp_src_classify (vlib_main_t * vm,
+                 vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+  return (gbp_classify_inline (vm, node, frame, GBP_SRC_CLASSIFY_PORT, 0));
+}
+
+static uword
+gbp_null_classify (vlib_main_t * vm,
+                  vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+  return (gbp_classify_inline (vm, node, frame, GBP_SRC_CLASSIFY_NULL, 0));
+}
+
+static uword
+gbp_ip4_src_classify (vlib_main_t * vm,
+                     vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+  return (gbp_classify_inline (vm, node, frame, 0, 1));
+}
+
+static uword
+gbp_ip6_src_classify (vlib_main_t * vm,
+                     vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+  return (gbp_classify_inline (vm, node, frame, 0, 1));
+}
+
+
+/* packet trace format function */
+static u8 *
+format_gbp_classify_trace (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  gbp_classify_trace_t *t = va_arg (*args, gbp_classify_trace_t *);
+
+  s = format (s, "src-epg:%d", t->src_epg);
+
+  return s;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (gbp_null_classify_node) = {
+  .function = gbp_null_classify,
+  .name = "gbp-null-classify",
+  .vector_size = sizeof (u32),
+  .format_trace = format_gbp_classify_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = 0,
+  .n_next_nodes = 0,
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (gbp_null_classify_node, gbp_null_classify);
+
+VLIB_REGISTER_NODE (gbp_src_classify_node) = {
+  .function = gbp_src_classify,
+  .name = "gbp-src-classify",
+  .vector_size = sizeof (u32),
+  .format_trace = format_gbp_classify_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = 0,
+  .n_next_nodes = 0,
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (gbp_src_classify_node, gbp_src_classify);
+
+VLIB_REGISTER_NODE (gbp_ip4_src_classify_node) = {
+  .function = gbp_ip4_src_classify,
+  .name = "ip4-gbp-src-classify",
+  .vector_size = sizeof (u32),
+  .format_trace = format_gbp_classify_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = 0,
+  .n_next_nodes = 1,
+  .next_nodes = {
+    [0] = "ip4-lookup"
+  },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (gbp_ip4_src_classify_node, gbp_ip4_src_classify);
+
+VLIB_REGISTER_NODE (gbp_ip6_src_classify_node) = {
+  .function = gbp_ip6_src_classify,
+  .name = "ip6-gbp-src-classify",
+  .vector_size = sizeof (u32),
+  .format_trace = format_gbp_classify_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = 0,
+  .n_next_nodes = 1,
+  .next_nodes = {
+    [0] = "ip6-lookup"
+  },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (gbp_ip6_src_classify_node, gbp_ip6_src_classify);
+
+VNET_FEATURE_INIT (gbp_ip4_src_classify_feat_node, static) =
+{
+  .arc_name = "ip4-unicast",
+  .node_name = "ip4-gbp-src-classify",
+  .runs_before = VNET_FEATURES ("nat44-out2in"),
+};
+VNET_FEATURE_INIT (gbp_ip6_src_classify_feat_node, static) =
+{
+  .arc_name = "ip6-unicast",
+  .node_name = "ip6-gbp-src-classify",
+  .runs_before = VNET_FEATURES ("nat66-out2in"),
+};
+
+static clib_error_t *
+gbp_src_classify_init (vlib_main_t * vm)
+{
+  gbp_src_classify_main_t *em = &gbp_src_classify_main;
+
+  /* Initialize the feature next-node indexes */
+  feat_bitmap_init_next_nodes (vm,
+                              gbp_src_classify_node.index,
+                              L2INPUT_N_FEAT,
+                              l2input_get_feat_names (),
+                              em->l2_input_feat_next[GBP_SRC_CLASSIFY_NULL]);
+  feat_bitmap_init_next_nodes (vm,
+                              gbp_null_classify_node.index,
+                              L2INPUT_N_FEAT,
+                              l2input_get_feat_names (),
+                              em->l2_input_feat_next[GBP_SRC_CLASSIFY_PORT]);
+
+  return 0;
+}
+
+VLIB_INIT_FUNCTION (gbp_src_classify_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/gbp/gbp_contract.c b/src/plugins/gbp/gbp_contract.c
new file mode 100644 (file)
index 0000000..71d8bcf
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * gbp.h : Group Based Policy
+ *
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <plugins/gbp/gbp.h>
+
+/**
+ * Single contract DB instance
+ */
+gbp_contract_db_t gbp_contract_db;
+
+void
+gbp_contract_update (epg_id_t src_epg, epg_id_t dst_epg, u32 acl_index)
+{
+  gbp_contract_key_t key = {
+    .gck_src = src_epg,
+    .gck_dst = dst_epg,
+  };
+
+  hash_set (gbp_contract_db.gc_hash, key.as_u64, acl_index);
+}
+
+void
+gbp_contract_delete (epg_id_t src_epg, epg_id_t dst_epg)
+{
+  gbp_contract_key_t key = {
+    .gck_src = src_epg,
+    .gck_dst = dst_epg,
+  };
+
+  hash_unset (gbp_contract_db.gc_hash, key.as_u64);
+}
+
+void
+gbp_contract_walk (gbp_contract_cb_t cb, void *ctx)
+{
+  gbp_contract_key_t key;
+  u32 acl_index;
+
+  /* *INDENT-OFF* */
+  hash_foreach(key.as_u64, acl_index, gbp_contract_db.gc_hash,
+  ({
+    gbp_contract_t gbpc = {
+      .gc_key = key,
+      .gc_acl_index = acl_index,
+    };
+
+    if (!cb(&gbpc, ctx))
+      break;
+  }));
+  /* *INDENT-ON* */
+}
+
+static clib_error_t *
+gbp_contract_cli (vlib_main_t * vm,
+                 unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  epg_id_t src_epg_id = EPG_INVALID, dst_epg_id = EPG_INVALID;
+  u32 acl_index = ~0;
+  u8 add = 1;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "add"))
+       add = 1;
+      else if (unformat (input, "del"))
+       add = 0;
+      else if (unformat (input, "src-epg %d", &src_epg_id))
+       ;
+      else if (unformat (input, "dst-epg %d", &dst_epg_id))
+       ;
+      else if (unformat (input, "acl-index %d", &acl_index))
+       ;
+      else
+       break;
+    }
+
+  if (EPG_INVALID == src_epg_id)
+    return clib_error_return (0, "Source EPG-ID must be specified");
+  if (EPG_INVALID == dst_epg_id)
+    return clib_error_return (0, "Destination EPG-ID must be specified");
+
+  if (add)
+    {
+      gbp_contract_update (src_epg_id, dst_epg_id, acl_index);
+    }
+  else
+    {
+      gbp_contract_delete (src_epg_id, dst_epg_id);
+    }
+
+  return (NULL);
+}
+
+/*?
+ * Configure a GBP Contract
+ *
+ * @cliexpar
+ * @cliexstart{set gbp contract [del] src-epg <ID> dst-epg <ID> acl-index <ACL>}
+ * @cliexend
+ ?*/
+VLIB_CLI_COMMAND (gbp_contract_cli_node, static) =
+{
+.path = "gbp contract",.short_help =
+    "gbp contract [del] src-epg <ID> dst-epg <ID> acl-index <ACL>",.function
+    = gbp_contract_cli,};
+/* *INDENT-ON* */
+
+static clib_error_t *
+gbp_contract_show (vlib_main_t * vm,
+                  unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  gbp_contract_key_t key;
+  epg_id_t epg_id;
+
+  vlib_cli_output (vm, "Contracts:");
+
+  /* *INDENT-OFF* */
+  hash_foreach (key.as_u64, epg_id, gbp_contract_db.gc_hash,
+  {
+    vlib_cli_output (vm, "  {%d,%d} -> %d", key.gck_src,
+                     key.gck_dst, epg_id);
+  });
+  /* *INDENT-ON* */
+
+  return (NULL);
+}
+
+/*?
+ * Show Group Based Policy Contracts
+ *
+ * @cliexpar
+ * @cliexstart{show gbp contract}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_contract_show_node, static) = {
+  .path = "show gbp contract",
+  .short_help = "show gbp contract\n",
+  .function = gbp_contract_show,
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/gbp/gbp_contract.h b/src/plugins/gbp/gbp_contract.h
new file mode 100644 (file)
index 0000000..1964098
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __GBP_CONTRACT_H__
+#define __GBP_CONTRACT_H__
+
+#include <plugins/gbp/gbp_types.h>
+
+/**
+ * The key for an Contract
+ */
+typedef struct gbp_contract_key_t_
+{
+  union
+  {
+    struct
+    {
+      /**
+       * source and destination EPGs for which the ACL applies
+       */
+      epg_id_t gck_src;
+      epg_id_t gck_dst;
+    };
+    u64 as_u64;
+  };
+} gbp_contract_key_t;
+
+/**
+ * A Group Based Policy Contract.
+ *  Determines the ACL that applies to traffic pass between two endpoint groups
+ */
+typedef struct gbp_contract_t_
+{
+  /**
+   * source and destination EPGs
+   */
+  gbp_contract_key_t gc_key;
+
+  /**
+   * The ACL to apply for packets from the source to the destination EPG
+   */
+  u32 gc_acl_index;;
+} gbp_contract_t;
+
+/**
+ * EPG src,dst pair to ACL mapping table, aka contract DB
+ */
+typedef struct gbp_contract_db_t_
+{
+  /**
+   * We can form a u64 key from the pair, so use a simple hash table
+   */
+  uword *gc_hash;
+} gbp_contract_db_t;
+
+extern void gbp_contract_update (epg_id_t src_epg,
+                                epg_id_t dst_epg, u32 acl_index);
+extern void gbp_contract_delete (epg_id_t src_epg, epg_id_t dst_epg);
+
+typedef int (*gbp_contract_cb_t) (gbp_contract_t * gbpe, void *ctx);
+extern void gbp_contract_walk (gbp_contract_cb_t bgpe, void *ctx);
+
+
+/**
+ * DP functions and databases
+ */
+extern gbp_contract_db_t gbp_contract_db;
+
+always_inline u32
+gbp_acl_lookup (gbp_contract_key_t * key)
+{
+  uword *p;
+
+  p = hash_get (gbp_contract_db.gc_hash, key->as_u64);
+
+  if (NULL != p)
+    return (p[0]);
+
+  return (~0);
+}
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/gbp/gbp_endpoint.c b/src/plugins/gbp/gbp_endpoint.c
new file mode 100644 (file)
index 0000000..91505da
--- /dev/null
@@ -0,0 +1,393 @@
+/*
+ * gbp.h : Group Based Policy
+ *
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <plugins/gbp/gbp_endpoint.h>
+#include <plugins/gbp/gbp_endpoint_group.h>
+
+#include <vnet/ethernet/arp_packet.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;
+
+/**
+ * IP6 destintion address to destination EPG mapping table
+ */
+typedef struct gbp_ip6_to_epg_db_t_
+{
+  /**
+   * use a memroy hash table
+   */
+  uword *g6ie_hash;
+} gbp_ip6_to_epg_db_t;
+
+static gbp_ip6_to_epg_db_t gbp_ip6_to_epg_db;
+
+
+const static gbp_itf_t ITF_INVALID = {
+  .gi_epg = EPG_INVALID,
+  .gi_ref_count = 0,
+};
+
+gbp_itf_to_epg_db_t gbp_itf_to_epg_db;
+
+/**
+ * Pool of GBP endpoints
+ */
+static gbp_endpoint_t *gbp_endpoint_pool;
+
+/**
+ * DB of endpoints
+ */
+static uword *gbp_endpoint_db;
+
+static void
+gbp_ip_epg_update (const ip46_address_t * ip, epg_id_t epg_id)
+{
+  /*
+   * 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);
+    }
+}
+
+static void
+gbp_ip_epg_delete (const ip46_address_t * ip)
+{
+  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);
+    }
+}
+
+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--;
+}
+
+int
+gbp_endpoint_update (u32 sw_if_index,
+                    const ip46_address_t * ip, epg_id_t epg_id)
+{
+  gbp_endpoint_key_t key = {
+    .gek_ip = *ip,
+    .gek_sw_if_index = sw_if_index,
+  };
+  gbp_endpoint_group_t *gepg;
+  gbp_endpoint_t *gbpe;
+  uword *p;
+
+  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)
+    {
+      gbpe = pool_elt_at_index (gbp_endpoint_pool, p[0]);
+    }
+  else
+    {
+      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));
+
+      hash_set_mem (gbp_endpoint_db, gbpe->ge_key, gbpe - gbp_endpoint_pool);
+    }
+
+  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,
+                         vnet_get_sup_hw_interface
+                         (vnet_get_main (), gepg->gepg_uplink_sw_if_index));
+  else
+    send_ip6_na_w_addr (vlib_get_main (),
+                       &gbpe->ge_key->gek_ip.ip6,
+                       vnet_get_sup_hw_interface
+                       (vnet_get_main (), gepg->gepg_uplink_sw_if_index));
+
+  return (0);
+}
+
+void
+gbp_endpoint_delete (u32 sw_if_index, const ip46_address_t * ip)
+{
+  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 (p)
+    {
+      gbpe = pool_elt_at_index (gbp_endpoint_pool, p[0]);
+
+      hash_unset_mem (gbp_endpoint_db, gbpe->ge_key);
+
+      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);
+
+      clib_mem_free (gbpe->ge_key);
+
+      pool_put (gbp_endpoint_pool, gbpe);
+    }
+}
+
+void
+gbp_endpoint_walk (gbp_endpoint_cb_t cb, void *ctx)
+{
+  gbp_endpoint_t *gbpe;
+
+  /* *INDENT-OFF* */
+  pool_foreach(gbpe, gbp_endpoint_pool,
+  {
+    if (!cb(gbpe, ctx))
+      break;
+  });
+  /* *INDENT-ON* */
+}
+
+static clib_error_t *
+gbp_endpoint_cli (vlib_main_t * vm,
+                 unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  vnet_main_t *vnm = vnet_get_main ();
+  epg_id_t epg_id = EPG_INVALID;
+  ip46_address_t ip = { };
+  u32 sw_if_index = ~0;
+  u8 add = 1;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "%U", unformat_vnet_sw_interface,
+                   vnm, &sw_if_index))
+       ;
+      else if (unformat (input, "add"))
+       add = 1;
+      else if (unformat (input, "del"))
+       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, "ip %U", unformat_ip6_address, &ip.ip6))
+       ;
+      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);
+  else
+    gbp_endpoint_delete (sw_if_index, &ip);
+
+  return (NULL);
+}
+
+
+/*?
+ * Configure a GBP Endpoint
+ *
+ * @cliexpar
+ * @cliexstart{set gbp endpoint [del] <interface> epg <ID> ip <IP>}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_endpoint_cli_node, static) = {
+  .path = "gbp endpoint",
+  .short_help = "gbp endpoint [del] <interface> epg <ID> ip <IP>",
+  .function = gbp_endpoint_cli,
+};
+/* *INDENT-ON* */
+
+static int
+gbp_endpoint_show_one (gbp_endpoint_t * gbpe, void *ctx)
+{
+  vnet_main_t *vnm = vnet_get_main ();
+  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);
+
+  return (1);
+}
+
+static clib_error_t *
+gbp_endpoint_show (vlib_main_t * vm,
+                  unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  vnet_main_t *vnm = vnet_get_main ();
+  ip46_address_t ip, *ipp;
+  epg_id_t epg_id;
+  u32 sw_if_index;
+
+  vlib_cli_output (vm, "Endpoints:");
+  gbp_endpoint_walk (gbp_endpoint_show_one, vm);
+
+  vlib_cli_output (vm, "\nSource interface to EPG:");
+
+  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, "\nDestination IP4 to EPG:");
+
+  /* *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* */
+
+  vlib_cli_output (vm, "\nDestination IP6 to EPG:");
+
+  /* *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* */
+
+  return (NULL);
+}
+
+
+/*?
+ * Show Group Based Policy Endpoints and derived information
+ *
+ * @cliexpar
+ * @cliexstart{show gbp endpoint}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_endpoint_show_node, static) = {
+  .path = "show gbp endpoint",
+  .short_help = "show gbp endpoint\n",
+  .function = gbp_endpoint_show,
+};
+/* *INDENT-ON* */
+
+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;
+}
+
+VLIB_INIT_FUNCTION (gbp_endpoint_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/gbp/gbp_endpoint.h b/src/plugins/gbp/gbp_endpoint.h
new file mode 100644 (file)
index 0000000..000c211
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __GBP_ENDPOINT_H__
+#define __GBP_ENDPOINT_H__
+
+#include <plugins/gbp/gbp_types.h>
+#include <vnet/ip/ip.h>
+
+/**
+ * The key for an Endpoint
+ */
+typedef struct gbp_endpoint_key_t_
+{
+  /**
+   * The interface on which the EP is connected
+   */
+  u32 gek_sw_if_index;
+
+  /**
+   * The IP[46] address of the endpoint
+   */
+  ip46_address_t gek_ip;
+} gbp_endpoint_key_t;
+
+/**
+ * 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
+   */
+  gbp_endpoint_key_t *ge_key;
+
+  /**
+   * The endpoint's designated EPG
+   */
+  epg_id_t ge_epg_id;
+} 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;
+
+/**
+ * Interface to source EPG DB - a per-interface vector
+ */
+typedef struct gbp_itf_to_epg_db_t_
+{
+  gbp_itf_t *gte_vec;
+} gbp_itf_to_epg_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);
+
+typedef int (*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;
+
+/**
+ * Get the source EPG for a port/interface
+ */
+always_inline u32
+gbp_port_to_epg (u32 sw_if_index)
+{
+  return (gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg);
+}
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/gbp/gbp_endpoint_group.c b/src/plugins/gbp/gbp_endpoint_group.c
new file mode 100644 (file)
index 0000000..ed312d3
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * gbp.h : Group Based Policy
+ *
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <plugins/gbp/gbp_endpoint_group.h>
+#include <plugins/gbp/gbp_endpoint.h>
+
+#include <vnet/dpo/dvr_dpo.h>
+#include <vnet/fib/fib_table.h>
+
+/**
+ * Pool of GBP endpoint_groups
+ */
+gbp_endpoint_group_t *gbp_endpoint_group_pool;
+
+/**
+ * DB of endpoint_groups
+ */
+gbp_endpoint_group_db_t gbp_endpoint_group_db;
+
+gbp_endpoint_group_t *
+gbp_endpoint_group_find (epg_id_t epg_id)
+{
+  uword *p;
+
+  p = hash_get (gbp_endpoint_group_db.gepg_hash, epg_id);
+
+  if (NULL != p)
+    return (pool_elt_at_index (gbp_endpoint_group_pool, p[0]));
+
+  return (NULL);
+}
+
+int
+gbp_endpoint_group_add (epg_id_t epg_id,
+                       u32 bd_id,
+                       u32 ip4_table_id,
+                       u32 ip6_table_id, u32 uplink_sw_if_index)
+{
+  gbp_endpoint_group_t *gepg;
+
+  gepg = gbp_endpoint_group_find (epg_id);
+
+  if (NULL == gepg)
+    {
+      fib_protocol_t fproto;
+
+      pool_get (gbp_endpoint_group_pool, gepg);
+      memset (gepg, 0, sizeof (*gepg));
+
+      gepg->gepg_id = epg_id;
+      gepg->gepg_bd = bd_id;
+      gepg->gepg_rd[FIB_PROTOCOL_IP4] = ip4_table_id;
+      gepg->gepg_rd[FIB_PROTOCOL_IP6] = ip6_table_id;
+      gepg->gepg_uplink_sw_if_index = uplink_sw_if_index;
+
+      /*
+       * an egress DVR dpo for internal subnets to use when sending
+       * on the uplink interface
+       */
+      FOR_EACH_FIB_IP_PROTOCOL (fproto)
+      {
+       gepg->gepg_fib_index[fproto] =
+         fib_table_find_or_create_and_lock (fproto,
+                                            gepg->gepg_rd[fproto],
+                                            FIB_SOURCE_PLUGIN_HI);
+
+       if (~0 == gepg->gepg_fib_index[fproto])
+         {
+           return (VNET_API_ERROR_NO_SUCH_FIB);
+         }
+
+       dvr_dpo_add_or_lock (uplink_sw_if_index,
+                            fib_proto_to_dpo (fproto),
+                            &gepg->gepg_dpo[fproto]);
+      }
+
+      /*
+       * packets direct from the uplink have had policy applied
+       */
+      l2input_intf_bitmap_enable (gepg->gepg_uplink_sw_if_index,
+                                 L2INPUT_FEAT_GBP_NULL_CLASSIFY, 1);
+
+      hash_set (gbp_endpoint_group_db.gepg_hash,
+               gepg->gepg_id, gepg - gbp_endpoint_group_pool);
+
+    }
+
+  return (0);
+}
+
+void
+gbp_endpoint_group_delete (epg_id_t epg_id)
+{
+  gbp_endpoint_group_t *gepg;
+  uword *p;
+
+  p = hash_get (gbp_endpoint_group_db.gepg_hash, epg_id);
+
+  if (NULL != p)
+    {
+      fib_protocol_t fproto;
+
+      gepg = pool_elt_at_index (gbp_endpoint_group_pool, p[0]);
+
+      l2input_intf_bitmap_enable (gepg->gepg_uplink_sw_if_index,
+                                 L2INPUT_FEAT_GBP_NULL_CLASSIFY, 0);
+
+      FOR_EACH_FIB_IP_PROTOCOL (fproto)
+      {
+       dpo_reset (&gepg->gepg_dpo[fproto]);
+       fib_table_unlock (gepg->gepg_fib_index[fproto],
+                         fproto, FIB_SOURCE_PLUGIN_HI);
+      }
+
+      hash_unset (gbp_endpoint_group_db.gepg_hash, epg_id);
+
+      pool_put (gbp_endpoint_group_pool, gepg);
+    }
+}
+
+void
+gbp_endpoint_group_walk (gbp_endpoint_group_cb_t cb, void *ctx)
+{
+  gbp_endpoint_group_t *gbpe;
+
+  /* *INDENT-OFF* */
+  pool_foreach(gbpe, gbp_endpoint_group_pool,
+  {
+    if (!cb(gbpe, ctx))
+      break;
+  });
+  /* *INDENT-ON* */
+}
+
+static clib_error_t *
+gbp_endpoint_group_cli (vlib_main_t * vm,
+                       unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  vnet_main_t *vnm = vnet_get_main ();
+  epg_id_t epg_id = EPG_INVALID;
+  u32 uplink_sw_if_index = ~0;
+  u32 bd_id = ~0;
+  u32 rd_id = ~0;
+  u8 add = 1;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "%U", unformat_vnet_sw_interface,
+                   vnm, &uplink_sw_if_index))
+       ;
+      else if (unformat (input, "add"))
+       add = 1;
+      else if (unformat (input, "del"))
+       add = 0;
+      else if (unformat (input, "epg %d", &epg_id))
+       ;
+      else if (unformat (input, "bd %d", &bd_id))
+       ;
+      else if (unformat (input, "rd %d", &rd_id))
+       ;
+      else
+       break;
+    }
+
+  if (EPG_INVALID == epg_id)
+    return clib_error_return (0, "EPG-ID must be specified");
+
+  if (add)
+    {
+      if (~0 == uplink_sw_if_index)
+       return clib_error_return (0, "interface must be specified");
+      if (~0 == bd_id)
+       return clib_error_return (0, "Bridge-domain must be specified");
+      if (~0 == rd_id)
+       return clib_error_return (0, "route-domain must be specified");
+
+      gbp_endpoint_group_add (epg_id, bd_id, rd_id, rd_id,
+                             uplink_sw_if_index);
+    }
+  else
+    gbp_endpoint_group_delete (epg_id);
+
+  return (NULL);
+}
+
+/*?
+ * Configure a GBP Endpoint Group
+ *
+ * @cliexpar
+ * @cliexstart{set gbp endpoint-group [del] epg <ID> bd <ID> <interface>}
+ * @cliexend
+ ?*/
+/* *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>",
+  .function = gbp_endpoint_group_cli,
+};
+
+static int
+gbp_endpoint_group_show_one (gbp_endpoint_group_t *gepg, void *ctx)
+{
+  vnet_main_t *vnm = vnet_get_main ();
+  vlib_main_t *vm;
+
+  vm = ctx;
+  vlib_cli_output (vm, "  %d, bd:%d, ip4:%d ip6:%d uplink:%U",
+                   gepg->gepg_id,
+                   gepg->gepg_bd,
+                   gepg->gepg_rd[FIB_PROTOCOL_IP4],
+                   gepg->gepg_rd[FIB_PROTOCOL_IP6],
+                  format_vnet_sw_if_index_name, vnm, gepg->gepg_uplink_sw_if_index);
+
+  return (1);
+}
+
+static clib_error_t *
+gbp_endpoint_group_show (vlib_main_t * vm,
+                  unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  vlib_cli_output (vm, "Endpoint-Groups:");
+  gbp_endpoint_group_walk (gbp_endpoint_group_show_one, vm);
+
+  return (NULL);
+}
+
+
+/*?
+ * Show Group Based Policy Endpoint_Groups and derived information
+ *
+ * @cliexpar
+ * @cliexstart{show gbp endpoint_group}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_endpoint_group_show_node, static) = {
+  .path = "show gbp endpoint-group",
+  .short_help = "show gbp endpoint-group\n",
+  .function = gbp_endpoint_group_show,
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/gbp/gbp_endpoint_group.h b/src/plugins/gbp/gbp_endpoint_group.h
new file mode 100644 (file)
index 0000000..f71e5f5
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __GBP_ENDPOINT_GROUP_H__
+#define __GBP_ENDPOINT_GROUP_H__
+
+#include <plugins/gbp/gbp_types.h>
+
+#include <vnet/fib/fib_types.h>
+
+/**
+ * An Endpoint Group representation
+ */
+typedef struct gpb_endpoint_group_t_
+{
+  /**
+   * ID
+   */
+  epg_id_t gepg_id;
+
+  /**
+   * Bridge-domain ID the EPG is in
+   */
+  u32 gepg_bd;
+
+  /**
+   * route-domain/IP-table ID the EPG is in
+   */
+  u32 gepg_rd[FIB_PROTOCOL_IP_MAX];
+
+  /**
+   * resulting FIB indices
+   */
+  u32 gepg_fib_index[FIB_PROTOCOL_IP_MAX];
+
+  /**
+   * Is the EPG an external/NAT
+   */
+  u8 gepg_is_ext;
+
+  /**
+   * the uplink interface dedicated to the EPG
+   */
+  u32 gepg_uplink_sw_if_index;
+
+  /**
+   * The DPO used in the L3 path for forwarding internal subnets
+   */
+  dpo_id_t gepg_dpo[FIB_PROTOCOL_IP_MAX];
+} gbp_endpoint_group_t;
+
+/**
+ * EPG DB, key'd on EGP-ID
+ */
+typedef struct gbp_endpoint_group_db_t_
+{
+  uword *gepg_hash;
+} gbp_endpoint_group_db_t;
+
+extern int gbp_endpoint_group_add (epg_id_t epg_id,
+                                  u32 bd_id,
+                                  u32 ip4_table_id,
+                                  u32 ip6_table_id, u32 uplink_sw_if_index);
+extern void gbp_endpoint_group_delete (epg_id_t epg_id);
+
+typedef int (*gbp_endpoint_group_cb_t) (gbp_endpoint_group_t * gbpe,
+                                       void *ctx);
+extern void gbp_endpoint_group_walk (gbp_endpoint_group_cb_t bgpe, void *ctx);
+
+extern gbp_endpoint_group_t *gbp_endpoint_group_find (epg_id_t epg_id);
+
+/**
+ * DP functions and databases
+ */
+extern gbp_endpoint_group_db_t gbp_endpoint_group_db;
+extern gbp_endpoint_group_t *gbp_endpoint_group_pool;
+
+always_inline u32
+gbp_epg_itf_lookup (epg_id_t epg)
+{
+  uword *p;
+
+  p = hash_get (gbp_endpoint_group_db.gepg_hash, epg);
+
+  if (NULL != p)
+    {
+      gbp_endpoint_group_t *gepg;
+
+      gepg = pool_elt_at_index (gbp_endpoint_group_pool, p[0]);
+      return (gepg->gepg_uplink_sw_if_index);
+    }
+  return (~0);
+}
+
+always_inline const dpo_id_t *
+gbp_epg_dpo_lookup (epg_id_t epg, fib_protocol_t fproto)
+{
+  uword *p;
+
+  p = hash_get (gbp_endpoint_group_db.gepg_hash, epg);
+
+  if (NULL != p)
+    {
+      gbp_endpoint_group_t *gepg;
+
+      gepg = pool_elt_at_index (gbp_endpoint_group_pool, p[0]);
+      return (&gepg->gepg_dpo[fproto]);
+    }
+  return (NULL);
+}
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/gbp/gbp_fwd.c b/src/plugins/gbp/gbp_fwd.c
new file mode 100644 (file)
index 0000000..fec5703
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <plugins/gbp/gbp.h>
+
+/**
+ * Grouping of global data for the GBP source EPG classification feature
+ */
+typedef struct gbp_fwd_main_t_
+{
+  /**
+   * Next nodes for L2 output features
+   */
+  u32 l2_input_feat_next[32];
+} gbp_fwd_main_t;
+
+static gbp_fwd_main_t gbp_fwd_main;
+
+#define foreach_gbp_fwd                      \
+  _(DROP,    "drop")                         \
+  _(OUTPUT,  "output")
+
+typedef enum
+{
+#define _(sym,str) GBP_FWD_ERROR_##sym,
+  foreach_gbp_fwd
+#undef _
+    GBP_FWD_N_ERROR,
+} gbp_fwd_error_t;
+
+static char *gbp_fwd_error_strings[] = {
+#define _(sym,string) string,
+  foreach_gbp_fwd
+#undef _
+};
+
+typedef enum
+{
+#define _(sym,str) GBP_FWD_NEXT_##sym,
+  foreach_gbp_fwd
+#undef _
+    GBP_FWD_N_NEXT,
+} gbp_fwd_next_t;
+
+/**
+ * per-packet trace data
+ */
+typedef struct gbp_fwd_trace_t_
+{
+  /* per-pkt trace data */
+  epg_id_t src_epg;
+  u32 sw_if_index;
+} gbp_fwd_trace_t;
+
+static uword
+gbp_fwd (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+  u32 n_left_from, *from, *to_next;
+  u32 next_index;
+
+  next_index = 0;
+  n_left_from = frame->n_vectors;
+  from = vlib_frame_vector_args (frame);
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         u32 bi0, sw_if_index0, src_epg;
+         gbp_fwd_next_t next0;
+         vlib_buffer_t *b0;
+
+         next0 = GBP_FWD_NEXT_DROP;
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+
+         b0 = vlib_get_buffer (vm, bi0);
+
+         /*
+          * lookup the uplink based on src EPG
+          */
+         src_epg = vnet_buffer2 (b0)->gbp.src_epg;
+
+         sw_if_index0 = gbp_epg_itf_lookup (src_epg);
+
+         if (~0 != sw_if_index0)
+           {
+             vnet_buffer (b0)->sw_if_index[VLIB_TX] = sw_if_index0;
+
+             next0 = GBP_FWD_NEXT_OUTPUT;
+           }
+         /*
+          * else
+          *  don't know the uplink interface for this EPG => drop
+          */
+
+         if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
+           {
+             gbp_fwd_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
+             t->src_epg = src_epg;
+             t->sw_if_index = sw_if_index0;
+           }
+
+         /* verify speculative enqueue, maybe switch current next frame */
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+                                          to_next, n_left_to_next,
+                                          bi0, next0);
+       }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+  return frame->n_vectors;
+}
+
+/* packet trace format function */
+static u8 *
+format_gbp_fwd_trace (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  gbp_fwd_trace_t *t = va_arg (*args, gbp_fwd_trace_t *);
+
+  s = format (s, "src-epg:%d", t->src_epg);
+
+  return s;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (gbp_fwd_node) = {
+  .function = gbp_fwd,
+  .name = "gbp-fwd",
+  .vector_size = sizeof (u32),
+  .format_trace = format_gbp_fwd_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = ARRAY_LEN(gbp_fwd_error_strings),
+  .error_strings = gbp_fwd_error_strings,
+
+  .n_next_nodes = GBP_FWD_N_NEXT,
+
+  .next_nodes = {
+    [GBP_FWD_NEXT_DROP] = "error-drop",
+    [GBP_FWD_NEXT_OUTPUT] = "l2-output",
+  },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (gbp_fwd_node, gbp_fwd);
+
+/* *INDENT-ON* */
+
+static clib_error_t *
+gbp_fwd_init (vlib_main_t * vm)
+{
+  gbp_fwd_main_t *gpm = &gbp_fwd_main;
+
+  /* Initialize the feature next-node indices */
+  feat_bitmap_init_next_nodes (vm,
+                              gbp_fwd_node.index,
+                              L2INPUT_N_FEAT,
+                              l2input_get_feat_names (),
+                              gpm->l2_input_feat_next);
+
+  return 0;
+}
+
+VLIB_INIT_FUNCTION (gbp_fwd_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/gbp/gbp_fwd_dpo.c b/src/plugins/gbp/gbp_fwd_dpo.c
new file mode 100644 (file)
index 0000000..e3dba5f
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <plugins/gbp/gbp.h>
+#include <plugins/gbp/gbp_fwd_dpo.h>
+
+#include <vnet/ethernet/ethernet.h>
+
+/**
+ * The 'DB' of GBP FWD DPOs.
+ * There is one per-proto
+ */
+static index_t gbp_fwd_dpo_db[DPO_PROTO_NUM] = { INDEX_INVALID };
+
+/**
+ * DPO type registered for these GBP FWD
+ */
+static dpo_type_t gbp_fwd_dpo_type;
+
+/**
+ * @brief pool of all interface DPOs
+ */
+gbp_fwd_dpo_t *gbp_fwd_dpo_pool;
+
+static gbp_fwd_dpo_t *
+gbp_fwd_dpo_alloc (void)
+{
+  gbp_fwd_dpo_t *gfd;
+
+  pool_get (gbp_fwd_dpo_pool, gfd);
+
+  return (gfd);
+}
+
+static inline gbp_fwd_dpo_t *
+gbp_fwd_dpo_get_from_dpo (const dpo_id_t * dpo)
+{
+  ASSERT (gbp_fwd_dpo_type == dpo->dpoi_type);
+
+  return (gbp_fwd_dpo_get (dpo->dpoi_index));
+}
+
+static inline index_t
+gbp_fwd_dpo_get_index (gbp_fwd_dpo_t * gfd)
+{
+  return (gfd - gbp_fwd_dpo_pool);
+}
+
+static void
+gbp_fwd_dpo_lock (dpo_id_t * dpo)
+{
+  gbp_fwd_dpo_t *gfd;
+
+  gfd = gbp_fwd_dpo_get_from_dpo (dpo);
+  gfd->gfd_locks++;
+}
+
+static void
+gbp_fwd_dpo_unlock (dpo_id_t * dpo)
+{
+  gbp_fwd_dpo_t *gfd;
+
+  gfd = gbp_fwd_dpo_get_from_dpo (dpo);
+  gfd->gfd_locks--;
+
+  if (0 == gfd->gfd_locks)
+    {
+      gbp_fwd_dpo_db[gfd->gfd_proto] = INDEX_INVALID;
+      pool_put (gbp_fwd_dpo_pool, gfd);
+    }
+}
+
+void
+gbp_fwd_dpo_add_or_lock (dpo_proto_t dproto, dpo_id_t * dpo)
+{
+  gbp_fwd_dpo_t *gfd;
+
+  if (INDEX_INVALID == gbp_fwd_dpo_db[dproto])
+    {
+      gfd = gbp_fwd_dpo_alloc ();
+
+      gfd->gfd_proto = dproto;
+
+      gbp_fwd_dpo_db[dproto] = gbp_fwd_dpo_get_index (gfd);
+    }
+  else
+    {
+      gfd = gbp_fwd_dpo_get (gbp_fwd_dpo_db[dproto]);
+    }
+
+  dpo_set (dpo, gbp_fwd_dpo_type, dproto, gbp_fwd_dpo_get_index (gfd));
+}
+
+u8 *
+format_gbp_fwd_dpo (u8 * s, va_list * ap)
+{
+  index_t index = va_arg (*ap, index_t);
+  CLIB_UNUSED (u32 indent) = va_arg (*ap, u32);
+  gbp_fwd_dpo_t *gfd = gbp_fwd_dpo_get (index);
+
+  return (format (s, "gbp-fwd-dpo: %U", format_dpo_proto, gfd->gfd_proto));
+}
+
+const static dpo_vft_t gbp_fwd_dpo_vft = {
+  .dv_lock = gbp_fwd_dpo_lock,
+  .dv_unlock = gbp_fwd_dpo_unlock,
+  .dv_format = format_gbp_fwd_dpo,
+};
+
+/**
+ * @brief The per-protocol VLIB graph nodes that are assigned to a glean
+ *        object.
+ *
+ * this means that these graph nodes are ones from which a glean is the
+ * parent object in the DPO-graph.
+ */
+const static char *const gbp_fwd_dpo_ip4_nodes[] = {
+  "ip4-gbp-fwd-dpo",
+  NULL,
+};
+
+const static char *const gbp_fwd_dpo_ip6_nodes[] = {
+  "ip6-gbp-fwd-dpo",
+  NULL,
+};
+
+const static char *const *const gbp_fwd_dpo_nodes[DPO_PROTO_NUM] = {
+  [DPO_PROTO_IP4] = gbp_fwd_dpo_ip4_nodes,
+  [DPO_PROTO_IP6] = gbp_fwd_dpo_ip6_nodes,
+};
+
+dpo_type_t
+gbp_fwd_dpo_get_type (void)
+{
+  return (gbp_fwd_dpo_type);
+}
+
+static clib_error_t *
+gbp_fwd_dpo_module_init (vlib_main_t * vm)
+{
+  dpo_proto_t dproto;
+
+  FOR_EACH_DPO_PROTO (dproto)
+  {
+    gbp_fwd_dpo_db[dproto] = INDEX_INVALID;
+  }
+
+  gbp_fwd_dpo_type = dpo_register_new_type (&gbp_fwd_dpo_vft,
+                                           gbp_fwd_dpo_nodes);
+
+  return (NULL);
+}
+
+VLIB_INIT_FUNCTION (gbp_fwd_dpo_module_init);
+
+typedef struct gbp_fwd_dpo_trace_t_
+{
+  u32 src_epg;
+  u32 dpo_index;
+} gbp_fwd_dpo_trace_t;
+
+typedef enum
+{
+  GBP_FWD_DROP,
+  GBP_FWD_FWD,
+  GBP_FWD_N_NEXT,
+} gbp_fwd_next_t;
+
+always_inline uword
+gbp_fwd_dpo_inline (vlib_main_t * vm,
+                   vlib_node_runtime_t * node,
+                   vlib_frame_t * from_frame, fib_protocol_t fproto)
+{
+  u32 n_left_from, next_index, *from, *to_next;
+
+  from = vlib_frame_vector_args (from_frame);
+  n_left_from = from_frame->n_vectors;
+
+  next_index = node->cached_next_index;
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         const dpo_id_t *next_dpo0;
+         vlib_buffer_t *b0;
+         epg_id_t src_epg0;
+         u32 bi0, next0;
+
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+
+         b0 = vlib_get_buffer (vm, bi0);
+
+         src_epg0 = vnet_buffer2 (b0)->gbp.src_epg;
+         next_dpo0 = gbp_epg_dpo_lookup (src_epg0, fproto);
+
+         if (PREDICT_TRUE (NULL != next_dpo0))
+           {
+             vnet_buffer (b0)->ip.adj_index[VLIB_TX] = next_dpo0->dpoi_index;
+             next0 = GBP_FWD_FWD;
+           }
+         else
+           {
+             next0 = GBP_FWD_DROP;
+           }
+
+         if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+           {
+             gbp_fwd_dpo_trace_t *tr;
+
+             tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
+             tr->src_epg = src_epg0;
+             tr->dpo_index = (NULL != next_dpo0 ?
+                              next_dpo0->dpoi_index : ~0);
+           }
+
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
+                                          n_left_to_next, bi0, next0);
+       }
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+  return from_frame->n_vectors;
+}
+
+static u8 *
+format_gbp_fwd_dpo_trace (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  gbp_fwd_dpo_trace_t *t = va_arg (*args, gbp_fwd_dpo_trace_t *);
+
+  s = format (s, " epg:%d dpo:%d", t->src_epg, t->dpo_index);
+
+  return s;
+}
+
+static uword
+ip4_gbp_fwd_dpo (vlib_main_t * vm,
+                vlib_node_runtime_t * node, vlib_frame_t * from_frame)
+{
+  return (gbp_fwd_dpo_inline (vm, node, from_frame, FIB_PROTOCOL_IP4));
+}
+
+static uword
+ip6_gbp_fwd_dpo (vlib_main_t * vm,
+                vlib_node_runtime_t * node, vlib_frame_t * from_frame)
+{
+  return (gbp_fwd_dpo_inline (vm, node, from_frame, FIB_PROTOCOL_IP6));
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (ip4_gbp_fwd_dpo_node) = {
+    .function = ip4_gbp_fwd_dpo,
+    .name = "ip4-gbp-fwd-dpo",
+    .vector_size = sizeof (u32),
+    .format_trace = format_gbp_fwd_dpo_trace,
+    .n_next_nodes = GBP_FWD_N_NEXT,
+    .next_nodes =
+    {
+        [GBP_FWD_DROP] = "ip4-drop",
+        [GBP_FWD_FWD] = "ip4-dvr-dpo",
+    }
+};
+VLIB_REGISTER_NODE (ip6_gbp_fwd_dpo_node) = {
+    .function = ip6_gbp_fwd_dpo,
+    .name = "ip6-gbp-fwd-dpo",
+    .vector_size = sizeof (u32),
+    .format_trace = format_gbp_fwd_dpo_trace,
+    .n_next_nodes = GBP_FWD_N_NEXT,
+    .next_nodes =
+    {
+        [GBP_FWD_DROP] = "ip6-drop",
+        [GBP_FWD_FWD] = "ip6-dvr-dpo",
+    }
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (ip4_gbp_fwd_dpo_node, ip4_gbp_fwd_dpo)
+VLIB_NODE_FUNCTION_MULTIARCH (ip6_gbp_fwd_dpo_node, ip6_gbp_fwd_dpo)
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/gbp/gbp_fwd_dpo.h b/src/plugins/gbp/gbp_fwd_dpo.h
new file mode 100644 (file)
index 0000000..6092d62
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __GBP_FWD_DPO_H__
+#define __GBP_FWD_DPO_H__
+
+#include <vnet/dpo/dpo.h>
+
+/**
+ * @brief
+ * The GBP FWD DPO. Used in the L3 path to select the correct EPG uplink
+ * based on the source EPG.
+ */
+typedef struct gbp_fwd_dpo_t_
+{
+  /**
+   * The protocol of packets using this DPO
+   */
+  dpo_proto_t gfd_proto;
+
+  /**
+   * number of locks.
+   */
+  u16 gfd_locks;
+} gbp_fwd_dpo_t;
+
+extern void gbp_fwd_dpo_add_or_lock (dpo_proto_t dproto, dpo_id_t * dpo);
+
+extern dpo_type_t gbp_fwd_dpo_get_type (void);
+
+/**
+ * @brief pool of all interface DPOs
+ */
+extern gbp_fwd_dpo_t *gbp_fwd_dpo_pool;
+
+static inline gbp_fwd_dpo_t *
+gbp_fwd_dpo_get (index_t index)
+{
+  return (pool_elt_at_index (gbp_fwd_dpo_pool, index));
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
+
+#endif
index 6f134a8..c6f34f9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016 Cisco and/or its affiliates.
+ * Copyright (c) 2018 Cisco and/or its affiliates.
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at:
diff --git a/src/plugins/gbp/gbp_policy.c b/src/plugins/gbp/gbp_policy.c
new file mode 100644 (file)
index 0000000..8f3fc76
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <plugins/gbp/gbp.h>
+
+/**
+ * Grouping of global data for the GBP source EPG classification feature
+ */
+typedef struct gbp_policy_main_t_
+{
+  /**
+   * Next nodes for L2 output features
+   */
+  u32 l2_output_feat_next[32];
+} gbp_policy_main_t;
+
+static gbp_policy_main_t gbp_policy_main;
+
+#define foreach_gbp_policy                      \
+  _(DENY,    "deny")
+
+typedef enum
+{
+#define _(sym,str) GBP_ERROR_##sym,
+  foreach_gbp_policy
+#undef _
+    GBP_POLICY_N_ERROR,
+} gbp_policy_error_t;
+
+static char *gbp_policy_error_strings[] = {
+#define _(sym,string) string,
+  foreach_gbp_policy
+#undef _
+};
+
+typedef enum
+{
+#define _(sym,str) GBP_POLICY_NEXT_##sym,
+  foreach_gbp_policy
+#undef _
+    GBP_POLICY_N_NEXT,
+} gbp_policy_next_t;
+
+/**
+ * per-packet trace data
+ */
+typedef struct gbp_policy_trace_t_
+{
+  /* per-pkt trace data */
+  epg_id_t src_epg;
+  epg_id_t dst_epg;
+  u32 acl_index;
+} gbp_policy_trace_t;
+
+static uword
+gbp_policy (vlib_main_t * vm,
+           vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+  gbp_policy_main_t *gpm = &gbp_policy_main;
+  u32 n_left_from, *from, *to_next;
+  u32 next_index;
+
+  next_index = 0;
+  n_left_from = frame->n_vectors;
+  from = vlib_frame_vector_args (frame);
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         gbp_policy_next_t next0;
+         gbp_contract_key_t key0;
+         u32 bi0, sw_if_index0;
+         vlib_buffer_t *b0;
+         u32 acl_index0;
+
+         next0 = GBP_POLICY_NEXT_DENY;
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+
+         b0 = vlib_get_buffer (vm, bi0);
+
+         /*
+          * 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);
+         key0.gck_src = vnet_buffer2 (b0)->gbp.src_epg;
+         acl_index0 = ~0;
+
+         if (~0 != key0.gck_src)
+           {
+             if (PREDICT_FALSE (key0.gck_src == key0.gck_dst))
+               {
+                 /*
+                  * intra-epg allowed
+                  */
+                 next0 = vnet_l2_feature_next (b0, gpm->l2_output_feat_next,
+                                               L2OUTPUT_FEAT_GBP_POLICY);
+               }
+             else
+               {
+                 acl_index0 = gbp_acl_lookup (&key0);
+
+                 if (~0 != acl_index0)
+                   {
+                     /*
+                      * TODO tests against the ACL
+                      */
+                     /*
+                      * ACL tables are not available outside of ACL plugin
+                      * until then bypass the ACL to next node
+                      */
+                     next0 =
+                       vnet_l2_feature_next (b0, gpm->l2_output_feat_next,
+                                             L2OUTPUT_FEAT_GBP_POLICY);
+                   }
+               }
+           }
+         else
+           {
+             /*
+              * the src EPG is not set when the packet arrives on an EPG
+              * uplink interface and we do not need to apply policy
+              */
+             next0 = vnet_l2_feature_next (b0, gpm->l2_output_feat_next,
+                                           L2OUTPUT_FEAT_GBP_POLICY);
+           }
+
+         if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
+           {
+             gbp_policy_trace_t *t =
+               vlib_add_trace (vm, node, b0, sizeof (*t));
+             t->src_epg = key0.gck_src;
+             t->dst_epg = key0.gck_dst;
+             t->acl_index = acl_index0;
+           }
+
+         /* verify speculative enqueue, maybe switch current next frame */
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+                                          to_next, n_left_to_next,
+                                          bi0, next0);
+       }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+  return frame->n_vectors;
+}
+
+/* packet trace format function */
+static u8 *
+format_gbp_policy_trace (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  gbp_policy_trace_t *t = va_arg (*args, gbp_policy_trace_t *);
+
+  s =
+    format (s, "src:%d, dst:%d, acl:%d", t->src_epg, t->dst_epg,
+           t->acl_index);
+
+  return s;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (gbp_policy_node) = {
+  .function = gbp_policy,
+  .name = "gbp-policy",
+  .vector_size = sizeof (u32),
+  .format_trace = format_gbp_policy_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = ARRAY_LEN(gbp_policy_error_strings),
+  .error_strings = gbp_policy_error_strings,
+
+  .n_next_nodes = GBP_POLICY_N_NEXT,
+
+  .next_nodes = {
+    [GBP_POLICY_NEXT_DENY] = "error-drop",
+  },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (gbp_policy_node, gbp_policy);
+
+/* *INDENT-ON* */
+
+static clib_error_t *
+gbp_policy_init (vlib_main_t * vm)
+{
+  gbp_policy_main_t *gpm = &gbp_policy_main;
+
+  /* Initialize the feature next-node indexes */
+  feat_bitmap_init_next_nodes (vm,
+                              gbp_policy_node.index,
+                              L2OUTPUT_N_FEAT,
+                              l2output_get_feat_names (),
+                              gpm->l2_output_feat_next);
+
+  return 0;
+}
+
+VLIB_INIT_FUNCTION (gbp_policy_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/gbp/gbp_policy_dpo.c b/src/plugins/gbp/gbp_policy_dpo.c
new file mode 100644 (file)
index 0000000..bfe5974
--- /dev/null
@@ -0,0 +1,537 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <plugins/gbp/gbp.h>
+#include <plugins/gbp/gbp_policy_dpo.h>
+#include <plugins/gbp/gbp_recirc.h>
+
+#include <vnet/dpo/dvr_dpo.h>
+#include <vnet/fib/ip4_fib.h>
+#include <vnet/fib/ip6_fib.h>
+#include <vnet/dpo/load_balance.h>
+
+/**
+ * DPO pool
+ */
+static gbp_policy_dpo_t *gbp_policy_dpo_pool;
+
+/**
+ * DPO type registered for these GBP FWD
+ */
+static dpo_type_t gbp_policy_dpo_type;
+
+static inline gbp_policy_dpo_t *
+gbp_policy_dpo_get_i (index_t index)
+{
+  return (pool_elt_at_index (gbp_policy_dpo_pool, index));
+}
+
+gbp_policy_dpo_t *
+gbp_policy_dpo_get (index_t index)
+{
+  return (gbp_policy_dpo_get_i (index));
+}
+
+static gbp_policy_dpo_t *
+gbp_policy_dpo_alloc (void)
+{
+  gbp_policy_dpo_t *gpd;
+
+  pool_get (gbp_policy_dpo_pool, gpd);
+
+  return (gpd);
+}
+
+static inline gbp_policy_dpo_t *
+gbp_policy_dpo_get_from_dpo (const dpo_id_t * dpo)
+{
+  ASSERT (gbp_policy_dpo_type == dpo->dpoi_type);
+
+  return (gbp_policy_dpo_get_i (dpo->dpoi_index));
+}
+
+static inline index_t
+gbp_policy_dpo_get_index (gbp_policy_dpo_t * gpd)
+{
+  return (gpd - gbp_policy_dpo_pool);
+}
+
+static void
+gbp_policy_dpo_lock (dpo_id_t * dpo)
+{
+  gbp_policy_dpo_t *gpd;
+
+  gpd = gbp_policy_dpo_get_from_dpo (dpo);
+  gpd->gpd_locks++;
+}
+
+static void
+gbp_policy_dpo_unlock (dpo_id_t * dpo)
+{
+  gbp_policy_dpo_t *gpd;
+
+  gpd = gbp_policy_dpo_get_from_dpo (dpo);
+  gpd->gpd_locks--;
+
+  if (0 == gpd->gpd_locks)
+    {
+      dpo_reset (&gpd->gpd_dpo);
+      pool_put (gbp_policy_dpo_pool, gpd);
+    }
+}
+
+void
+gbp_policy_dpo_add_or_lock (dpo_proto_t dproto,
+                           epg_id_t epg, u32 sw_if_index, dpo_id_t * dpo)
+{
+  gbp_policy_dpo_t *gpd;
+  dpo_id_t parent = DPO_INVALID;
+
+  gpd = gbp_policy_dpo_alloc ();
+  memset (gpd, 0, sizeof (*gpd));
+
+  gpd->gpd_proto = dproto;
+  gpd->gpd_sw_if_index = sw_if_index;
+  gpd->gpd_epg = epg;
+
+  /*
+   * stack on the DVR DPO for the output interface
+   */
+  dvr_dpo_add_or_lock (sw_if_index, dproto, &parent);
+
+  dpo_stack (gbp_policy_dpo_type, dproto, &gpd->gpd_dpo, &parent);
+
+  dpo_set (dpo, gbp_policy_dpo_type, dproto, gbp_policy_dpo_get_index (gpd));
+}
+
+u8 *
+format_gbp_policy_dpo (u8 * s, va_list * ap)
+{
+  index_t index = va_arg (*ap, index_t);
+  u32 indent = va_arg (*ap, u32);
+  gbp_policy_dpo_t *gpd = gbp_policy_dpo_get_i (index);
+  vnet_main_t *vnm = vnet_get_main ();
+
+  s = format (s, "gbp-policy-dpo: %U, epg:%d out:%U",
+             format_dpo_proto, gpd->gpd_proto,
+             gpd->gpd_epg,
+             format_vnet_sw_if_index_name, vnm, gpd->gpd_sw_if_index);
+  s = format (s, "\n%U", format_white_space, indent + 2);
+  s = format (s, "%U", format_dpo_id, &gpd->gpd_dpo, indent + 4);
+
+  return (s);
+}
+
+const static dpo_vft_t gbp_policy_dpo_vft = {
+  .dv_lock = gbp_policy_dpo_lock,
+  .dv_unlock = gbp_policy_dpo_unlock,
+  .dv_format = format_gbp_policy_dpo,
+};
+
+/**
+ * @brief The per-protocol VLIB graph nodes that are assigned to a glean
+ *        object.
+ *
+ * this means that these graph nodes are ones from which a glean is the
+ * parent object in the DPO-graph.
+ */
+const static char *const gbp_policy_dpo_ip4_nodes[] = {
+  "ip4-gbp-policy-dpo",
+  NULL,
+};
+
+const static char *const gbp_policy_dpo_ip6_nodes[] = {
+  "ip6-gbp-policy-dpo",
+  NULL,
+};
+
+const static char *const *const gbp_policy_dpo_nodes[DPO_PROTO_NUM] = {
+  [DPO_PROTO_IP4] = gbp_policy_dpo_ip4_nodes,
+  [DPO_PROTO_IP6] = gbp_policy_dpo_ip6_nodes,
+};
+
+dpo_type_t
+gbp_policy_dpo_get_type (void)
+{
+  return (gbp_policy_dpo_type);
+}
+
+static clib_error_t *
+gbp_policy_dpo_module_init (vlib_main_t * vm)
+{
+  gbp_policy_dpo_type = dpo_register_new_type (&gbp_policy_dpo_vft,
+                                              gbp_policy_dpo_nodes);
+
+  return (NULL);
+}
+
+VLIB_INIT_FUNCTION (gbp_policy_dpo_module_init);
+
+typedef struct gbp_policy_dpo_trace_t_
+{
+  u32 src_epg;
+  u32 dst_epg;
+  u32 acl_index;
+} gbp_policy_dpo_trace_t;
+
+typedef enum
+{
+  GBP_POLICY_DROP,
+  GBP_POLICY_N_NEXT,
+} gbp_policy_next_t;
+
+always_inline uword
+gbp_policy_dpo_inline (vlib_main_t * vm,
+                      vlib_node_runtime_t * node,
+                      vlib_frame_t * from_frame, fib_protocol_t fproto)
+{
+  u32 n_left_from, next_index, *from, *to_next;
+
+  from = vlib_frame_vector_args (from_frame);
+  n_left_from = from_frame->n_vectors;
+
+  next_index = node->cached_next_index;
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         const gbp_policy_dpo_t *gpd0;
+         u32 bi0, next0, acl_index0;
+         gbp_contract_key_t key0;
+         vlib_buffer_t *b0;
+
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+         next0 = GBP_POLICY_DROP;
+         acl_index0 = ~0;
+
+         b0 = vlib_get_buffer (vm, bi0);
+         gpd0 =
+           gbp_policy_dpo_get_i (vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
+         vnet_buffer (b0)->ip.adj_index[VLIB_TX] = gpd0->gpd_dpo.dpoi_index;
+
+         key0.gck_src = vnet_buffer2 (b0)->gbp.src_epg;
+         key0.gck_dst = gpd0->gpd_epg;
+
+         if (~0 != key0.gck_src)
+           {
+             if (PREDICT_FALSE (key0.gck_src == key0.gck_dst))
+               {
+                 /*
+                  * intra-epg allowed
+                  */
+                 next0 = gpd0->gpd_dpo.dpoi_next_node;
+               }
+             else
+               {
+                 acl_index0 = gbp_acl_lookup (&key0);
+
+                 if (~0 != acl_index0)
+                   {
+                     /*
+                      * TODO tests against the ACL
+                      */
+                     /*
+                      * ACL tables are not available outside of ACL plugin
+                      * until then bypass the ACL to next node
+                      */
+                     next0 = gpd0->gpd_dpo.dpoi_next_node;
+                   }
+               }
+           }
+         else
+           {
+             /*
+              * the src EPG is not set when the packet arrives on an EPG
+              * uplink interface and we do not need to apply policy
+              */
+             next0 = gpd0->gpd_dpo.dpoi_next_node;
+           }
+
+         if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+           {
+             gbp_policy_dpo_trace_t *tr;
+
+             tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
+             tr->src_epg = key0.gck_src;
+             tr->dst_epg = key0.gck_dst;
+             tr->acl_index = acl_index0;
+           }
+
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
+                                          n_left_to_next, bi0, next0);
+       }
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+  return from_frame->n_vectors;
+}
+
+static u8 *
+format_gbp_policy_dpo_trace (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  gbp_policy_dpo_trace_t *t = va_arg (*args, gbp_policy_dpo_trace_t *);
+
+  s = format (s, " src-epg:%d dst-epg:%d acl-index:%d",
+             t->src_epg, t->dst_epg, t->acl_index);
+
+  return s;
+}
+
+static uword
+ip4_gbp_policy_dpo (vlib_main_t * vm,
+                   vlib_node_runtime_t * node, vlib_frame_t * from_frame)
+{
+  return (gbp_policy_dpo_inline (vm, node, from_frame, FIB_PROTOCOL_IP4));
+}
+
+static uword
+ip6_gbp_policy_dpo (vlib_main_t * vm,
+                   vlib_node_runtime_t * node, vlib_frame_t * from_frame)
+{
+  return (gbp_policy_dpo_inline (vm, node, from_frame, FIB_PROTOCOL_IP6));
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (ip4_gbp_policy_dpo_node) = {
+    .function = ip4_gbp_policy_dpo,
+    .name = "ip4-gbp-policy-dpo",
+    .vector_size = sizeof (u32),
+    .format_trace = format_gbp_policy_dpo_trace,
+    .n_next_nodes = GBP_POLICY_N_NEXT,
+    .next_nodes =
+    {
+        [GBP_POLICY_DROP] = "ip4-drop",
+    }
+};
+VLIB_REGISTER_NODE (ip6_gbp_policy_dpo_node) = {
+    .function = ip6_gbp_policy_dpo,
+    .name = "ip6-gbp-policy-dpo",
+    .vector_size = sizeof (u32),
+    .format_trace = format_gbp_policy_dpo_trace,
+    .n_next_nodes = GBP_POLICY_N_NEXT,
+    .next_nodes =
+    {
+        [GBP_POLICY_DROP] = "ip6-drop",
+    }
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (ip4_gbp_policy_dpo_node, ip4_gbp_policy_dpo)
+VLIB_NODE_FUNCTION_MULTIARCH (ip6_gbp_policy_dpo_node, ip6_gbp_policy_dpo)
+/* *INDENT-ON* */
+
+ /**
+ * per-packet trace data
+ */
+typedef struct gbp_classify_trace_t_
+{
+  /* per-pkt trace data */
+  epg_id_t src_epg;
+} gbp_classify_trace_t;
+
+typedef enum gbp_lpm_classify_next_t_
+{
+  GPB_LPM_CLASSIFY_DROP,
+} gbp_lpm_classify_next_t;
+
+/*
+ * Determine the SRC EPG from a LPM
+ */
+always_inline uword
+gbp_lpm_classify_inline (vlib_main_t * vm,
+                        vlib_node_runtime_t * node,
+                        vlib_frame_t * frame, fib_protocol_t fproto)
+{
+  u32 n_left_from, *from, *to_next;
+  u32 next_index;
+
+  next_index = 0;
+  n_left_from = frame->n_vectors;
+  from = vlib_frame_vector_args (frame);
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         u32 bi0, sw_if_index0, fib_index0, lbi0;
+         gbp_lpm_classify_next_t next0;
+         const gbp_policy_dpo_t *gpd0;
+         const gbp_recirc_t *gr0;
+         const dpo_id_t *dpo0;
+         load_balance_t *lb0;
+         ip4_header_t *ip4_0;
+         ip6_header_t *ip6_0;
+         vlib_buffer_t *b0;
+         epg_id_t src_epg0;
+
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+         next0 = GPB_LPM_CLASSIFY_DROP;
+
+         b0 = vlib_get_buffer (vm, bi0);
+
+         sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+         gr0 = gbp_recirc_get (sw_if_index0);
+         fib_index0 = gr0->gr_fib_index[fproto];
+
+         if (FIB_PROTOCOL_IP4 == fproto)
+           {
+             ip4_0 = vlib_buffer_get_current (b0);
+             lbi0 = ip4_fib_forwarding_lookup (fib_index0,
+                                               &ip4_0->src_address);
+           }
+         else
+           {
+             ip6_0 = vlib_buffer_get_current (b0);
+             lbi0 = ip6_fib_table_fwding_lookup (&ip6_main, fib_index0,
+                                                 &ip6_0->src_address);
+           }
+
+         lb0 = load_balance_get (lbi0);
+         dpo0 = load_balance_get_bucket_i (lb0, 0);
+
+         if (gbp_policy_dpo_type == dpo0->dpoi_type)
+           {
+             gpd0 = gbp_policy_dpo_get_i (dpo0->dpoi_index);
+             src_epg0 = gpd0->gpd_epg;
+             vnet_feature_next (vnet_buffer (b0)->sw_if_index[VLIB_RX],
+                                &next0, b0);
+           }
+         else
+           {
+             /* could not classify => drop */
+             src_epg0 = 0;
+           }
+
+         vnet_buffer2 (b0)->gbp.src_epg = src_epg0;
+
+         if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
+           {
+             gbp_classify_trace_t *t =
+               vlib_add_trace (vm, node, b0, sizeof (*t));
+             t->src_epg = src_epg0;
+           }
+
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+                                          to_next, n_left_to_next,
+                                          bi0, next0);
+       }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+  return frame->n_vectors;
+}
+
+static uword
+gbp_ip4_lpm_classify (vlib_main_t * vm,
+                     vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+  return (gbp_lpm_classify_inline (vm, node, frame, FIB_PROTOCOL_IP4));
+}
+
+static uword
+gbp_ip6_lpm_classify (vlib_main_t * vm,
+                     vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+  return (gbp_lpm_classify_inline (vm, node, frame, FIB_PROTOCOL_IP6));
+}
+
+ /* packet trace format function */
+static u8 *
+format_gbp_classify_trace (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  gbp_classify_trace_t *t = va_arg (*args, gbp_classify_trace_t *);
+
+  s = format (s, "src-epg:%d", t->src_epg);
+
+  return s;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (gbp_ip4_lpm_classify_node) = {
+  .function = gbp_ip4_lpm_classify,
+  .name = "ip4-gbp-lpm-classify",
+  .vector_size = sizeof (u32),
+  .format_trace = format_gbp_classify_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = 0,
+  .n_next_nodes = 1,
+  .next_nodes = {
+    [GPB_LPM_CLASSIFY_DROP] = "ip4-drop"
+  },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (gbp_ip4_lpm_classify_node, gbp_ip4_lpm_classify);
+
+VLIB_REGISTER_NODE (gbp_ip6_lpm_classify_node) = {
+  .function = gbp_ip6_lpm_classify,
+  .name = "ip6-gpb-lpm-classify",
+  .vector_size = sizeof (u32),
+  .format_trace = format_gbp_classify_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = 0,
+  .n_next_nodes = 1,
+  .next_nodes = {
+    [GPB_LPM_CLASSIFY_DROP] = "ip6-drop"
+  },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (gbp_ip6_lpm_classify_node, gbp_ip6_lpm_classify);
+
+VNET_FEATURE_INIT (gbp_ip4_lpm_classify_feat_node, static) =
+{
+  .arc_name = "ip4-unicast",
+  .node_name = "ip4-gbp-lpm-classify",
+  .runs_before = VNET_FEATURES ("nat44-out2in"),
+};
+VNET_FEATURE_INIT (gbp_ip6_lpm_classify_feat_node, static) =
+{
+  .arc_name = "ip6-unicast",
+  .node_name = "ip6-gbp-lpm-classify",
+  .runs_before = VNET_FEATURES ("nat66-out2in"),
+};
+
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/gbp/gbp_policy_dpo.h b/src/plugins/gbp/gbp_policy_dpo.h
new file mode 100644 (file)
index 0000000..d6a8986
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __GBP_POLICY_DPO_H__
+#define __GBP_POLICY_DPO_H__
+
+#include <vnet/dpo/dpo.h>
+
+/**
+ * @brief
+ * The GBP FWD DPO. Used in the L3 path to select the correct EPG uplink
+ * based on the source EPG.
+ */
+typedef struct gbp_policy_dpo_t_
+{
+  /**
+   * The protocol of packets using this DPO
+   */
+  dpo_proto_t gpd_proto;
+
+  /**
+   * EPG
+   */
+  epg_id_t gpd_epg;
+
+  /**
+   * output sw_if_index
+   */
+  u32 gpd_sw_if_index;
+
+  /**
+   * number of locks.
+   */
+  u16 gpd_locks;
+
+  /**
+   * Stacked DPO on DVR of output interface
+   */
+  dpo_id_t gpd_dpo;
+} gbp_policy_dpo_t;
+
+extern void gbp_policy_dpo_add_or_lock (dpo_proto_t dproto,
+                                       epg_id_t epg,
+                                       u32 sw_if_index, dpo_id_t * dpo);
+
+extern gbp_policy_dpo_t *gbp_policy_dpo_get (index_t index);
+
+extern dpo_type_t gbp_policy_dpo_get_type (void);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
+
+#endif
diff --git a/src/plugins/gbp/gbp_recirc.c b/src/plugins/gbp/gbp_recirc.c
new file mode 100644 (file)
index 0000000..1debbe4
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <plugins/gbp/gbp_recirc.h>
+#include <plugins/gbp/gbp_endpoint_group.h>
+#include <plugins/gbp/gbp_endpoint.h>
+
+#include <vnet/dpo/dvr_dpo.h>
+#include <vnet/fib/fib_table.h>
+
+/**
+ * Pool of GBP recircs
+ */
+gbp_recirc_t *gbp_recirc_pool;
+
+/**
+ * Recirc configs keyed by sw_if_index
+ */
+index_t *gbp_recirc_db;
+
+int
+gbp_recirc_add (u32 sw_if_index, epg_id_t epg_id, u8 is_ext)
+{
+  gbp_recirc_t *gr;
+  index_t gri;
+
+  vec_validate_init_empty (gbp_recirc_db, sw_if_index, INDEX_INVALID);
+
+  gri = gbp_recirc_db[sw_if_index];
+
+  if (INDEX_INVALID == gri)
+    {
+      gbp_endpoint_group_t *gepg;
+      fib_protocol_t fproto;
+
+      pool_get (gbp_recirc_pool, gr);
+      memset (gr, 0, sizeof (*gr));
+      gri = gr - gbp_recirc_pool;
+
+      gr->gr_epg = epg_id;
+      gr->gr_is_ext = is_ext;
+      gr->gr_sw_if_index = sw_if_index;
+
+      /*
+       * IP enable the recirc interface
+       */
+      ip4_sw_interface_enable_disable (gr->gr_sw_if_index, 1);
+      ip6_sw_interface_enable_disable (gr->gr_sw_if_index, 1);
+
+      /*
+       * cache the FIB indicies of the EPG
+       */
+      gepg = gbp_endpoint_group_find (gr->gr_epg);
+
+      if (NULL == gepg)
+       return (VNET_API_ERROR_NO_SUCH_ENTRY);
+
+      FOR_EACH_FIB_IP_PROTOCOL (fproto)
+      {
+       gr->gr_fib_index[fproto] = gepg->gepg_fib_index[fproto];
+      }
+
+      /*
+       * Packets on the recirculation interface are subjet to src-EPG
+       * classification. Recirc interfaces are L2-emulation mode.
+       *   for internal EPGs this is via an LPM on all external subnets.
+       *   for external EPGs this is via a port mapping.
+       */
+      if (gr->gr_is_ext)
+       {
+         /*
+          * recirc is for post-NAT translation packets going into
+          * 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);
+         vnet_feature_enable_disable ("ip4-unicast",
+                                      "ip4-gbp-src-classify",
+                                      gr->gr_sw_if_index, 1, 0, 0);
+         vnet_feature_enable_disable ("ip6-unicast",
+                                      "ip6-gbp-src-classify",
+                                      gr->gr_sw_if_index, 1, 0, 0);
+       }
+      else
+       {
+         /*
+          * recirc is for pre-NAT translation packets coming from
+          * the external EPG, these are classified based on a LPM
+          * in the EPG's route-domain
+          */
+         vnet_feature_enable_disable ("ip4-unicast",
+                                      "ip4-gbp-lpm-classify",
+                                      gr->gr_sw_if_index, 1, 0, 0);
+         vnet_feature_enable_disable ("ip6-unicast",
+                                      "ip6-gbp-lpm-classify",
+                                      gr->gr_sw_if_index, 1, 0, 0);
+       }
+
+      gbp_recirc_db[sw_if_index] = gri;
+    }
+
+  return (0);
+}
+
+void
+gbp_recirc_delete (u32 sw_if_index)
+{
+  gbp_recirc_t *gr;
+  index_t gri;
+
+  gri = gbp_recirc_db[sw_if_index];
+
+  if (INDEX_INVALID != gri)
+    {
+      gr = pool_elt_at_index (gbp_recirc_pool, gri);
+
+      if (gr->gr_is_ext)
+       {
+         gbp_itf_epg_delete (gr->gr_sw_if_index);
+         vnet_feature_enable_disable ("ip4-unicast",
+                                      "ip4-gbp-src-classify",
+                                      gr->gr_sw_if_index, 0, 0, 0);
+         vnet_feature_enable_disable ("ip6-unicast",
+                                      "ip6-gbp-src-classify",
+                                      gr->gr_sw_if_index, 0, 0, 0);
+       }
+      else
+       {
+         vnet_feature_enable_disable ("ip4-unicast",
+                                      "ip4-gbp-lpm-classify",
+                                      gr->gr_sw_if_index, 0, 0, 0);
+         vnet_feature_enable_disable ("ip6-unicast",
+                                      "ip6-gbp-lpm-classify",
+                                      gr->gr_sw_if_index, 0, 0, 0);
+       }
+
+      ip4_sw_interface_enable_disable (gr->gr_sw_if_index, 0);
+      ip6_sw_interface_enable_disable (gr->gr_sw_if_index, 0);
+
+      gbp_recirc_db[sw_if_index] = INDEX_INVALID;
+      pool_put (gbp_recirc_pool, gr);
+    }
+}
+
+void
+gbp_recirc_walk (gbp_recirc_cb_t cb, void *ctx)
+{
+  gbp_recirc_t *gbpe;
+
+  /* *INDENT-OFF* */
+  pool_foreach(gbpe, gbp_recirc_pool,
+  {
+    if (!cb(gbpe, ctx))
+      break;
+  });
+  /* *INDENT-ON* */
+}
+
+static int
+gbp_recirc_show_one (gbp_recirc_t * gr, void *ctx)
+{
+  vnet_main_t *vnm = vnet_get_main ();
+  vlib_main_t *vm;
+
+  vm = ctx;
+  vlib_cli_output (vm, "  %U, epg:%d, ext:%d",
+                  format_vnet_sw_if_index_name, vnm,
+                  gr->gr_sw_if_index, gr->gr_epg, gr->gr_is_ext);
+
+  return (1);
+}
+
+static clib_error_t *
+gbp_recirc_show (vlib_main_t * vm,
+                unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  vlib_cli_output (vm, "Recirculation-Interfaces:");
+  gbp_recirc_walk (gbp_recirc_show_one, vm);
+
+  return (NULL);
+}
+
+
+/*?
+ * Show Group Based Policy Recircs and derived information
+ *
+ * @cliexpar
+ * @cliexstart{show gbp recirc}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_recirc_show_node, static) = {
+  .path = "show gbp recirc",
+  .short_help = "show gbp recirc\n",
+  .function = gbp_recirc_show,
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/gbp/gbp_recirc.h b/src/plugins/gbp/gbp_recirc.h
new file mode 100644 (file)
index 0000000..10f3da6
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __GBP_RECIRC_H__
+#define __GBP_RECIRC_H__
+
+#include <plugins/gbp/gbp_types.h>
+#include <vnet/fib/fib_types.h>
+
+/**
+ * An Endpoint Group representation
+ */
+typedef struct gpb_recirc_t_
+{
+  /**
+   * EPG ID that packets will classify to when they arrive on this recirc
+   */
+  epg_id_t gr_epg;
+
+  /**
+   * FIB indices the EPG is mapped to
+   */
+  u32 gr_fib_index[FIB_PROTOCOL_IP_MAX];
+
+  /**
+   * Is the interface for packets post-NAT translatoin (i.e. ext)
+   * or pre-NAT ranslation (i.e. internal)
+   */
+  u8 gr_is_ext;
+
+  /**
+   */
+  u32 gr_sw_if_index;
+
+} gbp_recirc_t;
+
+extern int gbp_recirc_add (u32 sw_if_index, epg_id_t epg_id, u8 is_ext);
+extern void gbp_recirc_delete (u32 sw_if_index);
+
+typedef int (*gbp_recirc_cb_t) (gbp_recirc_t * gbpe, void *ctx);
+extern void gbp_recirc_walk (gbp_recirc_cb_t bgpe, void *ctx);
+
+/**
+ * Data plane functions
+ */
+extern gbp_recirc_t *gbp_recirc_pool;
+extern index_t *gbp_recirc_db;
+
+always_inline const gbp_recirc_t *
+gbp_recirc_get (u32 sw_if_index)
+{
+  return (pool_elt_at_index (gbp_recirc_pool, gbp_recirc_db[sw_if_index]));
+}
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/gbp/gbp_subnet.c b/src/plugins/gbp/gbp_subnet.c
new file mode 100644 (file)
index 0000000..b699084
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <plugins/gbp/gbp.h>
+#include <plugins/gbp/gbp_fwd_dpo.h>
+#include <plugins/gbp/gbp_policy_dpo.h>
+
+#include <vnet/fib/fib_table.h>
+#include <vnet/dpo/load_balance.h>
+
+static int
+gbp_internal_subnet_add (u32 fib_index, const fib_prefix_t * pfx)
+{
+  dpo_id_t gfd = DPO_INVALID;
+
+  gbp_fwd_dpo_add_or_lock (fib_proto_to_dpo (pfx->fp_proto), &gfd);
+
+  fib_table_entry_special_dpo_update (fib_index,
+                                     pfx,
+                                     FIB_SOURCE_PLUGIN_HI,
+                                     FIB_ENTRY_FLAG_EXCLUSIVE, &gfd);
+
+  dpo_reset (&gfd);
+
+  return (0);
+}
+
+static int
+gbp_external_subnet_add (u32 fib_index,
+                        const fib_prefix_t * pfx,
+                        u32 sw_if_index, epg_id_t epg)
+{
+  dpo_id_t gpd = DPO_INVALID;
+
+  gbp_policy_dpo_add_or_lock (fib_proto_to_dpo (pfx->fp_proto),
+                             epg, sw_if_index, &gpd);
+
+  fib_table_entry_special_dpo_update (fib_index,
+                                     pfx,
+                                     FIB_SOURCE_PLUGIN_HI,
+                                     (FIB_ENTRY_FLAG_EXCLUSIVE |
+                                      FIB_ENTRY_FLAG_LOOSE_URPF_EXEMPT),
+                                     &gpd);
+
+  dpo_reset (&gpd);
+
+  return (0);
+}
+
+static int
+gbp_subnet_del (u32 fib_index, const fib_prefix_t * pfx)
+{
+  fib_table_entry_delete (fib_index, pfx, FIB_SOURCE_PLUGIN_HI);
+
+  return (0);
+}
+
+int
+gbp_subnet_add_del (u32 table_id,
+                   const fib_prefix_t * pfx,
+                   u32 sw_if_index, epg_id_t epg, u8 is_add, u8 is_internal)
+{
+  u32 fib_index;
+
+  fib_index = fib_table_find (pfx->fp_proto, table_id);
+
+  if (~0 == fib_index)
+    return (VNET_API_ERROR_NO_SUCH_FIB);
+
+  if (is_internal && is_add)
+    return (gbp_internal_subnet_add (fib_index, pfx));
+  else if (!is_internal && is_add)
+    return (gbp_external_subnet_add (fib_index, pfx, sw_if_index, epg));
+
+  return (gbp_subnet_del (fib_index, pfx));
+}
+
+typedef struct gbp_subnet_fib_table_walk_ctx_t_
+{
+  gbp_subnet_cb_t cb;
+  void *ctx;
+} gbp_subnet_fib_table_walk_ctx_t;
+
+static fib_table_walk_rc_t
+gbp_subnet_fib_table_walk (fib_node_index_t fei, void *arg)
+{
+  gbp_subnet_fib_table_walk_ctx_t *ctx = arg;
+  const dpo_id_t *dpo;
+  fib_prefix_t pfx;
+  u32 table_id;
+
+  fib_entry_get_prefix (fei, &pfx);
+  table_id = fib_table_get_table_id (fib_entry_get_fib_index (fei),
+                                    pfx.fp_proto);
+  dpo = fib_entry_contribute_ip_forwarding (fei);
+
+  if (DPO_LOAD_BALANCE == dpo->dpoi_type)
+    {
+      dpo = load_balance_get_bucket (dpo->dpoi_index, 0);
+
+      if (dpo->dpoi_type == gbp_policy_dpo_get_type ())
+       {
+         gbp_policy_dpo_t *gpd;
+
+         gpd = gbp_policy_dpo_get (dpo->dpoi_index);
+
+          /* *INDENT-OFF* */
+          ctx->cb (table_id, &pfx,
+                   gpd->gpd_sw_if_index,
+                   gpd->gpd_epg,
+                   0,  // is_internal
+                   ctx->ctx);
+          /* *INDENT-ON* */
+       }
+      else if (dpo->dpoi_type == gbp_fwd_dpo_get_type ())
+       {
+          /* *INDENT-OFF* */
+          ctx->cb (table_id, &pfx,
+                   ~0, // sw_if_index
+                   ~0,  // epg
+                   1,   // is_internal
+                   ctx->ctx);
+          /* *INDENT-ON* */
+       }
+    }
+
+  return (FIB_TABLE_WALK_CONTINUE);
+}
+
+void
+gbp_subnet_walk (gbp_subnet_cb_t cb, void *ctx)
+{
+  fib_table_t *fib_table;
+
+  gbp_subnet_fib_table_walk_ctx_t wctx = {
+    .cb = cb,
+    .ctx = ctx,
+  };
+
+  /* *INDENT-OFF* */
+  pool_foreach (fib_table, ip4_main.fibs,
+  ({
+    fib_table_walk(fib_table->ft_index,
+                   FIB_PROTOCOL_IP4,
+                   gbp_subnet_fib_table_walk,
+                   &wctx);
+  }));
+  pool_foreach (fib_table, ip6_main.fibs,
+  ({
+    fib_table_walk(fib_table->ft_index,
+                   FIB_PROTOCOL_IP6,
+                   gbp_subnet_fib_table_walk,
+                   &wctx);
+  }));
+  /* *INDENT-ON* */
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/gbp/gbp_subnet.h b/src/plugins/gbp/gbp_subnet.h
new file mode 100644 (file)
index 0000000..24b4f3a
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __GBP_SUBNET_H__
+#define __GBP_SUBNET_H__
+
+#include <plugins/gbp/gbp_types.h>
+
+extern int gbp_subnet_add_del (u32 table_id,
+                              const fib_prefix_t * pfx,
+                              u32 sw_if_index,
+                              epg_id_t epg, u8 is_add, u8 is_internal);
+
+
+typedef int (*gbp_subnet_cb_t) (u32 table_id,
+                               const fib_prefix_t * pfx,
+                               u32 sw_if_index,
+                               epg_id_t epg, u8 is_internal, void *ctx);
+extern void gbp_subnet_walk (gbp_subnet_cb_t cb, void *ctx);
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/gbp/gbp_types.h b/src/plugins/gbp/gbp_types.h
new file mode 100644 (file)
index 0000000..fa33745
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __GBP_TYPES_H__
+#define __GBP_TYPES_H__
+
+#include <vnet/vnet.h>
+
+typedef u32 epg_id_t;
+#define EPG_INVALID (~0)
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
index 02688e2..86f7538 100644 (file)
@@ -387,6 +387,12 @@ typedef struct
 
   u8 __unused[2];
 
+  /* Group Based Policy */
+  struct
+  {
+    u32 src_epg;
+  } gbp;
+
   union
   {
     struct
@@ -396,7 +402,7 @@ typedef struct
       u16 *trajectory_trace;
 #endif
     };
-    u32 unused[11];
+    u32 unused[10];
   };
 } vnet_buffer_opaque2_t;
 
index 925fb2c..9114d7a 100644 (file)
@@ -2498,11 +2498,22 @@ ethernet_arp_change_mac (u32 sw_if_index)
 }
 
 void
-send_ip4_garp (vlib_main_t * vm, vnet_hw_interface_t * hi)
+send_ip4_garp (vlib_main_t * vm, const vnet_hw_interface_t * hi)
+{
+  ip4_main_t *i4m = &ip4_main;
+  ip4_address_t *ip4_addr =
+    ip4_interface_first_address (i4m, hi->sw_if_index, 0);
+
+  send_ip4_garp_w_addr (vm, ip4_addr, hi);
+}
+
+void
+send_ip4_garp_w_addr (vlib_main_t * vm,
+                     const ip4_address_t * ip4_addr,
+                     const vnet_hw_interface_t * hi)
 {
   ip4_main_t *i4m = &ip4_main;
   u32 sw_if_index = hi->sw_if_index;
-  ip4_address_t *ip4_addr = ip4_interface_first_address (i4m, sw_if_index, 0);
 
   if (ip4_addr)
     {
index 661f33f..4b7b048 100644 (file)
@@ -167,7 +167,10 @@ typedef struct
 ethernet_arp_ip4_entry_t *ip4_neighbor_entries (u32 sw_if_index);
 u8 *format_ethernet_arp_ip4_entry (u8 * s, va_list * va);
 
-void send_ip4_garp (vlib_main_t * vm, vnet_hw_interface_t * hi);
+void send_ip4_garp (vlib_main_t * vm, const vnet_hw_interface_t * hi);
+void send_ip4_garp_w_addr (vlib_main_t * vm,
+                          const ip4_address_t * ip4_addr,
+                          const vnet_hw_interface_t * hi);
 
 #endif /* included_ethernet_arp_packet_h */
 
index 324a35f..d0bc336 100644 (file)
@@ -1033,6 +1033,17 @@ fib_table_get_table_id_for_sw_if_index (fib_protocol_t proto,
     return ((NULL != fib_table ? fib_table->ft_table_id : ~0));
 }
 
+u32
+fib_table_get_table_id (u32 fib_index,
+                        fib_protocol_t proto)
+{
+    fib_table_t *fib_table;
+
+    fib_table = fib_table_get(fib_index, proto);
+
+    return ((NULL != fib_table ? fib_table->ft_table_id : ~0));
+}
+
 u32
 fib_table_find (fib_protocol_t proto,
                u32 table_id)
index ffad3c4..8b86f8d 100644 (file)
@@ -643,6 +643,21 @@ extern u32 fib_table_get_index_for_sw_if_index(fib_protocol_t proto,
 extern u32 fib_table_get_table_id_for_sw_if_index(fib_protocol_t proto,
                                                  u32 sw_if_index);
 
+/**
+ * @brief
+ *  Get the Table-ID of the FIB from protocol and index
+ *
+ * @param fib_index
+ *  The FIB index
+ *
+ * @paran proto
+ *  The protocol of the FIB (and thus the entries therein)
+ *
+ * @return fib_index
+ *  The tableID of the FIB
+ */
+extern u32 fib_table_get_table_id(u32 fib_index, fib_protocol_t proto);
+
 /**
  * @brief
  *  Get the index of the FIB for a Table-ID. This DOES NOT create the
index 959d72c..359c461 100644 (file)
@@ -404,7 +404,10 @@ int vnet_ip6_nd_term (vlib_main_t * vm,
                      ethernet_header_t * eth,
                      ip6_header_t * ip, u32 sw_if_index, u16 bd_index);
 
-void send_ip6_na (vlib_main_t * vm, vnet_hw_interface_t * hi);
+void send_ip6_na (vlib_main_t * vm, const vnet_hw_interface_t * hi);
+void send_ip6_na_w_addr (vlib_main_t * vm,
+                        const ip6_address_t * addr,
+                        const vnet_hw_interface_t * hi);
 
 u8 *format_ip6_forward_next_trace (u8 * s, va_list * args);
 
index 0df29c6..fee4356 100644 (file)
@@ -4670,11 +4670,23 @@ ethernet_ndp_change_mac (u32 sw_if_index)
 }
 
 void
-send_ip6_na (vlib_main_t * vm, vnet_hw_interface_t * hi)
+send_ip6_na (vlib_main_t * vm, const vnet_hw_interface_t * hi)
 {
   ip6_main_t *i6m = &ip6_main;
   u32 sw_if_index = hi->sw_if_index;
   ip6_address_t *ip6_addr = ip6_interface_first_address (i6m, sw_if_index);
+
+  send_ip6_na_w_addr (vm, ip6_addr, hi);
+}
+
+void
+send_ip6_na_w_addr (vlib_main_t * vm,
+                   const ip6_address_t * ip6_addr,
+                   const vnet_hw_interface_t * hi)
+{
+  ip6_main_t *i6m = &ip6_main;
+  u32 sw_if_index = hi->sw_if_index;
+
   if (ip6_addr)
     {
       clib_warning
index d8a0a6b..69de281 100644 (file)
@@ -176,7 +176,8 @@ classify_and_dispatch (l2input_main_t * msm, vlib_buffer_t * b0, u32 * next0)
       u8 protocol = ((ip6_header_t *) l3h0)->protocol;
 
       /* Disable bridge forwarding (flooding will execute instead if not xconnect) */
-      feat_mask &= ~(L2INPUT_FEAT_FWD | L2INPUT_FEAT_UU_FLOOD);
+      feat_mask &= ~(L2INPUT_FEAT_FWD |
+                    L2INPUT_FEAT_UU_FLOOD | L2INPUT_FEAT_GBP_FWD);
 
       /* Disable ARP-term for non-ARP and non-ICMP6 packet */
       if (ethertype != ETHERNET_TYPE_ARP &&
index dc9d954..5d67f25 100644 (file)
@@ -109,10 +109,13 @@ l2input_bd_config (u32 bd_index)
  _(FLOOD,         "l2-flood")                   \
  _(ARP_TERM,      "arp-term-l2bd")              \
  _(UU_FLOOD,      "l2-flood")                   \
+ _(GBP_FWD,       "gbp-fwd")                    \
  _(FWD,           "l2-fwd")                     \
  _(RW,            "l2-rw")                      \
  _(LEARN,         "l2-learn")                   \
  _(L2_EMULATION,  "l2-emulation")               \
+ _(GBP_NULL_CLASSIFY, "gbp-null-classify")      \
+ _(GBP_SRC_CLASSIFY,  "gbp-src-classify")       \
  _(VTR,           "l2-input-vtr")               \
  _(VPATH,         "vpath-input-l2")             \
  _(ACL,           "l2-input-acl")               \
index 3b6e480..6b00d01 100644 (file)
@@ -85,6 +85,7 @@ extern vlib_node_registration_t l2output_node;
 #define foreach_l2output_feat \
  _(OUTPUT,            "interface-output")           \
  _(SPAN,              "span-l2-output")             \
+ _(GBP_POLICY,        "gbp-policy")                 \
  _(CFM,               "feature-bitmap-drop")        \
  _(QOS,               "feature-bitmap-drop")        \
  _(ACL,               "l2-output-acl")              \
index dad4863..f802849 100644 (file)
@@ -66,8 +66,14 @@ endif
 GBP_SOURCES =
 if ENABLE_GBP_PLUGIN
 GBP_SOURCES +=                                 \
+       gbp_recirc_cmds.cpp             \
+       gbp_recirc.cpp                  \
+       gbp_subnet_cmds.cpp             \
+       gbp_subnet.cpp                  \
        gbp_endpoint_cmds.cpp           \
        gbp_endpoint.cpp                \
+       gbp_endpoint_group_cmds.cpp     \
+       gbp_endpoint_group.cpp          \
        gbp_contract_cmds.cpp           \
        gbp_contract.cpp
 endif
@@ -142,7 +148,7 @@ vomincludedir = $(includedir)/vom
 
 ACL_INCLUDES =
 if ENABLE_ACL_PLUGIN
-ACL_INCLUDES +=                                \
+ACL_INCLUDES +=                        \
        acl_binding.hpp                 \
        acl_ethertype.hpp               \
        acl_l2_rule.hpp                 \
@@ -153,7 +159,7 @@ endif
 
 NAT_INCLUDES =
 if ENABLE_NAT_PLUGIN
-NAT_INCLUDES +=                                \
+NAT_INCLUDES +=                        \
        nat_static.hpp                  \
        nat_binding.hpp
 endif
@@ -166,8 +172,11 @@ endif
 
 GBP_INCLUDES =
 if ENABLE_GBP_PLUGIN
-GBP_INCLUDES +=                                \
+GBP_INCLUDES +=                        \
        gbp_endpoint.hpp                \
+       gbp_endpoint_group.hpp          \
+       gbp_subnet.hpp                  \
+       gbp_recirc.hpp                  \
        gbp_contract.hpp
 endif
 
index 1fb06e2..2b12e68 100644 (file)
@@ -65,7 +65,7 @@ l2_rule::priority() const
   return m_priority;
 }
 
-action_t
+const action_t&
 l2_rule::action() const
 {
   return m_action;
index 4faa628..8c094ae 100644 (file)
@@ -69,7 +69,7 @@ public:
    * Getters
    */
   uint32_t priority() const;
-  action_t action() const;
+  const action_t& action() const;
   const route::prefix_t& src_ip() const;
   const mac_address_t& mac() const;
   const mac_address_t& mac_mask() const;
index 4b96cae..417dc5f 100644 (file)
@@ -147,7 +147,7 @@ l3_rule::priority() const
   return m_priority;
 }
 
-action_t
+const action_t&
 l3_rule::action() const
 {
   return m_action;
index 25a2a47..c1f1cee 100644 (file)
@@ -121,7 +121,7 @@ public:
    */
   const route::prefix_t& src() const;
   uint32_t priority() const;
-  action_t action() const;
+  const action_t& action() const;
   const route::prefix_t& dst() const;
   uint8_t proto() const;
   uint16_t srcport_or_icmptype_first() const;
index ccf0a1c..cf5bee3 100644 (file)
@@ -25,16 +25,6 @@ namespace ACL {
  */
 struct action_t : public enum_base<action_t>
 {
-  /**
-   * Constructor
-   */
-  action_t(int v, const std::string s);
-
-  /**
-   * Destructor
-   */
-  ~action_t() = default;
-
   /**
    * Permit and Reflexive
    */
@@ -60,6 +50,9 @@ struct action_t : public enum_base<action_t>
    *which implements the connection tracking ....
    */
   static const action_t& from_bool(bool b, uint8_t c);
+
+private:
+  action_t(int v, const std::string s);
 };
 };
 };
index be520f5..b8c89e1 100644 (file)
@@ -31,6 +31,33 @@ bridge_domain::learning_mode_t::learning_mode_t(int v, const std::string& s)
 {
 }
 
+const bridge_domain::flood_mode_t bridge_domain::flood_mode_t::ON(1, "on");
+const bridge_domain::flood_mode_t bridge_domain::flood_mode_t::OFF(0, "off");
+
+bridge_domain::flood_mode_t::flood_mode_t(int v, const std::string& s)
+  : enum_base<bridge_domain::flood_mode_t>(v, s)
+{
+}
+
+const bridge_domain::mac_age_mode_t bridge_domain::mac_age_mode_t::ON(1, "on");
+const bridge_domain::mac_age_mode_t bridge_domain::mac_age_mode_t::OFF(0,
+                                                                       "off");
+
+bridge_domain::mac_age_mode_t::mac_age_mode_t(int v, const std::string& s)
+  : enum_base<bridge_domain::mac_age_mode_t>(v, s)
+{
+}
+
+const bridge_domain::arp_term_mode_t bridge_domain::arp_term_mode_t::ON(1,
+                                                                        "on");
+const bridge_domain::arp_term_mode_t bridge_domain::arp_term_mode_t::OFF(0,
+                                                                         "off");
+
+bridge_domain::arp_term_mode_t::arp_term_mode_t(int v, const std::string& s)
+  : enum_base<bridge_domain::arp_term_mode_t>(v, s)
+{
+}
+
 /**
  * A DB of al the interfaces, key on the name
  */
@@ -41,15 +68,25 @@ bridge_domain::event_handler bridge_domain::m_evh;
 /**
  * Construct a new object matching the desried state
  */
-bridge_domain::bridge_domain(uint32_t id, const learning_mode_t& lmode)
+bridge_domain::bridge_domain(uint32_t id,
+                             const learning_mode_t& lmode,
+                             const arp_term_mode_t& amode,
+                             const flood_mode_t& fmode,
+                             const mac_age_mode_t& mmode)
   : m_id(id)
   , m_learning_mode(lmode)
+  , m_arp_term_mode(amode)
+  , m_flood_mode(fmode)
+  , m_mac_age_mode(mmode)
 {
 }
 
 bridge_domain::bridge_domain(const bridge_domain& o)
   : m_id(o.m_id)
   , m_learning_mode(o.m_learning_mode)
+  , m_arp_term_mode(o.m_arp_term_mode)
+  , m_flood_mode(o.m_flood_mode)
+  , m_mac_age_mode(o.m_mac_age_mode)
 {
 }
 
@@ -68,7 +105,10 @@ bridge_domain::id() const
 bool
 bridge_domain::operator==(const bridge_domain& b) const
 {
-  return ((m_learning_mode == b.m_learning_mode) && id() == b.id());
+  return ((m_learning_mode == b.m_learning_mode) &&
+          (m_flood_mode == b.m_flood_mode) &&
+          (m_mac_age_mode == b.m_mac_age_mode) &&
+          (m_arp_term_mode == b.m_arp_term_mode) && id() == b.id());
 }
 
 void
@@ -84,7 +124,8 @@ void
 bridge_domain::replay()
 {
   if (rc_t::OK == m_id.rc()) {
-    HW::enqueue(new bridge_domain_cmds::create_cmd(m_id, m_learning_mode));
+    HW::enqueue(new bridge_domain_cmds::create_cmd(
+      m_id, m_learning_mode, m_arp_term_mode, m_flood_mode, m_mac_age_mode));
   }
 }
 
@@ -119,7 +160,8 @@ bridge_domain::update(const bridge_domain& desired)
    * the desired state is always that the interface should be created
    */
   if (rc_t::OK != m_id.rc()) {
-    HW::enqueue(new bridge_domain_cmds::create_cmd(m_id, m_learning_mode));
+    HW::enqueue(new bridge_domain_cmds::create_cmd(
+      m_id, m_learning_mode, m_arp_term_mode, m_flood_mode, m_mac_age_mode));
   }
 }
 
@@ -173,8 +215,10 @@ bridge_domain::event_handler::handle_populate(const client_db::key_t& key)
     for (unsigned int ii = 0; ii < payload.n_sw_ifs; ii++) {
       std::shared_ptr<interface> itf =
         interface::find(payload.sw_if_details[ii].sw_if_index);
-      l2_binding l2(*itf, bd);
-      OM::commit(key, l2);
+      if (itf) {
+        l2_binding l2(*itf, bd);
+        OM::commit(key, l2);
+      }
     }
   }
 }
index c7f84e9..d345da2 100644 (file)
@@ -51,6 +51,51 @@ public:
     learning_mode_t(int v, const std::string& s);
   };
 
+  /**
+   * Bridge Domain ARP termination mode
+   */
+  struct arp_term_mode_t : enum_base<arp_term_mode_t>
+  {
+    const static arp_term_mode_t ON;
+    const static arp_term_mode_t OFF;
+
+  private:
+    /**
+     * Private constructor taking the value and the string name
+     */
+    arp_term_mode_t(int v, const std::string& s);
+  };
+
+  /**
+   * Bridge Domain MAC aging mode
+   */
+  struct mac_age_mode_t : enum_base<mac_age_mode_t>
+  {
+    const static mac_age_mode_t ON;
+    const static mac_age_mode_t OFF;
+
+  private:
+    /**
+     * Private constructor taking the value and the string name
+     */
+    mac_age_mode_t(int v, const std::string& s);
+  };
+
+  /**
+   * Bridge Domain Learning mode
+   */
+  struct flood_mode_t : enum_base<flood_mode_t>
+  {
+    const static flood_mode_t ON;
+    const static flood_mode_t OFF;
+
+  private:
+    /**
+     * Private constructor taking the value and the string name
+     */
+    flood_mode_t(int v, const std::string& s);
+  };
+
   /**
    * The value of the defaultbridge domain
    */
@@ -60,7 +105,10 @@ public:
    * Construct a new object matching the desried state
    */
   bridge_domain(uint32_t id,
-                const learning_mode_t& lmode = learning_mode_t::ON);
+                const learning_mode_t& lmode = learning_mode_t::ON,
+                const arp_term_mode_t& amode = arp_term_mode_t::ON,
+                const flood_mode_t& fmode = flood_mode_t::ON,
+                const mac_age_mode_t& mmode = mac_age_mode_t::OFF);
 
   /**
    * Copy Constructor
@@ -179,10 +227,25 @@ private:
   HW::item<uint32_t> m_id;
 
   /**
-   * The leanring mode of the bridge
+   * The learning mode of the bridge
    */
   learning_mode_t m_learning_mode;
 
+  /**
+   * The ARP termination mode of the bridge
+   */
+  arp_term_mode_t m_arp_term_mode;
+
+  /**
+   * The flood mode of the bridge
+   */
+  flood_mode_t m_flood_mode;
+
+  /**
+   * The MAC aging mode of the bridge
+   */
+  mac_age_mode_t m_mac_age_mode;
+
   /**
    * A map of all interfaces key against the interface's name
    */
index 498569f..d1d536f 100644 (file)
@@ -20,9 +20,15 @@ DEFINE_VAPI_MSG_IDS_L2_API_JSON;
 namespace VOM {
 namespace bridge_domain_cmds {
 create_cmd::create_cmd(HW::item<uint32_t>& item,
-                       const bridge_domain::learning_mode_t& lmode)
+                       const bridge_domain::learning_mode_t& lmode,
+                       const bridge_domain::arp_term_mode_t& amode,
+                       const bridge_domain::flood_mode_t& fmode,
+                       const bridge_domain::mac_age_mode_t& mmode)
   : rpc_cmd(item)
   , m_learning_mode(lmode)
+  , m_arp_term_mode(amode)
+  , m_flood_mode(fmode)
+  , m_mac_age_mode(mmode)
 {
 }
 
@@ -39,12 +45,12 @@ create_cmd::issue(connection& con)
 
   auto& payload = req.get_request().get_payload();
   payload.bd_id = m_hw_item.data();
-  payload.flood = 1;
-  payload.uu_flood = 1;
+  payload.flood = m_flood_mode.value();
+  payload.uu_flood = m_flood_mode.value();
   payload.forward = 1;
   payload.learn = m_learning_mode.value();
-  payload.arp_term = 1;
-  payload.mac_age = 0;
+  payload.arp_term = m_arp_term_mode.value();
+  payload.mac_age = m_mac_age_mode.value();
   payload.is_add = 1;
 
   VAPI_CALL(req.execute());
index f263b32..0216236 100644 (file)
@@ -35,7 +35,10 @@ public:
    * Constructor
    */
   create_cmd(HW::item<uint32_t>& item,
-             const bridge_domain::learning_mode_t& lmode);
+             const bridge_domain::learning_mode_t& lmode,
+             const bridge_domain::arp_term_mode_t& amode,
+             const bridge_domain::flood_mode_t& fmode,
+             const bridge_domain::mac_age_mode_t& mmode);
 
   /**
    * Issue the command to VPP/HW
@@ -56,6 +59,18 @@ private:
    * the learning mode for the bridge
    */
   bridge_domain::learning_mode_t m_learning_mode;
+  /**
+   * the learning mode for the bridge
+   */
+  bridge_domain::arp_term_mode_t m_arp_term_mode;
+  /**
+   * the flood mode for the bridge
+   */
+  bridge_domain::flood_mode_t m_flood_mode;
+  /**
+   * the flood mode for the bridge
+   */
+  bridge_domain::mac_age_mode_t m_mac_age_mode;
 };
 
 /**
index cd5d7e1..9762a91 100644 (file)
@@ -25,48 +25,48 @@ gbp_endpoint::event_handler gbp_endpoint::m_evh;
 
 gbp_endpoint::gbp_endpoint(const interface& itf,
                            const boost::asio::ip::address& ip_addr,
-                           epg_id_t epg_id)
+                           const mac_address_t& mac,
+                           const gbp_endpoint_group& epg)
   : m_hw(false)
   , m_itf(itf.singular())
-  , m_ip_addr(ip_addr)
-  , m_epg_id(epg_id)
+  , m_ip(ip_addr)
+  , m_mac(mac)
+  , m_epg(epg.singular())
 {
 }
 
 gbp_endpoint::gbp_endpoint(const gbp_endpoint& gbpe)
   : m_hw(gbpe.m_hw)
   , m_itf(gbpe.m_itf)
-  , m_ip_addr(gbpe.m_ip_addr)
-  , m_epg_id(gbpe.m_epg_id)
+  , m_ip(gbpe.m_ip)
+  , m_mac(gbpe.m_mac)
+  , m_epg(gbpe.m_epg)
 {
 }
 
 gbp_endpoint::~gbp_endpoint()
 {
   sweep();
-
-  // not in the DB anymore.
   m_db.release(key(), this);
 }
 
 const gbp_endpoint::key_t
 gbp_endpoint::key() const
 {
-  return (std::make_pair(m_itf->key(), m_ip_addr));
+  return (std::make_pair(m_itf->key(), m_ip));
 }
 
 bool
 gbp_endpoint::operator==(const gbp_endpoint& gbpe) const
 {
-  return ((key() == gbpe.key()) && (m_epg_id == gbpe.m_epg_id));
+  return ((key() == gbpe.key()) && (m_epg == gbpe.m_epg));
 }
 
 void
 gbp_endpoint::sweep()
 {
   if (m_hw) {
-    HW::enqueue(
-      new gbp_endpoint_cmds::delete_cmd(m_hw, m_itf->handle(), m_ip_addr));
+    HW::enqueue(new gbp_endpoint_cmds::delete_cmd(m_hw, m_itf->handle(), m_ip));
   }
   HW::write();
 }
@@ -75,8 +75,8 @@ void
 gbp_endpoint::replay()
 {
   if (m_hw) {
-    HW::enqueue(new gbp_endpoint_cmds::create_cmd(m_hw, m_itf->handle(),
-                                                  m_ip_addr, m_epg_id));
+    HW::enqueue(new gbp_endpoint_cmds::create_cmd(m_hw, m_itf->handle(), m_ip,
+                                                  m_mac, m_epg->id()));
   }
 }
 
@@ -84,8 +84,8 @@ std::string
 gbp_endpoint::to_string() const
 {
   std::ostringstream s;
-  s << "gbp-endpoint:[" << m_itf->to_string() << ", " << m_ip_addr.to_string()
-    << ", epg-id:" << m_epg_id << "]";
+  s << "gbp-endpoint:[" << m_itf->to_string() << ", " << m_ip.to_string()
+    << ", " << m_mac.to_string() << ", epg:" << m_epg->to_string() << "]";
 
   return (s.str());
 }
@@ -93,12 +93,9 @@ gbp_endpoint::to_string() const
 void
 gbp_endpoint::update(const gbp_endpoint& r)
 {
-  /*
- * create the table if it is not yet created
- */
   if (rc_t::OK != m_hw.rc()) {
-    HW::enqueue(new gbp_endpoint_cmds::create_cmd(m_hw, m_itf->handle(),
-                                                  m_ip_addr, m_epg_id));
+    HW::enqueue(new gbp_endpoint_cmds::create_cmd(m_hw, m_itf->handle(), m_ip,
+                                                  m_mac, m_epg->id()));
   }
 }
 
@@ -154,11 +151,14 @@ gbp_endpoint::event_handler::handle_populate(const client_db::key_t& key)
       from_bytes(payload.endpoint.is_ip6, payload.endpoint.address);
     std::shared_ptr<interface> itf =
       interface::find(payload.endpoint.sw_if_index);
+    std::shared_ptr<gbp_endpoint_group> epg =
+      gbp_endpoint_group::find(payload.endpoint.epg_id);
+    mac_address_t mac(payload.endpoint.mac);
 
     VOM_LOG(log_level_t::DEBUG) << "data: " << payload.endpoint.sw_if_index;
 
-    if (itf) {
-      gbp_endpoint gbpe(*itf, address, payload.endpoint.epg_id);
+    if (itf && epg) {
+      gbp_endpoint gbpe(*itf, address, mac, *epg);
       OM::commit(key, gbpe);
 
       VOM_LOG(log_level_t::DEBUG) << "read: " << gbpe.to_string();
index 6ece4fa..f6466a6 100644 (file)
 
 #include <ostream>
 
+#include "vom/gbp_endpoint_group.hpp"
 #include "vom/interface.hpp"
 #include "vom/singular_db.hpp"
-#include "vom/types.hpp"
 
 namespace VOM {
-
-/**
- * EPG IDs are 32 bit integers
- */
-typedef uint32_t epg_id_t;
-
 /**
- * A entry in the ARP termination table of a Bridge Domain
+ * A GBP Enpoint (i.e. a VM)
  */
 class gbp_endpoint : public object_base
 {
@@ -45,7 +39,8 @@ public:
    */
   gbp_endpoint(const interface& itf,
                const boost::asio::ip::address& ip_addr,
-               epg_id_t epg_id);
+               const mac_address_t& mac,
+               const gbp_endpoint_group& epg);
 
   /**
    * Copy Construct
@@ -166,12 +161,17 @@ private:
   /**
    * The IP address of the endpoint
    */
-  boost::asio::ip::address m_ip_addr;
+  boost::asio::ip::address m_ip;
+
+  /**
+   * The MAC address of the endpoint
+   */
+  mac_address_t m_mac;
 
   /**
-   * The EPG ID
+   * The EPG the endpoint is in
    */
-  epg_id_t m_epg_id;
+  std::shared_ptr<gbp_endpoint_group> m_epg;
 
   /**
    * A map of all bridge_domains
index 4f85b7e..88d2f37 100644 (file)
@@ -23,10 +23,12 @@ namespace gbp_endpoint_cmds {
 create_cmd::create_cmd(HW::item<bool>& item,
                        const handle_t& itf,
                        const boost::asio::ip::address& ip_addr,
+                       const mac_address_t& mac,
                        epg_id_t epg_id)
   : rpc_cmd(item)
   , m_itf(itf)
   , m_ip_addr(ip_addr)
+  , m_mac(mac)
   , m_epg_id(epg_id)
 {
 }
@@ -35,7 +37,7 @@ bool
 create_cmd::operator==(const create_cmd& other) const
 {
   return ((m_itf == other.m_itf) && (m_ip_addr == other.m_ip_addr) &&
-          (m_epg_id == other.m_epg_id));
+          (m_mac == other.m_mac) && (m_epg_id == other.m_epg_id));
 }
 
 rc_t
@@ -48,6 +50,7 @@ create_cmd::issue(connection& con)
   payload.endpoint.sw_if_index = m_itf.value();
   payload.endpoint.epg_id = m_epg_id;
   to_bytes(m_ip_addr, &payload.endpoint.is_ip6, payload.endpoint.address);
+  m_mac.to_bytes(payload.endpoint.mac, 6);
 
   VAPI_CALL(req.execute());
 
index cc78849..2893ef5 100644 (file)
@@ -37,6 +37,7 @@ public:
   create_cmd(HW::item<bool>& item,
              const handle_t& itf,
              const boost::asio::ip::address& ip_addr,
+             const mac_address_t& mac,
              epg_id_t epg_id);
 
   /**
@@ -57,6 +58,7 @@ public:
 private:
   const handle_t m_itf;
   const boost::asio::ip::address m_ip_addr;
+  const mac_address_t m_mac;
   const epg_id_t m_epg_id;
 };
 
diff --git a/src/vpp-api/vom/gbp_endpoint_group.cpp b/src/vpp-api/vom/gbp_endpoint_group.cpp
new file mode 100644 (file)
index 0000000..d9f0d38
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/gbp_endpoint_group.hpp"
+#include "vom/gbp_endpoint_group_cmds.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+
+singular_db<gbp_endpoint_group::key_t, gbp_endpoint_group>
+  gbp_endpoint_group::m_db;
+
+gbp_endpoint_group::event_handler gbp_endpoint_group::m_evh;
+
+gbp_endpoint_group::gbp_endpoint_group(epg_id_t epg_id,
+                                       const interface& itf,
+                                       const route_domain& rd,
+                                       const bridge_domain& bd)
+  : m_hw(false)
+  , m_epg_id(epg_id)
+  , m_itf(itf.singular())
+  , m_rd(rd.singular())
+  , m_bd(bd.singular())
+{
+}
+
+gbp_endpoint_group::gbp_endpoint_group(const gbp_endpoint_group& epg)
+  : m_hw(epg.m_hw)
+  , m_epg_id(epg.m_epg_id)
+  , m_itf(epg.m_itf)
+  , m_rd(epg.m_rd)
+  , m_bd(epg.m_bd)
+{
+}
+
+gbp_endpoint_group::~gbp_endpoint_group()
+{
+  sweep();
+  m_db.release(key(), this);
+}
+
+const gbp_endpoint_group::key_t
+gbp_endpoint_group::key() const
+{
+  return (m_epg_id);
+}
+
+epg_id_t
+gbp_endpoint_group::id() const
+{
+  return (m_epg_id);
+}
+
+bool
+gbp_endpoint_group::operator==(const gbp_endpoint_group& gbpe) const
+{
+  return (key() == gbpe.key() && (m_itf == gbpe.m_itf) && (m_rd == gbpe.m_rd) &&
+          (m_bd == gbpe.m_bd));
+}
+
+void
+gbp_endpoint_group::sweep()
+{
+  if (m_hw) {
+    HW::enqueue(new gbp_endpoint_group_cmds::delete_cmd(m_hw, m_epg_id));
+  }
+  HW::write();
+}
+
+void
+gbp_endpoint_group::replay()
+{
+  if (m_hw) {
+    HW::enqueue(new gbp_endpoint_group_cmds::create_cmd(
+      m_hw, m_epg_id, m_bd->id(), m_rd->table_id(), m_itf->handle()));
+  }
+}
+
+std::string
+gbp_endpoint_group::to_string() const
+{
+  std::ostringstream s;
+  s << "gbp-endpoint-group:["
+    << "epg:" << m_epg_id << ", " << m_itf->to_string() << ", "
+    << m_bd->to_string() << ", " << m_rd->to_string() << "]";
+
+  return (s.str());
+}
+
+void
+gbp_endpoint_group::update(const gbp_endpoint_group& r)
+{
+  if (rc_t::OK != m_hw.rc()) {
+    HW::enqueue(new gbp_endpoint_group_cmds::create_cmd(
+      m_hw, m_epg_id, m_bd->id(), m_rd->table_id(), m_itf->handle()));
+  }
+}
+
+std::shared_ptr<gbp_endpoint_group>
+gbp_endpoint_group::find_or_add(const gbp_endpoint_group& temp)
+{
+  return (m_db.find_or_add(temp.key(), temp));
+}
+
+std::shared_ptr<gbp_endpoint_group>
+gbp_endpoint_group::find(const key_t& k)
+{
+  return (m_db.find(k));
+}
+
+std::shared_ptr<gbp_endpoint_group>
+gbp_endpoint_group::singular() const
+{
+  return find_or_add(*this);
+}
+
+void
+gbp_endpoint_group::dump(std::ostream& os)
+{
+  db_dump(m_db, os);
+}
+
+gbp_endpoint_group::event_handler::event_handler()
+{
+  OM::register_listener(this);
+  inspect::register_handler({ "gbp-endpoint-group" }, "GBP Endpoint_Groups",
+                            this);
+}
+
+void
+gbp_endpoint_group::event_handler::handle_replay()
+{
+  m_db.replay();
+}
+
+void
+gbp_endpoint_group::event_handler::handle_populate(const client_db::key_t& key)
+{
+  std::shared_ptr<gbp_endpoint_group_cmds::dump_cmd> cmd =
+    std::make_shared<gbp_endpoint_group_cmds::dump_cmd>();
+
+  HW::enqueue(cmd);
+  HW::write();
+
+  for (auto& record : *cmd) {
+    auto& payload = record.get_payload();
+
+    std::shared_ptr<interface> itf =
+      interface::find(payload.epg.uplink_sw_if_index);
+    std::shared_ptr<route_domain> rd =
+      route_domain::find(payload.epg.ip4_table_id);
+    std::shared_ptr<bridge_domain> bd = bridge_domain::find(payload.epg.bd_id);
+
+    VOM_LOG(log_level_t::DEBUG) << "data: [" << payload.epg.uplink_sw_if_index
+                                << ", " << payload.epg.ip4_table_id << ", "
+                                << payload.epg.bd_id << "]";
+
+    if (itf && bd && rd) {
+      gbp_endpoint_group gbpe(payload.epg.epg_id, *itf, *rd, *bd);
+      OM::commit(key, gbpe);
+
+      VOM_LOG(log_level_t::DEBUG) << "read: " << gbpe.to_string();
+    }
+  }
+}
+
+dependency_t
+gbp_endpoint_group::event_handler::order() const
+{
+  return (dependency_t::ACL);
+}
+
+void
+gbp_endpoint_group::event_handler::show(std::ostream& os)
+{
+  db_dump(m_db, os);
+}
+} // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/src/vpp-api/vom/gbp_endpoint_group.hpp b/src/vpp-api/vom/gbp_endpoint_group.hpp
new file mode 100644 (file)
index 0000000..f7c900f
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_GBP_ENDPOINT_GROUP_H__
+#define __VOM_GBP_ENDPOINT_GROUP_H__
+
+#include "vom/interface.hpp"
+#include "vom/singular_db.hpp"
+#include "vom/types.hpp"
+
+#include "vom/bridge_domain.hpp"
+#include "vom/route_domain.hpp"
+
+namespace VOM {
+
+/**
+ * EPG IDs are 32 bit integers
+ */
+typedef uint32_t epg_id_t;
+
+/**
+ * A entry in the ARP termination table of a Bridge Domain
+ */
+class gbp_endpoint_group : public object_base
+{
+public:
+  /**
+   * The key for a GBP endpoint group is its ID
+   */
+  typedef epg_id_t key_t;
+
+  /**
+   * Construct a GBP endpoint_group
+   */
+  gbp_endpoint_group(epg_id_t epg_id,
+                     const interface& itf,
+                     const route_domain& rd,
+                     const bridge_domain& bd);
+
+  /**
+   * Copy Construct
+   */
+  gbp_endpoint_group(const gbp_endpoint_group& r);
+
+  /**
+   * Destructor
+   */
+  ~gbp_endpoint_group();
+
+  /**
+   * Return the object's key
+   */
+  const key_t key() const;
+
+  /**
+   * comparison operator
+   */
+  bool operator==(const gbp_endpoint_group& bdae) const;
+
+  /**
+   * Return the matching 'singular instance'
+   */
+  std::shared_ptr<gbp_endpoint_group> singular() const;
+
+  /**
+   * Find the instnace of the bridge_domain domain in the OM
+   */
+  static std::shared_ptr<gbp_endpoint_group> find(const key_t& k);
+
+  /**
+   * Dump all bridge_domain-doamin into the stream provided
+   */
+  static void dump(std::ostream& os);
+
+  /**
+   * replay the object to create it in hardware
+   */
+  void replay(void);
+
+  /**
+   * Convert to string for debugging
+   */
+  std::string to_string() const;
+
+  /**
+   * Get the ID of the EPG
+   */
+  epg_id_t id() const;
+
+private:
+  /**
+   * Class definition for listeners to OM events
+   */
+  class event_handler : public OM::listener, public inspect::command_handler
+  {
+  public:
+    event_handler();
+    virtual ~event_handler() = default;
+
+    /**
+     * Handle a populate event
+     */
+    void handle_populate(const client_db::key_t& key);
+
+    /**
+     * Handle a replay event
+     */
+    void handle_replay();
+
+    /**
+     * Show the object in the Singular DB
+     */
+    void show(std::ostream& os);
+
+    /**
+     * Get the sortable Id of the listener
+     */
+    dependency_t order() const;
+  };
+
+  /**
+   * event_handler to register with OM
+   */
+  static event_handler m_evh;
+
+  /**
+   * Commit the acculmulated changes into VPP. i.e. to a 'HW" write.
+   */
+  void update(const gbp_endpoint_group& obj);
+
+  /**
+   * Find or add the instnace of the bridge_domain domain in the OM
+   */
+  static std::shared_ptr<gbp_endpoint_group> find_or_add(
+    const gbp_endpoint_group& temp);
+
+  /*
+   * It's the VPPHW class that updates the objects in HW
+   */
+  friend class OM;
+
+  /**
+   * It's the singular_db class that calls replay()
+   */
+  friend class singular_db<key_t, gbp_endpoint_group>;
+
+  /**
+   * Sweep/reap the object if still stale
+   */
+  void sweep(void);
+
+  /**
+   * HW configuration for the result of creating the endpoint_group
+   */
+  HW::item<bool> m_hw;
+
+  /**
+   * The EPG ID
+   */
+  epg_id_t m_epg_id;
+
+  /**
+   * The uplink interface for the endpoint group
+   */
+  std::shared_ptr<interface> m_itf;
+
+  /**
+   * The route-domain the EPG uses
+   */
+  std::shared_ptr<route_domain> m_rd;
+
+  /**
+   * The bridge-domain the EPG uses
+   */
+  std::shared_ptr<bridge_domain> m_bd;
+
+  /**
+   * A map of all bridge_domains
+   */
+  static singular_db<key_t, gbp_endpoint_group> m_db;
+};
+
+}; // namespace
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/src/vpp-api/vom/gbp_endpoint_group_cmds.cpp b/src/vpp-api/vom/gbp_endpoint_group_cmds.cpp
new file mode 100644 (file)
index 0000000..55e81d3
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/gbp_endpoint_group_cmds.hpp"
+
+namespace VOM {
+namespace gbp_endpoint_group_cmds {
+
+create_cmd::create_cmd(HW::item<bool>& item,
+                       epg_id_t epg_id,
+                       uint32_t bd_id,
+                       route::table_id_t rd_id,
+                       const handle_t& itf)
+  : rpc_cmd(item)
+  , m_epg_id(epg_id)
+  , m_bd_id(bd_id)
+  , m_rd_id(rd_id)
+  , m_itf(itf)
+{
+}
+
+bool
+create_cmd::operator==(const create_cmd& other) const
+{
+  return ((m_itf == other.m_itf) && (m_bd_id == other.m_bd_id) &&
+          (m_rd_id == other.m_rd_id) && (m_epg_id == other.m_epg_id));
+}
+
+rc_t
+create_cmd::issue(connection& con)
+{
+  msg_t req(con.ctx(), std::ref(*this));
+
+  auto& payload = req.get_request().get_payload();
+  payload.is_add = 1;
+  payload.epg.uplink_sw_if_index = m_itf.value();
+  payload.epg.epg_id = m_epg_id;
+  payload.epg.bd_id = m_bd_id;
+  payload.epg.ip4_table_id = m_rd_id;
+  payload.epg.ip6_table_id = m_rd_id;
+
+  VAPI_CALL(req.execute());
+
+  m_hw_item.set(wait());
+
+  return rc_t::OK;
+}
+
+std::string
+create_cmd::to_string() const
+{
+  std::ostringstream s;
+  s << "gbp-endpoint-group-create: " << m_hw_item.to_string()
+    << " epg-id:" << m_epg_id << " bd-id:" << m_bd_id << " rd-id:" << m_rd_id
+    << " itf:" << m_itf;
+
+  return (s.str());
+}
+
+delete_cmd::delete_cmd(HW::item<bool>& item, epg_id_t epg_id)
+  : rpc_cmd(item)
+  , m_epg_id(epg_id)
+{
+}
+
+bool
+delete_cmd::operator==(const delete_cmd& other) const
+{
+  return (m_epg_id == other.m_epg_id);
+}
+
+rc_t
+delete_cmd::issue(connection& con)
+{
+  msg_t req(con.ctx(), std::ref(*this));
+
+  auto& payload = req.get_request().get_payload();
+  payload.is_add = 0;
+  payload.epg.epg_id = m_epg_id;
+
+  VAPI_CALL(req.execute());
+
+  m_hw_item.set(wait());
+
+  return rc_t::OK;
+}
+
+std::string
+delete_cmd::to_string() const
+{
+  std::ostringstream s;
+  s << "gbp-endpoint-group-delete: " << m_hw_item.to_string()
+    << " epg:" << m_epg_id;
+
+  return (s.str());
+}
+
+dump_cmd::dump_cmd()
+{
+}
+
+bool
+dump_cmd::operator==(const dump_cmd& other) const
+{
+  return (true);
+}
+
+rc_t
+dump_cmd::issue(connection& con)
+{
+  m_dump.reset(new msg_t(con.ctx(), std::ref(*this)));
+
+  VAPI_CALL(m_dump->execute());
+
+  wait();
+
+  return rc_t::OK;
+}
+
+std::string
+dump_cmd::to_string() const
+{
+  return ("gbp-endpoint-group-dump");
+}
+
+}; // namespace gbp_endpoint_group_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/src/vpp-api/vom/gbp_endpoint_group_cmds.hpp b/src/vpp-api/vom/gbp_endpoint_group_cmds.hpp
new file mode 100644 (file)
index 0000000..4da3a42
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_GBP_ENDPOINT_GROUP_CMDS_H__
+#define __VOM_GBP_ENDPOINT_GROUP_CMDS_H__
+
+#include "vom/dump_cmd.hpp"
+#include "vom/gbp_endpoint_group.hpp"
+
+#include <vapi/gbp.api.vapi.hpp>
+
+namespace VOM {
+namespace gbp_endpoint_group_cmds {
+
+/**
+* A command class that creates or updates the GBP endpoint_group
+*/
+class create_cmd
+  : public rpc_cmd<HW::item<bool>, rc_t, vapi::Gbp_endpoint_group_add_del>
+{
+public:
+  /**
+   * Constructor
+   */
+  create_cmd(HW::item<bool>& item,
+             epg_id_t epg_id,
+             uint32_t bd_id,
+             route::table_id_t rd_id,
+             const handle_t& itf);
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * Comparison operator - only used for UT
+   */
+  bool operator==(const create_cmd& i) const;
+
+private:
+  const epg_id_t m_epg_id;
+  const uint32_t m_bd_id;
+  const route::table_id_t m_rd_id;
+  const handle_t m_itf;
+};
+
+/**
+ * A cmd class that deletes a GBP endpoint_group
+ */
+class delete_cmd
+  : public rpc_cmd<HW::item<bool>, rc_t, vapi::Gbp_endpoint_group_add_del>
+{
+public:
+  /**
+   * Constructor
+   */
+  delete_cmd(HW::item<bool>& item, epg_id_t epg_id);
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * Comparison operator - only used for UT
+   */
+  bool operator==(const delete_cmd& i) const;
+
+private:
+  const epg_id_t m_epg_id;
+};
+
+/**
+ * A cmd class that Dumps all the GBP endpoint_groups
+ */
+class dump_cmd : public VOM::dump_cmd<vapi::Gbp_endpoint_group_dump>
+{
+public:
+  /**
+   * Constructor
+   */
+  dump_cmd();
+  dump_cmd(const dump_cmd& d);
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * Comparison operator - only used for UT
+   */
+  bool operator==(const dump_cmd& i) const;
+
+private:
+  /**
+   * HW reutrn code
+   */
+  HW::item<bool> item;
+};
+}; // namespace gbp_enpoint_cms
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/src/vpp-api/vom/gbp_recirc.cpp b/src/vpp-api/vom/gbp_recirc.cpp
new file mode 100644 (file)
index 0000000..250e304
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/gbp_recirc.hpp"
+#include "vom/gbp_recirc_cmds.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+
+gbp_recirc::type_t::type_t(int v, const std::string s)
+  : enum_base<gbp_recirc::type_t>(v, s)
+{
+}
+
+const gbp_recirc::type_t gbp_recirc::type_t::INTERNAL(0, "internal");
+const gbp_recirc::type_t gbp_recirc::type_t::EXTERNAL(1, "external");
+
+singular_db<gbp_recirc::key_t, gbp_recirc> gbp_recirc::m_db;
+
+gbp_recirc::event_handler gbp_recirc::m_evh;
+
+gbp_recirc::gbp_recirc(const interface& itf,
+                       const type_t& type,
+                       const gbp_endpoint_group& epg)
+  : m_hw(false)
+  , m_itf(itf.singular())
+  , m_type(type)
+  , m_epg(epg.singular())
+{
+}
+
+gbp_recirc::gbp_recirc(const gbp_recirc& gbpe)
+  : m_hw(gbpe.m_hw)
+  , m_itf(gbpe.m_itf)
+  , m_type(gbpe.m_type)
+  , m_epg(gbpe.m_epg)
+{
+}
+
+gbp_recirc::~gbp_recirc()
+{
+  sweep();
+  m_db.release(key(), this);
+}
+
+const gbp_recirc::key_t
+gbp_recirc::key() const
+{
+  return (m_itf->key());
+}
+
+const handle_t&
+gbp_recirc::handle() const
+{
+  return m_itf->handle();
+}
+
+bool
+gbp_recirc::operator==(const gbp_recirc& gbpe) const
+{
+  return ((key() == gbpe.key()) && (m_type == gbpe.m_type) &&
+          (m_itf == gbpe.m_itf) && (m_epg == gbpe.m_epg));
+}
+
+void
+gbp_recirc::sweep()
+{
+  if (m_hw) {
+    HW::enqueue(new gbp_recirc_cmds::delete_cmd(m_hw, m_itf->handle()));
+  }
+  HW::write();
+}
+
+void
+gbp_recirc::replay()
+{
+  if (m_hw) {
+    HW::enqueue(new gbp_recirc_cmds::create_cmd(
+      m_hw, m_itf->handle(), (m_type == type_t::EXTERNAL), m_epg->id()));
+  }
+}
+
+std::string
+gbp_recirc::to_string() const
+{
+  std::ostringstream s;
+  s << "gbp-recirc:[" << m_itf->to_string() << ", type:" << m_type.to_string()
+    << ", " << m_epg->to_string() << "]";
+
+  return (s.str());
+}
+
+void
+gbp_recirc::update(const gbp_recirc& r)
+{
+  if (rc_t::OK != m_hw.rc()) {
+    HW::enqueue(new gbp_recirc_cmds::create_cmd(
+      m_hw, m_itf->handle(), (m_type == type_t::EXTERNAL), m_epg->id()));
+  }
+}
+
+std::shared_ptr<gbp_recirc>
+gbp_recirc::find_or_add(const gbp_recirc& temp)
+{
+  return (m_db.find_or_add(temp.key(), temp));
+}
+
+std::shared_ptr<gbp_recirc>
+gbp_recirc::find(const key_t& k)
+{
+  return (m_db.find(k));
+}
+
+std::shared_ptr<gbp_recirc>
+gbp_recirc::singular() const
+{
+  return find_or_add(*this);
+}
+
+void
+gbp_recirc::dump(std::ostream& os)
+{
+  db_dump(m_db, os);
+}
+
+gbp_recirc::event_handler::event_handler()
+{
+  OM::register_listener(this);
+  inspect::register_handler({ "gbp-recirc" }, "GBP Recircs", this);
+}
+
+void
+gbp_recirc::event_handler::handle_replay()
+{
+  m_db.replay();
+}
+
+void
+gbp_recirc::event_handler::handle_populate(const client_db::key_t& key)
+{
+  std::shared_ptr<gbp_recirc_cmds::dump_cmd> cmd =
+    std::make_shared<gbp_recirc_cmds::dump_cmd>();
+
+  HW::enqueue(cmd);
+  HW::write();
+
+  for (auto& record : *cmd) {
+    auto& payload = record.get_payload();
+
+    std::shared_ptr<interface> itf =
+      interface::find(payload.recirc.sw_if_index);
+    std::shared_ptr<gbp_endpoint_group> epg =
+      gbp_endpoint_group::find(payload.recirc.epg_id);
+
+    VOM_LOG(log_level_t::DEBUG) << "data: [" << payload.recirc.sw_if_index
+                                << ", " << payload.recirc.epg_id << "]";
+
+    if (itf && epg) {
+      gbp_recirc recirc(
+        *itf, (payload.recirc.is_ext ? type_t::EXTERNAL : type_t::INTERNAL),
+        *epg);
+      OM::commit(key, recirc);
+
+      VOM_LOG(log_level_t::DEBUG) << "read: " << recirc.to_string();
+    }
+  }
+}
+
+dependency_t
+gbp_recirc::event_handler::order() const
+{
+  return (dependency_t::BINDING);
+}
+
+void
+gbp_recirc::event_handler::show(std::ostream& os)
+{
+  db_dump(m_db, os);
+}
+} // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/src/vpp-api/vom/gbp_recirc.hpp b/src/vpp-api/vom/gbp_recirc.hpp
new file mode 100644 (file)
index 0000000..fee4f6c
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_GBP_RECIRC_H__
+#define __VOM_GBP_RECIRC_H__
+
+#include "vom/gbp_endpoint_group.hpp"
+#include "vom/interface.hpp"
+#include "vom/singular_db.hpp"
+
+namespace VOM {
+/**
+ * A recirculation interface for GBP use pre/post NAT
+ */
+class gbp_recirc : public object_base
+{
+public:
+  /**
+   * The key for a GBP recirc interface
+   */
+  typedef interface::key_t key_t;
+
+  struct type_t : public enum_base<type_t>
+  {
+    /**
+     * Internal recirclation interfaces accept per-NAT translation
+     * traffic from the external/NAT EPG and inject into the
+     * private/NAT-inside EPG
+     */
+    const static type_t INTERNAL;
+
+    /**
+     * External recirculation interfaces accept post-NAT translation
+     * traffic from the internal EPG and inject into the
+     * NAT EPG
+     */
+    const static type_t EXTERNAL;
+
+  private:
+    type_t(int v, const std::string s);
+  };
+
+  /**
+   * Construct a GBP recirc
+   */
+  gbp_recirc(const interface& itf,
+             const type_t& type,
+             const gbp_endpoint_group& epg);
+
+  /**
+   * Copy Construct
+   */
+  gbp_recirc(const gbp_recirc& r);
+
+  /**
+   * Destructor
+   */
+  ~gbp_recirc();
+
+  /**
+   * Return the object's key
+   */
+  const key_t key() const;
+
+  /**
+   * comparison operator
+   */
+  bool operator==(const gbp_recirc& bdae) const;
+
+  /**
+   * Return the matching 'singular instance'
+   */
+  std::shared_ptr<gbp_recirc> singular() const;
+
+  /**
+   * Find the instnace of the recirc interface in the OM
+   */
+  static std::shared_ptr<gbp_recirc> find(const key_t& k);
+
+  /**
+   * Dump all bridge_domain-doamin into the stream provided
+   */
+  static void dump(std::ostream& os);
+
+  /**
+   * replay the object to create it in hardware
+   */
+  void replay(void);
+
+  /**
+   * Convert to string for debugging
+   */
+  std::string to_string() const;
+
+  /**
+   * return the recirculation interface's handle
+   */
+  const handle_t& handle() const;
+
+private:
+  /**
+   * Class definition for listeners to OM events
+   */
+  class event_handler : public OM::listener, public inspect::command_handler
+  {
+  public:
+    event_handler();
+    virtual ~event_handler() = default;
+
+    /**
+     * Handle a populate event
+     */
+    void handle_populate(const client_db::key_t& key);
+
+    /**
+     * Handle a replay event
+     */
+    void handle_replay();
+
+    /**
+     * Show the object in the Singular DB
+     */
+    void show(std::ostream& os);
+
+    /**
+     * Get the sortable Id of the listener
+     */
+    dependency_t order() const;
+  };
+
+  /**
+   * event_handler to register with OM
+   */
+  static event_handler m_evh;
+
+  /**
+   * Commit the acculmulated changes into VPP. i.e. to a 'HW" write.
+   */
+  void update(const gbp_recirc& obj);
+
+  /**
+   * Find or add the instnace of the bridge_domain domain in the OM
+   */
+  static std::shared_ptr<gbp_recirc> find_or_add(const gbp_recirc& temp);
+
+  /*
+   * It's the VPPHW class that updates the objects in HW
+   */
+  friend class OM;
+
+  /**
+   * It's the singular_db class that calls replay()
+   */
+  friend class singular_db<key_t, gbp_recirc>;
+
+  /**
+   * Sweep/reap the object if still stale
+   */
+  void sweep(void);
+
+  /**
+   * HW configuration for the result of creating the recirc
+   */
+  HW::item<bool> m_hw;
+
+  /**
+   * The interface the recirc is attached to.
+   */
+  std::shared_ptr<interface> m_itf;
+
+  /**
+   * Is the reicrc for the external (i.e. post-NAT) or internal
+   */
+  type_t m_type;
+
+  /**
+   * The EPG the recirc is in
+   */
+  std::shared_ptr<gbp_endpoint_group> m_epg;
+
+  /**
+   * A map of all bridge_domains
+   */
+  static singular_db<key_t, gbp_recirc> m_db;
+};
+
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/src/vpp-api/vom/gbp_recirc_cmds.cpp b/src/vpp-api/vom/gbp_recirc_cmds.cpp
new file mode 100644 (file)
index 0000000..757fcb9
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/gbp_recirc_cmds.hpp"
+
+namespace VOM {
+namespace gbp_recirc_cmds {
+
+create_cmd::create_cmd(HW::item<bool>& item,
+                       const handle_t& itf,
+                       bool is_ext,
+                       epg_id_t epg_id)
+  : rpc_cmd(item)
+  , m_itf(itf)
+  , m_is_ext(is_ext)
+  , m_epg_id(epg_id)
+{
+}
+
+bool
+create_cmd::operator==(const create_cmd& other) const
+{
+  return ((m_itf == other.m_itf) && (m_is_ext == other.m_is_ext) &&
+          (m_epg_id == other.m_epg_id));
+}
+
+rc_t
+create_cmd::issue(connection& con)
+{
+  msg_t req(con.ctx(), std::ref(*this));
+
+  auto& payload = req.get_request().get_payload();
+  payload.is_add = 1;
+  payload.recirc.sw_if_index = m_itf.value();
+  payload.recirc.epg_id = m_epg_id;
+  payload.recirc.is_ext = m_is_ext;
+
+  VAPI_CALL(req.execute());
+
+  m_hw_item.set(wait());
+
+  return rc_t::OK;
+}
+
+std::string
+create_cmd::to_string() const
+{
+  std::ostringstream s;
+  s << "gbp-recirc-create: " << m_hw_item.to_string() << " itf:" << m_itf
+    << " ext:" << m_is_ext << " epg-id:" << m_epg_id;
+
+  return (s.str());
+}
+
+delete_cmd::delete_cmd(HW::item<bool>& item, const handle_t& itf)
+  : rpc_cmd(item)
+  , m_itf(itf)
+{
+}
+
+bool
+delete_cmd::operator==(const delete_cmd& other) const
+{
+  return (m_itf == other.m_itf);
+}
+
+rc_t
+delete_cmd::issue(connection& con)
+{
+  msg_t req(con.ctx(), std::ref(*this));
+
+  auto& payload = req.get_request().get_payload();
+  payload.is_add = 0;
+  payload.recirc.sw_if_index = m_itf.value();
+  payload.recirc.epg_id = ~0;
+
+  VAPI_CALL(req.execute());
+
+  m_hw_item.set(wait());
+
+  return rc_t::OK;
+}
+
+std::string
+delete_cmd::to_string() const
+{
+  std::ostringstream s;
+  s << "gbp-recirc-delete: " << m_hw_item.to_string() << " itf:" << m_itf;
+
+  return (s.str());
+}
+
+dump_cmd::dump_cmd()
+{
+}
+
+bool
+dump_cmd::operator==(const dump_cmd& other) const
+{
+  return (true);
+}
+
+rc_t
+dump_cmd::issue(connection& con)
+{
+  m_dump.reset(new msg_t(con.ctx(), std::ref(*this)));
+
+  VAPI_CALL(m_dump->execute());
+
+  wait();
+
+  return rc_t::OK;
+}
+
+std::string
+dump_cmd::to_string() const
+{
+  return ("gbp-recirc-dump");
+}
+
+}; // namespace gbp_recirc_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/src/vpp-api/vom/gbp_recirc_cmds.hpp b/src/vpp-api/vom/gbp_recirc_cmds.hpp
new file mode 100644 (file)
index 0000000..fe17834
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_GBP_RECIRC_CMDS_H__
+#define __VOM_GBP_RECIRC_CMDS_H__
+
+#include "vom/dump_cmd.hpp"
+#include "vom/gbp_recirc.hpp"
+
+#include <vapi/gbp.api.vapi.hpp>
+
+namespace VOM {
+namespace gbp_recirc_cmds {
+
+/**
+* A command class that creates or updates the GBP recirc
+*/
+class create_cmd
+  : public rpc_cmd<HW::item<bool>, rc_t, vapi::Gbp_recirc_add_del>
+{
+public:
+  /**
+   * Constructor
+   */
+  create_cmd(HW::item<bool>& item,
+             const handle_t& itf,
+             bool is_ext,
+             epg_id_t epg_id);
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * Comparison operator - only used for UT
+   */
+  bool operator==(const create_cmd& i) const;
+
+private:
+  const handle_t m_itf;
+  bool m_is_ext;
+  const epg_id_t m_epg_id;
+};
+
+/**
+ * A cmd class that deletes a GBP recirc
+ */
+class delete_cmd
+  : public rpc_cmd<HW::item<bool>, rc_t, vapi::Gbp_recirc_add_del>
+{
+public:
+  /**
+   * Constructor
+   */
+  delete_cmd(HW::item<bool>& item, const handle_t& itf);
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * Comparison operator - only used for UT
+   */
+  bool operator==(const delete_cmd& i) const;
+
+private:
+  const handle_t m_itf;
+};
+
+/**
+ * A cmd class that Dumps all the GBP recircs
+ */
+class dump_cmd : public VOM::dump_cmd<vapi::Gbp_recirc_dump>
+{
+public:
+  /**
+   * Constructor
+   */
+  dump_cmd();
+  dump_cmd(const dump_cmd& d);
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * Comparison operator - only used for UT
+   */
+  bool operator==(const dump_cmd& i) const;
+
+private:
+  /**
+   * HW reutrn code
+   */
+  HW::item<bool> item;
+};
+}; // namespace gbp_enpoint_cms
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/src/vpp-api/vom/gbp_subnet.cpp b/src/vpp-api/vom/gbp_subnet.cpp
new file mode 100644 (file)
index 0000000..84dbd22
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/gbp_subnet.hpp"
+#include "vom/gbp_subnet_cmds.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+
+gbp_subnet::type_t::type_t(int v, const std::string s)
+  : enum_base<gbp_subnet::type_t>(v, s)
+{
+}
+
+const gbp_subnet::type_t gbp_subnet::type_t::INTERNAL(0, "internal");
+const gbp_subnet::type_t gbp_subnet::type_t::EXTERNAL(1, "external");
+
+singular_db<gbp_subnet::key_t, gbp_subnet> gbp_subnet::m_db;
+
+gbp_subnet::event_handler gbp_subnet::m_evh;
+
+gbp_subnet::gbp_subnet(const route_domain& rd, const route::prefix_t& prefix)
+  : m_hw(false)
+  , m_rd(rd.singular())
+  , m_prefix(prefix)
+  , m_type(type_t::INTERNAL)
+  , m_recirc(nullptr)
+  , m_epg(nullptr)
+{
+}
+
+gbp_subnet::gbp_subnet(const route_domain& rd,
+                       const route::prefix_t& prefix,
+                       const gbp_recirc& recirc,
+                       const gbp_endpoint_group& epg)
+  : m_hw(false)
+  , m_rd(rd.singular())
+  , m_prefix(prefix)
+  , m_type(type_t::EXTERNAL)
+  , m_recirc(recirc.singular())
+  , m_epg(epg.singular())
+{
+}
+
+gbp_subnet::gbp_subnet(const gbp_subnet& o)
+  : m_hw(o.m_hw)
+  , m_rd(o.m_rd)
+  , m_prefix(o.m_prefix)
+  , m_type(o.m_type)
+  , m_recirc(o.m_recirc)
+  , m_epg(o.m_epg)
+{
+}
+
+gbp_subnet::~gbp_subnet()
+{
+  sweep();
+  m_db.release(key(), this);
+}
+
+const gbp_subnet::key_t
+gbp_subnet::key() const
+{
+  return (std::make_pair(m_rd->key(), m_prefix));
+}
+
+bool
+gbp_subnet::operator==(const gbp_subnet& gbpe) const
+{
+  return ((key() == gbpe.key()) && (m_recirc == gbpe.m_recirc) &&
+          (m_epg == gbpe.m_epg));
+}
+
+void
+gbp_subnet::sweep()
+{
+  if (m_hw) {
+    HW::enqueue(
+      new gbp_subnet_cmds::delete_cmd(m_hw, m_rd->table_id(), m_prefix));
+  }
+  HW::write();
+}
+
+void
+gbp_subnet::replay()
+{
+  if (m_hw) {
+    HW::enqueue(new gbp_subnet_cmds::create_cmd(
+      m_hw, m_rd->table_id(), m_prefix, (m_type == type_t::INTERNAL),
+      (m_recirc ? m_recirc->handle() : handle_t::INVALID),
+      (m_epg ? m_epg->id() : ~0)));
+  }
+}
+
+std::string
+gbp_subnet::to_string() const
+{
+  std::ostringstream s;
+  s << "gbp-subnet:[" << m_type.to_string() << ", " << m_rd->to_string() << ":"
+    << m_prefix.to_string();
+  if (m_recirc)
+    s << ", " << m_recirc->to_string();
+  if (m_epg)
+    s << ", " << m_epg->to_string();
+
+  s << "]";
+
+  return (s.str());
+}
+
+void
+gbp_subnet::update(const gbp_subnet& r)
+{
+  if (rc_t::OK != m_hw.rc()) {
+    HW::enqueue(new gbp_subnet_cmds::create_cmd(
+      m_hw, m_rd->table_id(), m_prefix, (m_type == type_t::INTERNAL),
+      (m_recirc ? m_recirc->handle() : handle_t::INVALID),
+      (m_epg ? m_epg->id() : ~0)));
+  }
+}
+
+std::shared_ptr<gbp_subnet>
+gbp_subnet::find_or_add(const gbp_subnet& temp)
+{
+  return (m_db.find_or_add(temp.key(), temp));
+}
+
+std::shared_ptr<gbp_subnet>
+gbp_subnet::find(const key_t& k)
+{
+  return (m_db.find(k));
+}
+
+std::shared_ptr<gbp_subnet>
+gbp_subnet::singular() const
+{
+  return find_or_add(*this);
+}
+
+void
+gbp_subnet::dump(std::ostream& os)
+{
+  db_dump(m_db, os);
+}
+
+gbp_subnet::event_handler::event_handler()
+{
+  OM::register_listener(this);
+  inspect::register_handler({ "gbp-subnet" }, "GBP Subnets", this);
+}
+
+void
+gbp_subnet::event_handler::handle_replay()
+{
+  m_db.replay();
+}
+
+void
+gbp_subnet::event_handler::handle_populate(const client_db::key_t& key)
+{
+  std::shared_ptr<gbp_subnet_cmds::dump_cmd> cmd =
+    std::make_shared<gbp_subnet_cmds::dump_cmd>();
+
+  HW::enqueue(cmd);
+  HW::write();
+
+  for (auto& record : *cmd) {
+    auto& payload = record.get_payload();
+
+    route::prefix_t pfx(payload.subnet.is_ip6, payload.subnet.address,
+                        payload.subnet.address_length);
+    std::shared_ptr<route_domain> rd =
+      route_domain::find(payload.subnet.table_id);
+
+    if (rd) {
+      if (payload.subnet.is_internal) {
+        gbp_subnet gs(*rd, pfx);
+        OM::commit(key, gs);
+        VOM_LOG(log_level_t::DEBUG) << "read: " << gs.to_string();
+      } else {
+        std::shared_ptr<interface> itf =
+          interface::find(payload.subnet.sw_if_index);
+        std::shared_ptr<gbp_endpoint_group> epg =
+          gbp_endpoint_group::find(payload.subnet.epg_id);
+
+        if (itf && epg) {
+          std::shared_ptr<gbp_recirc> recirc = gbp_recirc::find(itf->key());
+
+          if (recirc) {
+            gbp_subnet gs(*rd, pfx, *recirc, *epg);
+            OM::commit(key, gs);
+            VOM_LOG(log_level_t::DEBUG) << "read: " << gs.to_string();
+          }
+        }
+      }
+    }
+  }
+}
+
+dependency_t
+gbp_subnet::event_handler::order() const
+{
+  return (dependency_t::ENTRY);
+}
+
+void
+gbp_subnet::event_handler::show(std::ostream& os)
+{
+  db_dump(m_db, os);
+}
+} // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/src/vpp-api/vom/gbp_subnet.hpp b/src/vpp-api/vom/gbp_subnet.hpp
new file mode 100644 (file)
index 0000000..9c9166e
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_GBP_SUBNET_H__
+#define __VOM_GBP_SUBNET_H__
+
+#include "vom/gbp_endpoint_group.hpp"
+#include "vom/gbp_recirc.hpp"
+#include "vom/route.hpp"
+#include "vom/singular_db.hpp"
+
+namespace VOM {
+/**
+ * A GBP Enpoint (i.e. a VM)
+ */
+class gbp_subnet : public object_base
+{
+public:
+  /**
+   * The key for a GBP subnet; table and prefix
+   */
+  typedef std::pair<route_domain::key_t, route::prefix_t> key_t;
+
+  /**
+    * Construct an internal GBP subnet
+    */
+  gbp_subnet(const route_domain& rd, const route::prefix_t& prefix);
+
+  /**
+   * Construct an external GBP subnet
+   */
+  gbp_subnet(const route_domain& rd,
+             const route::prefix_t& prefix,
+             const gbp_recirc& recirc,
+             const gbp_endpoint_group& epg);
+
+  /**
+   * Copy Construct
+   */
+  gbp_subnet(const gbp_subnet& r);
+
+  /**
+   * Destructor
+   */
+  ~gbp_subnet();
+
+  /**
+   * Return the object's key
+   */
+  const key_t key() const;
+
+  /**
+   * comparison operator
+   */
+  bool operator==(const gbp_subnet& bdae) const;
+
+  /**
+   * Return the matching 'singular instance'
+   */
+  std::shared_ptr<gbp_subnet> singular() const;
+
+  /**
+   * Find the instnace of the bridge_domain domain in the OM
+   */
+  static std::shared_ptr<gbp_subnet> find(const key_t& k);
+
+  /**
+   * Dump all bridge_domain-doamin into the stream provided
+   */
+  static void dump(std::ostream& os);
+
+  /**
+   * replay the object to create it in hardware
+   */
+  void replay(void);
+
+  /**
+   * Convert to string for debugging
+   */
+  std::string to_string() const;
+
+private:
+  struct type_t : public enum_base<type_t>
+  {
+    /**
+     * Internal subnet is reachable through the source EPG's
+     * uplink interface.
+     */
+    const static type_t INTERNAL;
+
+    /**
+     * External subnet requires NAT translation before egress.
+     */
+    const static type_t EXTERNAL;
+
+  private:
+    type_t(int v, const std::string s);
+  };
+
+  /**
+   * Class definition for listeners to OM events
+   */
+  class event_handler : public OM::listener, public inspect::command_handler
+  {
+  public:
+    event_handler();
+    virtual ~event_handler() = default;
+
+    /**
+     * Handle a populate event
+     */
+    void handle_populate(const client_db::key_t& key);
+
+    /**
+     * Handle a replay event
+     */
+    void handle_replay();
+
+    /**
+     * Show the object in the Singular DB
+     */
+    void show(std::ostream& os);
+
+    /**
+     * Get the sortable Id of the listener
+     */
+    dependency_t order() const;
+  };
+
+  /**
+   * event_handler to register with OM
+   */
+  static event_handler m_evh;
+
+  /**
+   * Commit the acculmulated changes into VPP. i.e. to a 'HW" write.
+   */
+  void update(const gbp_subnet& obj);
+
+  /**
+   * Find or add the instnace of the bridge_domain domain in the OM
+   */
+  static std::shared_ptr<gbp_subnet> find_or_add(const gbp_subnet& temp);
+
+  /*
+   * It's the VPPHW class that updates the objects in HW
+   */
+  friend class OM;
+
+  /**
+   * It's the singular_db class that calls replay()
+   */
+  friend class singular_db<key_t, gbp_subnet>;
+
+  /**
+   * Sweep/reap the object if still stale
+   */
+  void sweep(void);
+
+  /**
+   * HW configuration for the result of creating the subnet
+   */
+  HW::item<bool> m_hw;
+
+  /**
+   * the route domain the prefix is in
+   */
+  std::shared_ptr<route_domain> m_rd;
+
+  /**
+   * prefix to match
+   */
+  const route::prefix_t m_prefix;
+
+  /*
+   * Subnet type
+   */
+  const type_t m_type;
+
+  /**
+   * The interface the prefix is reachable through
+   */
+  std::shared_ptr<gbp_recirc> m_recirc;
+
+  /**
+   * The EPG the subnet is in
+   */
+  std::shared_ptr<gbp_endpoint_group> m_epg;
+
+  /**
+   * A map of all bridge_domains
+   */
+  static singular_db<key_t, gbp_subnet> m_db;
+};
+
+}; // namespace
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/src/vpp-api/vom/gbp_subnet_cmds.cpp b/src/vpp-api/vom/gbp_subnet_cmds.cpp
new file mode 100644 (file)
index 0000000..d087e5c
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/gbp_subnet_cmds.hpp"
+
+namespace VOM {
+namespace gbp_subnet_cmds {
+
+create_cmd::create_cmd(HW::item<bool>& item,
+                       route::table_id_t rd,
+                       const route::prefix_t& prefix,
+                       bool internal,
+                       const handle_t& itf,
+                       epg_id_t epg_id)
+  : rpc_cmd(item)
+  , m_rd(rd)
+  , m_prefix(prefix)
+  , m_internal(internal)
+  , m_itf(itf)
+  , m_epg_id(epg_id)
+{
+}
+
+bool
+create_cmd::operator==(const create_cmd& other) const
+{
+  return ((m_itf == other.m_itf) && (m_rd == other.m_rd) &&
+          (m_prefix == other.m_prefix) && (m_itf == other.m_itf) &&
+          (m_epg_id == other.m_epg_id));
+}
+
+rc_t
+create_cmd::issue(connection& con)
+{
+  msg_t req(con.ctx(), std::ref(*this));
+
+  auto& payload = req.get_request().get_payload();
+  payload.is_add = 1;
+  payload.subnet.is_internal = m_internal;
+  payload.subnet.table_id = m_rd;
+  payload.subnet.sw_if_index = m_itf.value();
+  payload.subnet.epg_id = m_epg_id;
+  m_prefix.to_vpp(&payload.subnet.is_ip6, payload.subnet.address,
+                  &payload.subnet.address_length);
+
+  VAPI_CALL(req.execute());
+
+  m_hw_item.set(wait());
+
+  return rc_t::OK;
+}
+
+std::string
+create_cmd::to_string() const
+{
+  std::ostringstream s;
+  s << "gbp-subnet-create: " << m_hw_item.to_string()
+    << "internal:" << m_internal << ", " << m_rd << ":" << m_prefix.to_string()
+    << " itf:" << m_itf << " epg-id:" << m_epg_id;
+
+  return (s.str());
+}
+
+delete_cmd::delete_cmd(HW::item<bool>& item,
+                       route::table_id_t rd,
+                       const route::prefix_t& prefix)
+  : rpc_cmd(item)
+  , m_rd(rd)
+  , m_prefix(prefix)
+{
+}
+
+bool
+delete_cmd::operator==(const delete_cmd& other) const
+{
+  return ((m_rd == other.m_rd) && (m_prefix == other.m_prefix));
+}
+
+rc_t
+delete_cmd::issue(connection& con)
+{
+  msg_t req(con.ctx(), std::ref(*this));
+
+  auto& payload = req.get_request().get_payload();
+  payload.is_add = 0;
+  payload.subnet.table_id = m_rd;
+  m_prefix.to_vpp(&payload.subnet.is_ip6, payload.subnet.address,
+                  &payload.subnet.address_length);
+
+  payload.subnet.is_internal = 0;
+  payload.subnet.sw_if_index = ~0;
+  payload.subnet.epg_id = ~0;
+
+  VAPI_CALL(req.execute());
+
+  m_hw_item.set(wait());
+
+  return rc_t::OK;
+}
+
+std::string
+delete_cmd::to_string() const
+{
+  std::ostringstream s;
+  s << "gbp-subnet-delete: " << m_hw_item.to_string() << ", " << m_rd << ":"
+    << m_prefix.to_string();
+
+  return (s.str());
+}
+
+dump_cmd::dump_cmd()
+{
+}
+
+bool
+dump_cmd::operator==(const dump_cmd& other) const
+{
+  return (true);
+}
+
+rc_t
+dump_cmd::issue(connection& con)
+{
+  m_dump.reset(new msg_t(con.ctx(), std::ref(*this)));
+
+  VAPI_CALL(m_dump->execute());
+
+  wait();
+
+  return rc_t::OK;
+}
+
+std::string
+dump_cmd::to_string() const
+{
+  return ("gbp-subnet-dump");
+}
+
+}; // namespace gbp_subnet_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/src/vpp-api/vom/gbp_subnet_cmds.hpp b/src/vpp-api/vom/gbp_subnet_cmds.hpp
new file mode 100644 (file)
index 0000000..3dbc8db
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_GBP_SUBNET_CMDS_H__
+#define __VOM_GBP_SUBNET_CMDS_H__
+
+#include "vom/dump_cmd.hpp"
+#include "vom/gbp_subnet.hpp"
+
+#include <vapi/gbp.api.vapi.hpp>
+
+namespace VOM {
+namespace gbp_subnet_cmds {
+
+/**
+* A command class that creates or updates the GBP subnet
+*/
+class create_cmd
+  : public rpc_cmd<HW::item<bool>, rc_t, vapi::Gbp_subnet_add_del>
+{
+public:
+  /**
+   * Constructor
+   */
+  create_cmd(HW::item<bool>& item,
+             route::table_id_t rd,
+             const route::prefix_t& prefix,
+             bool internal,
+             const handle_t& itf,
+             epg_id_t epg_id);
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * Comparison operator - only used for UT
+   */
+  bool operator==(const create_cmd& i) const;
+
+private:
+  const route::table_id_t m_rd;
+  const route::prefix_t m_prefix;
+  const bool m_internal;
+  const handle_t m_itf;
+  const epg_id_t m_epg_id;
+};
+
+/**
+ * A cmd class that deletes a GBP subnet
+ */
+class delete_cmd
+  : public rpc_cmd<HW::item<bool>, rc_t, vapi::Gbp_subnet_add_del>
+{
+public:
+  /**
+   * Constructor
+   */
+  delete_cmd(HW::item<bool>& item,
+             route::table_id_t rd,
+             const route::prefix_t& prefix);
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * Comparison operator - only used for UT
+   */
+  bool operator==(const delete_cmd& i) const;
+
+private:
+  const route::table_id_t m_rd;
+  const route::prefix_t m_prefix;
+};
+
+/**
+ * A cmd class that Dumps all the GBP subnets
+ */
+class dump_cmd : public VOM::dump_cmd<vapi::Gbp_subnet_dump>
+{
+public:
+  /**
+   * Constructor
+   */
+  dump_cmd();
+  dump_cmd(const dump_cmd& d);
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * Comparison operator - only used for UT
+   */
+  bool operator==(const dump_cmd& i) const;
+
+private:
+  /**
+   * HW reutrn code
+   */
+  HW::item<bool> item;
+};
+}; // namespace gbp_enpoint_cms
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
index e9b7a1a..6faf349 100644 (file)
@@ -275,9 +275,7 @@ interface::key() const
 std::queue<cmd*>&
 interface::mk_create_cmd(std::queue<cmd*>& q)
 {
-  if (type_t::LOOPBACK == m_type) {
-    q.push(new interface_cmds::loopback_create_cmd(m_hdl, m_name));
-  } else if (type_t::BVI == m_type) {
+  if ((type_t::LOOPBACK == m_type) || (type_t::BVI == m_type)) {
     q.push(new interface_cmds::loopback_create_cmd(m_hdl, m_name));
     q.push(new interface_cmds::set_tag(m_hdl, m_name));
     /*
@@ -516,8 +514,12 @@ interface::event_handler::handle_populate(const client_db::key_t& key)
   HW::write();
 
   for (auto& itf_record : *cmd) {
-    std::shared_ptr<interface> itf =
-      interface_factory::new_interface(itf_record.get_payload());
+    auto payload = itf_record.get_payload();
+    VOM_LOG(log_level_t::DEBUG) << "dump: [" << payload.sw_if_index
+                                << " name:" << (char*)payload.interface_name
+                                << " tag:" << (char*)payload.tag << "]";
+
+    std::shared_ptr<interface> itf = interface_factory::new_interface(payload);
 
     if (itf && interface::type_t::LOCAL != itf->type()) {
       VOM_LOG(log_level_t::DEBUG) << "dump: " << itf->to_string();
index 911282d..139bdd5 100644 (file)
@@ -51,7 +51,8 @@ interface::type_t::from_string(const std::string& str)
     return interface::type_t::ETHERNET;
   } else if (str.find("vxlan") != std::string::npos) {
     return interface::type_t::VXLAN;
-  } else if (str.find("loop") != std::string::npos) {
+  } else if ((str.find("loop") != std::string::npos) ||
+             (str.find("recirc") != std::string::npos)) {
     return interface::type_t::LOOPBACK;
   } else if (str.find("host-") != std::string::npos) {
     return interface::type_t::AFPACKET;
index bb94048..1b51192 100644 (file)
@@ -255,6 +255,238 @@ private:
   HW::item<bool> item;
 };
 
+/////
+/**
+* A functor class that binds a NAT configuration to an input interface
+*/
+class bind_66_input_cmd
+  : public rpc_cmd<HW::item<bool>, rc_t, vapi::Nat66_add_del_interface>
+{
+public:
+  /**
+   * Constructor
+   */
+  bind_66_input_cmd(HW::item<bool>& item,
+                    const handle_t& itf,
+                    const nat_binding::zone_t& zone);
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * Comparison operator - only used for UT
+   */
+  bool operator==(const bind_66_input_cmd& i) const;
+
+private:
+  /**
+   * The interface to bind
+   */
+  const handle_t m_itf;
+
+  /**
+   * The zone the interface is in
+   */
+  const nat_binding::zone_t m_zone;
+};
+
+/**
+ * A cmd class that unbinds a NAT configuration from an input interface
+ */
+class unbind_66_input_cmd
+  : public rpc_cmd<HW::item<bool>, rc_t, vapi::Nat66_add_del_interface>
+{
+public:
+  /**
+   * Constructor
+   */
+  unbind_66_input_cmd(HW::item<bool>& item,
+                      const handle_t& itf,
+                      const nat_binding::zone_t& zone);
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * Comparison operator - only used for UT
+   */
+  bool operator==(const unbind_66_input_cmd& i) const;
+
+private:
+  /**
+   * The interface to bind
+   */
+  const handle_t m_itf;
+
+  /**
+   * The zone the interface is in
+   */
+  const nat_binding::zone_t m_zone;
+};
+
+/**
+ * A functor class that binds a NAT configuration to an output interface
+ */
+/* class bind_66_output_cmd */
+/*   : public rpc_cmd<HW::item<bool>, */
+/*                    rc_t, */
+/*                    vapi::Nat66_interface_add_del_output_feature> */
+/* { */
+/* public: */
+/*   /\** */
+/*    * Constructor */
+/*    *\/ */
+/*   bind_66_output_cmd(HW::item<bool>& item, */
+/*                      const handle_t& itf, */
+/*                      const nat_binding::zone_t& zone); */
+
+/*   /\** */
+/*    * Issue the command to VPP/HW */
+/*    *\/ */
+/*   rc_t issue(connection& con); */
+/*   /\** */
+/*    * convert to string format for debug purposes */
+/*    *\/ */
+/*   std::string to_string() const; */
+
+/*   /\** */
+/*    * Comparison operator - only used for UT */
+/*    *\/ */
+/*   bool operator==(const bind_66_output_cmd& i) const; */
+
+/* private: */
+/*   /\** */
+/*    * The interface to bind */
+/*    *\/ */
+/*   const handle_t m_itf; */
+
+/*   /\** */
+/*    * The zone the interface is in */
+/*    *\/ */
+/*   const nat_binding::zone_t m_zone; */
+/* }; */
+
+/* /\** */
+/*  * A cmd class that unbinds a NAT configuration from an output interface */
+/*  *\/ */
+/* class unbind_66_output_cmd */
+/*   : public rpc_cmd<HW::item<bool>, */
+/*                    rc_t, */
+/*                    vapi::Nat66_interface_add_del_output_feature> */
+/* { */
+/* public: */
+/*   /\** */
+/*    * Constructor */
+/*    *\/ */
+/*   unbind_66_output_cmd(HW::item<bool>& item, */
+/*                        const handle_t& itf, */
+/*                        const nat_binding::zone_t& zone); */
+
+/*   /\** */
+/*    * Issue the command to VPP/HW */
+/*    *\/ */
+/*   rc_t issue(connection& con); */
+/*   /\** */
+/*    * convert to string format for debug purposes */
+/*    *\/ */
+/*   std::string to_string() const; */
+
+/*   /\** */
+/*    * Comparison operator - only used for UT */
+/*    *\/ */
+/*   bool operator==(const unbind_66_output_cmd& i) const; */
+
+/* private: */
+/*   /\** */
+/*    * The interface to bind */
+/*    *\/ */
+/*   const handle_t m_itf; */
+
+/*   /\** */
+/*    * The zone the interface is in */
+/*    *\/ */
+/*   const nat_binding::zone_t m_zone; */
+/* }; */
+
+/**
+ * A cmd class that Dumps all the nat_statics
+ */
+class dump_input_66_cmd : public dump_cmd<vapi::Nat66_interface_dump>
+{
+public:
+  /**
+   * Constructor
+   */
+  dump_input_66_cmd();
+  dump_input_66_cmd(const dump_input_66_cmd& d);
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * Comparison operator - only used for UT
+   */
+  bool operator==(const dump_input_66_cmd& i) const;
+
+private:
+  /**
+   * HW reutrn code
+   */
+  HW::item<bool> item;
+};
+
+/**
+ * A cmd class that Dumps all the nat_statics
+ */
+/* class dump_output_66_cmd */
+/*   : public dump_cmd<vapi::Nat66_interface_output_feature_dump> */
+/* { */
+/* public: */
+/*   /\** */
+/*    * Constructor */
+/*    *\/ */
+/*   dump_output_66_cmd(); */
+/*   dump_output_66_cmd(const dump_output_66_cmd& d); */
+
+/*   /\** */
+/*    * Issue the command to VPP/HW */
+/*    *\/ */
+/*   rc_t issue(connection& con); */
+/*   /\** */
+/*    * convert to string format for debug purposes */
+/*    *\/ */
+/*   std::string to_string() const; */
+
+/*   /\** */
+/*    * Comparison operator - only used for UT */
+/*    *\/ */
+/*   bool operator==(const dump_output_66_cmd& i) const; */
+
+/* private: */
+/*   /\** */
+/*    * HW reutrn code */
+/*    *\/ */
+/*   HW::item<bool> item; */
+/* }; */
+
 }; // namespace nat_binding_cmds
 }; // namespace VOM
 
index 3185b56..bf8573d 100644 (file)
@@ -22,7 +22,7 @@ singular_db<nat_static::key_t, nat_static> nat_static::m_db;
 nat_static::event_handler nat_static::m_evh;
 
 nat_static::nat_static(const boost::asio::ip::address& inside,
-                       const boost::asio::ip::address_v4& outside)
+                       const boost::asio::ip::address& outside)
   : m_hw(false)
   , m_rd(route_domain::get_default())
   , m_inside(inside)
@@ -32,7 +32,7 @@ nat_static::nat_static(const boost::asio::ip::address& inside,
 
 nat_static::nat_static(const route_domain& rd,
                        const boost::asio::ip::address& inside,
-                       const boost::asio::ip::address_v4& outside)
+                       const boost::asio::ip::address& outside)
   : m_hw(false)
   , m_rd(rd.singular())
   , m_inside(inside)
@@ -74,7 +74,10 @@ nat_static::sweep()
   if (m_hw) {
     if (m_inside.is_v4()) {
       HW::enqueue(new nat_static_cmds::delete_44_cmd(
-        m_hw, m_rd->table_id(), m_inside.to_v4(), m_outside));
+        m_hw, m_rd->table_id(), m_inside.to_v4(), m_outside.to_v4()));
+    } else {
+      HW::enqueue(new nat_static_cmds::delete_66_cmd(
+        m_hw, m_rd->table_id(), m_inside.to_v6(), m_outside.to_v6()));
     }
   }
   HW::write();
@@ -86,7 +89,10 @@ nat_static::replay()
   if (m_hw) {
     if (m_inside.is_v4()) {
       HW::enqueue(new nat_static_cmds::create_44_cmd(
-        m_hw, m_rd->table_id(), m_inside.to_v4(), m_outside));
+        m_hw, m_rd->table_id(), m_inside.to_v4(), m_outside.to_v4()));
+    } else {
+      HW::enqueue(new nat_static_cmds::create_66_cmd(
+        m_hw, m_rd->table_id(), m_inside.to_v6(), m_outside.to_v6()));
     }
   }
 }
@@ -100,7 +106,10 @@ nat_static::update(const nat_static& r)
   if (rc_t::OK != m_hw.rc()) {
     if (m_inside.is_v4()) {
       HW::enqueue(new nat_static_cmds::create_44_cmd(
-        m_hw, m_rd->table_id(), m_inside.to_v4(), m_outside));
+        m_hw, m_rd->table_id(), m_inside.to_v4(), m_outside.to_v4()));
+    } else {
+      HW::enqueue(new nat_static_cmds::create_66_cmd(
+        m_hw, m_rd->table_id(), m_inside.to_v6(), m_outside.to_v6()));
     }
   }
 }
@@ -158,20 +167,43 @@ nat_static::event_handler::handle_populate(const client_db::key_t& key)
   /*
    * dump VPP current states
    */
-  std::shared_ptr<nat_static_cmds::dump_44_cmd> cmd =
+  std::shared_ptr<nat_static_cmds::dump_44_cmd> cmd44 =
     std::make_shared<nat_static_cmds::dump_44_cmd>();
 
-  HW::enqueue(cmd);
+  HW::enqueue(cmd44);
   HW::write();
 
-  for (auto& record : *cmd) {
+  for (auto& record : *cmd44) {
 
     auto& payload = record.get_payload();
 
     boost::asio::ip::address inside = from_bytes(0, payload.local_ip_address);
     boost::asio::ip::address outside =
       from_bytes(0, payload.external_ip_address);
-    nat_static n(route_domain(payload.vrf_id), inside, outside.to_v4());
+    nat_static n(route_domain(payload.vrf_id), inside, outside);
+
+    /*
+     * Write each of the discovered mappings into the OM,
+     * but disable the HW Command q whilst we do, so that no
+     * commands are sent to VPP
+     */
+    OM::commit(key, n);
+  }
+
+  std::shared_ptr<nat_static_cmds::dump_66_cmd> cmd66 =
+    std::make_shared<nat_static_cmds::dump_66_cmd>();
+
+  HW::enqueue(cmd66);
+  HW::write();
+
+  for (auto& record : *cmd66) {
+
+    auto& payload = record.get_payload();
+
+    boost::asio::ip::address inside = from_bytes(1, payload.local_ip_address);
+    boost::asio::ip::address outside =
+      from_bytes(1, payload.external_ip_address);
+    nat_static n(route_domain(payload.vrf_id), inside, outside);
 
     /*
      * Write each of the discovered mappings into the OM,
index 3b0d2d0..2dcadb3 100644 (file)
@@ -39,7 +39,7 @@ public:
    * table
    */
   nat_static(const boost::asio::ip::address& inside,
-             const boost::asio::ip::address_v4& outside);
+             const boost::asio::ip::address& outside);
 
   /**
    * Construct an NAT Static binding with the outside address in
@@ -47,7 +47,7 @@ public:
    */
   nat_static(const route_domain& rd,
              const boost::asio::ip::address& inside,
-             const boost::asio::ip::address_v4& outside);
+             const boost::asio::ip::address& outside);
 
   /**
    * Copy Construct
@@ -171,9 +171,9 @@ private:
   const boost::asio::ip::address m_inside;
 
   /**
-   * The 'outside' IP address - always v4
+   * The 'outside' IP address
    */
-  const boost::asio::ip::address_v4 m_outside;
+  const boost::asio::ip::address m_outside;
 
   /**
    * A map of all NAT statics
index facc7c6..a80e474 100644 (file)
@@ -143,6 +143,125 @@ dump_44_cmd::issue(connection& con)
 
 std::string
 dump_44_cmd::to_string() const
+{
+  return ("nat-44-static-dump");
+}
+
+create_66_cmd::create_66_cmd(HW::item<bool>& item,
+                             route::table_id_t id,
+                             const boost::asio::ip::address_v6& inside,
+                             const boost::asio::ip::address_v6& outside)
+  : rpc_cmd(item)
+  , m_id(id)
+  , m_inside(inside)
+  , m_outside(outside)
+{
+}
+
+bool
+create_66_cmd::operator==(const create_66_cmd& other) const
+{
+  return ((m_id == other.m_id) && (m_inside == other.m_inside) &&
+          (m_outside == other.m_outside));
+}
+
+rc_t
+create_66_cmd::issue(connection& con)
+{
+  msg_t req(con.ctx(), std::ref(*this));
+
+  auto& payload = req.get_request().get_payload();
+  payload.is_add = 1;
+  payload.vrf_id = m_id;
+  to_bytes(m_inside, payload.local_ip_address);
+  to_bytes(m_outside, payload.external_ip_address);
+
+  VAPI_CALL(req.execute());
+
+  m_hw_item.set(wait());
+
+  return rc_t::OK;
+}
+
+std::string
+create_66_cmd::to_string() const
+{
+  std::ostringstream s;
+  s << "nat-66-static-create: " << m_hw_item.to_string() << " table:" << m_id
+    << " inside:" << m_inside.to_string()
+    << " outside:" << m_outside.to_string();
+
+  return (s.str());
+}
+
+delete_66_cmd::delete_66_cmd(HW::item<bool>& item,
+                             route::table_id_t id,
+                             const boost::asio::ip::address_v6& inside,
+                             const boost::asio::ip::address_v6& outside)
+  : rpc_cmd(item)
+  , m_id(id)
+  , m_inside(inside)
+  , m_outside(outside)
+{
+}
+
+bool
+delete_66_cmd::operator==(const delete_66_cmd& other) const
+{
+  return ((m_id == other.m_id) && (m_inside == other.m_inside) &&
+          (m_outside == other.m_outside));
+}
+
+rc_t
+delete_66_cmd::issue(connection& con)
+{
+  msg_t req(con.ctx(), std::ref(*this));
+
+  auto& payload = req.get_request().get_payload();
+  payload.is_add = 0;
+  payload.vrf_id = m_id;
+  to_bytes(m_inside, payload.local_ip_address);
+  to_bytes(m_outside, payload.external_ip_address);
+
+  VAPI_CALL(req.execute());
+
+  wait();
+  m_hw_item.set(rc_t::NOOP);
+
+  return rc_t::OK;
+}
+
+std::string
+delete_66_cmd::to_string() const
+{
+  std::ostringstream s;
+  s << "nat-66-static-delete: " << m_hw_item.to_string() << " table:" << m_id
+    << " inside:" << m_inside.to_string()
+    << " outside:" << m_outside.to_string();
+
+  return (s.str());
+}
+
+bool
+dump_66_cmd::operator==(const dump_66_cmd& other) const
+{
+  return (true);
+}
+
+rc_t
+dump_66_cmd::issue(connection& con)
+{
+  m_dump.reset(new msg_t(con.ctx(), std::ref(*this)));
+
+  VAPI_CALL(m_dump->execute());
+
+  wait();
+
+  return rc_t::OK;
+}
+
+std::string
+dump_66_cmd::to_string() const
 {
   return ("nat-static-dump");
 }
index a4adcef..95061ca 100644 (file)
@@ -129,6 +129,111 @@ private:
   HW::item<bool> item;
 };
 
+/**
+ * A command class that creates NAT 66 static mapping
+ */
+class create_66_cmd
+  : public rpc_cmd<HW::item<bool>, rc_t, vapi::Nat66_add_del_static_mapping>
+{
+public:
+  /**
+   * Constructor
+   */
+  create_66_cmd(HW::item<bool>& item,
+                route::table_id_t id,
+                const boost::asio::ip::address_v6& inside,
+                const boost::asio::ip::address_v6& outside);
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * Comparison operator - only used for UT
+   */
+  bool operator==(const create_66_cmd& i) const;
+
+private:
+  route::table_id_t m_id;
+  const boost::asio::ip::address_v6 m_inside;
+  const boost::asio::ip::address_v6 m_outside;
+};
+
+/**
+ * A cmd class that deletes a NAT 66 static mapping
+ */
+class delete_66_cmd
+  : public rpc_cmd<HW::item<bool>, rc_t, vapi::Nat66_add_del_static_mapping>
+{
+public:
+  /**
+   * Constructor
+   */
+  delete_66_cmd(HW::item<bool>& item,
+                route::table_id_t id,
+                const boost::asio::ip::address_v6& inside,
+                const boost::asio::ip::address_v6& outside);
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * Comparison operator - only used for UT
+   */
+  bool operator==(const delete_66_cmd& i) const;
+
+private:
+  route::table_id_t m_id;
+  const boost::asio::ip::address_v6 m_inside;
+  const boost::asio::ip::address_v6 m_outside;
+};
+
+/**
+ * A cmd class that Dumps all the nat_statics
+ */
+class dump_66_cmd : public dump_cmd<vapi::Nat66_static_mapping_dump>
+{
+public:
+  /**
+   * Constructor
+   */
+  dump_66_cmd() = default;
+  ~dump_66_cmd() = default;
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * Comparison operator - only used for UT
+   */
+  bool operator==(const dump_66_cmd& i) const;
+
+private:
+  /**
+   * HW reutrn code
+   */
+  HW::item<bool> item;
+};
+
 }; // namespace nat_static_cmds
 }; // namespace vom
 
index 247afa0..ec56c44 100644 (file)
@@ -508,7 +508,7 @@ ip_route::event_handler::handle_populate(const client_db::key_t& key)
 dependency_t
 ip_route::event_handler::order() const
 {
-  return (dependency_t::ENTRY);
+  return (dependency_t::TABLE);
 }
 
 void
index 44e0dd0..c6093eb 100644 (file)
@@ -28,9 +28,6 @@ rc_t::rc_t(int v, const std::string s)
   : enum_base<rc_t>(v, s)
 {
 }
-rc_t::~rc_t()
-{
-}
 
 const rc_t&
 rc_t::from_vpp_retval(int32_t rv)
index 302e5ee..53654c5 100644 (file)
@@ -94,7 +94,7 @@ struct rc_t : public enum_base<rc_t>
   /**
    * Destructor
    */
-  ~rc_t();
+  ~rc_t() = default;
 
   /**
    * The value un-set
index 29738e2..6958eb1 100644 (file)
@@ -892,7 +892,11 @@ BOOST_AUTO_TEST_CASE(test_bridge) {
     bridge_domain bd1(33);
 
     HW::item<uint32_t> hw_bd(33, rc_t::OK);
-    ADD_EXPECT(bridge_domain_cmds::create_cmd(hw_bd, bridge_domain::learning_mode_t::ON));
+    ADD_EXPECT(bridge_domain_cmds::create_cmd(hw_bd,
+                                              bridge_domain::learning_mode_t::ON,
+                                              bridge_domain::arp_term_mode_t::ON,
+                                              bridge_domain::flood_mode_t::ON,
+                                              bridge_domain::mac_age_mode_t::OFF));
 
     TRY_CHECK_RC(OM::write(franz, bd1));
 
@@ -973,7 +977,11 @@ BOOST_AUTO_TEST_CASE(test_bridge) {
     bridge_domain bd2(99);
 
     HW::item<uint32_t> hw_bd2(99, rc_t::OK);
-    ADD_EXPECT(bridge_domain_cmds::create_cmd(hw_bd2, bridge_domain::learning_mode_t::ON));
+    ADD_EXPECT(bridge_domain_cmds::create_cmd(hw_bd2,
+                                              bridge_domain::learning_mode_t::ON,
+                                              bridge_domain::arp_term_mode_t::ON,
+                                              bridge_domain::flood_mode_t::ON,
+                                              bridge_domain::mac_age_mode_t::OFF));
 
     TRY_CHECK_RC(OM::write(jkr, bd2));
 
@@ -1020,8 +1028,8 @@ BOOST_AUTO_TEST_CASE(test_vxlan) {
 
     // VXLAN create
     vxlan_tunnel::endpoint_t ep(boost::asio::ip::address::from_string("10.10.10.10"),
-                               boost::asio::ip::address::from_string("10.10.10.11"),
-                               322);
+                                boost::asio::ip::address::from_string("10.10.10.11"),
+                                322);
 
     vxlan_tunnel vxt(ep.src, ep.dst, ep.vni);
 
@@ -1031,10 +1039,17 @@ BOOST_AUTO_TEST_CASE(test_vxlan) {
     TRY_CHECK_RC(OM::write(franz, vxt));
 
     // bridge-domain create
-    bridge_domain bd1(33, bridge_domain::learning_mode_t::OFF);
+    bridge_domain bd1(33, bridge_domain::learning_mode_t::OFF,
+                      bridge_domain::arp_term_mode_t::OFF,
+                      bridge_domain::flood_mode_t::OFF,
+                      bridge_domain::mac_age_mode_t::ON);
 
     HW::item<uint32_t> hw_bd(33, rc_t::OK);
-    ADD_EXPECT(bridge_domain_cmds::create_cmd(hw_bd, bridge_domain::learning_mode_t::OFF));
+    ADD_EXPECT(bridge_domain_cmds::create_cmd(hw_bd,
+                                              bridge_domain::learning_mode_t::OFF,
+                                              bridge_domain::arp_term_mode_t::OFF,
+                                              bridge_domain::flood_mode_t::OFF,
+                                              bridge_domain::mac_age_mode_t::ON));
 
     TRY_CHECK_RC(OM::write(franz, bd1));
 
index 427b14d..805f261 100644 (file)
@@ -6,14 +6,19 @@ import struct
 
 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 scapy.packet import Raw
-from scapy.layers.l2 import Ether
+from scapy.layers.l2 import Ether, ARP
 from scapy.layers.inet import IP, UDP
-from scapy.layers.inet6 import IPv6
+from scapy.layers.inet6 import IPv6, ICMPv6ND_NS,  ICMPv6NDOptSrcLLAddr, \
+    ICMPv6NDOptDstLLAddr, ICMPv6ND_NA
+from scapy.utils6 import in6_getnsma, in6_getnsmac
 
 from socket import AF_INET, AF_INET6
-from scapy.utils import inet_pton
+from scapy.utils import inet_pton, inet_ntop
+from util import Host, mactobinary
 
 
 class VppGbpEndpoint(VppObject):
@@ -21,47 +26,221 @@ class VppGbpEndpoint(VppObject):
     GDB Endpoint
     """
 
-    def __init__(self, test, sw_if_index, addr, epg, is_ip6=0):
+    @property
+    def bin_mac(self):
+        return mactobinary(self.itf.remote_mac)
+
+    @property
+    def mac(self):
+        return self.itf.remote_mac
+
+    def __init__(self, test, itf, epg, recirc, ip, fip, is_ip6=False):
         self._test = test
-        self.sw_if_index = sw_if_index
+        self.itf = itf
         self.epg = epg
-        self.addr_p = addr
+        self.recirc = recirc
+        self.ip = ip
+        self.floating_ip = fip
         self.is_ip6 = is_ip6
         if is_ip6:
-            self.addr = inet_pton(AF_INET6, addr)
+            self.proto = DpoProto.DPO_PROTO_IP6
+            self.af = AF_INET6
         else:
-            self.addr = inet_pton(AF_INET, addr)
+            self.proto = DpoProto.DPO_PROTO_IP4
+            self.af = AF_INET
+        self.ip_n = inet_pton(self.af, ip)
+        self.floating_ip_n = inet_pton(self.af, fip)
 
     def add_vpp_config(self):
         self._test.vapi.gbp_endpoint_add_del(
             1,
-            self.sw_if_index,
-            self.addr,
+            self.itf.sw_if_index,
+            self.ip_n,
             self.is_ip6,
-            self.epg)
+            self.epg.epg)
         self._test.registry.register(self, self._test.logger)
 
     def remove_vpp_config(self):
         self._test.vapi.gbp_endpoint_add_del(
             0,
-            self.sw_if_index,
-            self.addr,
+            self.itf.sw_if_index,
+            self.ip_n,
             self.is_ip6,
-            self.epg)
+            self.epg.epg)
 
     def __str__(self):
         return self.object_id()
 
     def object_id(self):
-        return "gbp-endpoint;[%d:%s:%d]" % (self.sw_if_index,
-                                            self.addr_p,
-                                            self.epg)
+        return "gbp-endpoint;[%d:%s:%d]" % (self.itf.sw_if_index,
+                                            self.ip,
+                                            self.epg.epg)
 
     def query_vpp_config(self):
         eps = self._test.vapi.gbp_endpoint_dump()
         for ep in eps:
-            if ep.endpoint.address == self.addr \
-               and ep.endpoint.sw_if_index == self.sw_if_index:
+            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
+
+
+class VppGbpRecirc(VppObject):
+    """
+    GDB Recirculation Interface
+    """
+
+    def __init__(self, test, epg, recirc, is_ext=False):
+        self._test = test
+        self.recirc = recirc
+        self.epg = epg
+        self.is_ext = is_ext
+
+    def add_vpp_config(self):
+        self._test.vapi.gbp_recirc_add_del(
+            1,
+            self.recirc.sw_if_index,
+            self.epg.epg,
+            self.is_ext)
+        self._test.registry.register(self, self._test.logger)
+
+    def remove_vpp_config(self):
+        self._test.vapi.gbp_recirc_add_del(
+            0,
+            self.recirc.sw_if_index,
+            self.epg.epg,
+            self.is_ext)
+
+    def __str__(self):
+        return self.object_id()
+
+    def object_id(self):
+        return "gbp-recirc;[%d]" % (self.recirc.sw_if_index)
+
+    def query_vpp_config(self):
+        rs = self._test.vapi.gbp_recirc_dump()
+        for r in rs:
+            if r.recirc.sw_if_index == self.recirc.sw_if_index:
+                return True
+        return False
+
+
+class VppGbpSubnet(VppObject):
+    """
+    GDB Subnet
+    """
+
+    def __init__(self, test, table_id, address, address_len,
+                 is_internal=True, is_ip6=False,
+                 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.is_internal = is_internal
+        self.sw_if_index = sw_if_index
+        self.epg = epg
+
+    def add_vpp_config(self):
+        self._test.vapi.gbp_subnet_add_del(
+            1,
+            self.table_id,
+            self.is_internal,
+            self.address_n,
+            self.address_len,
+            sw_if_index=self.sw_if_index if self.sw_if_index else 0xffffffff,
+            epg_id=self.epg if self.epg else 0xffffffff,
+            is_ip6=self.is_ip6)
+        self._test.registry.register(self, self._test.logger)
+
+    def remove_vpp_config(self):
+        self._test.vapi.gbp_subnet_add_del(
+            0,
+            self.table_id,
+            self.is_internal,
+            self.address_n,
+            self.address_len,
+            is_ip6=self.is_ip6)
+
+    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)
+
+    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:
+                if self.is_ip6:
+                    if s.subnet.address == self.address_n:
+                        return True
+                else:
+                    if s.subnet.address[:4] == self.address_n:
+                        return True
+        return False
+
+
+class VppGbpEndpointGroup(VppObject):
+    """
+    GDB Endpoint Group
+    """
+
+    def __init__(self, test, epg, rd, bd, uplink,
+                 bvi, bvi_ip4, bvi_ip6=None):
+        self._test = test
+        self.uplink = uplink
+        self.bvi = bvi
+        self.bvi_ip4 = bvi_ip4
+        self.bvi_ip4_n = inet_pton(AF_INET, bvi_ip4)
+        self.bvi_ip6 = bvi_ip6
+        self.bvi_ip6_n = inet_pton(AF_INET6, bvi_ip6)
+        self.epg = epg
+        self.bd = bd
+        self.rd = rd
+
+    def add_vpp_config(self):
+        self._test.vapi.gbp_endpoint_group_add_del(
+            1,
+            self.epg,
+            self.bd,
+            self.rd,
+            self.rd,
+            self.uplink.sw_if_index)
+        self._test.registry.register(self, self._test.logger)
+
+    def remove_vpp_config(self):
+        self._test.vapi.gbp_endpoint_group_add_del(
+            0,
+            self.epg,
+            self.bd,
+            self.rd,
+            self.rd,
+            self.uplink.sw_if_index)
+
+    def __str__(self):
+        return self.object_id()
+
+    def object_id(self):
+        return "gbp-endpoint-group;[%d]" % (self.epg)
+
+    def query_vpp_config(self):
+        epgs = self._test.vapi.gbp_endpoint_group_dump()
+        for epg in epgs:
+            if epg.epg.epg_id == self.epg:
                 return True
         return False
 
@@ -101,10 +280,10 @@ class VppGbpContract(VppObject):
                                             self.acl_index)
 
     def query_vpp_config(self):
-        eps = self._test.vapi.gbp_contract_dump()
-        for ep in eps:
-            if ep.contract.src_epg == self.src_epg \
-               and ep.contract.dst_epg == self.dst_epg:
+        cs = self._test.vapi.gbp_contract_dump()
+        for c in cs:
+            if c.contract.src_epg == self.src_epg \
+               and c.contract.dst_epg == self.dst_epg:
                 return True
         return False
 
@@ -115,233 +294,579 @@ class TestGBP(VppTestCase):
     def setUp(self):
         super(TestGBP, self).setUp()
 
-        # create 6 pg interfaces for pg0 to pg5
-        self.create_pg_interfaces(range(6))
+        self.create_pg_interfaces(range(9))
+        self.create_loopback_interfaces(range(9))
+
+        self.router_mac = "00:11:22:33:44:55"
 
         for i in self.pg_interfaces:
             i.admin_up()
-            i.config_ip4()
-            i.resolve_arp()
-            i.config_ip6()
-            i.resolve_ndp()
+        for i in self.lo_interfaces:
+            i.admin_up()
+            self.vapi.sw_interface_set_mac_address(
+                i.sw_if_index,
+                mactobinary(self.router_mac))
 
     def tearDown(self):
         for i in self.pg_interfaces:
-            i.unconfig_ip4()
-            i.unconfig_ip6()
+            i.admin_down()
 
         super(TestGBP, self).tearDown()
 
-    def test_gbp4(self):
-        """ Group Based Policy v4 """
-
-        ep1 = VppGbpEndpoint(self,
-                             self.pg0.sw_if_index,
-                             self.pg0.remote_ip4,
-                             220)
-        ep1.add_vpp_config()
-        ep2 = VppGbpEndpoint(self,
-                             self.pg1.sw_if_index,
-                             self.pg1.remote_ip4,
-                             220)
-        ep2.add_vpp_config()
-
-        ep3 = VppGbpEndpoint(self,
-                             self.pg2.sw_if_index,
-                             self.pg2.remote_ip4,
-                             221)
-        ep3.add_vpp_config()
-        ep4 = VppGbpEndpoint(self,
-                             self.pg3.sw_if_index,
-                             self.pg3.remote_ip4,
-                             222)
-        ep4.add_vpp_config()
+    def send_and_expect_bridged(self, src, tx, dst):
+        rx = self.send_and_expect(src, tx, dst)
 
-        self.logger.info(self.vapi.cli("sh gbp endpoint"))
+        for r in rx:
+            self.assertEqual(r[Ether].src, tx[0][Ether].src)
+            self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
+            self.assertEqual(r[IP].src, tx[0][IP].src)
+            self.assertEqual(r[IP].dst, tx[0][IP].dst)
+        return rx
+
+    def send_and_expect_bridged6(self, src, tx, dst):
+        rx = self.send_and_expect(src, tx, dst)
+
+        for r in rx:
+            self.assertEqual(r[Ether].src, tx[0][Ether].src)
+            self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
+            self.assertEqual(r[IPv6].src, tx[0][IPv6].src)
+            self.assertEqual(r[IPv6].dst, tx[0][IPv6].dst)
+        return rx
+
+    def send_and_expect_routed(self, src, tx, dst, src_mac):
+        rx = self.send_and_expect(src, tx, dst)
+
+        for r in rx:
+            self.assertEqual(r[Ether].src, src_mac)
+            self.assertEqual(r[Ether].dst, dst.remote_mac)
+            self.assertEqual(r[IP].src, tx[0][IP].src)
+            self.assertEqual(r[IP].dst, tx[0][IP].dst)
+        return rx
+
+    def send_and_expect_natted(self, src, tx, dst, src_ip):
+        rx = self.send_and_expect(src, tx, dst)
+
+        for r in rx:
+            self.assertEqual(r[Ether].src, tx[0][Ether].src)
+            self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
+            self.assertEqual(r[IP].src, src_ip)
+            self.assertEqual(r[IP].dst, tx[0][IP].dst)
+        return rx
+
+    def send_and_expect_unnatted(self, src, tx, dst, dst_ip):
+        rx = self.send_and_expect(src, tx, dst)
+
+        for r in rx:
+            self.assertEqual(r[Ether].src, tx[0][Ether].src)
+            self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
+            self.assertEqual(r[IP].dst, dst_ip)
+            self.assertEqual(r[IP].src, tx[0][IP].src)
+        return rx
+
+    def send_and_expect_double_natted(self, src, tx, dst, src_ip, dst_ip):
+        rx = self.send_and_expect(src, tx, dst)
+
+        for r in rx:
+            self.assertEqual(r[Ether].src, self.router_mac)
+            self.assertEqual(r[Ether].dst, dst.remote_mac)
+            self.assertEqual(r[IP].dst, dst_ip)
+            self.assertEqual(r[IP].src, src_ip)
+        return rx
+
+    def test_gbp(self):
+        """ Group Based Policy """
+
+        nat_table = VppIpTable(self, 20)
+        nat_table.add_vpp_config()
 
         #
-        # in the abscense of policy, endpoints in the same EPG
-        # can communicate
+        # Bridge Domains
         #
-        pkt_intra_epg = (Ether(src=self.pg0.remote_mac,
-                               dst=self.pg0.local_mac) /
-                         IP(src=self.pg0.remote_ip4,
-                            dst=self.pg1.remote_ip4) /
-                         UDP(sport=1234, dport=1234) /
-                         Raw('\xa5' * 100))
+        self.vapi.bridge_domain_add_del(1, flood=1, uu_flood=1, forward=1,
+                                        learn=0, arp_term=1, is_add=1)
+        self.vapi.bridge_domain_add_del(2, flood=1, uu_flood=1, forward=1,
+                                        learn=0, arp_term=1, is_add=1)
+        self.vapi.bridge_domain_add_del(20, flood=1, uu_flood=1, forward=1,
+                                        learn=0, arp_term=1, is_add=1)
 
-        self.send_and_expect(self.pg0, pkt_intra_epg * 65, self.pg1)
+        #
+        # 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))
 
         #
-        # in the abscense of policy, endpoints in the different EPG
-        # cannot communicate
+        # 2 NAT EPGs, one for floating-IP subnets, the other for internet
         #
-        pkt_inter_epg_220_to_221 = (Ether(src=self.pg0.remote_mac,
-                                          dst=self.pg0.local_mac) /
-                                    IP(src=self.pg0.remote_ip4,
-                                       dst=self.pg2.remote_ip4) /
-                                    UDP(sport=1234, dport=1234) /
-                                    Raw('\xa5' * 100))
-        pkt_inter_epg_220_to_222 = (Ether(src=self.pg0.remote_mac,
-                                          dst=self.pg0.local_mac) /
-                                    IP(src=self.pg0.remote_ip4,
-                                       dst=self.pg3.remote_ip4) /
-                                    UDP(sport=1234, dport=1234) /
-                                    Raw('\xa5' * 100))
-        pkt_inter_epg_221_to_220 = (Ether(src=self.pg2.remote_mac,
-                                          dst=self.pg2.local_mac) /
-                                    IP(src=self.pg2.remote_ip4,
-                                       dst=self.pg0.remote_ip4) /
-                                    UDP(sport=1234, dport=1234) /
-                                    Raw('\xa5' * 100))
+        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))
 
-        self.send_and_assert_no_replies(self.pg0,
-                                        pkt_inter_epg_220_to_221 * 65)
-        self.send_and_assert_no_replies(self.pg0,
-                                        pkt_inter_epg_221_to_220 * 65)
+        epg_nat = epgs[3]
+        recirc_nat = recircs[3]
 
         #
-        # A uni-directional contract from EPG 220 -> 221
+        # 4 end-points, 2 in the same subnet, 3 in the same BD
         #
-        c1 = VppGbpContract(self, 220, 221, 0xffffffff)
-        c1.add_vpp_config()
+        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))
 
-        self.send_and_expect(self.pg0,
-                             pkt_inter_epg_220_to_221 * 65,
-                             self.pg2)
-        self.send_and_assert_no_replies(self.pg2,
-                                        pkt_inter_epg_221_to_220 * 65)
+        #
+        # Config related to each of the EPGs
+        #
+        for epg in epgs:
+            # IP config on the BVI interfaces
+            if epg != epgs[1] and epg != epgs[4]:
+                epg.bvi.set_table_ip4(epg.rd)
+                epg.bvi.set_table_ip6(epg.rd)
+
+                # The BVIs are NAT inside interfaces
+                self.vapi.nat44_interface_add_del_feature(epg.bvi.sw_if_index,
+                                                          is_inside=1,
+                                                          is_add=1)
+                # self.vapi.nat66_add_del_interface(epg.bvi.sw_if_index,
+                #                                  is_inside=1,
+                #                                  is_add=1)
+
+            self.vapi.sw_interface_add_del_address(epg.bvi.sw_if_index,
+                                                   epg.bvi_ip4_n,
+                                                   32)
+            self.vapi.sw_interface_add_del_address(epg.bvi.sw_if_index,
+                                                   epg.bvi_ip6_n,
+                                                   128,
+                                                   is_ipv6=1)
+
+            # EPG uplink interfaces in the BD
+            epg.uplink.set_table_ip4(epg.rd)
+            self.vapi.sw_interface_set_l2_bridge(epg.uplink.sw_if_index,
+                                                 epg.bd)
+
+            # add the BD ARP termination entry for BVI IP
+            self.vapi.bd_ip_mac_add_del(bd_id=epg.bd,
+                                        mac=mactobinary(self.router_mac),
+                                        ip=epg.bvi_ip4_n,
+                                        is_ipv6=0,
+                                        is_add=1)
+            self.vapi.bd_ip_mac_add_del(bd_id=epg.bd,
+                                        mac=mactobinary(self.router_mac),
+                                        ip=epg.bvi_ip6_n,
+                                        is_ipv6=1,
+                                        is_add=1)
+
+            # epg[1] shares the same BVI to epg[0]
+            if epg != epgs[1] and epg != epgs[4]:
+                # BVI in BD
+                self.vapi.sw_interface_set_l2_bridge(epg.bvi.sw_if_index,
+                                                     epg.bd,
+                                                     bvi=1)
+                # BVI L2 FIB entry
+                self.vapi.l2fib_add_del(self.router_mac,
+                                        epg.bd,
+                                        epg.bvi.sw_if_index,
+                                        is_add=1, bvi_mac=1)
+
+            # EPG in VPP
+            epg.add_vpp_config()
+
+        for recirc in recircs:
+            # EPG's ingress recirculation interface maps to its RD
+            recirc.recirc.set_table_ip4(recirc.epg.rd)
+
+            # in the bridge to allow DVR. L2 emulation to punt to L3
+            self.vapi.sw_interface_set_l2_bridge(recirc.recirc.sw_if_index,
+                                                 recirc.epg.bd)
+            self.vapi.sw_interface_set_l2_emulation(
+                recirc.recirc.sw_if_index)
+
+            if recirc.is_ext:
+                # recirc interfaces on NAT EPGs are outside and an
+                # output feature
+                self.vapi.nat44_interface_add_del_output_feature(
+                    recirc.recirc.sw_if_index,
+                    is_inside=0,
+                    is_add=1)
+            else:
+                self.vapi.nat44_interface_add_del_feature(
+                    recirc.recirc.sw_if_index,
+                    is_inside=0,
+                    is_add=1)
+                # self.vapi.nat66_add_del_interface(
+                #    recirc.recirc.sw_if_index,
+                #    is_inside=0,
+                #    is_add=1)
+
+            recirc.add_vpp_config()
+
+        ep_routes = []
+        ep_arps = []
+        for ep in eps:
+            self.pg_enable_capture(self.pg_interfaces)
+            self.pg_start()
+            #
+            # routes to the endpoints. We need these since there are no
+            # adj-fibs due to the fact the the BVI address has /32 and
+            # the subnet is not attached.
+            #
+            r = VppIpRoute(self, ep.ip, 32,
+                           [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)
+
+            # 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=20)
+
+            # VPP EP create ...
+            ep.add_vpp_config()
+
+            # ... results in a Gratuitous ARP/ND on the EPG's uplink
+            rx = ep.epg.uplink.get_capture(1, 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)
+
+            # 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=0,
+                                        is_add=1)
+
+            # floating IPs route via EPG recirc
+            r = VppIpRoute(self, ep.floating_ip, 32,
+                           [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)
+
+            # L2 FIB entries in the NAT EPG BD to bridge the packets from
+            # the outside direct to the internal EPG
+            self.vapi.l2fib_add_del(ep.mac,
+                                    epg_nat.bd,
+                                    ep.recirc.recirc.sw_if_index,
+                                    is_add=1)
 
         #
-        # contract for the return direction
+        # ARP packets for unknown IP are flooded
         #
-        c2 = VppGbpContract(self, 221, 220, 0xffffffff)
-        c2.add_vpp_config()
+        pkt_arp = (Ether(dst="ff:ff:ff:ff:ff:ff",
+                         src=self.pg0.remote_mac) /
+                   ARP(op="who-has",
+                       hwdst="ff:ff:ff:ff:ff:ff",
+                       hwsrc=self.pg0.remote_mac,
+                       pdst=epgs[0].bvi_ip4,
+                       psrc="10.0.0.88"))
 
-        self.send_and_expect(self.pg0,
-                             pkt_inter_epg_220_to_221 * 65,
-                             self.pg2)
-        self.send_and_expect(self.pg2,
-                             pkt_inter_epg_221_to_220 * 65,
-                             self.pg0)
+        self.send_and_expect(self.pg0, [pkt_arp], self.pg0)
 
         #
-        # check that inter group is still disabled for the groups
-        # not in the contract.
+        # ARP/ND packets get a response
         #
-        self.send_and_assert_no_replies(self.pg0,
-                                        pkt_inter_epg_220_to_222 * 65)
+        pkt_arp = (Ether(dst="ff:ff:ff:ff:ff:ff",
+                         src=self.pg0.remote_mac) /
+                   ARP(op="who-has",
+                       hwdst="ff:ff:ff:ff:ff:ff",
+                       hwsrc=self.pg0.remote_mac,
+                       pdst=epgs[0].bvi_ip4,
+                       psrc=eps[0].ip))
 
-        self.logger.info(self.vapi.cli("sh gbp contract"))
+        self.send_and_expect(self.pg0, [pkt_arp], self.pg0)
+
+        nsma = in6_getnsma(inet_pton(AF_INET6, eps[4].ip))
+        d = inet_ntop(AF_INET6, nsma)
+        pkt_nd = (Ether(dst=in6_getnsmac(nsma)) /
+                  IPv6(dst=d, src=eps[4].ip) /
+                  ICMPv6ND_NS(tgt=epgs[0].bvi_ip6) /
+                  ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac))
+        self.send_and_expect(self.pg0, [pkt_nd], self.pg0)
 
         #
-        # remove both contracts, traffic stops in both directions
+        # broadcast packets are flooded
         #
-        c2.remove_vpp_config()
-        c1.remove_vpp_config()
+        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") /
+                     UDP(sport=1234, dport=1234) /
+                     Raw('\xa5' * 100))
 
-        self.send_and_assert_no_replies(self.pg2,
-                                        pkt_inter_epg_221_to_220 * 65)
-        self.send_and_assert_no_replies(self.pg0,
-                                        pkt_inter_epg_220_to_221 * 65)
-        self.send_and_expect(self.pg0, pkt_intra_epg * 65, self.pg1)
-
-    def test_gbp6(self):
-        """ Group Based Policy v6 """
-
-        ep1 = VppGbpEndpoint(self,
-                             self.pg0.sw_if_index,
-                             self.pg0.remote_ip6,
-                             220,
-                             is_ip6=1)
-        ep1.add_vpp_config()
-        ep2 = VppGbpEndpoint(self,
-                             self.pg1.sw_if_index,
-                             self.pg1.remote_ip6,
-                             220,
-                             is_ip6=1)
-        ep2.add_vpp_config()
-
-        ep3 = VppGbpEndpoint(self,
-                             self.pg2.sw_if_index,
-                             self.pg2.remote_ip6,
-                             221,
-                             is_ip6=1)
-        ep3.add_vpp_config()
-        ep4 = VppGbpEndpoint(self,
-                             self.pg3.sw_if_index,
-                             self.pg3.remote_ip6,
-                             222,
-                             is_ip6=1)
-        ep4.add_vpp_config()
+        self.vapi.cli("clear trace")
+        self.pg0.add_stream(pkt_bcast)
 
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+
+        rxd = eps[1].itf.get_capture(1)
+        self.assertEqual(rxd[0][Ether].dst, pkt_bcast[Ether].dst)
+        rxd = epgs[0].uplink.get_capture(1)
+        self.assertEqual(rxd[0][Ether].dst, pkt_bcast[Ether].dst)
+
+        #
+        # packets to non-local L3 destinations dropped
+        #
+        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") /
+                                 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") /
+                                 UDP(sport=1234, dport=1234) /
+                                 Raw('\xa5' * 100))
+
+        self.send_and_assert_no_replies(self.pg0, pkt_intra_epg_220_ip4 * 65)
+
+        pkt_inter_epg_222_ip6 = (Ether(src=self.pg0.remote_mac,
+                                       dst=self.router_mac) /
+                                 IPv6(src=eps[4].ip, 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)
+
+        #
+        # Add the subnet routes
+        #
+        s41 = VppGbpSubnet(self, 0, "10.0.0.0", 24)
+        s42 = VppGbpSubnet(self, 0, "10.0.1.0", 24)
+        s43 = VppGbpSubnet(self, 0, "10.0.2.0", 24)
+        s41.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.add_vpp_config()
+        s62.add_vpp_config()
+        s63.add_vpp_config()
+
+        self.send_and_expect_bridged(self.pg0,
+                                     pkt_intra_epg_220_ip4 * 65,
+                                     self.pg4)
+        self.send_and_expect_bridged(self.pg3,
+                                     pkt_inter_epg_222_ip4 * 65,
+                                     self.pg6)
+        self.send_and_expect_bridged6(self.pg3,
+                                      pkt_inter_epg_222_ip6 * 65,
+                                      self.pg6)
+
+        self.logger.info(self.vapi.cli("sh ip fib 11.0.0.2"))
+        self.logger.info(self.vapi.cli("sh gbp endpoint-group"))
         self.logger.info(self.vapi.cli("sh gbp endpoint"))
+        self.logger.info(self.vapi.cli("sh gbp recirc"))
+        self.logger.info(self.vapi.cli("sh int"))
+        self.logger.info(self.vapi.cli("sh int addr"))
+        self.logger.info(self.vapi.cli("sh int feat loop6"))
+        self.logger.info(self.vapi.cli("sh vlib graph ip4-gbp-src-classify"))
+        self.logger.info(self.vapi.cli("sh int feat loop3"))
+
+        #
+        # Packet destined to unknown unicast is sent on the epg uplink ...
+        #
+        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") /
+                                       UDP(sport=1234, dport=1234) /
+                                       Raw('\xa5' * 100))
+
+        self.send_and_expect_bridged(self.pg0,
+                                     pkt_intra_epg_220_to_uplink * 65,
+                                     self.pg4)
+        # ... and nowhere else
+        self.pg1.get_capture(0, timeout=0.1)
+        self.pg1.assert_nothing_captured(remark="Flood onto other VMS")
+
+        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") /
+                                       UDP(sport=1234, dport=1234) /
+                                       Raw('\xa5' * 100))
 
+        self.send_and_expect_bridged(self.pg2,
+                                     pkt_intra_epg_221_to_uplink * 65,
+                                     self.pg5)
+
+        #
+        # Packets from the uplink are forwarded in the absence of a contract
         #
-        # in the abscense of policy, endpoints in the same EPG
+        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") /
+                                         UDP(sport=1234, dport=1234) /
+                                         Raw('\xa5' * 100))
+
+        self.send_and_expect_bridged(self.pg4,
+                                     pkt_intra_epg_220_from_uplink * 65,
+                                     self.pg0)
+
+        #
+        # in the absence of policy, endpoints in the same EPG
         # can communicate
         #
         pkt_intra_epg = (Ether(src=self.pg0.remote_mac,
-                               dst=self.pg0.local_mac) /
-                         IPv6(src=self.pg0.remote_ip6,
-                              dst=self.pg1.remote_ip6) /
+                               dst=self.pg1.remote_mac) /
+                         IP(src=eps[0].ip, dst=eps[1].ip) /
                          UDP(sport=1234, dport=1234) /
                          Raw('\xa5' * 100))
 
-        self.send_and_expect(self.pg0, pkt_intra_epg * 65, self.pg1)
+        self.send_and_expect_bridged(self.pg0, pkt_intra_epg * 65, self.pg1)
 
         #
         # in the abscense of policy, endpoints in the different EPG
         # cannot communicate
         #
         pkt_inter_epg_220_to_221 = (Ether(src=self.pg0.remote_mac,
-                                          dst=self.pg0.local_mac) /
-                                    IPv6(src=self.pg0.remote_ip6,
-                                         dst=self.pg2.remote_ip6) /
+                                          dst=self.pg2.remote_mac) /
+                                    IP(src=eps[0].ip, dst=eps[2].ip) /
                                     UDP(sport=1234, dport=1234) /
                                     Raw('\xa5' * 100))
-        pkt_inter_epg_220_to_222 = (Ether(src=self.pg0.remote_mac,
-                                          dst=self.pg0.local_mac) /
-                                    IPv6(src=self.pg0.remote_ip6,
-                                         dst=self.pg3.remote_ip6) /
+        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) /
                                     UDP(sport=1234, dport=1234) /
                                     Raw('\xa5' * 100))
-        pkt_inter_epg_221_to_220 = (Ether(src=self.pg2.remote_mac,
-                                          dst=self.pg2.local_mac) /
-                                    IPv6(src=self.pg2.remote_ip6,
-                                         dst=self.pg0.remote_ip6) /
+        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) /
                                     UDP(sport=1234, dport=1234) /
                                     Raw('\xa5' * 100))
 
         self.send_and_assert_no_replies(self.pg0,
                                         pkt_inter_epg_220_to_221 * 65)
         self.send_and_assert_no_replies(self.pg0,
-                                        pkt_inter_epg_221_to_220 * 65)
+                                        pkt_inter_epg_220_to_222 * 65)
 
         #
         # A uni-directional contract from EPG 220 -> 221
         #
-        c1 = VppGbpContract(self, 220, 221, 0xffffffff)
+        c1 = VppGbpContract(self, 220, 221, 0)
         c1.add_vpp_config()
 
-        self.send_and_expect(self.pg0,
-                             pkt_inter_epg_220_to_221 * 65,
-                             self.pg2)
-        self.send_and_assert_no_replies(self.pg2,
-                                        pkt_inter_epg_221_to_220 * 65)
+        self.send_and_expect_bridged(self.pg0,
+                                     pkt_inter_epg_220_to_221 * 65,
+                                     self.pg2)
+        self.send_and_assert_no_replies(self.pg0,
+                                        pkt_inter_epg_220_to_222 * 65)
 
         #
         # contract for the return direction
         #
-        c2 = VppGbpContract(self, 221, 220, 0xffffffff)
+        c2 = VppGbpContract(self, 221, 220, 0)
         c2.add_vpp_config()
 
-        self.send_and_expect(self.pg0,
-                             pkt_inter_epg_220_to_221 * 65,
-                             self.pg2)
-        self.send_and_expect(self.pg2,
-                             pkt_inter_epg_221_to_220 * 65,
-                             self.pg0)
+        self.send_and_expect_bridged(self.pg0,
+                                     pkt_inter_epg_220_to_221 * 65,
+                                     self.pg2)
+        self.send_and_expect_bridged(self.pg2,
+                                     pkt_inter_epg_221_to_220 * 65,
+                                     self.pg0)
 
         #
         # check that inter group is still disabled for the groups
@@ -350,19 +875,165 @@ class TestGBP(VppTestCase):
         self.send_and_assert_no_replies(self.pg0,
                                         pkt_inter_epg_220_to_222 * 65)
 
+        #
+        # A uni-directional contract from EPG 220 -> 222 'L3 routed'
+        #
+        c3 = VppGbpContract(self, 220, 222, 0)
+        c3.add_vpp_config()
+
         self.logger.info(self.vapi.cli("sh gbp contract"))
 
+        self.send_and_expect_routed(self.pg0,
+                                    pkt_inter_epg_220_to_222 * 65,
+                                    self.pg3,
+                                    self.router_mac)
+
         #
         # remove both contracts, traffic stops in both directions
         #
         c2.remove_vpp_config()
         c1.remove_vpp_config()
+        c3.remove_vpp_config()
 
         self.send_and_assert_no_replies(self.pg2,
                                         pkt_inter_epg_221_to_220 * 65)
         self.send_and_assert_no_replies(self.pg0,
                                         pkt_inter_epg_220_to_221 * 65)
-        self.send_and_expect(self.pg0, pkt_intra_epg * 65, self.pg1)
+        self.send_and_expect_bridged(self.pg0, pkt_intra_epg * 65, self.pg1)
+
+        #
+        # EPs to the outside world
+        #
+
+        # in the EP's RD an external subnet via the NAT EPG's recirc
+        se1 = VppGbpSubnet(self, 0, "0.0.0.0", 0,
+                           is_internal=False,
+                           sw_if_index=recirc_nat.recirc.sw_if_index,
+                           epg=epg_nat.epg)
+        se1.add_vpp_config()
+        se2 = VppGbpSubnet(self, 0, "11.0.0.0", 8,
+                           is_internal=False,
+                           sw_if_index=recirc_nat.recirc.sw_if_index,
+                           epg=epg_nat.epg)
+        se2.add_vpp_config()
+        # in the NAT RD an external subnet via the NAT EPG's uplink
+        se3 = VppGbpSubnet(self, 20, "0.0.0.0", 0,
+                           is_internal=False,
+                           sw_if_index=epg_nat.uplink.sw_if_index,
+                           epg=epg_nat.epg)
+        se4 = VppGbpSubnet(self, 20, "11.0.0.0", 8,
+                           is_internal=False,
+                           sw_if_index=epg_nat.uplink.sw_if_index,
+                           epg=epg_nat.epg)
+        se3.add_vpp_config()
+        se4.add_vpp_config()
+
+        self.logger.info(self.vapi.cli("sh ip fib 0.0.0.0/0"))
+        self.logger.info(self.vapi.cli("sh ip fib 11.0.0.1"))
+
+        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") /
+                                       UDP(sport=1234, dport=1234) /
+                                       Raw('\xa5' * 100))
+
+        # no policy yet
+        self.send_and_assert_no_replies(self.pg0,
+                                        pkt_inter_epg_220_to_global * 65)
+
+        c4 = VppGbpContract(self, 220, 333, 0)
+        c4.add_vpp_config()
+
+        self.send_and_expect_natted(self.pg0,
+                                    pkt_inter_epg_220_to_global * 65,
+                                    self.pg7,
+                                    "11.0.0.1")
+
+        pkt_inter_epg_220_from_global = (Ether(src=self.router_mac,
+                                               dst=self.pg0.remote_mac) /
+                                         IP(dst=eps[0].floating_ip,
+                                            src="1.1.1.1") /
+                                         UDP(sport=1234, dport=1234) /
+                                         Raw('\xa5' * 100))
+
+        self.send_and_assert_no_replies(self.pg7,
+                                        pkt_inter_epg_220_from_global * 65)
+
+        c5 = VppGbpContract(self, 333, 220, 0)
+        c5.add_vpp_config()
+
+        self.send_and_expect_unnatted(self.pg7,
+                                      pkt_inter_epg_220_from_global * 65,
+                                      self.pg0,
+                                      "10.0.0.1")
+
+        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) /
+                                    UDP(sport=1234, dport=1234) /
+                                    Raw('\xa5' * 100))
+
+        self.send_and_expect_double_natted(self.pg0,
+                                           pkt_intra_epg_220_global * 65,
+                                           self.pg1,
+                                           "11.0.0.1",
+                                           "10.0.0.2")
+
+        #
+        # 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)
+
+        for epg in epgs:
+            # IP config on the BVI interfaces
+            self.vapi.sw_interface_add_del_address(epg.bvi.sw_if_index,
+                                                   epg.bvi_ip4_n,
+                                                   32,
+                                                   is_add=0)
+            self.logger.info(self.vapi.cli("sh int addr"))
+
+            epg.uplink.set_table_ip4(0)
+
+            if epg != epgs[0] and epg != epgs[3]:
+                epg.bvi.set_table_ip4(0)
+
+                self.vapi.nat44_interface_add_del_feature(epg.bvi.sw_if_index,
+                                                          is_inside=1,
+                                                          is_add=0)
+                # self.vapi.nat66_add_del_interface(epg.bvi.sw_if_index,
+                #                                  is_inside=1,
+                #                                  is_add=0)
+
+        for recirc in recircs:
+            recirc.recirc.set_table_ip4(0)
+
+            if recirc.is_ext:
+                self.vapi.nat44_interface_add_del_output_feature(
+                    recirc.recirc.sw_if_index,
+                    is_inside=0,
+                    is_add=0)
+            else:
+                self.vapi.nat44_interface_add_del_feature(
+                    recirc.recirc.sw_if_index,
+                    is_inside=0,
+                    is_add=0)
+                # self.vapi.nat66_add_del_interface(
+                #    recirc.recirc.sw_if_index,
+                #    is_inside=0,
+                #    is_add=0)
 
 
 if __name__ == '__main__':
index 24483fe..ad887e8 100644 (file)
@@ -668,6 +668,16 @@ class VppPapiProvider(object):
                         {'sw_if_index': sw_if_index,
                          'mtu': mtu})
 
+    def sw_interface_set_promiscuous(self, sw_if_index, enable):
+        """
+        :param sw_if_index:
+        :param enable:
+
+        """
+        return self.api(self.papi.sw_interface_set_promiscuous,
+                        {'sw_if_index': sw_if_index,
+                         'enable': enable})
+
     def sw_interface_set_mac_address(self, sw_if_index, mac):
         return self.api(self.papi.sw_interface_set_mac_address,
                         {'sw_if_index': sw_if_index,
@@ -3238,6 +3248,59 @@ class VppPapiProvider(object):
         """ GBP endpoint Dump """
         return self.api(self.papi.gbp_endpoint_dump, {})
 
+    def gbp_endpoint_group_add_del(self, is_add, epg, bd,
+                                   ip4_rd,
+                                   ip6_rd,
+                                   uplink_sw_if_index):
+        """ GBP endpoint group Add/Del """
+        return self.api(self.papi.gbp_endpoint_group_add_del,
+                        {'is_add': is_add,
+                         'epg': {
+                             'uplink_sw_if_index': uplink_sw_if_index,
+                             'bd_id': bd,
+                             'ip4_table_id': ip4_rd,
+                             'ip6_table_id': ip6_rd,
+                             'epg_id': epg}})
+
+    def gbp_endpoint_group_dump(self):
+        """ GBP endpoint group Dump """
+        return self.api(self.papi.gbp_endpoint_group_dump, {})
+
+    def gbp_recirc_add_del(self, is_add, sw_if_index, epg, is_ext):
+        """ GBP recirc Add/Del """
+        return self.api(self.papi.gbp_recirc_add_del,
+                        {'is_add': is_add,
+                         'recirc': {
+                             'is_ext': is_ext,
+                             'sw_if_index': sw_if_index,
+                             'epg_id': epg}})
+
+    def gbp_recirc_dump(self):
+        """ GBP recirc Dump """
+        return self.api(self.papi.gbp_recirc_dump, {})
+
+    def gbp_subnet_add_del(self, is_add, table_id,
+                           is_internal,
+                           addr, addr_len,
+                           sw_if_index=0xffffffff,
+                           epg_id=0xffffffff,
+                           is_ip6=False):
+        """ GBP Subnet Add/Del """
+        return self.api(self.papi.gbp_subnet_add_del,
+                        {'is_add': is_add,
+                         'subnet': {
+                             'is_internal': is_internal,
+                             'is_ip6': is_ip6,
+                             'sw_if_index': sw_if_index,
+                             'epg_id': epg_id,
+                             'address': addr,
+                             'address_length': addr_len,
+                             'table_id': table_id}})
+
+    def gbp_subnet_dump(self):
+        """ GBP Subnet Dump """
+        return self.api(self.papi.gbp_subnet_dump, {})
+
     def gbp_contract_add_del(self, is_add, src_epg, dst_epg, acl_index):
         """ GBP contract Add/Del """
         return self.api(self.papi.gbp_contract_add_del,