summaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
authorNeale Ranns <neale.ranns@cisco.com>2018-04-04 09:34:50 -0700
committerDamjan Marion <dmarion.lists@gmail.com>2018-04-13 08:51:21 +0000
commit25b049484fcf9161edb2c19250066b893c38c264 (patch)
tree69204352a0648cfc4089fa0e37214bffbae61e81 /src/plugins
parent4f8863b21405d1ab3e067e978a60be72a343358b (diff)
GBP V2
update the GBP plugin to implement the full NAT feature set of opflex agent Change-Id: Ic06a039c889445ed0b9087fa1f292634192b0f8d Signed-off-by: Neale Ranns <neale.ranns@cisco.com>
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/gbp.am13
-rw-r--r--src/plugins/gbp/gbp.api90
-rw-r--r--src/plugins/gbp/gbp.c782
-rw-r--r--src/plugins/gbp/gbp.h104
-rw-r--r--src/plugins/gbp/gbp_all_api_h.h2
-rw-r--r--src/plugins/gbp/gbp_api.c230
-rw-r--r--src/plugins/gbp/gbp_classify.c276
-rw-r--r--src/plugins/gbp/gbp_contract.c163
-rw-r--r--src/plugins/gbp/gbp_contract.h102
-rw-r--r--src/plugins/gbp/gbp_endpoint.c393
-rw-r--r--src/plugins/gbp/gbp_endpoint.h111
-rw-r--r--src/plugins/gbp/gbp_endpoint_group.c263
-rw-r--r--src/plugins/gbp/gbp_endpoint_group.h132
-rw-r--r--src/plugins/gbp/gbp_fwd.c195
-rw-r--r--src/plugins/gbp/gbp_fwd_dpo.c308
-rw-r--r--src/plugins/gbp/gbp_fwd_dpo.h62
-rw-r--r--src/plugins/gbp/gbp_msg_enum.h2
-rw-r--r--src/plugins/gbp/gbp_policy.c231
-rw-r--r--src/plugins/gbp/gbp_policy_dpo.c537
-rw-r--r--src/plugins/gbp/gbp_policy_dpo.h70
-rw-r--r--src/plugins/gbp/gbp_recirc.c217
-rw-r--r--src/plugins/gbp/gbp_recirc.h74
-rw-r--r--src/plugins/gbp/gbp_subnet.c176
-rw-r--r--src/plugins/gbp/gbp_subnet.h41
-rw-r--r--src/plugins/gbp/gbp_types.h32
25 files changed, 3720 insertions, 886 deletions
diff --git a/src/plugins/gbp.am b/src/plugins/gbp.am
index 644d08ccb49..7271a0de45c 100644
--- a/src/plugins/gbp.am
+++ b/src/plugins/gbp.am
@@ -13,8 +13,17 @@
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
diff --git a/src/plugins/gbp/gbp.api b/src/plugins/gbp/gbp.api
index 491ad363c75..b2b32e90d4b 100644
--- a/src/plugins/gbp/gbp.api
+++ b/src/plugins/gbp/gbp.api
@@ -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
index 0b87d0b4d89..00000000000
--- a/src/plugins/gbp/gbp.c
+++ /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:
- */
diff --git a/src/plugins/gbp/gbp.h b/src/plugins/gbp/gbp.h
index 334a7438f53..8672fd3b092 100644
--- a/src/plugins/gbp/gbp.h
+++ b/src/plugins/gbp/gbp.h
@@ -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:
@@ -28,101 +28,15 @@
*
*/
-#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
diff --git a/src/plugins/gbp/gbp_all_api_h.h b/src/plugins/gbp/gbp_all_api_h.h
index a65d9fcd164..3093309e80f 100644
--- a/src/plugins/gbp/gbp_all_api_h.h
+++ b/src/plugins/gbp/gbp_all_api_h.h
@@ -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_api.c b/src/plugins/gbp/gbp_api.c
index 998d5d9bdbb..88b2cd1cea9 100644
--- a/src/plugins/gbp/gbp_api.c
+++ b/src/plugins/gbp/gbp_api.c
@@ -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:
@@ -56,6 +54,12 @@
#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
{
@@ -163,6 +168,223 @@ vl_api_gbp_endpoint_dump_t_handler (vl_api_gbp_endpoint_dump_t * mp)
}
static void
+ vl_api_gbp_endpoint_group_add_del_t_handler
+ (vl_api_gbp_endpoint_group_add_del_t * mp)
+{
+ vl_api_gbp_endpoint_group_add_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)
{
vl_api_gbp_contract_add_del_reply_t *rmp;
diff --git a/src/plugins/gbp/gbp_classify.c b/src/plugins/gbp/gbp_classify.c
new file mode 100644
index 00000000000..859d4f95173
--- /dev/null
+++ b/src/plugins/gbp/gbp_classify.c
@@ -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
index 00000000000..71d8bcf4671
--- /dev/null
+++ b/src/plugins/gbp/gbp_contract.c
@@ -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
index 00000000000..1964098adc3
--- /dev/null
+++ b/src/plugins/gbp/gbp_contract.h
@@ -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
index 00000000000..91505daee2a
--- /dev/null
+++ b/src/plugins/gbp/gbp_endpoint.c
@@ -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
index 00000000000..000c211cfa1
--- /dev/null
+++ b/src/plugins/gbp/gbp_endpoint.h
@@ -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
index 00000000000..ed312d305a1
--- /dev/null
+++ b/src/plugins/gbp/gbp_endpoint_group.c
@@ -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
index 00000000000..f71e5f5d70b
--- /dev/null
+++ b/src/plugins/gbp/gbp_endpoint_group.h
@@ -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
index 00000000000..fec5703d014
--- /dev/null
+++ b/src/plugins/gbp/gbp_fwd.c
@@ -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
index 00000000000..e3dba5fa527
--- /dev/null
+++ b/src/plugins/gbp/gbp_fwd_dpo.c
@@ -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
index 00000000000..6092d6241b5
--- /dev/null
+++ b/src/plugins/gbp/gbp_fwd_dpo.h
@@ -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
diff --git a/src/plugins/gbp/gbp_msg_enum.h b/src/plugins/gbp/gbp_msg_enum.h
index 6f134a84027..c6f34f92005 100644
--- a/src/plugins/gbp/gbp_msg_enum.h
+++ b/src/plugins/gbp/gbp_msg_enum.h
@@ -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
index 00000000000..8f3fc76a019
--- /dev/null
+++ b/src/plugins/gbp/gbp_policy.c
@@ -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
index 00000000000..bfe5974f807
--- /dev/null
+++ b/src/plugins/gbp/gbp_policy_dpo.c
@@ -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
index 00000000000..d6a8986d622
--- /dev/null
+++ b/src/plugins/gbp/gbp_policy_dpo.h
@@ -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
index 00000000000..1debbe406f3
--- /dev/null
+++ b/src/plugins/gbp/gbp_recirc.c
@@ -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
index 00000000000..10f3da631c1
--- /dev/null
+++ b/src/plugins/gbp/gbp_recirc.h
@@ -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
index 00000000000..b6990844cd3
--- /dev/null
+++ b/src/plugins/gbp/gbp_subnet.c
@@ -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
index 00000000000..24b4f3a5816
--- /dev/null
+++ b/src/plugins/gbp/gbp_subnet.h
@@ -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
index 00000000000..fa337451028
--- /dev/null
+++ b/src/plugins/gbp/gbp_types.h
@@ -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:
+ */