summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
-rw-r--r--src/vnet/buffer.h8
-rw-r--r--src/vnet/ethernet/arp.c15
-rw-r--r--src/vnet/ethernet/arp_packet.h5
-rw-r--r--src/vnet/fib/fib_table.c11
-rw-r--r--src/vnet/fib/fib_table.h15
-rw-r--r--src/vnet/ip/ip6.h5
-rw-r--r--src/vnet/ip/ip6_neighbor.c14
-rw-r--r--src/vnet/l2/l2_input.c3
-rw-r--r--src/vnet/l2/l2_input.h3
-rw-r--r--src/vnet/l2/l2_output.h1
-rw-r--r--src/vpp-api/vom/Makefile.am15
-rw-r--r--src/vpp-api/vom/acl_l2_rule.cpp2
-rw-r--r--src/vpp-api/vom/acl_l2_rule.hpp2
-rw-r--r--src/vpp-api/vom/acl_l3_rule.cpp2
-rw-r--r--src/vpp-api/vom/acl_l3_rule.hpp2
-rw-r--r--src/vpp-api/vom/acl_types.hpp13
-rw-r--r--src/vpp-api/vom/bridge_domain.cpp56
-rw-r--r--src/vpp-api/vom/bridge_domain.hpp67
-rw-r--r--src/vpp-api/vom/bridge_domain_cmds.cpp16
-rw-r--r--src/vpp-api/vom/bridge_domain_cmds.hpp17
-rw-r--r--src/vpp-api/vom/gbp_endpoint.cpp44
-rw-r--r--src/vpp-api/vom/gbp_endpoint.hpp24
-rw-r--r--src/vpp-api/vom/gbp_endpoint_cmds.cpp5
-rw-r--r--src/vpp-api/vom/gbp_endpoint_cmds.hpp2
-rw-r--r--src/vpp-api/vom/gbp_endpoint_group.cpp198
-rw-r--r--src/vpp-api/vom/gbp_endpoint_group.hpp205
-rw-r--r--src/vpp-api/vom/gbp_endpoint_group_cmds.cpp147
-rw-r--r--src/vpp-api/vom/gbp_endpoint_group_cmds.hpp139
-rw-r--r--src/vpp-api/vom/gbp_recirc.cpp200
-rw-r--r--src/vpp-api/vom/gbp_recirc.hpp209
-rw-r--r--src/vpp-api/vom/gbp_recirc_cmds.cpp142
-rw-r--r--src/vpp-api/vom/gbp_recirc_cmds.hpp137
-rw-r--r--src/vpp-api/vom/gbp_subnet.cpp231
-rw-r--r--src/vpp-api/vom/gbp_subnet.hpp218
-rw-r--r--src/vpp-api/vom/gbp_subnet_cmds.cpp160
-rw-r--r--src/vpp-api/vom/gbp_subnet_cmds.hpp144
-rw-r--r--src/vpp-api/vom/interface.cpp12
-rw-r--r--src/vpp-api/vom/interface_types.cpp3
-rw-r--r--src/vpp-api/vom/nat_binding_cmds.hpp232
-rw-r--r--src/vpp-api/vom/nat_static.cpp50
-rw-r--r--src/vpp-api/vom/nat_static.hpp8
-rw-r--r--src/vpp-api/vom/nat_static_cmds.cpp119
-rw-r--r--src/vpp-api/vom/nat_static_cmds.hpp105
-rw-r--r--src/vpp-api/vom/route.cpp2
-rw-r--r--src/vpp-api/vom/types.cpp3
-rw-r--r--src/vpp-api/vom/types.hpp2
-rw-r--r--test/ext/vom_test.cpp27
-rw-r--r--test/test_gbp.py1029
-rw-r--r--test/vpp_papi_provider.py63
74 files changed, 7570 insertions, 1168 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:
+ */
diff --git a/src/vnet/buffer.h b/src/vnet/buffer.h
index 02688e26530..86f75389d70 100644
--- a/src/vnet/buffer.h
+++ b/src/vnet/buffer.h
@@ -387,6 +387,12 @@ typedef struct
u8 __unused[2];
+ /* Group Based Policy */
+ struct
+ {
+ u32 src_epg;
+ } gbp;
+
union
{
struct
@@ -396,7 +402,7 @@ typedef struct
u16 *trajectory_trace;
#endif
};
- u32 unused[11];
+ u32 unused[10];
};
} vnet_buffer_opaque2_t;
diff --git a/src/vnet/ethernet/arp.c b/src/vnet/ethernet/arp.c
index 925fb2cc6ac..9114d7a90f3 100644
--- a/src/vnet/ethernet/arp.c
+++ b/src/vnet/ethernet/arp.c
@@ -2498,11 +2498,22 @@ ethernet_arp_change_mac (u32 sw_if_index)
}
void
-send_ip4_garp (vlib_main_t * vm, vnet_hw_interface_t * hi)
+send_ip4_garp (vlib_main_t * vm, const vnet_hw_interface_t * hi)
+{
+ ip4_main_t *i4m = &ip4_main;
+ ip4_address_t *ip4_addr =
+ ip4_interface_first_address (i4m, hi->sw_if_index, 0);
+
+ send_ip4_garp_w_addr (vm, ip4_addr, hi);
+}
+
+void
+send_ip4_garp_w_addr (vlib_main_t * vm,
+ const ip4_address_t * ip4_addr,
+ const vnet_hw_interface_t * hi)
{
ip4_main_t *i4m = &ip4_main;
u32 sw_if_index = hi->sw_if_index;
- ip4_address_t *ip4_addr = ip4_interface_first_address (i4m, sw_if_index, 0);
if (ip4_addr)
{
diff --git a/src/vnet/ethernet/arp_packet.h b/src/vnet/ethernet/arp_packet.h
index 661f33f93af..4b7b0482fb7 100644
--- a/src/vnet/ethernet/arp_packet.h
+++ b/src/vnet/ethernet/arp_packet.h
@@ -167,7 +167,10 @@ typedef struct
ethernet_arp_ip4_entry_t *ip4_neighbor_entries (u32 sw_if_index);
u8 *format_ethernet_arp_ip4_entry (u8 * s, va_list * va);
-void send_ip4_garp (vlib_main_t * vm, vnet_hw_interface_t * hi);
+void send_ip4_garp (vlib_main_t * vm, const vnet_hw_interface_t * hi);
+void send_ip4_garp_w_addr (vlib_main_t * vm,
+ const ip4_address_t * ip4_addr,
+ const vnet_hw_interface_t * hi);
#endif /* included_ethernet_arp_packet_h */
diff --git a/src/vnet/fib/fib_table.c b/src/vnet/fib/fib_table.c
index 324a35fe1e8..d0bc33639b9 100644
--- a/src/vnet/fib/fib_table.c
+++ b/src/vnet/fib/fib_table.c
@@ -1034,6 +1034,17 @@ fib_table_get_table_id_for_sw_if_index (fib_protocol_t proto,
}
u32
+fib_table_get_table_id (u32 fib_index,
+ fib_protocol_t proto)
+{
+ fib_table_t *fib_table;
+
+ fib_table = fib_table_get(fib_index, proto);
+
+ return ((NULL != fib_table ? fib_table->ft_table_id : ~0));
+}
+
+u32
fib_table_find (fib_protocol_t proto,
u32 table_id)
{
diff --git a/src/vnet/fib/fib_table.h b/src/vnet/fib/fib_table.h
index ffad3c43d3e..8b86f8d6dd9 100644
--- a/src/vnet/fib/fib_table.h
+++ b/src/vnet/fib/fib_table.h
@@ -645,6 +645,21 @@ extern u32 fib_table_get_table_id_for_sw_if_index(fib_protocol_t proto,
/**
* @brief
+ * Get the Table-ID of the FIB from protocol and index
+ *
+ * @param fib_index
+ * The FIB index
+ *
+ * @paran proto
+ * The protocol of the FIB (and thus the entries therein)
+ *
+ * @return fib_index
+ * The tableID of the FIB
+ */
+extern u32 fib_table_get_table_id(u32 fib_index, fib_protocol_t proto);
+
+/**
+ * @brief
* Get the index of the FIB for a Table-ID. This DOES NOT create the
* FIB if it does not exist.
*
diff --git a/src/vnet/ip/ip6.h b/src/vnet/ip/ip6.h
index 959d72c0fad..359c461d1e0 100644
--- a/src/vnet/ip/ip6.h
+++ b/src/vnet/ip/ip6.h
@@ -404,7 +404,10 @@ int vnet_ip6_nd_term (vlib_main_t * vm,
ethernet_header_t * eth,
ip6_header_t * ip, u32 sw_if_index, u16 bd_index);
-void send_ip6_na (vlib_main_t * vm, vnet_hw_interface_t * hi);
+void send_ip6_na (vlib_main_t * vm, const vnet_hw_interface_t * hi);
+void send_ip6_na_w_addr (vlib_main_t * vm,
+ const ip6_address_t * addr,
+ const vnet_hw_interface_t * hi);
u8 *format_ip6_forward_next_trace (u8 * s, va_list * args);
diff --git a/src/vnet/ip/ip6_neighbor.c b/src/vnet/ip/ip6_neighbor.c
index 0df29c6bc02..fee4356f5e0 100644
--- a/src/vnet/ip/ip6_neighbor.c
+++ b/src/vnet/ip/ip6_neighbor.c
@@ -4670,11 +4670,23 @@ ethernet_ndp_change_mac (u32 sw_if_index)
}
void
-send_ip6_na (vlib_main_t * vm, vnet_hw_interface_t * hi)
+send_ip6_na (vlib_main_t * vm, const vnet_hw_interface_t * hi)
{
ip6_main_t *i6m = &ip6_main;
u32 sw_if_index = hi->sw_if_index;
ip6_address_t *ip6_addr = ip6_interface_first_address (i6m, sw_if_index);
+
+ send_ip6_na_w_addr (vm, ip6_addr, hi);
+}
+
+void
+send_ip6_na_w_addr (vlib_main_t * vm,
+ const ip6_address_t * ip6_addr,
+ const vnet_hw_interface_t * hi)
+{
+ ip6_main_t *i6m = &ip6_main;
+ u32 sw_if_index = hi->sw_if_index;
+
if (ip6_addr)
{
clib_warning
diff --git a/src/vnet/l2/l2_input.c b/src/vnet/l2/l2_input.c
index d8a0a6bd798..69de2815536 100644
--- a/src/vnet/l2/l2_input.c
+++ b/src/vnet/l2/l2_input.c
@@ -176,7 +176,8 @@ classify_and_dispatch (l2input_main_t * msm, vlib_buffer_t * b0, u32 * next0)
u8 protocol = ((ip6_header_t *) l3h0)->protocol;
/* Disable bridge forwarding (flooding will execute instead if not xconnect) */
- feat_mask &= ~(L2INPUT_FEAT_FWD | L2INPUT_FEAT_UU_FLOOD);
+ feat_mask &= ~(L2INPUT_FEAT_FWD |
+ L2INPUT_FEAT_UU_FLOOD | L2INPUT_FEAT_GBP_FWD);
/* Disable ARP-term for non-ARP and non-ICMP6 packet */
if (ethertype != ETHERNET_TYPE_ARP &&
diff --git a/src/vnet/l2/l2_input.h b/src/vnet/l2/l2_input.h
index dc9d95484ee..5d67f257e46 100644
--- a/src/vnet/l2/l2_input.h
+++ b/src/vnet/l2/l2_input.h
@@ -109,10 +109,13 @@ l2input_bd_config (u32 bd_index)
_(FLOOD, "l2-flood") \
_(ARP_TERM, "arp-term-l2bd") \
_(UU_FLOOD, "l2-flood") \
+ _(GBP_FWD, "gbp-fwd") \
_(FWD, "l2-fwd") \
_(RW, "l2-rw") \
_(LEARN, "l2-learn") \
_(L2_EMULATION, "l2-emulation") \
+ _(GBP_NULL_CLASSIFY, "gbp-null-classify") \
+ _(GBP_SRC_CLASSIFY, "gbp-src-classify") \
_(VTR, "l2-input-vtr") \
_(VPATH, "vpath-input-l2") \
_(ACL, "l2-input-acl") \
diff --git a/src/vnet/l2/l2_output.h b/src/vnet/l2/l2_output.h
index 3b6e480d1dc..6b00d01655c 100644
--- a/src/vnet/l2/l2_output.h
+++ b/src/vnet/l2/l2_output.h
@@ -85,6 +85,7 @@ extern vlib_node_registration_t l2output_node;
#define foreach_l2output_feat \
_(OUTPUT, "interface-output") \
_(SPAN, "span-l2-output") \
+ _(GBP_POLICY, "gbp-policy") \
_(CFM, "feature-bitmap-drop") \
_(QOS, "feature-bitmap-drop") \
_(ACL, "l2-output-acl") \
diff --git a/src/vpp-api/vom/Makefile.am b/src/vpp-api/vom/Makefile.am
index dad4863c17c..f802849d758 100644
--- a/src/vpp-api/vom/Makefile.am
+++ b/src/vpp-api/vom/Makefile.am
@@ -66,8 +66,14 @@ endif
GBP_SOURCES =
if ENABLE_GBP_PLUGIN
GBP_SOURCES += \
+ gbp_recirc_cmds.cpp \
+ gbp_recirc.cpp \
+ gbp_subnet_cmds.cpp \
+ gbp_subnet.cpp \
gbp_endpoint_cmds.cpp \
gbp_endpoint.cpp \
+ gbp_endpoint_group_cmds.cpp \
+ gbp_endpoint_group.cpp \
gbp_contract_cmds.cpp \
gbp_contract.cpp
endif
@@ -142,7 +148,7 @@ vomincludedir = $(includedir)/vom
ACL_INCLUDES =
if ENABLE_ACL_PLUGIN
-ACL_INCLUDES += \
+ACL_INCLUDES += \
acl_binding.hpp \
acl_ethertype.hpp \
acl_l2_rule.hpp \
@@ -153,7 +159,7 @@ endif
NAT_INCLUDES =
if ENABLE_NAT_PLUGIN
-NAT_INCLUDES += \
+NAT_INCLUDES += \
nat_static.hpp \
nat_binding.hpp
endif
@@ -166,8 +172,11 @@ endif
GBP_INCLUDES =
if ENABLE_GBP_PLUGIN
-GBP_INCLUDES += \
+GBP_INCLUDES += \
gbp_endpoint.hpp \
+ gbp_endpoint_group.hpp \
+ gbp_subnet.hpp \
+ gbp_recirc.hpp \
gbp_contract.hpp
endif
diff --git a/src/vpp-api/vom/acl_l2_rule.cpp b/src/vpp-api/vom/acl_l2_rule.cpp
index 1fb06e29b81..2b12e68c184 100644
--- a/src/vpp-api/vom/acl_l2_rule.cpp
+++ b/src/vpp-api/vom/acl_l2_rule.cpp
@@ -65,7 +65,7 @@ l2_rule::priority() const
return m_priority;
}
-action_t
+const action_t&
l2_rule::action() const
{
return m_action;
diff --git a/src/vpp-api/vom/acl_l2_rule.hpp b/src/vpp-api/vom/acl_l2_rule.hpp
index 4faa628ca11..8c094aef5f4 100644
--- a/src/vpp-api/vom/acl_l2_rule.hpp
+++ b/src/vpp-api/vom/acl_l2_rule.hpp
@@ -69,7 +69,7 @@ public:
* Getters
*/
uint32_t priority() const;
- action_t action() const;
+ const action_t& action() const;
const route::prefix_t& src_ip() const;
const mac_address_t& mac() const;
const mac_address_t& mac_mask() const;
diff --git a/src/vpp-api/vom/acl_l3_rule.cpp b/src/vpp-api/vom/acl_l3_rule.cpp
index 4b96cae6c50..417dc5f2eb7 100644
--- a/src/vpp-api/vom/acl_l3_rule.cpp
+++ b/src/vpp-api/vom/acl_l3_rule.cpp
@@ -147,7 +147,7 @@ l3_rule::priority() const
return m_priority;
}
-action_t
+const action_t&
l3_rule::action() const
{
return m_action;
diff --git a/src/vpp-api/vom/acl_l3_rule.hpp b/src/vpp-api/vom/acl_l3_rule.hpp
index 25a2a471d24..c1f1ceea5a1 100644
--- a/src/vpp-api/vom/acl_l3_rule.hpp
+++ b/src/vpp-api/vom/acl_l3_rule.hpp
@@ -121,7 +121,7 @@ public:
*/
const route::prefix_t& src() const;
uint32_t priority() const;
- action_t action() const;
+ const action_t& action() const;
const route::prefix_t& dst() const;
uint8_t proto() const;
uint16_t srcport_or_icmptype_first() const;
diff --git a/src/vpp-api/vom/acl_types.hpp b/src/vpp-api/vom/acl_types.hpp
index ccf0a1c0231..cf5bee3e478 100644
--- a/src/vpp-api/vom/acl_types.hpp
+++ b/src/vpp-api/vom/acl_types.hpp
@@ -26,16 +26,6 @@ namespace ACL {
struct action_t : public enum_base<action_t>
{
/**
- * Constructor
- */
- action_t(int v, const std::string s);
-
- /**
- * Destructor
- */
- ~action_t() = default;
-
- /**
* Permit and Reflexive
*/
const static action_t PERMITANDREFLEX;
@@ -60,6 +50,9 @@ struct action_t : public enum_base<action_t>
*which implements the connection tracking ....
*/
static const action_t& from_bool(bool b, uint8_t c);
+
+private:
+ action_t(int v, const std::string s);
};
};
};
diff --git a/src/vpp-api/vom/bridge_domain.cpp b/src/vpp-api/vom/bridge_domain.cpp
index be520f5ee45..b8c89e10bd9 100644
--- a/src/vpp-api/vom/bridge_domain.cpp
+++ b/src/vpp-api/vom/bridge_domain.cpp
@@ -31,6 +31,33 @@ bridge_domain::learning_mode_t::learning_mode_t(int v, const std::string& s)
{
}
+const bridge_domain::flood_mode_t bridge_domain::flood_mode_t::ON(1, "on");
+const bridge_domain::flood_mode_t bridge_domain::flood_mode_t::OFF(0, "off");
+
+bridge_domain::flood_mode_t::flood_mode_t(int v, const std::string& s)
+ : enum_base<bridge_domain::flood_mode_t>(v, s)
+{
+}
+
+const bridge_domain::mac_age_mode_t bridge_domain::mac_age_mode_t::ON(1, "on");
+const bridge_domain::mac_age_mode_t bridge_domain::mac_age_mode_t::OFF(0,
+ "off");
+
+bridge_domain::mac_age_mode_t::mac_age_mode_t(int v, const std::string& s)
+ : enum_base<bridge_domain::mac_age_mode_t>(v, s)
+{
+}
+
+const bridge_domain::arp_term_mode_t bridge_domain::arp_term_mode_t::ON(1,
+ "on");
+const bridge_domain::arp_term_mode_t bridge_domain::arp_term_mode_t::OFF(0,
+ "off");
+
+bridge_domain::arp_term_mode_t::arp_term_mode_t(int v, const std::string& s)
+ : enum_base<bridge_domain::arp_term_mode_t>(v, s)
+{
+}
+
/**
* A DB of al the interfaces, key on the name
*/
@@ -41,15 +68,25 @@ bridge_domain::event_handler bridge_domain::m_evh;
/**
* Construct a new object matching the desried state
*/
-bridge_domain::bridge_domain(uint32_t id, const learning_mode_t& lmode)
+bridge_domain::bridge_domain(uint32_t id,
+ const learning_mode_t& lmode,
+ const arp_term_mode_t& amode,
+ const flood_mode_t& fmode,
+ const mac_age_mode_t& mmode)
: m_id(id)
, m_learning_mode(lmode)
+ , m_arp_term_mode(amode)
+ , m_flood_mode(fmode)
+ , m_mac_age_mode(mmode)
{
}
bridge_domain::bridge_domain(const bridge_domain& o)
: m_id(o.m_id)
, m_learning_mode(o.m_learning_mode)
+ , m_arp_term_mode(o.m_arp_term_mode)
+ , m_flood_mode(o.m_flood_mode)
+ , m_mac_age_mode(o.m_mac_age_mode)
{
}
@@ -68,7 +105,10 @@ bridge_domain::id() const
bool
bridge_domain::operator==(const bridge_domain& b) const
{
- return ((m_learning_mode == b.m_learning_mode) && id() == b.id());
+ return ((m_learning_mode == b.m_learning_mode) &&
+ (m_flood_mode == b.m_flood_mode) &&
+ (m_mac_age_mode == b.m_mac_age_mode) &&
+ (m_arp_term_mode == b.m_arp_term_mode) && id() == b.id());
}
void
@@ -84,7 +124,8 @@ void
bridge_domain::replay()
{
if (rc_t::OK == m_id.rc()) {
- HW::enqueue(new bridge_domain_cmds::create_cmd(m_id, m_learning_mode));
+ HW::enqueue(new bridge_domain_cmds::create_cmd(
+ m_id, m_learning_mode, m_arp_term_mode, m_flood_mode, m_mac_age_mode));
}
}
@@ -119,7 +160,8 @@ bridge_domain::update(const bridge_domain& desired)
* the desired state is always that the interface should be created
*/
if (rc_t::OK != m_id.rc()) {
- HW::enqueue(new bridge_domain_cmds::create_cmd(m_id, m_learning_mode));
+ HW::enqueue(new bridge_domain_cmds::create_cmd(
+ m_id, m_learning_mode, m_arp_term_mode, m_flood_mode, m_mac_age_mode));
}
}
@@ -173,8 +215,10 @@ bridge_domain::event_handler::handle_populate(const client_db::key_t& key)
for (unsigned int ii = 0; ii < payload.n_sw_ifs; ii++) {
std::shared_ptr<interface> itf =
interface::find(payload.sw_if_details[ii].sw_if_index);
- l2_binding l2(*itf, bd);
- OM::commit(key, l2);
+ if (itf) {
+ l2_binding l2(*itf, bd);
+ OM::commit(key, l2);
+ }
}
}
}
diff --git a/src/vpp-api/vom/bridge_domain.hpp b/src/vpp-api/vom/bridge_domain.hpp
index c7f84e9ec3f..d345da238ca 100644
--- a/src/vpp-api/vom/bridge_domain.hpp
+++ b/src/vpp-api/vom/bridge_domain.hpp
@@ -52,6 +52,51 @@ public:
};
/**
+ * Bridge Domain ARP termination mode
+ */
+ struct arp_term_mode_t : enum_base<arp_term_mode_t>
+ {
+ const static arp_term_mode_t ON;
+ const static arp_term_mode_t OFF;
+
+ private:
+ /**
+ * Private constructor taking the value and the string name
+ */
+ arp_term_mode_t(int v, const std::string& s);
+ };
+
+ /**
+ * Bridge Domain MAC aging mode
+ */
+ struct mac_age_mode_t : enum_base<mac_age_mode_t>
+ {
+ const static mac_age_mode_t ON;
+ const static mac_age_mode_t OFF;
+
+ private:
+ /**
+ * Private constructor taking the value and the string name
+ */
+ mac_age_mode_t(int v, const std::string& s);
+ };
+
+ /**
+ * Bridge Domain Learning mode
+ */
+ struct flood_mode_t : enum_base<flood_mode_t>
+ {
+ const static flood_mode_t ON;
+ const static flood_mode_t OFF;
+
+ private:
+ /**
+ * Private constructor taking the value and the string name
+ */
+ flood_mode_t(int v, const std::string& s);
+ };
+
+ /**
* The value of the defaultbridge domain
*/
const static uint32_t DEFAULT_TABLE = 0;
@@ -60,7 +105,10 @@ public:
* Construct a new object matching the desried state
*/
bridge_domain(uint32_t id,
- const learning_mode_t& lmode = learning_mode_t::ON);
+ const learning_mode_t& lmode = learning_mode_t::ON,
+ const arp_term_mode_t& amode = arp_term_mode_t::ON,
+ const flood_mode_t& fmode = flood_mode_t::ON,
+ const mac_age_mode_t& mmode = mac_age_mode_t::OFF);
/**
* Copy Constructor
@@ -179,11 +227,26 @@ private:
HW::item<uint32_t> m_id;
/**
- * The leanring mode of the bridge
+ * The learning mode of the bridge
*/
learning_mode_t m_learning_mode;
/**
+ * The ARP termination mode of the bridge
+ */
+ arp_term_mode_t m_arp_term_mode;
+
+ /**
+ * The flood mode of the bridge
+ */
+ flood_mode_t m_flood_mode;
+
+ /**
+ * The MAC aging mode of the bridge
+ */
+ mac_age_mode_t m_mac_age_mode;
+
+ /**
* A map of all interfaces key against the interface's name
*/
static singular_db<key_t, bridge_domain> m_db;
diff --git a/src/vpp-api/vom/bridge_domain_cmds.cpp b/src/vpp-api/vom/bridge_domain_cmds.cpp
index 498569f9fab..d1d536f6d39 100644
--- a/src/vpp-api/vom/bridge_domain_cmds.cpp
+++ b/src/vpp-api/vom/bridge_domain_cmds.cpp
@@ -20,9 +20,15 @@ DEFINE_VAPI_MSG_IDS_L2_API_JSON;
namespace VOM {
namespace bridge_domain_cmds {
create_cmd::create_cmd(HW::item<uint32_t>& item,
- const bridge_domain::learning_mode_t& lmode)
+ const bridge_domain::learning_mode_t& lmode,
+ const bridge_domain::arp_term_mode_t& amode,
+ const bridge_domain::flood_mode_t& fmode,
+ const bridge_domain::mac_age_mode_t& mmode)
: rpc_cmd(item)
, m_learning_mode(lmode)
+ , m_arp_term_mode(amode)
+ , m_flood_mode(fmode)
+ , m_mac_age_mode(mmode)
{
}
@@ -39,12 +45,12 @@ create_cmd::issue(connection& con)
auto& payload = req.get_request().get_payload();
payload.bd_id = m_hw_item.data();
- payload.flood = 1;
- payload.uu_flood = 1;
+ payload.flood = m_flood_mode.value();
+ payload.uu_flood = m_flood_mode.value();
payload.forward = 1;
payload.learn = m_learning_mode.value();
- payload.arp_term = 1;
- payload.mac_age = 0;
+ payload.arp_term = m_arp_term_mode.value();
+ payload.mac_age = m_mac_age_mode.value();
payload.is_add = 1;
VAPI_CALL(req.execute());
diff --git a/src/vpp-api/vom/bridge_domain_cmds.hpp b/src/vpp-api/vom/bridge_domain_cmds.hpp
index f263b323e41..0216236d42c 100644
--- a/src/vpp-api/vom/bridge_domain_cmds.hpp
+++ b/src/vpp-api/vom/bridge_domain_cmds.hpp
@@ -35,7 +35,10 @@ public:
* Constructor
*/
create_cmd(HW::item<uint32_t>& item,
- const bridge_domain::learning_mode_t& lmode);
+ const bridge_domain::learning_mode_t& lmode,
+ const bridge_domain::arp_term_mode_t& amode,
+ const bridge_domain::flood_mode_t& fmode,
+ const bridge_domain::mac_age_mode_t& mmode);
/**
* Issue the command to VPP/HW
@@ -56,6 +59,18 @@ private:
* the learning mode for the bridge
*/
bridge_domain::learning_mode_t m_learning_mode;
+ /**
+ * the learning mode for the bridge
+ */
+ bridge_domain::arp_term_mode_t m_arp_term_mode;
+ /**
+ * the flood mode for the bridge
+ */
+ bridge_domain::flood_mode_t m_flood_mode;
+ /**
+ * the flood mode for the bridge
+ */
+ bridge_domain::mac_age_mode_t m_mac_age_mode;
};
/**
diff --git a/src/vpp-api/vom/gbp_endpoint.cpp b/src/vpp-api/vom/gbp_endpoint.cpp
index cd5d7e11ad6..9762a91429a 100644
--- a/src/vpp-api/vom/gbp_endpoint.cpp
+++ b/src/vpp-api/vom/gbp_endpoint.cpp
@@ -25,48 +25,48 @@ gbp_endpoint::event_handler gbp_endpoint::m_evh;
gbp_endpoint::gbp_endpoint(const interface& itf,
const boost::asio::ip::address& ip_addr,
- epg_id_t epg_id)
+ const mac_address_t& mac,
+ const gbp_endpoint_group& epg)
: m_hw(false)
, m_itf(itf.singular())
- , m_ip_addr(ip_addr)
- , m_epg_id(epg_id)
+ , m_ip(ip_addr)
+ , m_mac(mac)
+ , m_epg(epg.singular())
{
}
gbp_endpoint::gbp_endpoint(const gbp_endpoint& gbpe)
: m_hw(gbpe.m_hw)
, m_itf(gbpe.m_itf)
- , m_ip_addr(gbpe.m_ip_addr)
- , m_epg_id(gbpe.m_epg_id)
+ , m_ip(gbpe.m_ip)
+ , m_mac(gbpe.m_mac)
+ , m_epg(gbpe.m_epg)
{
}
gbp_endpoint::~gbp_endpoint()
{
sweep();
-
- // not in the DB anymore.
m_db.release(key(), this);
}
const gbp_endpoint::key_t
gbp_endpoint::key() const
{
- return (std::make_pair(m_itf->key(), m_ip_addr));
+ return (std::make_pair(m_itf->key(), m_ip));
}
bool
gbp_endpoint::operator==(const gbp_endpoint& gbpe) const
{
- return ((key() == gbpe.key()) && (m_epg_id == gbpe.m_epg_id));
+ return ((key() == gbpe.key()) && (m_epg == gbpe.m_epg));
}
void
gbp_endpoint::sweep()
{
if (m_hw) {
- HW::enqueue(
- new gbp_endpoint_cmds::delete_cmd(m_hw, m_itf->handle(), m_ip_addr));
+ HW::enqueue(new gbp_endpoint_cmds::delete_cmd(m_hw, m_itf->handle(), m_ip));
}
HW::write();
}
@@ -75,8 +75,8 @@ void
gbp_endpoint::replay()
{
if (m_hw) {
- HW::enqueue(new gbp_endpoint_cmds::create_cmd(m_hw, m_itf->handle(),
- m_ip_addr, m_epg_id));
+ HW::enqueue(new gbp_endpoint_cmds::create_cmd(m_hw, m_itf->handle(), m_ip,
+ m_mac, m_epg->id()));
}
}
@@ -84,8 +84,8 @@ std::string
gbp_endpoint::to_string() const
{
std::ostringstream s;
- s << "gbp-endpoint:[" << m_itf->to_string() << ", " << m_ip_addr.to_string()
- << ", epg-id:" << m_epg_id << "]";
+ s << "gbp-endpoint:[" << m_itf->to_string() << ", " << m_ip.to_string()
+ << ", " << m_mac.to_string() << ", epg:" << m_epg->to_string() << "]";
return (s.str());
}
@@ -93,12 +93,9 @@ gbp_endpoint::to_string() const
void
gbp_endpoint::update(const gbp_endpoint& r)
{
- /*
- * create the table if it is not yet created
- */
if (rc_t::OK != m_hw.rc()) {
- HW::enqueue(new gbp_endpoint_cmds::create_cmd(m_hw, m_itf->handle(),
- m_ip_addr, m_epg_id));
+ HW::enqueue(new gbp_endpoint_cmds::create_cmd(m_hw, m_itf->handle(), m_ip,
+ m_mac, m_epg->id()));
}
}
@@ -154,11 +151,14 @@ gbp_endpoint::event_handler::handle_populate(const client_db::key_t& key)
from_bytes(payload.endpoint.is_ip6, payload.endpoint.address);
std::shared_ptr<interface> itf =
interface::find(payload.endpoint.sw_if_index);
+ std::shared_ptr<gbp_endpoint_group> epg =
+ gbp_endpoint_group::find(payload.endpoint.epg_id);
+ mac_address_t mac(payload.endpoint.mac);
VOM_LOG(log_level_t::DEBUG) << "data: " << payload.endpoint.sw_if_index;
- if (itf) {
- gbp_endpoint gbpe(*itf, address, payload.endpoint.epg_id);
+ if (itf && epg) {
+ gbp_endpoint gbpe(*itf, address, mac, *epg);
OM::commit(key, gbpe);
VOM_LOG(log_level_t::DEBUG) << "read: " << gbpe.to_string();
diff --git a/src/vpp-api/vom/gbp_endpoint.hpp b/src/vpp-api/vom/gbp_endpoint.hpp
index 6ece4fa5431..f6466a6077d 100644
--- a/src/vpp-api/vom/gbp_endpoint.hpp
+++ b/src/vpp-api/vom/gbp_endpoint.hpp
@@ -18,19 +18,13 @@
#include <ostream>
+#include "vom/gbp_endpoint_group.hpp"
#include "vom/interface.hpp"
#include "vom/singular_db.hpp"
-#include "vom/types.hpp"
namespace VOM {
-
-/**
- * EPG IDs are 32 bit integers
- */
-typedef uint32_t epg_id_t;
-
/**
- * A entry in the ARP termination table of a Bridge Domain
+ * A GBP Enpoint (i.e. a VM)
*/
class gbp_endpoint : public object_base
{
@@ -45,7 +39,8 @@ public:
*/
gbp_endpoint(const interface& itf,
const boost::asio::ip::address& ip_addr,
- epg_id_t epg_id);
+ const mac_address_t& mac,
+ const gbp_endpoint_group& epg);
/**
* Copy Construct
@@ -166,12 +161,17 @@ private:
/**
* The IP address of the endpoint
*/
- boost::asio::ip::address m_ip_addr;
+ boost::asio::ip::address m_ip;
+
+ /**
+ * The MAC address of the endpoint
+ */
+ mac_address_t m_mac;
/**
- * The EPG ID
+ * The EPG the endpoint is in
*/
- epg_id_t m_epg_id;
+ std::shared_ptr<gbp_endpoint_group> m_epg;
/**
* A map of all bridge_domains
diff --git a/src/vpp-api/vom/gbp_endpoint_cmds.cpp b/src/vpp-api/vom/gbp_endpoint_cmds.cpp
index 4f85b7eff4e..88d2f377bc7 100644
--- a/src/vpp-api/vom/gbp_endpoint_cmds.cpp
+++ b/src/vpp-api/vom/gbp_endpoint_cmds.cpp
@@ -23,10 +23,12 @@ namespace gbp_endpoint_cmds {
create_cmd::create_cmd(HW::item<bool>& item,
const handle_t& itf,
const boost::asio::ip::address& ip_addr,
+ const mac_address_t& mac,
epg_id_t epg_id)
: rpc_cmd(item)
, m_itf(itf)
, m_ip_addr(ip_addr)
+ , m_mac(mac)
, m_epg_id(epg_id)
{
}
@@ -35,7 +37,7 @@ bool
create_cmd::operator==(const create_cmd& other) const
{
return ((m_itf == other.m_itf) && (m_ip_addr == other.m_ip_addr) &&
- (m_epg_id == other.m_epg_id));
+ (m_mac == other.m_mac) && (m_epg_id == other.m_epg_id));
}
rc_t
@@ -48,6 +50,7 @@ create_cmd::issue(connection& con)
payload.endpoint.sw_if_index = m_itf.value();
payload.endpoint.epg_id = m_epg_id;
to_bytes(m_ip_addr, &payload.endpoint.is_ip6, payload.endpoint.address);
+ m_mac.to_bytes(payload.endpoint.mac, 6);
VAPI_CALL(req.execute());
diff --git a/src/vpp-api/vom/gbp_endpoint_cmds.hpp b/src/vpp-api/vom/gbp_endpoint_cmds.hpp
index cc7884979e8..2893ef51eec 100644
--- a/src/vpp-api/vom/gbp_endpoint_cmds.hpp
+++ b/src/vpp-api/vom/gbp_endpoint_cmds.hpp
@@ -37,6 +37,7 @@ public:
create_cmd(HW::item<bool>& item,
const handle_t& itf,
const boost::asio::ip::address& ip_addr,
+ const mac_address_t& mac,
epg_id_t epg_id);
/**
@@ -57,6 +58,7 @@ public:
private:
const handle_t m_itf;
const boost::asio::ip::address m_ip_addr;
+ const mac_address_t m_mac;
const epg_id_t m_epg_id;
};
diff --git a/src/vpp-api/vom/gbp_endpoint_group.cpp b/src/vpp-api/vom/gbp_endpoint_group.cpp
new file mode 100644
index 00000000000..d9f0d38d594
--- /dev/null
+++ b/src/vpp-api/vom/gbp_endpoint_group.cpp
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/gbp_endpoint_group.hpp"
+#include "vom/gbp_endpoint_group_cmds.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+
+singular_db<gbp_endpoint_group::key_t, gbp_endpoint_group>
+ gbp_endpoint_group::m_db;
+
+gbp_endpoint_group::event_handler gbp_endpoint_group::m_evh;
+
+gbp_endpoint_group::gbp_endpoint_group(epg_id_t epg_id,
+ const interface& itf,
+ const route_domain& rd,
+ const bridge_domain& bd)
+ : m_hw(false)
+ , m_epg_id(epg_id)
+ , m_itf(itf.singular())
+ , m_rd(rd.singular())
+ , m_bd(bd.singular())
+{
+}
+
+gbp_endpoint_group::gbp_endpoint_group(const gbp_endpoint_group& epg)
+ : m_hw(epg.m_hw)
+ , m_epg_id(epg.m_epg_id)
+ , m_itf(epg.m_itf)
+ , m_rd(epg.m_rd)
+ , m_bd(epg.m_bd)
+{
+}
+
+gbp_endpoint_group::~gbp_endpoint_group()
+{
+ sweep();
+ m_db.release(key(), this);
+}
+
+const gbp_endpoint_group::key_t
+gbp_endpoint_group::key() const
+{
+ return (m_epg_id);
+}
+
+epg_id_t
+gbp_endpoint_group::id() const
+{
+ return (m_epg_id);
+}
+
+bool
+gbp_endpoint_group::operator==(const gbp_endpoint_group& gbpe) const
+{
+ return (key() == gbpe.key() && (m_itf == gbpe.m_itf) && (m_rd == gbpe.m_rd) &&
+ (m_bd == gbpe.m_bd));
+}
+
+void
+gbp_endpoint_group::sweep()
+{
+ if (m_hw) {
+ HW::enqueue(new gbp_endpoint_group_cmds::delete_cmd(m_hw, m_epg_id));
+ }
+ HW::write();
+}
+
+void
+gbp_endpoint_group::replay()
+{
+ if (m_hw) {
+ HW::enqueue(new gbp_endpoint_group_cmds::create_cmd(
+ m_hw, m_epg_id, m_bd->id(), m_rd->table_id(), m_itf->handle()));
+ }
+}
+
+std::string
+gbp_endpoint_group::to_string() const
+{
+ std::ostringstream s;
+ s << "gbp-endpoint-group:["
+ << "epg:" << m_epg_id << ", " << m_itf->to_string() << ", "
+ << m_bd->to_string() << ", " << m_rd->to_string() << "]";
+
+ return (s.str());
+}
+
+void
+gbp_endpoint_group::update(const gbp_endpoint_group& r)
+{
+ if (rc_t::OK != m_hw.rc()) {
+ HW::enqueue(new gbp_endpoint_group_cmds::create_cmd(
+ m_hw, m_epg_id, m_bd->id(), m_rd->table_id(), m_itf->handle()));
+ }
+}
+
+std::shared_ptr<gbp_endpoint_group>
+gbp_endpoint_group::find_or_add(const gbp_endpoint_group& temp)
+{
+ return (m_db.find_or_add(temp.key(), temp));
+}
+
+std::shared_ptr<gbp_endpoint_group>
+gbp_endpoint_group::find(const key_t& k)
+{
+ return (m_db.find(k));
+}
+
+std::shared_ptr<gbp_endpoint_group>
+gbp_endpoint_group::singular() const
+{
+ return find_or_add(*this);
+}
+
+void
+gbp_endpoint_group::dump(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+
+gbp_endpoint_group::event_handler::event_handler()
+{
+ OM::register_listener(this);
+ inspect::register_handler({ "gbp-endpoint-group" }, "GBP Endpoint_Groups",
+ this);
+}
+
+void
+gbp_endpoint_group::event_handler::handle_replay()
+{
+ m_db.replay();
+}
+
+void
+gbp_endpoint_group::event_handler::handle_populate(const client_db::key_t& key)
+{
+ std::shared_ptr<gbp_endpoint_group_cmds::dump_cmd> cmd =
+ std::make_shared<gbp_endpoint_group_cmds::dump_cmd>();
+
+ HW::enqueue(cmd);
+ HW::write();
+
+ for (auto& record : *cmd) {
+ auto& payload = record.get_payload();
+
+ std::shared_ptr<interface> itf =
+ interface::find(payload.epg.uplink_sw_if_index);
+ std::shared_ptr<route_domain> rd =
+ route_domain::find(payload.epg.ip4_table_id);
+ std::shared_ptr<bridge_domain> bd = bridge_domain::find(payload.epg.bd_id);
+
+ VOM_LOG(log_level_t::DEBUG) << "data: [" << payload.epg.uplink_sw_if_index
+ << ", " << payload.epg.ip4_table_id << ", "
+ << payload.epg.bd_id << "]";
+
+ if (itf && bd && rd) {
+ gbp_endpoint_group gbpe(payload.epg.epg_id, *itf, *rd, *bd);
+ OM::commit(key, gbpe);
+
+ VOM_LOG(log_level_t::DEBUG) << "read: " << gbpe.to_string();
+ }
+ }
+}
+
+dependency_t
+gbp_endpoint_group::event_handler::order() const
+{
+ return (dependency_t::ACL);
+}
+
+void
+gbp_endpoint_group::event_handler::show(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+} // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/src/vpp-api/vom/gbp_endpoint_group.hpp b/src/vpp-api/vom/gbp_endpoint_group.hpp
new file mode 100644
index 00000000000..f7c900f20be
--- /dev/null
+++ b/src/vpp-api/vom/gbp_endpoint_group.hpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_GBP_ENDPOINT_GROUP_H__
+#define __VOM_GBP_ENDPOINT_GROUP_H__
+
+#include "vom/interface.hpp"
+#include "vom/singular_db.hpp"
+#include "vom/types.hpp"
+
+#include "vom/bridge_domain.hpp"
+#include "vom/route_domain.hpp"
+
+namespace VOM {
+
+/**
+ * EPG IDs are 32 bit integers
+ */
+typedef uint32_t epg_id_t;
+
+/**
+ * A entry in the ARP termination table of a Bridge Domain
+ */
+class gbp_endpoint_group : public object_base
+{
+public:
+ /**
+ * The key for a GBP endpoint group is its ID
+ */
+ typedef epg_id_t key_t;
+
+ /**
+ * Construct a GBP endpoint_group
+ */
+ gbp_endpoint_group(epg_id_t epg_id,
+ const interface& itf,
+ const route_domain& rd,
+ const bridge_domain& bd);
+
+ /**
+ * Copy Construct
+ */
+ gbp_endpoint_group(const gbp_endpoint_group& r);
+
+ /**
+ * Destructor
+ */
+ ~gbp_endpoint_group();
+
+ /**
+ * Return the object's key
+ */
+ const key_t key() const;
+
+ /**
+ * comparison operator
+ */
+ bool operator==(const gbp_endpoint_group& bdae) const;
+
+ /**
+ * Return the matching 'singular instance'
+ */
+ std::shared_ptr<gbp_endpoint_group> singular() const;
+
+ /**
+ * Find the instnace of the bridge_domain domain in the OM
+ */
+ static std::shared_ptr<gbp_endpoint_group> find(const key_t& k);
+
+ /**
+ * Dump all bridge_domain-doamin into the stream provided
+ */
+ static void dump(std::ostream& os);
+
+ /**
+ * replay the object to create it in hardware
+ */
+ void replay(void);
+
+ /**
+ * Convert to string for debugging
+ */
+ std::string to_string() const;
+
+ /**
+ * Get the ID of the EPG
+ */
+ epg_id_t id() const;
+
+private:
+ /**
+ * Class definition for listeners to OM events
+ */
+ class event_handler : public OM::listener, public inspect::command_handler
+ {
+ public:
+ event_handler();
+ virtual ~event_handler() = default;
+
+ /**
+ * Handle a populate event
+ */
+ void handle_populate(const client_db::key_t& key);
+
+ /**
+ * Handle a replay event
+ */
+ void handle_replay();
+
+ /**
+ * Show the object in the Singular DB
+ */
+ void show(std::ostream& os);
+
+ /**
+ * Get the sortable Id of the listener
+ */
+ dependency_t order() const;
+ };
+
+ /**
+ * event_handler to register with OM
+ */
+ static event_handler m_evh;
+
+ /**
+ * Commit the acculmulated changes into VPP. i.e. to a 'HW" write.
+ */
+ void update(const gbp_endpoint_group& obj);
+
+ /**
+ * Find or add the instnace of the bridge_domain domain in the OM
+ */
+ static std::shared_ptr<gbp_endpoint_group> find_or_add(
+ const gbp_endpoint_group& temp);
+
+ /*
+ * It's the VPPHW class that updates the objects in HW
+ */
+ friend class OM;
+
+ /**
+ * It's the singular_db class that calls replay()
+ */
+ friend class singular_db<key_t, gbp_endpoint_group>;
+
+ /**
+ * Sweep/reap the object if still stale
+ */
+ void sweep(void);
+
+ /**
+ * HW configuration for the result of creating the endpoint_group
+ */
+ HW::item<bool> m_hw;
+
+ /**
+ * The EPG ID
+ */
+ epg_id_t m_epg_id;
+
+ /**
+ * The uplink interface for the endpoint group
+ */
+ std::shared_ptr<interface> m_itf;
+
+ /**
+ * The route-domain the EPG uses
+ */
+ std::shared_ptr<route_domain> m_rd;
+
+ /**
+ * The bridge-domain the EPG uses
+ */
+ std::shared_ptr<bridge_domain> m_bd;
+
+ /**
+ * A map of all bridge_domains
+ */
+ static singular_db<key_t, gbp_endpoint_group> m_db;
+};
+
+}; // namespace
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/src/vpp-api/vom/gbp_endpoint_group_cmds.cpp b/src/vpp-api/vom/gbp_endpoint_group_cmds.cpp
new file mode 100644
index 00000000000..55e81d3a528
--- /dev/null
+++ b/src/vpp-api/vom/gbp_endpoint_group_cmds.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/gbp_endpoint_group_cmds.hpp"
+
+namespace VOM {
+namespace gbp_endpoint_group_cmds {
+
+create_cmd::create_cmd(HW::item<bool>& item,
+ epg_id_t epg_id,
+ uint32_t bd_id,
+ route::table_id_t rd_id,
+ const handle_t& itf)
+ : rpc_cmd(item)
+ , m_epg_id(epg_id)
+ , m_bd_id(bd_id)
+ , m_rd_id(rd_id)
+ , m_itf(itf)
+{
+}
+
+bool
+create_cmd::operator==(const create_cmd& other) const
+{
+ return ((m_itf == other.m_itf) && (m_bd_id == other.m_bd_id) &&
+ (m_rd_id == other.m_rd_id) && (m_epg_id == other.m_epg_id));
+}
+
+rc_t
+create_cmd::issue(connection& con)
+{
+ msg_t req(con.ctx(), std::ref(*this));
+
+ auto& payload = req.get_request().get_payload();
+ payload.is_add = 1;
+ payload.epg.uplink_sw_if_index = m_itf.value();
+ payload.epg.epg_id = m_epg_id;
+ payload.epg.bd_id = m_bd_id;
+ payload.epg.ip4_table_id = m_rd_id;
+ payload.epg.ip6_table_id = m_rd_id;
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item.set(wait());
+
+ return rc_t::OK;
+}
+
+std::string
+create_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "gbp-endpoint-group-create: " << m_hw_item.to_string()
+ << " epg-id:" << m_epg_id << " bd-id:" << m_bd_id << " rd-id:" << m_rd_id
+ << " itf:" << m_itf;
+
+ return (s.str());
+}
+
+delete_cmd::delete_cmd(HW::item<bool>& item, epg_id_t epg_id)
+ : rpc_cmd(item)
+ , m_epg_id(epg_id)
+{
+}
+
+bool
+delete_cmd::operator==(const delete_cmd& other) const
+{
+ return (m_epg_id == other.m_epg_id);
+}
+
+rc_t
+delete_cmd::issue(connection& con)
+{
+ msg_t req(con.ctx(), std::ref(*this));
+
+ auto& payload = req.get_request().get_payload();
+ payload.is_add = 0;
+ payload.epg.epg_id = m_epg_id;
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item.set(wait());
+
+ return rc_t::OK;
+}
+
+std::string
+delete_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "gbp-endpoint-group-delete: " << m_hw_item.to_string()
+ << " epg:" << m_epg_id;
+
+ return (s.str());
+}
+
+dump_cmd::dump_cmd()
+{
+}
+
+bool
+dump_cmd::operator==(const dump_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+dump_cmd::issue(connection& con)
+{
+ m_dump.reset(new msg_t(con.ctx(), std::ref(*this)));
+
+ VAPI_CALL(m_dump->execute());
+
+ wait();
+
+ return rc_t::OK;
+}
+
+std::string
+dump_cmd::to_string() const
+{
+ return ("gbp-endpoint-group-dump");
+}
+
+}; // namespace gbp_endpoint_group_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/src/vpp-api/vom/gbp_endpoint_group_cmds.hpp b/src/vpp-api/vom/gbp_endpoint_group_cmds.hpp
new file mode 100644
index 00000000000..4da3a4247b4
--- /dev/null
+++ b/src/vpp-api/vom/gbp_endpoint_group_cmds.hpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_GBP_ENDPOINT_GROUP_CMDS_H__
+#define __VOM_GBP_ENDPOINT_GROUP_CMDS_H__
+
+#include "vom/dump_cmd.hpp"
+#include "vom/gbp_endpoint_group.hpp"
+
+#include <vapi/gbp.api.vapi.hpp>
+
+namespace VOM {
+namespace gbp_endpoint_group_cmds {
+
+/**
+* A command class that creates or updates the GBP endpoint_group
+*/
+class create_cmd
+ : public rpc_cmd<HW::item<bool>, rc_t, vapi::Gbp_endpoint_group_add_del>
+{
+public:
+ /**
+ * Constructor
+ */
+ create_cmd(HW::item<bool>& item,
+ epg_id_t epg_id,
+ uint32_t bd_id,
+ route::table_id_t rd_id,
+ const handle_t& itf);
+
+ /**
+ * Issue the command to VPP/HW
+ */
+ rc_t issue(connection& con);
+
+ /**
+ * convert to string format for debug purposes
+ */
+ std::string to_string() const;
+
+ /**
+ * Comparison operator - only used for UT
+ */
+ bool operator==(const create_cmd& i) const;
+
+private:
+ const epg_id_t m_epg_id;
+ const uint32_t m_bd_id;
+ const route::table_id_t m_rd_id;
+ const handle_t m_itf;
+};
+
+/**
+ * A cmd class that deletes a GBP endpoint_group
+ */
+class delete_cmd
+ : public rpc_cmd<HW::item<bool>, rc_t, vapi::Gbp_endpoint_group_add_del>
+{
+public:
+ /**
+ * Constructor
+ */
+ delete_cmd(HW::item<bool>& item, epg_id_t epg_id);
+
+ /**
+ * Issue the command to VPP/HW
+ */
+ rc_t issue(connection& con);
+
+ /**
+ * convert to string format for debug purposes
+ */
+ std::string to_string() const;
+
+ /**
+ * Comparison operator - only used for UT
+ */
+ bool operator==(const delete_cmd& i) const;
+
+private:
+ const epg_id_t m_epg_id;
+};
+
+/**
+ * A cmd class that Dumps all the GBP endpoint_groups
+ */
+class dump_cmd : public VOM::dump_cmd<vapi::Gbp_endpoint_group_dump>
+{
+public:
+ /**
+ * Constructor
+ */
+ dump_cmd();
+ dump_cmd(const dump_cmd& d);
+
+ /**
+ * Issue the command to VPP/HW
+ */
+ rc_t issue(connection& con);
+ /**
+ * convert to string format for debug purposes
+ */
+ std::string to_string() const;
+
+ /**
+ * Comparison operator - only used for UT
+ */
+ bool operator==(const dump_cmd& i) const;
+
+private:
+ /**
+ * HW reutrn code
+ */
+ HW::item<bool> item;
+};
+}; // namespace gbp_enpoint_cms
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/src/vpp-api/vom/gbp_recirc.cpp b/src/vpp-api/vom/gbp_recirc.cpp
new file mode 100644
index 00000000000..250e3048f8a
--- /dev/null
+++ b/src/vpp-api/vom/gbp_recirc.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/gbp_recirc.hpp"
+#include "vom/gbp_recirc_cmds.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+
+gbp_recirc::type_t::type_t(int v, const std::string s)
+ : enum_base<gbp_recirc::type_t>(v, s)
+{
+}
+
+const gbp_recirc::type_t gbp_recirc::type_t::INTERNAL(0, "internal");
+const gbp_recirc::type_t gbp_recirc::type_t::EXTERNAL(1, "external");
+
+singular_db<gbp_recirc::key_t, gbp_recirc> gbp_recirc::m_db;
+
+gbp_recirc::event_handler gbp_recirc::m_evh;
+
+gbp_recirc::gbp_recirc(const interface& itf,
+ const type_t& type,
+ const gbp_endpoint_group& epg)
+ : m_hw(false)
+ , m_itf(itf.singular())
+ , m_type(type)
+ , m_epg(epg.singular())
+{
+}
+
+gbp_recirc::gbp_recirc(const gbp_recirc& gbpe)
+ : m_hw(gbpe.m_hw)
+ , m_itf(gbpe.m_itf)
+ , m_type(gbpe.m_type)
+ , m_epg(gbpe.m_epg)
+{
+}
+
+gbp_recirc::~gbp_recirc()
+{
+ sweep();
+ m_db.release(key(), this);
+}
+
+const gbp_recirc::key_t
+gbp_recirc::key() const
+{
+ return (m_itf->key());
+}
+
+const handle_t&
+gbp_recirc::handle() const
+{
+ return m_itf->handle();
+}
+
+bool
+gbp_recirc::operator==(const gbp_recirc& gbpe) const
+{
+ return ((key() == gbpe.key()) && (m_type == gbpe.m_type) &&
+ (m_itf == gbpe.m_itf) && (m_epg == gbpe.m_epg));
+}
+
+void
+gbp_recirc::sweep()
+{
+ if (m_hw) {
+ HW::enqueue(new gbp_recirc_cmds::delete_cmd(m_hw, m_itf->handle()));
+ }
+ HW::write();
+}
+
+void
+gbp_recirc::replay()
+{
+ if (m_hw) {
+ HW::enqueue(new gbp_recirc_cmds::create_cmd(
+ m_hw, m_itf->handle(), (m_type == type_t::EXTERNAL), m_epg->id()));
+ }
+}
+
+std::string
+gbp_recirc::to_string() const
+{
+ std::ostringstream s;
+ s << "gbp-recirc:[" << m_itf->to_string() << ", type:" << m_type.to_string()
+ << ", " << m_epg->to_string() << "]";
+
+ return (s.str());
+}
+
+void
+gbp_recirc::update(const gbp_recirc& r)
+{
+ if (rc_t::OK != m_hw.rc()) {
+ HW::enqueue(new gbp_recirc_cmds::create_cmd(
+ m_hw, m_itf->handle(), (m_type == type_t::EXTERNAL), m_epg->id()));
+ }
+}
+
+std::shared_ptr<gbp_recirc>
+gbp_recirc::find_or_add(const gbp_recirc& temp)
+{
+ return (m_db.find_or_add(temp.key(), temp));
+}
+
+std::shared_ptr<gbp_recirc>
+gbp_recirc::find(const key_t& k)
+{
+ return (m_db.find(k));
+}
+
+std::shared_ptr<gbp_recirc>
+gbp_recirc::singular() const
+{
+ return find_or_add(*this);
+}
+
+void
+gbp_recirc::dump(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+
+gbp_recirc::event_handler::event_handler()
+{
+ OM::register_listener(this);
+ inspect::register_handler({ "gbp-recirc" }, "GBP Recircs", this);
+}
+
+void
+gbp_recirc::event_handler::handle_replay()
+{
+ m_db.replay();
+}
+
+void
+gbp_recirc::event_handler::handle_populate(const client_db::key_t& key)
+{
+ std::shared_ptr<gbp_recirc_cmds::dump_cmd> cmd =
+ std::make_shared<gbp_recirc_cmds::dump_cmd>();
+
+ HW::enqueue(cmd);
+ HW::write();
+
+ for (auto& record : *cmd) {
+ auto& payload = record.get_payload();
+
+ std::shared_ptr<interface> itf =
+ interface::find(payload.recirc.sw_if_index);
+ std::shared_ptr<gbp_endpoint_group> epg =
+ gbp_endpoint_group::find(payload.recirc.epg_id);
+
+ VOM_LOG(log_level_t::DEBUG) << "data: [" << payload.recirc.sw_if_index
+ << ", " << payload.recirc.epg_id << "]";
+
+ if (itf && epg) {
+ gbp_recirc recirc(
+ *itf, (payload.recirc.is_ext ? type_t::EXTERNAL : type_t::INTERNAL),
+ *epg);
+ OM::commit(key, recirc);
+
+ VOM_LOG(log_level_t::DEBUG) << "read: " << recirc.to_string();
+ }
+ }
+}
+
+dependency_t
+gbp_recirc::event_handler::order() const
+{
+ return (dependency_t::BINDING);
+}
+
+void
+gbp_recirc::event_handler::show(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+} // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/src/vpp-api/vom/gbp_recirc.hpp b/src/vpp-api/vom/gbp_recirc.hpp
new file mode 100644
index 00000000000..fee4f6c2502
--- /dev/null
+++ b/src/vpp-api/vom/gbp_recirc.hpp
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_GBP_RECIRC_H__
+#define __VOM_GBP_RECIRC_H__
+
+#include "vom/gbp_endpoint_group.hpp"
+#include "vom/interface.hpp"
+#include "vom/singular_db.hpp"
+
+namespace VOM {
+/**
+ * A recirculation interface for GBP use pre/post NAT
+ */
+class gbp_recirc : public object_base
+{
+public:
+ /**
+ * The key for a GBP recirc interface
+ */
+ typedef interface::key_t key_t;
+
+ struct type_t : public enum_base<type_t>
+ {
+ /**
+ * Internal recirclation interfaces accept per-NAT translation
+ * traffic from the external/NAT EPG and inject into the
+ * private/NAT-inside EPG
+ */
+ const static type_t INTERNAL;
+
+ /**
+ * External recirculation interfaces accept post-NAT translation
+ * traffic from the internal EPG and inject into the
+ * NAT EPG
+ */
+ const static type_t EXTERNAL;
+
+ private:
+ type_t(int v, const std::string s);
+ };
+
+ /**
+ * Construct a GBP recirc
+ */
+ gbp_recirc(const interface& itf,
+ const type_t& type,
+ const gbp_endpoint_group& epg);
+
+ /**
+ * Copy Construct
+ */
+ gbp_recirc(const gbp_recirc& r);
+
+ /**
+ * Destructor
+ */
+ ~gbp_recirc();
+
+ /**
+ * Return the object's key
+ */
+ const key_t key() const;
+
+ /**
+ * comparison operator
+ */
+ bool operator==(const gbp_recirc& bdae) const;
+
+ /**
+ * Return the matching 'singular instance'
+ */
+ std::shared_ptr<gbp_recirc> singular() const;
+
+ /**
+ * Find the instnace of the recirc interface in the OM
+ */
+ static std::shared_ptr<gbp_recirc> find(const key_t& k);
+
+ /**
+ * Dump all bridge_domain-doamin into the stream provided
+ */
+ static void dump(std::ostream& os);
+
+ /**
+ * replay the object to create it in hardware
+ */
+ void replay(void);
+
+ /**
+ * Convert to string for debugging
+ */
+ std::string to_string() const;
+
+ /**
+ * return the recirculation interface's handle
+ */
+ const handle_t& handle() const;
+
+private:
+ /**
+ * Class definition for listeners to OM events
+ */
+ class event_handler : public OM::listener, public inspect::command_handler
+ {
+ public:
+ event_handler();
+ virtual ~event_handler() = default;
+
+ /**
+ * Handle a populate event
+ */
+ void handle_populate(const client_db::key_t& key);
+
+ /**
+ * Handle a replay event
+ */
+ void handle_replay();
+
+ /**
+ * Show the object in the Singular DB
+ */
+ void show(std::ostream& os);
+
+ /**
+ * Get the sortable Id of the listener
+ */
+ dependency_t order() const;
+ };
+
+ /**
+ * event_handler to register with OM
+ */
+ static event_handler m_evh;
+
+ /**
+ * Commit the acculmulated changes into VPP. i.e. to a 'HW" write.
+ */
+ void update(const gbp_recirc& obj);
+
+ /**
+ * Find or add the instnace of the bridge_domain domain in the OM
+ */
+ static std::shared_ptr<gbp_recirc> find_or_add(const gbp_recirc& temp);
+
+ /*
+ * It's the VPPHW class that updates the objects in HW
+ */
+ friend class OM;
+
+ /**
+ * It's the singular_db class that calls replay()
+ */
+ friend class singular_db<key_t, gbp_recirc>;
+
+ /**
+ * Sweep/reap the object if still stale
+ */
+ void sweep(void);
+
+ /**
+ * HW configuration for the result of creating the recirc
+ */
+ HW::item<bool> m_hw;
+
+ /**
+ * The interface the recirc is attached to.
+ */
+ std::shared_ptr<interface> m_itf;
+
+ /**
+ * Is the reicrc for the external (i.e. post-NAT) or internal
+ */
+ type_t m_type;
+
+ /**
+ * The EPG the recirc is in
+ */
+ std::shared_ptr<gbp_endpoint_group> m_epg;
+
+ /**
+ * A map of all bridge_domains
+ */
+ static singular_db<key_t, gbp_recirc> m_db;
+};
+
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/src/vpp-api/vom/gbp_recirc_cmds.cpp b/src/vpp-api/vom/gbp_recirc_cmds.cpp
new file mode 100644
index 00000000000..757fcb99065
--- /dev/null
+++ b/src/vpp-api/vom/gbp_recirc_cmds.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/gbp_recirc_cmds.hpp"
+
+namespace VOM {
+namespace gbp_recirc_cmds {
+
+create_cmd::create_cmd(HW::item<bool>& item,
+ const handle_t& itf,
+ bool is_ext,
+ epg_id_t epg_id)
+ : rpc_cmd(item)
+ , m_itf(itf)
+ , m_is_ext(is_ext)
+ , m_epg_id(epg_id)
+{
+}
+
+bool
+create_cmd::operator==(const create_cmd& other) const
+{
+ return ((m_itf == other.m_itf) && (m_is_ext == other.m_is_ext) &&
+ (m_epg_id == other.m_epg_id));
+}
+
+rc_t
+create_cmd::issue(connection& con)
+{
+ msg_t req(con.ctx(), std::ref(*this));
+
+ auto& payload = req.get_request().get_payload();
+ payload.is_add = 1;
+ payload.recirc.sw_if_index = m_itf.value();
+ payload.recirc.epg_id = m_epg_id;
+ payload.recirc.is_ext = m_is_ext;
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item.set(wait());
+
+ return rc_t::OK;
+}
+
+std::string
+create_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "gbp-recirc-create: " << m_hw_item.to_string() << " itf:" << m_itf
+ << " ext:" << m_is_ext << " epg-id:" << m_epg_id;
+
+ return (s.str());
+}
+
+delete_cmd::delete_cmd(HW::item<bool>& item, const handle_t& itf)
+ : rpc_cmd(item)
+ , m_itf(itf)
+{
+}
+
+bool
+delete_cmd::operator==(const delete_cmd& other) const
+{
+ return (m_itf == other.m_itf);
+}
+
+rc_t
+delete_cmd::issue(connection& con)
+{
+ msg_t req(con.ctx(), std::ref(*this));
+
+ auto& payload = req.get_request().get_payload();
+ payload.is_add = 0;
+ payload.recirc.sw_if_index = m_itf.value();
+ payload.recirc.epg_id = ~0;
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item.set(wait());
+
+ return rc_t::OK;
+}
+
+std::string
+delete_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "gbp-recirc-delete: " << m_hw_item.to_string() << " itf:" << m_itf;
+
+ return (s.str());
+}
+
+dump_cmd::dump_cmd()
+{
+}
+
+bool
+dump_cmd::operator==(const dump_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+dump_cmd::issue(connection& con)
+{
+ m_dump.reset(new msg_t(con.ctx(), std::ref(*this)));
+
+ VAPI_CALL(m_dump->execute());
+
+ wait();
+
+ return rc_t::OK;
+}
+
+std::string
+dump_cmd::to_string() const
+{
+ return ("gbp-recirc-dump");
+}
+
+}; // namespace gbp_recirc_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/src/vpp-api/vom/gbp_recirc_cmds.hpp b/src/vpp-api/vom/gbp_recirc_cmds.hpp
new file mode 100644
index 00000000000..fe17834ebf5
--- /dev/null
+++ b/src/vpp-api/vom/gbp_recirc_cmds.hpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_GBP_RECIRC_CMDS_H__
+#define __VOM_GBP_RECIRC_CMDS_H__
+
+#include "vom/dump_cmd.hpp"
+#include "vom/gbp_recirc.hpp"
+
+#include <vapi/gbp.api.vapi.hpp>
+
+namespace VOM {
+namespace gbp_recirc_cmds {
+
+/**
+* A command class that creates or updates the GBP recirc
+*/
+class create_cmd
+ : public rpc_cmd<HW::item<bool>, rc_t, vapi::Gbp_recirc_add_del>
+{
+public:
+ /**
+ * Constructor
+ */
+ create_cmd(HW::item<bool>& item,
+ const handle_t& itf,
+ bool is_ext,
+ epg_id_t epg_id);
+
+ /**
+ * Issue the command to VPP/HW
+ */
+ rc_t issue(connection& con);
+
+ /**
+ * convert to string format for debug purposes
+ */
+ std::string to_string() const;
+
+ /**
+ * Comparison operator - only used for UT
+ */
+ bool operator==(const create_cmd& i) const;
+
+private:
+ const handle_t m_itf;
+ bool m_is_ext;
+ const epg_id_t m_epg_id;
+};
+
+/**
+ * A cmd class that deletes a GBP recirc
+ */
+class delete_cmd
+ : public rpc_cmd<HW::item<bool>, rc_t, vapi::Gbp_recirc_add_del>
+{
+public:
+ /**
+ * Constructor
+ */
+ delete_cmd(HW::item<bool>& item, const handle_t& itf);
+
+ /**
+ * Issue the command to VPP/HW
+ */
+ rc_t issue(connection& con);
+
+ /**
+ * convert to string format for debug purposes
+ */
+ std::string to_string() const;
+
+ /**
+ * Comparison operator - only used for UT
+ */
+ bool operator==(const delete_cmd& i) const;
+
+private:
+ const handle_t m_itf;
+};
+
+/**
+ * A cmd class that Dumps all the GBP recircs
+ */
+class dump_cmd : public VOM::dump_cmd<vapi::Gbp_recirc_dump>
+{
+public:
+ /**
+ * Constructor
+ */
+ dump_cmd();
+ dump_cmd(const dump_cmd& d);
+
+ /**
+ * Issue the command to VPP/HW
+ */
+ rc_t issue(connection& con);
+ /**
+ * convert to string format for debug purposes
+ */
+ std::string to_string() const;
+
+ /**
+ * Comparison operator - only used for UT
+ */
+ bool operator==(const dump_cmd& i) const;
+
+private:
+ /**
+ * HW reutrn code
+ */
+ HW::item<bool> item;
+};
+}; // namespace gbp_enpoint_cms
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/src/vpp-api/vom/gbp_subnet.cpp b/src/vpp-api/vom/gbp_subnet.cpp
new file mode 100644
index 00000000000..84dbd227ed9
--- /dev/null
+++ b/src/vpp-api/vom/gbp_subnet.cpp
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/gbp_subnet.hpp"
+#include "vom/gbp_subnet_cmds.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+
+gbp_subnet::type_t::type_t(int v, const std::string s)
+ : enum_base<gbp_subnet::type_t>(v, s)
+{
+}
+
+const gbp_subnet::type_t gbp_subnet::type_t::INTERNAL(0, "internal");
+const gbp_subnet::type_t gbp_subnet::type_t::EXTERNAL(1, "external");
+
+singular_db<gbp_subnet::key_t, gbp_subnet> gbp_subnet::m_db;
+
+gbp_subnet::event_handler gbp_subnet::m_evh;
+
+gbp_subnet::gbp_subnet(const route_domain& rd, const route::prefix_t& prefix)
+ : m_hw(false)
+ , m_rd(rd.singular())
+ , m_prefix(prefix)
+ , m_type(type_t::INTERNAL)
+ , m_recirc(nullptr)
+ , m_epg(nullptr)
+{
+}
+
+gbp_subnet::gbp_subnet(const route_domain& rd,
+ const route::prefix_t& prefix,
+ const gbp_recirc& recirc,
+ const gbp_endpoint_group& epg)
+ : m_hw(false)
+ , m_rd(rd.singular())
+ , m_prefix(prefix)
+ , m_type(type_t::EXTERNAL)
+ , m_recirc(recirc.singular())
+ , m_epg(epg.singular())
+{
+}
+
+gbp_subnet::gbp_subnet(const gbp_subnet& o)
+ : m_hw(o.m_hw)
+ , m_rd(o.m_rd)
+ , m_prefix(o.m_prefix)
+ , m_type(o.m_type)
+ , m_recirc(o.m_recirc)
+ , m_epg(o.m_epg)
+{
+}
+
+gbp_subnet::~gbp_subnet()
+{
+ sweep();
+ m_db.release(key(), this);
+}
+
+const gbp_subnet::key_t
+gbp_subnet::key() const
+{
+ return (std::make_pair(m_rd->key(), m_prefix));
+}
+
+bool
+gbp_subnet::operator==(const gbp_subnet& gbpe) const
+{
+ return ((key() == gbpe.key()) && (m_recirc == gbpe.m_recirc) &&
+ (m_epg == gbpe.m_epg));
+}
+
+void
+gbp_subnet::sweep()
+{
+ if (m_hw) {
+ HW::enqueue(
+ new gbp_subnet_cmds::delete_cmd(m_hw, m_rd->table_id(), m_prefix));
+ }
+ HW::write();
+}
+
+void
+gbp_subnet::replay()
+{
+ if (m_hw) {
+ HW::enqueue(new gbp_subnet_cmds::create_cmd(
+ m_hw, m_rd->table_id(), m_prefix, (m_type == type_t::INTERNAL),
+ (m_recirc ? m_recirc->handle() : handle_t::INVALID),
+ (m_epg ? m_epg->id() : ~0)));
+ }
+}
+
+std::string
+gbp_subnet::to_string() const
+{
+ std::ostringstream s;
+ s << "gbp-subnet:[" << m_type.to_string() << ", " << m_rd->to_string() << ":"
+ << m_prefix.to_string();
+ if (m_recirc)
+ s << ", " << m_recirc->to_string();
+ if (m_epg)
+ s << ", " << m_epg->to_string();
+
+ s << "]";
+
+ return (s.str());
+}
+
+void
+gbp_subnet::update(const gbp_subnet& r)
+{
+ if (rc_t::OK != m_hw.rc()) {
+ HW::enqueue(new gbp_subnet_cmds::create_cmd(
+ m_hw, m_rd->table_id(), m_prefix, (m_type == type_t::INTERNAL),
+ (m_recirc ? m_recirc->handle() : handle_t::INVALID),
+ (m_epg ? m_epg->id() : ~0)));
+ }
+}
+
+std::shared_ptr<gbp_subnet>
+gbp_subnet::find_or_add(const gbp_subnet& temp)
+{
+ return (m_db.find_or_add(temp.key(), temp));
+}
+
+std::shared_ptr<gbp_subnet>
+gbp_subnet::find(const key_t& k)
+{
+ return (m_db.find(k));
+}
+
+std::shared_ptr<gbp_subnet>
+gbp_subnet::singular() const
+{
+ return find_or_add(*this);
+}
+
+void
+gbp_subnet::dump(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+
+gbp_subnet::event_handler::event_handler()
+{
+ OM::register_listener(this);
+ inspect::register_handler({ "gbp-subnet" }, "GBP Subnets", this);
+}
+
+void
+gbp_subnet::event_handler::handle_replay()
+{
+ m_db.replay();
+}
+
+void
+gbp_subnet::event_handler::handle_populate(const client_db::key_t& key)
+{
+ std::shared_ptr<gbp_subnet_cmds::dump_cmd> cmd =
+ std::make_shared<gbp_subnet_cmds::dump_cmd>();
+
+ HW::enqueue(cmd);
+ HW::write();
+
+ for (auto& record : *cmd) {
+ auto& payload = record.get_payload();
+
+ route::prefix_t pfx(payload.subnet.is_ip6, payload.subnet.address,
+ payload.subnet.address_length);
+ std::shared_ptr<route_domain> rd =
+ route_domain::find(payload.subnet.table_id);
+
+ if (rd) {
+ if (payload.subnet.is_internal) {
+ gbp_subnet gs(*rd, pfx);
+ OM::commit(key, gs);
+ VOM_LOG(log_level_t::DEBUG) << "read: " << gs.to_string();
+ } else {
+ std::shared_ptr<interface> itf =
+ interface::find(payload.subnet.sw_if_index);
+ std::shared_ptr<gbp_endpoint_group> epg =
+ gbp_endpoint_group::find(payload.subnet.epg_id);
+
+ if (itf && epg) {
+ std::shared_ptr<gbp_recirc> recirc = gbp_recirc::find(itf->key());
+
+ if (recirc) {
+ gbp_subnet gs(*rd, pfx, *recirc, *epg);
+ OM::commit(key, gs);
+ VOM_LOG(log_level_t::DEBUG) << "read: " << gs.to_string();
+ }
+ }
+ }
+ }
+ }
+}
+
+dependency_t
+gbp_subnet::event_handler::order() const
+{
+ return (dependency_t::ENTRY);
+}
+
+void
+gbp_subnet::event_handler::show(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+} // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/src/vpp-api/vom/gbp_subnet.hpp b/src/vpp-api/vom/gbp_subnet.hpp
new file mode 100644
index 00000000000..9c9166ec018
--- /dev/null
+++ b/src/vpp-api/vom/gbp_subnet.hpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_GBP_SUBNET_H__
+#define __VOM_GBP_SUBNET_H__
+
+#include "vom/gbp_endpoint_group.hpp"
+#include "vom/gbp_recirc.hpp"
+#include "vom/route.hpp"
+#include "vom/singular_db.hpp"
+
+namespace VOM {
+/**
+ * A GBP Enpoint (i.e. a VM)
+ */
+class gbp_subnet : public object_base
+{
+public:
+ /**
+ * The key for a GBP subnet; table and prefix
+ */
+ typedef std::pair<route_domain::key_t, route::prefix_t> key_t;
+
+ /**
+ * Construct an internal GBP subnet
+ */
+ gbp_subnet(const route_domain& rd, const route::prefix_t& prefix);
+
+ /**
+ * Construct an external GBP subnet
+ */
+ gbp_subnet(const route_domain& rd,
+ const route::prefix_t& prefix,
+ const gbp_recirc& recirc,
+ const gbp_endpoint_group& epg);
+
+ /**
+ * Copy Construct
+ */
+ gbp_subnet(const gbp_subnet& r);
+
+ /**
+ * Destructor
+ */
+ ~gbp_subnet();
+
+ /**
+ * Return the object's key
+ */
+ const key_t key() const;
+
+ /**
+ * comparison operator
+ */
+ bool operator==(const gbp_subnet& bdae) const;
+
+ /**
+ * Return the matching 'singular instance'
+ */
+ std::shared_ptr<gbp_subnet> singular() const;
+
+ /**
+ * Find the instnace of the bridge_domain domain in the OM
+ */
+ static std::shared_ptr<gbp_subnet> find(const key_t& k);
+
+ /**
+ * Dump all bridge_domain-doamin into the stream provided
+ */
+ static void dump(std::ostream& os);
+
+ /**
+ * replay the object to create it in hardware
+ */
+ void replay(void);
+
+ /**
+ * Convert to string for debugging
+ */
+ std::string to_string() const;
+
+private:
+ struct type_t : public enum_base<type_t>
+ {
+ /**
+ * Internal subnet is reachable through the source EPG's
+ * uplink interface.
+ */
+ const static type_t INTERNAL;
+
+ /**
+ * External subnet requires NAT translation before egress.
+ */
+ const static type_t EXTERNAL;
+
+ private:
+ type_t(int v, const std::string s);
+ };
+
+ /**
+ * Class definition for listeners to OM events
+ */
+ class event_handler : public OM::listener, public inspect::command_handler
+ {
+ public:
+ event_handler();
+ virtual ~event_handler() = default;
+
+ /**
+ * Handle a populate event
+ */
+ void handle_populate(const client_db::key_t& key);
+
+ /**
+ * Handle a replay event
+ */
+ void handle_replay();
+
+ /**
+ * Show the object in the Singular DB
+ */
+ void show(std::ostream& os);
+
+ /**
+ * Get the sortable Id of the listener
+ */
+ dependency_t order() const;
+ };
+
+ /**
+ * event_handler to register with OM
+ */
+ static event_handler m_evh;
+
+ /**
+ * Commit the acculmulated changes into VPP. i.e. to a 'HW" write.
+ */
+ void update(const gbp_subnet& obj);
+
+ /**
+ * Find or add the instnace of the bridge_domain domain in the OM
+ */
+ static std::shared_ptr<gbp_subnet> find_or_add(const gbp_subnet& temp);
+
+ /*
+ * It's the VPPHW class that updates the objects in HW
+ */
+ friend class OM;
+
+ /**
+ * It's the singular_db class that calls replay()
+ */
+ friend class singular_db<key_t, gbp_subnet>;
+
+ /**
+ * Sweep/reap the object if still stale
+ */
+ void sweep(void);
+
+ /**
+ * HW configuration for the result of creating the subnet
+ */
+ HW::item<bool> m_hw;
+
+ /**
+ * the route domain the prefix is in
+ */
+ std::shared_ptr<route_domain> m_rd;
+
+ /**
+ * prefix to match
+ */
+ const route::prefix_t m_prefix;
+
+ /*
+ * Subnet type
+ */
+ const type_t m_type;
+
+ /**
+ * The interface the prefix is reachable through
+ */
+ std::shared_ptr<gbp_recirc> m_recirc;
+
+ /**
+ * The EPG the subnet is in
+ */
+ std::shared_ptr<gbp_endpoint_group> m_epg;
+
+ /**
+ * A map of all bridge_domains
+ */
+ static singular_db<key_t, gbp_subnet> m_db;
+};
+
+}; // namespace
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/src/vpp-api/vom/gbp_subnet_cmds.cpp b/src/vpp-api/vom/gbp_subnet_cmds.cpp
new file mode 100644
index 00000000000..d087e5c67d8
--- /dev/null
+++ b/src/vpp-api/vom/gbp_subnet_cmds.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/gbp_subnet_cmds.hpp"
+
+namespace VOM {
+namespace gbp_subnet_cmds {
+
+create_cmd::create_cmd(HW::item<bool>& item,
+ route::table_id_t rd,
+ const route::prefix_t& prefix,
+ bool internal,
+ const handle_t& itf,
+ epg_id_t epg_id)
+ : rpc_cmd(item)
+ , m_rd(rd)
+ , m_prefix(prefix)
+ , m_internal(internal)
+ , m_itf(itf)
+ , m_epg_id(epg_id)
+{
+}
+
+bool
+create_cmd::operator==(const create_cmd& other) const
+{
+ return ((m_itf == other.m_itf) && (m_rd == other.m_rd) &&
+ (m_prefix == other.m_prefix) && (m_itf == other.m_itf) &&
+ (m_epg_id == other.m_epg_id));
+}
+
+rc_t
+create_cmd::issue(connection& con)
+{
+ msg_t req(con.ctx(), std::ref(*this));
+
+ auto& payload = req.get_request().get_payload();
+ payload.is_add = 1;
+ payload.subnet.is_internal = m_internal;
+ payload.subnet.table_id = m_rd;
+ payload.subnet.sw_if_index = m_itf.value();
+ payload.subnet.epg_id = m_epg_id;
+ m_prefix.to_vpp(&payload.subnet.is_ip6, payload.subnet.address,
+ &payload.subnet.address_length);
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item.set(wait());
+
+ return rc_t::OK;
+}
+
+std::string
+create_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "gbp-subnet-create: " << m_hw_item.to_string()
+ << "internal:" << m_internal << ", " << m_rd << ":" << m_prefix.to_string()
+ << " itf:" << m_itf << " epg-id:" << m_epg_id;
+
+ return (s.str());
+}
+
+delete_cmd::delete_cmd(HW::item<bool>& item,
+ route::table_id_t rd,
+ const route::prefix_t& prefix)
+ : rpc_cmd(item)
+ , m_rd(rd)
+ , m_prefix(prefix)
+{
+}
+
+bool
+delete_cmd::operator==(const delete_cmd& other) const
+{
+ return ((m_rd == other.m_rd) && (m_prefix == other.m_prefix));
+}
+
+rc_t
+delete_cmd::issue(connection& con)
+{
+ msg_t req(con.ctx(), std::ref(*this));
+
+ auto& payload = req.get_request().get_payload();
+ payload.is_add = 0;
+ payload.subnet.table_id = m_rd;
+ m_prefix.to_vpp(&payload.subnet.is_ip6, payload.subnet.address,
+ &payload.subnet.address_length);
+
+ payload.subnet.is_internal = 0;
+ payload.subnet.sw_if_index = ~0;
+ payload.subnet.epg_id = ~0;
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item.set(wait());
+
+ return rc_t::OK;
+}
+
+std::string
+delete_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "gbp-subnet-delete: " << m_hw_item.to_string() << ", " << m_rd << ":"
+ << m_prefix.to_string();
+
+ return (s.str());
+}
+
+dump_cmd::dump_cmd()
+{
+}
+
+bool
+dump_cmd::operator==(const dump_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+dump_cmd::issue(connection& con)
+{
+ m_dump.reset(new msg_t(con.ctx(), std::ref(*this)));
+
+ VAPI_CALL(m_dump->execute());
+
+ wait();
+
+ return rc_t::OK;
+}
+
+std::string
+dump_cmd::to_string() const
+{
+ return ("gbp-subnet-dump");
+}
+
+}; // namespace gbp_subnet_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/src/vpp-api/vom/gbp_subnet_cmds.hpp b/src/vpp-api/vom/gbp_subnet_cmds.hpp
new file mode 100644
index 00000000000..3dbc8db2359
--- /dev/null
+++ b/src/vpp-api/vom/gbp_subnet_cmds.hpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_GBP_SUBNET_CMDS_H__
+#define __VOM_GBP_SUBNET_CMDS_H__
+
+#include "vom/dump_cmd.hpp"
+#include "vom/gbp_subnet.hpp"
+
+#include <vapi/gbp.api.vapi.hpp>
+
+namespace VOM {
+namespace gbp_subnet_cmds {
+
+/**
+* A command class that creates or updates the GBP subnet
+*/
+class create_cmd
+ : public rpc_cmd<HW::item<bool>, rc_t, vapi::Gbp_subnet_add_del>
+{
+public:
+ /**
+ * Constructor
+ */
+ create_cmd(HW::item<bool>& item,
+ route::table_id_t rd,
+ const route::prefix_t& prefix,
+ bool internal,
+ const handle_t& itf,
+ epg_id_t epg_id);
+
+ /**
+ * Issue the command to VPP/HW
+ */
+ rc_t issue(connection& con);
+
+ /**
+ * convert to string format for debug purposes
+ */
+ std::string to_string() const;
+
+ /**
+ * Comparison operator - only used for UT
+ */
+ bool operator==(const create_cmd& i) const;
+
+private:
+ const route::table_id_t m_rd;
+ const route::prefix_t m_prefix;
+ const bool m_internal;
+ const handle_t m_itf;
+ const epg_id_t m_epg_id;
+};
+
+/**
+ * A cmd class that deletes a GBP subnet
+ */
+class delete_cmd
+ : public rpc_cmd<HW::item<bool>, rc_t, vapi::Gbp_subnet_add_del>
+{
+public:
+ /**
+ * Constructor
+ */
+ delete_cmd(HW::item<bool>& item,
+ route::table_id_t rd,
+ const route::prefix_t& prefix);
+
+ /**
+ * Issue the command to VPP/HW
+ */
+ rc_t issue(connection& con);
+
+ /**
+ * convert to string format for debug purposes
+ */
+ std::string to_string() const;
+
+ /**
+ * Comparison operator - only used for UT
+ */
+ bool operator==(const delete_cmd& i) const;
+
+private:
+ const route::table_id_t m_rd;
+ const route::prefix_t m_prefix;
+};
+
+/**
+ * A cmd class that Dumps all the GBP subnets
+ */
+class dump_cmd : public VOM::dump_cmd<vapi::Gbp_subnet_dump>
+{
+public:
+ /**
+ * Constructor
+ */
+ dump_cmd();
+ dump_cmd(const dump_cmd& d);
+
+ /**
+ * Issue the command to VPP/HW
+ */
+ rc_t issue(connection& con);
+ /**
+ * convert to string format for debug purposes
+ */
+ std::string to_string() const;
+
+ /**
+ * Comparison operator - only used for UT
+ */
+ bool operator==(const dump_cmd& i) const;
+
+private:
+ /**
+ * HW reutrn code
+ */
+ HW::item<bool> item;
+};
+}; // namespace gbp_enpoint_cms
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/src/vpp-api/vom/interface.cpp b/src/vpp-api/vom/interface.cpp
index e9b7a1a1f9b..6faf3491e6f 100644
--- a/src/vpp-api/vom/interface.cpp
+++ b/src/vpp-api/vom/interface.cpp
@@ -275,9 +275,7 @@ interface::key() const
std::queue<cmd*>&
interface::mk_create_cmd(std::queue<cmd*>& q)
{
- if (type_t::LOOPBACK == m_type) {
- q.push(new interface_cmds::loopback_create_cmd(m_hdl, m_name));
- } else if (type_t::BVI == m_type) {
+ if ((type_t::LOOPBACK == m_type) || (type_t::BVI == m_type)) {
q.push(new interface_cmds::loopback_create_cmd(m_hdl, m_name));
q.push(new interface_cmds::set_tag(m_hdl, m_name));
/*
@@ -516,8 +514,12 @@ interface::event_handler::handle_populate(const client_db::key_t& key)
HW::write();
for (auto& itf_record : *cmd) {
- std::shared_ptr<interface> itf =
- interface_factory::new_interface(itf_record.get_payload());
+ auto payload = itf_record.get_payload();
+ VOM_LOG(log_level_t::DEBUG) << "dump: [" << payload.sw_if_index
+ << " name:" << (char*)payload.interface_name
+ << " tag:" << (char*)payload.tag << "]";
+
+ std::shared_ptr<interface> itf = interface_factory::new_interface(payload);
if (itf && interface::type_t::LOCAL != itf->type()) {
VOM_LOG(log_level_t::DEBUG) << "dump: " << itf->to_string();
diff --git a/src/vpp-api/vom/interface_types.cpp b/src/vpp-api/vom/interface_types.cpp
index 911282d51b1..139bdd52f9b 100644
--- a/src/vpp-api/vom/interface_types.cpp
+++ b/src/vpp-api/vom/interface_types.cpp
@@ -51,7 +51,8 @@ interface::type_t::from_string(const std::string& str)
return interface::type_t::ETHERNET;
} else if (str.find("vxlan") != std::string::npos) {
return interface::type_t::VXLAN;
- } else if (str.find("loop") != std::string::npos) {
+ } else if ((str.find("loop") != std::string::npos) ||
+ (str.find("recirc") != std::string::npos)) {
return interface::type_t::LOOPBACK;
} else if (str.find("host-") != std::string::npos) {
return interface::type_t::AFPACKET;
diff --git a/src/vpp-api/vom/nat_binding_cmds.hpp b/src/vpp-api/vom/nat_binding_cmds.hpp
index bb9404872eb..1b51192a73c 100644
--- a/src/vpp-api/vom/nat_binding_cmds.hpp
+++ b/src/vpp-api/vom/nat_binding_cmds.hpp
@@ -255,6 +255,238 @@ private:
HW::item<bool> item;
};
+/////
+/**
+* A functor class that binds a NAT configuration to an input interface
+*/
+class bind_66_input_cmd
+ : public rpc_cmd<HW::item<bool>, rc_t, vapi::Nat66_add_del_interface>
+{
+public:
+ /**
+ * Constructor
+ */
+ bind_66_input_cmd(HW::item<bool>& item,
+ const handle_t& itf,
+ const nat_binding::zone_t& zone);
+
+ /**
+ * Issue the command to VPP/HW
+ */
+ rc_t issue(connection& con);
+ /**
+ * convert to string format for debug purposes
+ */
+ std::string to_string() const;
+
+ /**
+ * Comparison operator - only used for UT
+ */
+ bool operator==(const bind_66_input_cmd& i) const;
+
+private:
+ /**
+ * The interface to bind
+ */
+ const handle_t m_itf;
+
+ /**
+ * The zone the interface is in
+ */
+ const nat_binding::zone_t m_zone;
+};
+
+/**
+ * A cmd class that unbinds a NAT configuration from an input interface
+ */
+class unbind_66_input_cmd
+ : public rpc_cmd<HW::item<bool>, rc_t, vapi::Nat66_add_del_interface>
+{
+public:
+ /**
+ * Constructor
+ */
+ unbind_66_input_cmd(HW::item<bool>& item,
+ const handle_t& itf,
+ const nat_binding::zone_t& zone);
+
+ /**
+ * Issue the command to VPP/HW
+ */
+ rc_t issue(connection& con);
+ /**
+ * convert to string format for debug purposes
+ */
+ std::string to_string() const;
+
+ /**
+ * Comparison operator - only used for UT
+ */
+ bool operator==(const unbind_66_input_cmd& i) const;
+
+private:
+ /**
+ * The interface to bind
+ */
+ const handle_t m_itf;
+
+ /**
+ * The zone the interface is in
+ */
+ const nat_binding::zone_t m_zone;
+};
+
+/**
+ * A functor class that binds a NAT configuration to an output interface
+ */
+/* class bind_66_output_cmd */
+/* : public rpc_cmd<HW::item<bool>, */
+/* rc_t, */
+/* vapi::Nat66_interface_add_del_output_feature> */
+/* { */
+/* public: */
+/* /\** */
+/* * Constructor */
+/* *\/ */
+/* bind_66_output_cmd(HW::item<bool>& item, */
+/* const handle_t& itf, */
+/* const nat_binding::zone_t& zone); */
+
+/* /\** */
+/* * Issue the command to VPP/HW */
+/* *\/ */
+/* rc_t issue(connection& con); */
+/* /\** */
+/* * convert to string format for debug purposes */
+/* *\/ */
+/* std::string to_string() const; */
+
+/* /\** */
+/* * Comparison operator - only used for UT */
+/* *\/ */
+/* bool operator==(const bind_66_output_cmd& i) const; */
+
+/* private: */
+/* /\** */
+/* * The interface to bind */
+/* *\/ */
+/* const handle_t m_itf; */
+
+/* /\** */
+/* * The zone the interface is in */
+/* *\/ */
+/* const nat_binding::zone_t m_zone; */
+/* }; */
+
+/* /\** */
+/* * A cmd class that unbinds a NAT configuration from an output interface */
+/* *\/ */
+/* class unbind_66_output_cmd */
+/* : public rpc_cmd<HW::item<bool>, */
+/* rc_t, */
+/* vapi::Nat66_interface_add_del_output_feature> */
+/* { */
+/* public: */
+/* /\** */
+/* * Constructor */
+/* *\/ */
+/* unbind_66_output_cmd(HW::item<bool>& item, */
+/* const handle_t& itf, */
+/* const nat_binding::zone_t& zone); */
+
+/* /\** */
+/* * Issue the command to VPP/HW */
+/* *\/ */
+/* rc_t issue(connection& con); */
+/* /\** */
+/* * convert to string format for debug purposes */
+/* *\/ */
+/* std::string to_string() const; */
+
+/* /\** */
+/* * Comparison operator - only used for UT */
+/* *\/ */
+/* bool operator==(const unbind_66_output_cmd& i) const; */
+
+/* private: */
+/* /\** */
+/* * The interface to bind */
+/* *\/ */
+/* const handle_t m_itf; */
+
+/* /\** */
+/* * The zone the interface is in */
+/* *\/ */
+/* const nat_binding::zone_t m_zone; */
+/* }; */
+
+/**
+ * A cmd class that Dumps all the nat_statics
+ */
+class dump_input_66_cmd : public dump_cmd<vapi::Nat66_interface_dump>
+{
+public:
+ /**
+ * Constructor
+ */
+ dump_input_66_cmd();
+ dump_input_66_cmd(const dump_input_66_cmd& d);
+
+ /**
+ * Issue the command to VPP/HW
+ */
+ rc_t issue(connection& con);
+ /**
+ * convert to string format for debug purposes
+ */
+ std::string to_string() const;
+
+ /**
+ * Comparison operator - only used for UT
+ */
+ bool operator==(const dump_input_66_cmd& i) const;
+
+private:
+ /**
+ * HW reutrn code
+ */
+ HW::item<bool> item;
+};
+
+/**
+ * A cmd class that Dumps all the nat_statics
+ */
+/* class dump_output_66_cmd */
+/* : public dump_cmd<vapi::Nat66_interface_output_feature_dump> */
+/* { */
+/* public: */
+/* /\** */
+/* * Constructor */
+/* *\/ */
+/* dump_output_66_cmd(); */
+/* dump_output_66_cmd(const dump_output_66_cmd& d); */
+
+/* /\** */
+/* * Issue the command to VPP/HW */
+/* *\/ */
+/* rc_t issue(connection& con); */
+/* /\** */
+/* * convert to string format for debug purposes */
+/* *\/ */
+/* std::string to_string() const; */
+
+/* /\** */
+/* * Comparison operator - only used for UT */
+/* *\/ */
+/* bool operator==(const dump_output_66_cmd& i) const; */
+
+/* private: */
+/* /\** */
+/* * HW reutrn code */
+/* *\/ */
+/* HW::item<bool> item; */
+/* }; */
+
}; // namespace nat_binding_cmds
}; // namespace VOM
diff --git a/src/vpp-api/vom/nat_static.cpp b/src/vpp-api/vom/nat_static.cpp
index 3185b56e228..bf8573d333e 100644
--- a/src/vpp-api/vom/nat_static.cpp
+++ b/src/vpp-api/vom/nat_static.cpp
@@ -22,7 +22,7 @@ singular_db<nat_static::key_t, nat_static> nat_static::m_db;
nat_static::event_handler nat_static::m_evh;
nat_static::nat_static(const boost::asio::ip::address& inside,
- const boost::asio::ip::address_v4& outside)
+ const boost::asio::ip::address& outside)
: m_hw(false)
, m_rd(route_domain::get_default())
, m_inside(inside)
@@ -32,7 +32,7 @@ nat_static::nat_static(const boost::asio::ip::address& inside,
nat_static::nat_static(const route_domain& rd,
const boost::asio::ip::address& inside,
- const boost::asio::ip::address_v4& outside)
+ const boost::asio::ip::address& outside)
: m_hw(false)
, m_rd(rd.singular())
, m_inside(inside)
@@ -74,7 +74,10 @@ nat_static::sweep()
if (m_hw) {
if (m_inside.is_v4()) {
HW::enqueue(new nat_static_cmds::delete_44_cmd(
- m_hw, m_rd->table_id(), m_inside.to_v4(), m_outside));
+ m_hw, m_rd->table_id(), m_inside.to_v4(), m_outside.to_v4()));
+ } else {
+ HW::enqueue(new nat_static_cmds::delete_66_cmd(
+ m_hw, m_rd->table_id(), m_inside.to_v6(), m_outside.to_v6()));
}
}
HW::write();
@@ -86,7 +89,10 @@ nat_static::replay()
if (m_hw) {
if (m_inside.is_v4()) {
HW::enqueue(new nat_static_cmds::create_44_cmd(
- m_hw, m_rd->table_id(), m_inside.to_v4(), m_outside));
+ m_hw, m_rd->table_id(), m_inside.to_v4(), m_outside.to_v4()));
+ } else {
+ HW::enqueue(new nat_static_cmds::create_66_cmd(
+ m_hw, m_rd->table_id(), m_inside.to_v6(), m_outside.to_v6()));
}
}
}
@@ -100,7 +106,10 @@ nat_static::update(const nat_static& r)
if (rc_t::OK != m_hw.rc()) {
if (m_inside.is_v4()) {
HW::enqueue(new nat_static_cmds::create_44_cmd(
- m_hw, m_rd->table_id(), m_inside.to_v4(), m_outside));
+ m_hw, m_rd->table_id(), m_inside.to_v4(), m_outside.to_v4()));
+ } else {
+ HW::enqueue(new nat_static_cmds::create_66_cmd(
+ m_hw, m_rd->table_id(), m_inside.to_v6(), m_outside.to_v6()));
}
}
}
@@ -158,20 +167,43 @@ nat_static::event_handler::handle_populate(const client_db::key_t& key)
/*
* dump VPP current states
*/
- std::shared_ptr<nat_static_cmds::dump_44_cmd> cmd =
+ std::shared_ptr<nat_static_cmds::dump_44_cmd> cmd44 =
std::make_shared<nat_static_cmds::dump_44_cmd>();
- HW::enqueue(cmd);
+ HW::enqueue(cmd44);
HW::write();
- for (auto& record : *cmd) {
+ for (auto& record : *cmd44) {
auto& payload = record.get_payload();
boost::asio::ip::address inside = from_bytes(0, payload.local_ip_address);
boost::asio::ip::address outside =
from_bytes(0, payload.external_ip_address);
- nat_static n(route_domain(payload.vrf_id), inside, outside.to_v4());
+ nat_static n(route_domain(payload.vrf_id), inside, outside);
+
+ /*
+ * Write each of the discovered mappings into the OM,
+ * but disable the HW Command q whilst we do, so that no
+ * commands are sent to VPP
+ */
+ OM::commit(key, n);
+ }
+
+ std::shared_ptr<nat_static_cmds::dump_66_cmd> cmd66 =
+ std::make_shared<nat_static_cmds::dump_66_cmd>();
+
+ HW::enqueue(cmd66);
+ HW::write();
+
+ for (auto& record : *cmd66) {
+
+ auto& payload = record.get_payload();
+
+ boost::asio::ip::address inside = from_bytes(1, payload.local_ip_address);
+ boost::asio::ip::address outside =
+ from_bytes(1, payload.external_ip_address);
+ nat_static n(route_domain(payload.vrf_id), inside, outside);
/*
* Write each of the discovered mappings into the OM,
diff --git a/src/vpp-api/vom/nat_static.hpp b/src/vpp-api/vom/nat_static.hpp
index 3b0d2d06ccc..2dcadb3c904 100644
--- a/src/vpp-api/vom/nat_static.hpp
+++ b/src/vpp-api/vom/nat_static.hpp
@@ -39,7 +39,7 @@ public:
* table
*/
nat_static(const boost::asio::ip::address& inside,
- const boost::asio::ip::address_v4& outside);
+ const boost::asio::ip::address& outside);
/**
* Construct an NAT Static binding with the outside address in
@@ -47,7 +47,7 @@ public:
*/
nat_static(const route_domain& rd,
const boost::asio::ip::address& inside,
- const boost::asio::ip::address_v4& outside);
+ const boost::asio::ip::address& outside);
/**
* Copy Construct
@@ -171,9 +171,9 @@ private:
const boost::asio::ip::address m_inside;
/**
- * The 'outside' IP address - always v4
+ * The 'outside' IP address
*/
- const boost::asio::ip::address_v4 m_outside;
+ const boost::asio::ip::address m_outside;
/**
* A map of all NAT statics
diff --git a/src/vpp-api/vom/nat_static_cmds.cpp b/src/vpp-api/vom/nat_static_cmds.cpp
index facc7c6db8b..a80e47416ea 100644
--- a/src/vpp-api/vom/nat_static_cmds.cpp
+++ b/src/vpp-api/vom/nat_static_cmds.cpp
@@ -144,6 +144,125 @@ dump_44_cmd::issue(connection& con)
std::string
dump_44_cmd::to_string() const
{
+ return ("nat-44-static-dump");
+}
+
+create_66_cmd::create_66_cmd(HW::item<bool>& item,
+ route::table_id_t id,
+ const boost::asio::ip::address_v6& inside,
+ const boost::asio::ip::address_v6& outside)
+ : rpc_cmd(item)
+ , m_id(id)
+ , m_inside(inside)
+ , m_outside(outside)
+{
+}
+
+bool
+create_66_cmd::operator==(const create_66_cmd& other) const
+{
+ return ((m_id == other.m_id) && (m_inside == other.m_inside) &&
+ (m_outside == other.m_outside));
+}
+
+rc_t
+create_66_cmd::issue(connection& con)
+{
+ msg_t req(con.ctx(), std::ref(*this));
+
+ auto& payload = req.get_request().get_payload();
+ payload.is_add = 1;
+ payload.vrf_id = m_id;
+ to_bytes(m_inside, payload.local_ip_address);
+ to_bytes(m_outside, payload.external_ip_address);
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item.set(wait());
+
+ return rc_t::OK;
+}
+
+std::string
+create_66_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "nat-66-static-create: " << m_hw_item.to_string() << " table:" << m_id
+ << " inside:" << m_inside.to_string()
+ << " outside:" << m_outside.to_string();
+
+ return (s.str());
+}
+
+delete_66_cmd::delete_66_cmd(HW::item<bool>& item,
+ route::table_id_t id,
+ const boost::asio::ip::address_v6& inside,
+ const boost::asio::ip::address_v6& outside)
+ : rpc_cmd(item)
+ , m_id(id)
+ , m_inside(inside)
+ , m_outside(outside)
+{
+}
+
+bool
+delete_66_cmd::operator==(const delete_66_cmd& other) const
+{
+ return ((m_id == other.m_id) && (m_inside == other.m_inside) &&
+ (m_outside == other.m_outside));
+}
+
+rc_t
+delete_66_cmd::issue(connection& con)
+{
+ msg_t req(con.ctx(), std::ref(*this));
+
+ auto& payload = req.get_request().get_payload();
+ payload.is_add = 0;
+ payload.vrf_id = m_id;
+ to_bytes(m_inside, payload.local_ip_address);
+ to_bytes(m_outside, payload.external_ip_address);
+
+ VAPI_CALL(req.execute());
+
+ wait();
+ m_hw_item.set(rc_t::NOOP);
+
+ return rc_t::OK;
+}
+
+std::string
+delete_66_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "nat-66-static-delete: " << m_hw_item.to_string() << " table:" << m_id
+ << " inside:" << m_inside.to_string()
+ << " outside:" << m_outside.to_string();
+
+ return (s.str());
+}
+
+bool
+dump_66_cmd::operator==(const dump_66_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+dump_66_cmd::issue(connection& con)
+{
+ m_dump.reset(new msg_t(con.ctx(), std::ref(*this)));
+
+ VAPI_CALL(m_dump->execute());
+
+ wait();
+
+ return rc_t::OK;
+}
+
+std::string
+dump_66_cmd::to_string() const
+{
return ("nat-static-dump");
}
diff --git a/src/vpp-api/vom/nat_static_cmds.hpp b/src/vpp-api/vom/nat_static_cmds.hpp
index a4adcef19b5..95061cae1ad 100644
--- a/src/vpp-api/vom/nat_static_cmds.hpp
+++ b/src/vpp-api/vom/nat_static_cmds.hpp
@@ -129,6 +129,111 @@ private:
HW::item<bool> item;
};
+/**
+ * A command class that creates NAT 66 static mapping
+ */
+class create_66_cmd
+ : public rpc_cmd<HW::item<bool>, rc_t, vapi::Nat66_add_del_static_mapping>
+{
+public:
+ /**
+ * Constructor
+ */
+ create_66_cmd(HW::item<bool>& item,
+ route::table_id_t id,
+ const boost::asio::ip::address_v6& inside,
+ const boost::asio::ip::address_v6& outside);
+
+ /**
+ * Issue the command to VPP/HW
+ */
+ rc_t issue(connection& con);
+
+ /**
+ * convert to string format for debug purposes
+ */
+ std::string to_string() const;
+
+ /**
+ * Comparison operator - only used for UT
+ */
+ bool operator==(const create_66_cmd& i) const;
+
+private:
+ route::table_id_t m_id;
+ const boost::asio::ip::address_v6 m_inside;
+ const boost::asio::ip::address_v6 m_outside;
+};
+
+/**
+ * A cmd class that deletes a NAT 66 static mapping
+ */
+class delete_66_cmd
+ : public rpc_cmd<HW::item<bool>, rc_t, vapi::Nat66_add_del_static_mapping>
+{
+public:
+ /**
+ * Constructor
+ */
+ delete_66_cmd(HW::item<bool>& item,
+ route::table_id_t id,
+ const boost::asio::ip::address_v6& inside,
+ const boost::asio::ip::address_v6& outside);
+
+ /**
+ * Issue the command to VPP/HW
+ */
+ rc_t issue(connection& con);
+
+ /**
+ * convert to string format for debug purposes
+ */
+ std::string to_string() const;
+
+ /**
+ * Comparison operator - only used for UT
+ */
+ bool operator==(const delete_66_cmd& i) const;
+
+private:
+ route::table_id_t m_id;
+ const boost::asio::ip::address_v6 m_inside;
+ const boost::asio::ip::address_v6 m_outside;
+};
+
+/**
+ * A cmd class that Dumps all the nat_statics
+ */
+class dump_66_cmd : public dump_cmd<vapi::Nat66_static_mapping_dump>
+{
+public:
+ /**
+ * Constructor
+ */
+ dump_66_cmd() = default;
+ ~dump_66_cmd() = default;
+
+ /**
+ * Issue the command to VPP/HW
+ */
+ rc_t issue(connection& con);
+ /**
+ * convert to string format for debug purposes
+ */
+ std::string to_string() const;
+
+ /**
+ * Comparison operator - only used for UT
+ */
+ bool operator==(const dump_66_cmd& i) const;
+
+private:
+ /**
+ * HW reutrn code
+ */
+ HW::item<bool> item;
+};
+
}; // namespace nat_static_cmds
}; // namespace vom
diff --git a/src/vpp-api/vom/route.cpp b/src/vpp-api/vom/route.cpp
index 247afa008a7..ec56c44a0d7 100644
--- a/src/vpp-api/vom/route.cpp
+++ b/src/vpp-api/vom/route.cpp
@@ -508,7 +508,7 @@ ip_route::event_handler::handle_populate(const client_db::key_t& key)
dependency_t
ip_route::event_handler::order() const
{
- return (dependency_t::ENTRY);
+ return (dependency_t::TABLE);
}
void
diff --git a/src/vpp-api/vom/types.cpp b/src/vpp-api/vom/types.cpp
index 44e0dd08f98..c6093ebd15d 100644
--- a/src/vpp-api/vom/types.cpp
+++ b/src/vpp-api/vom/types.cpp
@@ -28,9 +28,6 @@ rc_t::rc_t(int v, const std::string s)
: enum_base<rc_t>(v, s)
{
}
-rc_t::~rc_t()
-{
-}
const rc_t&
rc_t::from_vpp_retval(int32_t rv)
diff --git a/src/vpp-api/vom/types.hpp b/src/vpp-api/vom/types.hpp
index 302e5ee47b1..53654c51ac5 100644
--- a/src/vpp-api/vom/types.hpp
+++ b/src/vpp-api/vom/types.hpp
@@ -94,7 +94,7 @@ struct rc_t : public enum_base<rc_t>
/**
* Destructor
*/
- ~rc_t();
+ ~rc_t() = default;
/**
* The value un-set
diff --git a/test/ext/vom_test.cpp b/test/ext/vom_test.cpp
index 29738e2437e..6958eb117a4 100644
--- a/test/ext/vom_test.cpp
+++ b/test/ext/vom_test.cpp
@@ -892,7 +892,11 @@ BOOST_AUTO_TEST_CASE(test_bridge) {
bridge_domain bd1(33);
HW::item<uint32_t> hw_bd(33, rc_t::OK);
- ADD_EXPECT(bridge_domain_cmds::create_cmd(hw_bd, bridge_domain::learning_mode_t::ON));
+ ADD_EXPECT(bridge_domain_cmds::create_cmd(hw_bd,
+ bridge_domain::learning_mode_t::ON,
+ bridge_domain::arp_term_mode_t::ON,
+ bridge_domain::flood_mode_t::ON,
+ bridge_domain::mac_age_mode_t::OFF));
TRY_CHECK_RC(OM::write(franz, bd1));
@@ -973,7 +977,11 @@ BOOST_AUTO_TEST_CASE(test_bridge) {
bridge_domain bd2(99);
HW::item<uint32_t> hw_bd2(99, rc_t::OK);
- ADD_EXPECT(bridge_domain_cmds::create_cmd(hw_bd2, bridge_domain::learning_mode_t::ON));
+ ADD_EXPECT(bridge_domain_cmds::create_cmd(hw_bd2,
+ bridge_domain::learning_mode_t::ON,
+ bridge_domain::arp_term_mode_t::ON,
+ bridge_domain::flood_mode_t::ON,
+ bridge_domain::mac_age_mode_t::OFF));
TRY_CHECK_RC(OM::write(jkr, bd2));
@@ -1020,8 +1028,8 @@ BOOST_AUTO_TEST_CASE(test_vxlan) {
// VXLAN create
vxlan_tunnel::endpoint_t ep(boost::asio::ip::address::from_string("10.10.10.10"),
- boost::asio::ip::address::from_string("10.10.10.11"),
- 322);
+ boost::asio::ip::address::from_string("10.10.10.11"),
+ 322);
vxlan_tunnel vxt(ep.src, ep.dst, ep.vni);
@@ -1031,10 +1039,17 @@ BOOST_AUTO_TEST_CASE(test_vxlan) {
TRY_CHECK_RC(OM::write(franz, vxt));
// bridge-domain create
- bridge_domain bd1(33, bridge_domain::learning_mode_t::OFF);
+ bridge_domain bd1(33, bridge_domain::learning_mode_t::OFF,
+ bridge_domain::arp_term_mode_t::OFF,
+ bridge_domain::flood_mode_t::OFF,
+ bridge_domain::mac_age_mode_t::ON);
HW::item<uint32_t> hw_bd(33, rc_t::OK);
- ADD_EXPECT(bridge_domain_cmds::create_cmd(hw_bd, bridge_domain::learning_mode_t::OFF));
+ ADD_EXPECT(bridge_domain_cmds::create_cmd(hw_bd,
+ bridge_domain::learning_mode_t::OFF,
+ bridge_domain::arp_term_mode_t::OFF,
+ bridge_domain::flood_mode_t::OFF,
+ bridge_domain::mac_age_mode_t::ON));
TRY_CHECK_RC(OM::write(franz, bd1));
diff --git a/test/test_gbp.py b/test/test_gbp.py
index 427b14de506..805f2610ea3 100644
--- a/test/test_gbp.py
+++ b/test/test_gbp.py
@@ -6,14 +6,19 @@ import struct
from framework import VppTestCase, VppTestRunner
from vpp_object import VppObject
+from vpp_neighbor import VppNeighbor
+from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable, DpoProto
from scapy.packet import Raw
-from scapy.layers.l2 import Ether
+from scapy.layers.l2 import Ether, ARP
from scapy.layers.inet import IP, UDP
-from scapy.layers.inet6 import IPv6
+from scapy.layers.inet6 import IPv6, ICMPv6ND_NS, ICMPv6NDOptSrcLLAddr, \
+ ICMPv6NDOptDstLLAddr, ICMPv6ND_NA
+from scapy.utils6 import in6_getnsma, in6_getnsmac
from socket import AF_INET, AF_INET6
-from scapy.utils import inet_pton
+from scapy.utils import inet_pton, inet_ntop
+from util import Host, mactobinary
class VppGbpEndpoint(VppObject):
@@ -21,47 +26,221 @@ class VppGbpEndpoint(VppObject):
GDB Endpoint
"""
- def __init__(self, test, sw_if_index, addr, epg, is_ip6=0):
+ @property
+ def bin_mac(self):
+ return mactobinary(self.itf.remote_mac)
+
+ @property
+ def mac(self):
+ return self.itf.remote_mac
+
+ def __init__(self, test, itf, epg, recirc, ip, fip, is_ip6=False):
self._test = test
- self.sw_if_index = sw_if_index
+ self.itf = itf
self.epg = epg
- self.addr_p = addr
+ self.recirc = recirc
+ self.ip = ip
+ self.floating_ip = fip
self.is_ip6 = is_ip6
if is_ip6:
- self.addr = inet_pton(AF_INET6, addr)
+ self.proto = DpoProto.DPO_PROTO_IP6
+ self.af = AF_INET6
else:
- self.addr = inet_pton(AF_INET, addr)
+ self.proto = DpoProto.DPO_PROTO_IP4
+ self.af = AF_INET
+ self.ip_n = inet_pton(self.af, ip)
+ self.floating_ip_n = inet_pton(self.af, fip)
def add_vpp_config(self):
self._test.vapi.gbp_endpoint_add_del(
1,
- self.sw_if_index,
- self.addr,
+ self.itf.sw_if_index,
+ self.ip_n,
self.is_ip6,
- self.epg)
+ self.epg.epg)
self._test.registry.register(self, self._test.logger)
def remove_vpp_config(self):
self._test.vapi.gbp_endpoint_add_del(
0,
- self.sw_if_index,
- self.addr,
+ self.itf.sw_if_index,
+ self.ip_n,
self.is_ip6,
- self.epg)
+ self.epg.epg)
def __str__(self):
return self.object_id()
def object_id(self):
- return "gbp-endpoint;[%d:%s:%d]" % (self.sw_if_index,
- self.addr_p,
- self.epg)
+ return "gbp-endpoint;[%d:%s:%d]" % (self.itf.sw_if_index,
+ self.ip,
+ self.epg.epg)
def query_vpp_config(self):
eps = self._test.vapi.gbp_endpoint_dump()
for ep in eps:
- if ep.endpoint.address == self.addr \
- and ep.endpoint.sw_if_index == self.sw_if_index:
+ if self.is_ip6:
+ if ep.endpoint.address == self.ip_n \
+ and ep.endpoint.sw_if_index == self.itf.sw_if_index:
+ return True
+ else:
+ if ep.endpoint.address[:4] == self.ip_n \
+ and ep.endpoint.sw_if_index == self.itf.sw_if_index:
+ return True
+ return False
+
+
+class VppGbpRecirc(VppObject):
+ """
+ GDB Recirculation Interface
+ """
+
+ def __init__(self, test, epg, recirc, is_ext=False):
+ self._test = test
+ self.recirc = recirc
+ self.epg = epg
+ self.is_ext = is_ext
+
+ def add_vpp_config(self):
+ self._test.vapi.gbp_recirc_add_del(
+ 1,
+ self.recirc.sw_if_index,
+ self.epg.epg,
+ self.is_ext)
+ self._test.registry.register(self, self._test.logger)
+
+ def remove_vpp_config(self):
+ self._test.vapi.gbp_recirc_add_del(
+ 0,
+ self.recirc.sw_if_index,
+ self.epg.epg,
+ self.is_ext)
+
+ def __str__(self):
+ return self.object_id()
+
+ def object_id(self):
+ return "gbp-recirc;[%d]" % (self.recirc.sw_if_index)
+
+ def query_vpp_config(self):
+ rs = self._test.vapi.gbp_recirc_dump()
+ for r in rs:
+ if r.recirc.sw_if_index == self.recirc.sw_if_index:
+ return True
+ return False
+
+
+class VppGbpSubnet(VppObject):
+ """
+ GDB Subnet
+ """
+
+ def __init__(self, test, table_id, address, address_len,
+ is_internal=True, is_ip6=False,
+ sw_if_index=None, epg=None):
+ self._test = test
+ self.table_id = table_id
+ self.address = address
+ self.address_len = address_len
+ self.is_ip6 = is_ip6
+ if is_ip6:
+ self.address_n = inet_pton(AF_INET6, address)
+ else:
+ self.address_n = inet_pton(AF_INET, address)
+ self.is_internal = is_internal
+ self.sw_if_index = sw_if_index
+ self.epg = epg
+
+ def add_vpp_config(self):
+ self._test.vapi.gbp_subnet_add_del(
+ 1,
+ self.table_id,
+ self.is_internal,
+ self.address_n,
+ self.address_len,
+ sw_if_index=self.sw_if_index if self.sw_if_index else 0xffffffff,
+ epg_id=self.epg if self.epg else 0xffffffff,
+ is_ip6=self.is_ip6)
+ self._test.registry.register(self, self._test.logger)
+
+ def remove_vpp_config(self):
+ self._test.vapi.gbp_subnet_add_del(
+ 0,
+ self.table_id,
+ self.is_internal,
+ self.address_n,
+ self.address_len,
+ is_ip6=self.is_ip6)
+
+ def __str__(self):
+ return self.object_id()
+
+ def object_id(self):
+ return "gbp-subnet;[%d:%s/%d]" % (self.table_id,
+ self.address,
+ self.address_len)
+
+ def query_vpp_config(self):
+ ss = self._test.vapi.gbp_subnet_dump()
+ for s in ss:
+ if s.subnet.table_id == self.table_id and \
+ s.subnet.address_length == self.address_len:
+ if self.is_ip6:
+ if s.subnet.address == self.address_n:
+ return True
+ else:
+ if s.subnet.address[:4] == self.address_n:
+ return True
+ return False
+
+
+class VppGbpEndpointGroup(VppObject):
+ """
+ GDB Endpoint Group
+ """
+
+ def __init__(self, test, epg, rd, bd, uplink,
+ bvi, bvi_ip4, bvi_ip6=None):
+ self._test = test
+ self.uplink = uplink
+ self.bvi = bvi
+ self.bvi_ip4 = bvi_ip4
+ self.bvi_ip4_n = inet_pton(AF_INET, bvi_ip4)
+ self.bvi_ip6 = bvi_ip6
+ self.bvi_ip6_n = inet_pton(AF_INET6, bvi_ip6)
+ self.epg = epg
+ self.bd = bd
+ self.rd = rd
+
+ def add_vpp_config(self):
+ self._test.vapi.gbp_endpoint_group_add_del(
+ 1,
+ self.epg,
+ self.bd,
+ self.rd,
+ self.rd,
+ self.uplink.sw_if_index)
+ self._test.registry.register(self, self._test.logger)
+
+ def remove_vpp_config(self):
+ self._test.vapi.gbp_endpoint_group_add_del(
+ 0,
+ self.epg,
+ self.bd,
+ self.rd,
+ self.rd,
+ self.uplink.sw_if_index)
+
+ def __str__(self):
+ return self.object_id()
+
+ def object_id(self):
+ return "gbp-endpoint-group;[%d]" % (self.epg)
+
+ def query_vpp_config(self):
+ epgs = self._test.vapi.gbp_endpoint_group_dump()
+ for epg in epgs:
+ if epg.epg.epg_id == self.epg:
return True
return False
@@ -101,10 +280,10 @@ class VppGbpContract(VppObject):
self.acl_index)
def query_vpp_config(self):
- eps = self._test.vapi.gbp_contract_dump()
- for ep in eps:
- if ep.contract.src_epg == self.src_epg \
- and ep.contract.dst_epg == self.dst_epg:
+ cs = self._test.vapi.gbp_contract_dump()
+ for c in cs:
+ if c.contract.src_epg == self.src_epg \
+ and c.contract.dst_epg == self.dst_epg:
return True
return False
@@ -115,233 +294,579 @@ class TestGBP(VppTestCase):
def setUp(self):
super(TestGBP, self).setUp()
- # create 6 pg interfaces for pg0 to pg5
- self.create_pg_interfaces(range(6))
+ self.create_pg_interfaces(range(9))
+ self.create_loopback_interfaces(range(9))
+
+ self.router_mac = "00:11:22:33:44:55"
for i in self.pg_interfaces:
i.admin_up()
- i.config_ip4()
- i.resolve_arp()
- i.config_ip6()
- i.resolve_ndp()
+ for i in self.lo_interfaces:
+ i.admin_up()
+ self.vapi.sw_interface_set_mac_address(
+ i.sw_if_index,
+ mactobinary(self.router_mac))
def tearDown(self):
for i in self.pg_interfaces:
- i.unconfig_ip4()
- i.unconfig_ip6()
+ i.admin_down()
super(TestGBP, self).tearDown()
- def test_gbp4(self):
- """ Group Based Policy v4 """
-
- ep1 = VppGbpEndpoint(self,
- self.pg0.sw_if_index,
- self.pg0.remote_ip4,
- 220)
- ep1.add_vpp_config()
- ep2 = VppGbpEndpoint(self,
- self.pg1.sw_if_index,
- self.pg1.remote_ip4,
- 220)
- ep2.add_vpp_config()
-
- ep3 = VppGbpEndpoint(self,
- self.pg2.sw_if_index,
- self.pg2.remote_ip4,
- 221)
- ep3.add_vpp_config()
- ep4 = VppGbpEndpoint(self,
- self.pg3.sw_if_index,
- self.pg3.remote_ip4,
- 222)
- ep4.add_vpp_config()
+ def send_and_expect_bridged(self, src, tx, dst):
+ rx = self.send_and_expect(src, tx, dst)
- self.logger.info(self.vapi.cli("sh gbp endpoint"))
+ for r in rx:
+ self.assertEqual(r[Ether].src, tx[0][Ether].src)
+ self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
+ self.assertEqual(r[IP].src, tx[0][IP].src)
+ self.assertEqual(r[IP].dst, tx[0][IP].dst)
+ return rx
+
+ def send_and_expect_bridged6(self, src, tx, dst):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ self.assertEqual(r[Ether].src, tx[0][Ether].src)
+ self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
+ self.assertEqual(r[IPv6].src, tx[0][IPv6].src)
+ self.assertEqual(r[IPv6].dst, tx[0][IPv6].dst)
+ return rx
+
+ def send_and_expect_routed(self, src, tx, dst, src_mac):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ self.assertEqual(r[Ether].src, src_mac)
+ self.assertEqual(r[Ether].dst, dst.remote_mac)
+ self.assertEqual(r[IP].src, tx[0][IP].src)
+ self.assertEqual(r[IP].dst, tx[0][IP].dst)
+ return rx
+
+ def send_and_expect_natted(self, src, tx, dst, src_ip):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ self.assertEqual(r[Ether].src, tx[0][Ether].src)
+ self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
+ self.assertEqual(r[IP].src, src_ip)
+ self.assertEqual(r[IP].dst, tx[0][IP].dst)
+ return rx
+
+ def send_and_expect_unnatted(self, src, tx, dst, dst_ip):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ self.assertEqual(r[Ether].src, tx[0][Ether].src)
+ self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
+ self.assertEqual(r[IP].dst, dst_ip)
+ self.assertEqual(r[IP].src, tx[0][IP].src)
+ return rx
+
+ def send_and_expect_double_natted(self, src, tx, dst, src_ip, dst_ip):
+ rx = self.send_and_expect(src, tx, dst)
+
+ for r in rx:
+ self.assertEqual(r[Ether].src, self.router_mac)
+ self.assertEqual(r[Ether].dst, dst.remote_mac)
+ self.assertEqual(r[IP].dst, dst_ip)
+ self.assertEqual(r[IP].src, src_ip)
+ return rx
+
+ def test_gbp(self):
+ """ Group Based Policy """
+
+ nat_table = VppIpTable(self, 20)
+ nat_table.add_vpp_config()
#
- # in the abscense of policy, endpoints in the same EPG
- # can communicate
+ # Bridge Domains
#
- pkt_intra_epg = (Ether(src=self.pg0.remote_mac,
- dst=self.pg0.local_mac) /
- IP(src=self.pg0.remote_ip4,
- dst=self.pg1.remote_ip4) /
- UDP(sport=1234, dport=1234) /
- Raw('\xa5' * 100))
+ self.vapi.bridge_domain_add_del(1, flood=1, uu_flood=1, forward=1,
+ learn=0, arp_term=1, is_add=1)
+ self.vapi.bridge_domain_add_del(2, flood=1, uu_flood=1, forward=1,
+ learn=0, arp_term=1, is_add=1)
+ self.vapi.bridge_domain_add_del(20, flood=1, uu_flood=1, forward=1,
+ learn=0, arp_term=1, is_add=1)
- self.send_and_expect(self.pg0, pkt_intra_epg * 65, self.pg1)
+ #
+ # 3 EPGs, 2 of which share a BD.
+ #
+ epgs = []
+ recircs = []
+ epgs.append(VppGbpEndpointGroup(self, 220, 0, 1, self.pg4,
+ self.loop0,
+ "10.0.0.128",
+ "2001:10::128"))
+ recircs.append(VppGbpRecirc(self, epgs[0],
+ self.loop3))
+ epgs.append(VppGbpEndpointGroup(self, 221, 0, 1, self.pg5,
+ self.loop0,
+ "10.0.1.128",
+ "2001:10:1::128"))
+ recircs.append(VppGbpRecirc(self, epgs[1],
+ self.loop4))
+ epgs.append(VppGbpEndpointGroup(self, 222, 0, 2, self.pg6,
+ self.loop1,
+ "10.0.2.128",
+ "2001:10:2::128"))
+ recircs.append(VppGbpRecirc(self, epgs[2],
+ self.loop5))
#
- # in the abscense of policy, endpoints in the different EPG
- # cannot communicate
+ # 2 NAT EPGs, one for floating-IP subnets, the other for internet
#
- pkt_inter_epg_220_to_221 = (Ether(src=self.pg0.remote_mac,
- dst=self.pg0.local_mac) /
- IP(src=self.pg0.remote_ip4,
- dst=self.pg2.remote_ip4) /
- UDP(sport=1234, dport=1234) /
- Raw('\xa5' * 100))
- pkt_inter_epg_220_to_222 = (Ether(src=self.pg0.remote_mac,
- dst=self.pg0.local_mac) /
- IP(src=self.pg0.remote_ip4,
- dst=self.pg3.remote_ip4) /
- UDP(sport=1234, dport=1234) /
- Raw('\xa5' * 100))
- pkt_inter_epg_221_to_220 = (Ether(src=self.pg2.remote_mac,
- dst=self.pg2.local_mac) /
- IP(src=self.pg2.remote_ip4,
- dst=self.pg0.remote_ip4) /
- UDP(sport=1234, dport=1234) /
- Raw('\xa5' * 100))
+ epgs.append(VppGbpEndpointGroup(self, 333, 20, 20, self.pg7,
+ self.loop2,
+ "11.0.0.128",
+ "3001::128"))
+ recircs.append(VppGbpRecirc(self, epgs[3],
+ self.loop6, is_ext=True))
+ epgs.append(VppGbpEndpointGroup(self, 444, 20, 20, self.pg8,
+ self.loop2,
+ "11.0.0.129",
+ "3001::129"))
+ recircs.append(VppGbpRecirc(self, epgs[4],
+ self.loop8, is_ext=True))
- self.send_and_assert_no_replies(self.pg0,
- pkt_inter_epg_220_to_221 * 65)
- self.send_and_assert_no_replies(self.pg0,
- pkt_inter_epg_221_to_220 * 65)
+ epg_nat = epgs[3]
+ recirc_nat = recircs[3]
#
- # A uni-directional contract from EPG 220 -> 221
+ # 4 end-points, 2 in the same subnet, 3 in the same BD
#
- c1 = VppGbpContract(self, 220, 221, 0xffffffff)
- c1.add_vpp_config()
+ eps = []
+ eps.append(VppGbpEndpoint(self, self.pg0,
+ epgs[0], recircs[0],
+ "10.0.0.1",
+ "11.0.0.1"))
+ eps.append(VppGbpEndpoint(self, self.pg1,
+ epgs[0], recircs[0],
+ "10.0.0.2",
+ "11.0.0.2"))
+ eps.append(VppGbpEndpoint(self, self.pg2,
+ epgs[1], recircs[1],
+ "10.0.1.1",
+ "11.0.0.3"))
+ eps.append(VppGbpEndpoint(self, self.pg3,
+ epgs[2], recircs[2],
+ "10.0.2.1",
+ "11.0.0.4"))
+ eps.append(VppGbpEndpoint(self, self.pg0,
+ epgs[0], recircs[0],
+ "2001:10::1",
+ "3001::1",
+ is_ip6=True))
+ eps.append(VppGbpEndpoint(self, self.pg1,
+ epgs[0], recircs[0],
+ "2001:10::2",
+ "3001::2",
+ is_ip6=True))
+ eps.append(VppGbpEndpoint(self, self.pg2,
+ epgs[1], recircs[1],
+ "2001:10:1::1",
+ "3001::3",
+ is_ip6=True))
+ eps.append(VppGbpEndpoint(self, self.pg3,
+ epgs[2], recircs[2],
+ "2001:10:2::1",
+ "3001::4",
+ is_ip6=True))
- self.send_and_expect(self.pg0,
- pkt_inter_epg_220_to_221 * 65,
- self.pg2)
- self.send_and_assert_no_replies(self.pg2,
- pkt_inter_epg_221_to_220 * 65)
+ #
+ # Config related to each of the EPGs
+ #
+ for epg in epgs:
+ # IP config on the BVI interfaces
+ if epg != epgs[1] and epg != epgs[4]:
+ epg.bvi.set_table_ip4(epg.rd)
+ epg.bvi.set_table_ip6(epg.rd)
+
+ # The BVIs are NAT inside interfaces
+ self.vapi.nat44_interface_add_del_feature(epg.bvi.sw_if_index,
+ is_inside=1,
+ is_add=1)
+ # self.vapi.nat66_add_del_interface(epg.bvi.sw_if_index,
+ # is_inside=1,
+ # is_add=1)
+
+ self.vapi.sw_interface_add_del_address(epg.bvi.sw_if_index,
+ epg.bvi_ip4_n,
+ 32)
+ self.vapi.sw_interface_add_del_address(epg.bvi.sw_if_index,
+ epg.bvi_ip6_n,
+ 128,
+ is_ipv6=1)
+
+ # EPG uplink interfaces in the BD
+ epg.uplink.set_table_ip4(epg.rd)
+ self.vapi.sw_interface_set_l2_bridge(epg.uplink.sw_if_index,
+ epg.bd)
+
+ # add the BD ARP termination entry for BVI IP
+ self.vapi.bd_ip_mac_add_del(bd_id=epg.bd,
+ mac=mactobinary(self.router_mac),
+ ip=epg.bvi_ip4_n,
+ is_ipv6=0,
+ is_add=1)
+ self.vapi.bd_ip_mac_add_del(bd_id=epg.bd,
+ mac=mactobinary(self.router_mac),
+ ip=epg.bvi_ip6_n,
+ is_ipv6=1,
+ is_add=1)
+
+ # epg[1] shares the same BVI to epg[0]
+ if epg != epgs[1] and epg != epgs[4]:
+ # BVI in BD
+ self.vapi.sw_interface_set_l2_bridge(epg.bvi.sw_if_index,
+ epg.bd,
+ bvi=1)
+ # BVI L2 FIB entry
+ self.vapi.l2fib_add_del(self.router_mac,
+ epg.bd,
+ epg.bvi.sw_if_index,
+ is_add=1, bvi_mac=1)
+
+ # EPG in VPP
+ epg.add_vpp_config()
+
+ for recirc in recircs:
+ # EPG's ingress recirculation interface maps to its RD
+ recirc.recirc.set_table_ip4(recirc.epg.rd)
+
+ # in the bridge to allow DVR. L2 emulation to punt to L3
+ self.vapi.sw_interface_set_l2_bridge(recirc.recirc.sw_if_index,
+ recirc.epg.bd)
+ self.vapi.sw_interface_set_l2_emulation(
+ recirc.recirc.sw_if_index)
+
+ if recirc.is_ext:
+ # recirc interfaces on NAT EPGs are outside and an
+ # output feature
+ self.vapi.nat44_interface_add_del_output_feature(
+ recirc.recirc.sw_if_index,
+ is_inside=0,
+ is_add=1)
+ else:
+ self.vapi.nat44_interface_add_del_feature(
+ recirc.recirc.sw_if_index,
+ is_inside=0,
+ is_add=1)
+ # self.vapi.nat66_add_del_interface(
+ # recirc.recirc.sw_if_index,
+ # is_inside=0,
+ # is_add=1)
+
+ recirc.add_vpp_config()
+
+ ep_routes = []
+ ep_arps = []
+ for ep in eps:
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ #
+ # routes to the endpoints. We need these since there are no
+ # adj-fibs due to the fact the the BVI address has /32 and
+ # the subnet is not attached.
+ #
+ r = VppIpRoute(self, ep.ip, 32,
+ [VppRoutePath(ep.ip,
+ ep.epg.bvi.sw_if_index,
+ proto=ep.proto)],
+ is_ip6=ep.is_ip6)
+ r.add_vpp_config()
+ ep_routes.append(r)
+
+ #
+ # ARP entries for the endpoints
+ #
+ a = VppNeighbor(self,
+ ep.epg.bvi.sw_if_index,
+ ep.itf.remote_mac,
+ ep.ip, af=ep.af)
+ a.add_vpp_config()
+ ep_arps.append(a)
+
+ # add each EP itf to the its BD
+ self.vapi.sw_interface_set_l2_bridge(ep.itf.sw_if_index,
+ ep.epg.bd)
+
+ # add the BD ARP termination entry
+ self.vapi.bd_ip_mac_add_del(bd_id=ep.epg.bd,
+ mac=ep.bin_mac,
+ ip=ep.ip_n,
+ is_ipv6=0,
+ is_add=1)
+
+ # L2 FIB entry
+ self.vapi.l2fib_add_del(ep.mac,
+ ep.epg.bd,
+ ep.itf.sw_if_index,
+ is_add=1)
+
+ # Add static mappings for each EP from the 10/8 to 11/8 network
+ if ep.af == AF_INET:
+ self.vapi.nat44_add_del_static_mapping(ep.ip_n,
+ ep.floating_ip_n,
+ vrf_id=0,
+ addr_only=1)
+ # else:
+ # self.vapi.nat66_add_del_static_mapping(ep.ip_n,
+ # ep.floating_ip_n,
+ # vrf_id=20)
+
+ # VPP EP create ...
+ ep.add_vpp_config()
+
+ # ... results in a Gratuitous ARP/ND on the EPG's uplink
+ rx = ep.epg.uplink.get_capture(1, timeout=0.2)
+
+ if ep.is_ip6:
+ self.assertTrue(rx[0].haslayer(ICMPv6ND_NA))
+ self.assertEqual(rx[0][ICMPv6ND_NA].tgt, ep.ip)
+ else:
+ self.assertTrue(rx[0].haslayer(ARP))
+ self.assertEqual(rx[0][ARP].psrc, ep.ip)
+ self.assertEqual(rx[0][ARP].pdst, ep.ip)
+
+ # add the BD ARP termination entry for floating IP
+ self.vapi.bd_ip_mac_add_del(bd_id=epg_nat.bd,
+ mac=ep.bin_mac,
+ ip=ep.floating_ip_n,
+ is_ipv6=0,
+ is_add=1)
+
+ # floating IPs route via EPG recirc
+ r = VppIpRoute(self, ep.floating_ip, 32,
+ [VppRoutePath(ep.floating_ip,
+ ep.recirc.recirc.sw_if_index,
+ is_dvr=1,
+ proto=ep.proto)],
+ table_id=20,
+ is_ip6=ep.is_ip6)
+ r.add_vpp_config()
+ ep_routes.append(r)
+
+ # L2 FIB entries in the NAT EPG BD to bridge the packets from
+ # the outside direct to the internal EPG
+ self.vapi.l2fib_add_del(ep.mac,
+ epg_nat.bd,
+ ep.recirc.recirc.sw_if_index,
+ is_add=1)
#
- # contract for the return direction
+ # ARP packets for unknown IP are flooded
#
- c2 = VppGbpContract(self, 221, 220, 0xffffffff)
- c2.add_vpp_config()
+ pkt_arp = (Ether(dst="ff:ff:ff:ff:ff:ff",
+ src=self.pg0.remote_mac) /
+ ARP(op="who-has",
+ hwdst="ff:ff:ff:ff:ff:ff",
+ hwsrc=self.pg0.remote_mac,
+ pdst=epgs[0].bvi_ip4,
+ psrc="10.0.0.88"))
- self.send_and_expect(self.pg0,
- pkt_inter_epg_220_to_221 * 65,
- self.pg2)
- self.send_and_expect(self.pg2,
- pkt_inter_epg_221_to_220 * 65,
- self.pg0)
+ self.send_and_expect(self.pg0, [pkt_arp], self.pg0)
#
- # check that inter group is still disabled for the groups
- # not in the contract.
+ # ARP/ND packets get a response
#
- self.send_and_assert_no_replies(self.pg0,
- pkt_inter_epg_220_to_222 * 65)
+ pkt_arp = (Ether(dst="ff:ff:ff:ff:ff:ff",
+ src=self.pg0.remote_mac) /
+ ARP(op="who-has",
+ hwdst="ff:ff:ff:ff:ff:ff",
+ hwsrc=self.pg0.remote_mac,
+ pdst=epgs[0].bvi_ip4,
+ psrc=eps[0].ip))
- self.logger.info(self.vapi.cli("sh gbp contract"))
+ self.send_and_expect(self.pg0, [pkt_arp], self.pg0)
+
+ nsma = in6_getnsma(inet_pton(AF_INET6, eps[4].ip))
+ d = inet_ntop(AF_INET6, nsma)
+ pkt_nd = (Ether(dst=in6_getnsmac(nsma)) /
+ IPv6(dst=d, src=eps[4].ip) /
+ ICMPv6ND_NS(tgt=epgs[0].bvi_ip6) /
+ ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac))
+ self.send_and_expect(self.pg0, [pkt_nd], self.pg0)
#
- # remove both contracts, traffic stops in both directions
+ # broadcast packets are flooded
#
- c2.remove_vpp_config()
- c1.remove_vpp_config()
+ pkt_bcast = (Ether(dst="ff:ff:ff:ff:ff:ff",
+ src=self.pg0.remote_mac) /
+ IP(src=eps[0].ip, dst="232.1.1.1") /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
- self.send_and_assert_no_replies(self.pg2,
- pkt_inter_epg_221_to_220 * 65)
- self.send_and_assert_no_replies(self.pg0,
- pkt_inter_epg_220_to_221 * 65)
- self.send_and_expect(self.pg0, pkt_intra_epg * 65, self.pg1)
-
- def test_gbp6(self):
- """ Group Based Policy v6 """
-
- ep1 = VppGbpEndpoint(self,
- self.pg0.sw_if_index,
- self.pg0.remote_ip6,
- 220,
- is_ip6=1)
- ep1.add_vpp_config()
- ep2 = VppGbpEndpoint(self,
- self.pg1.sw_if_index,
- self.pg1.remote_ip6,
- 220,
- is_ip6=1)
- ep2.add_vpp_config()
-
- ep3 = VppGbpEndpoint(self,
- self.pg2.sw_if_index,
- self.pg2.remote_ip6,
- 221,
- is_ip6=1)
- ep3.add_vpp_config()
- ep4 = VppGbpEndpoint(self,
- self.pg3.sw_if_index,
- self.pg3.remote_ip6,
- 222,
- is_ip6=1)
- ep4.add_vpp_config()
+ self.vapi.cli("clear trace")
+ self.pg0.add_stream(pkt_bcast)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ rxd = eps[1].itf.get_capture(1)
+ self.assertEqual(rxd[0][Ether].dst, pkt_bcast[Ether].dst)
+ rxd = epgs[0].uplink.get_capture(1)
+ self.assertEqual(rxd[0][Ether].dst, pkt_bcast[Ether].dst)
+
+ #
+ # packets to non-local L3 destinations dropped
+ #
+ pkt_intra_epg_220_ip4 = (Ether(src=self.pg0.remote_mac,
+ dst=self.router_mac) /
+ IP(src=eps[0].ip, dst="10.0.0.99") /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+ pkt_inter_epg_222_ip4 = (Ether(src=self.pg0.remote_mac,
+ dst=self.router_mac) /
+ IP(src=eps[0].ip, dst="10.0.1.99") /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+
+ self.send_and_assert_no_replies(self.pg0, pkt_intra_epg_220_ip4 * 65)
+
+ pkt_inter_epg_222_ip6 = (Ether(src=self.pg0.remote_mac,
+ dst=self.router_mac) /
+ IPv6(src=eps[4].ip, dst="2001:10::99") /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+ self.send_and_assert_no_replies(self.pg0, pkt_inter_epg_222_ip6 * 65)
+
+ #
+ # Add the subnet routes
+ #
+ s41 = VppGbpSubnet(self, 0, "10.0.0.0", 24)
+ s42 = VppGbpSubnet(self, 0, "10.0.1.0", 24)
+ s43 = VppGbpSubnet(self, 0, "10.0.2.0", 24)
+ s41.add_vpp_config()
+ s42.add_vpp_config()
+ s43.add_vpp_config()
+ s61 = VppGbpSubnet(self, 0, "2001:10::1", 64, is_ip6=True)
+ s62 = VppGbpSubnet(self, 0, "2001:10:1::1", 64, is_ip6=True)
+ s63 = VppGbpSubnet(self, 0, "2001:10:2::1", 64, is_ip6=True)
+ s61.add_vpp_config()
+ s62.add_vpp_config()
+ s63.add_vpp_config()
+
+ self.send_and_expect_bridged(self.pg0,
+ pkt_intra_epg_220_ip4 * 65,
+ self.pg4)
+ self.send_and_expect_bridged(self.pg3,
+ pkt_inter_epg_222_ip4 * 65,
+ self.pg6)
+ self.send_and_expect_bridged6(self.pg3,
+ pkt_inter_epg_222_ip6 * 65,
+ self.pg6)
+
+ self.logger.info(self.vapi.cli("sh ip fib 11.0.0.2"))
+ self.logger.info(self.vapi.cli("sh gbp endpoint-group"))
self.logger.info(self.vapi.cli("sh gbp endpoint"))
+ self.logger.info(self.vapi.cli("sh gbp recirc"))
+ self.logger.info(self.vapi.cli("sh int"))
+ self.logger.info(self.vapi.cli("sh int addr"))
+ self.logger.info(self.vapi.cli("sh int feat loop6"))
+ self.logger.info(self.vapi.cli("sh vlib graph ip4-gbp-src-classify"))
+ self.logger.info(self.vapi.cli("sh int feat loop3"))
+
+ #
+ # Packet destined to unknown unicast is sent on the epg uplink ...
+ #
+ pkt_intra_epg_220_to_uplink = (Ether(src=self.pg0.remote_mac,
+ dst="00:00:00:33:44:55") /
+ IP(src=eps[0].ip, dst="10.0.0.99") /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+
+ self.send_and_expect_bridged(self.pg0,
+ pkt_intra_epg_220_to_uplink * 65,
+ self.pg4)
+ # ... and nowhere else
+ self.pg1.get_capture(0, timeout=0.1)
+ self.pg1.assert_nothing_captured(remark="Flood onto other VMS")
+
+ pkt_intra_epg_221_to_uplink = (Ether(src=self.pg2.remote_mac,
+ dst="00:00:00:33:44:66") /
+ IP(src=eps[0].ip, dst="10.0.0.99") /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+ self.send_and_expect_bridged(self.pg2,
+ pkt_intra_epg_221_to_uplink * 65,
+ self.pg5)
+
+ #
+ # Packets from the uplink are forwarded in the absence of a contract
#
- # in the abscense of policy, endpoints in the same EPG
+ pkt_intra_epg_220_from_uplink = (Ether(src="00:00:00:33:44:55",
+ dst=self.pg0.remote_mac) /
+ IP(src=eps[0].ip, dst="10.0.0.99") /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+
+ self.send_and_expect_bridged(self.pg4,
+ pkt_intra_epg_220_from_uplink * 65,
+ self.pg0)
+
+ #
+ # in the absence of policy, endpoints in the same EPG
# can communicate
#
pkt_intra_epg = (Ether(src=self.pg0.remote_mac,
- dst=self.pg0.local_mac) /
- IPv6(src=self.pg0.remote_ip6,
- dst=self.pg1.remote_ip6) /
+ dst=self.pg1.remote_mac) /
+ IP(src=eps[0].ip, dst=eps[1].ip) /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
- self.send_and_expect(self.pg0, pkt_intra_epg * 65, self.pg1)
+ self.send_and_expect_bridged(self.pg0, pkt_intra_epg * 65, self.pg1)
#
# in the abscense of policy, endpoints in the different EPG
# cannot communicate
#
pkt_inter_epg_220_to_221 = (Ether(src=self.pg0.remote_mac,
- dst=self.pg0.local_mac) /
- IPv6(src=self.pg0.remote_ip6,
- dst=self.pg2.remote_ip6) /
+ dst=self.pg2.remote_mac) /
+ IP(src=eps[0].ip, dst=eps[2].ip) /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
- pkt_inter_epg_220_to_222 = (Ether(src=self.pg0.remote_mac,
- dst=self.pg0.local_mac) /
- IPv6(src=self.pg0.remote_ip6,
- dst=self.pg3.remote_ip6) /
+ pkt_inter_epg_221_to_220 = (Ether(src=self.pg2.remote_mac,
+ dst=self.pg0.remote_mac) /
+ IP(src=eps[2].ip, dst=eps[0].ip) /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
- pkt_inter_epg_221_to_220 = (Ether(src=self.pg2.remote_mac,
- dst=self.pg2.local_mac) /
- IPv6(src=self.pg2.remote_ip6,
- dst=self.pg0.remote_ip6) /
+ pkt_inter_epg_220_to_222 = (Ether(src=self.pg0.remote_mac,
+ dst=self.router_mac) /
+ IP(src=eps[0].ip, dst=eps[3].ip) /
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
self.send_and_assert_no_replies(self.pg0,
pkt_inter_epg_220_to_221 * 65)
self.send_and_assert_no_replies(self.pg0,
- pkt_inter_epg_221_to_220 * 65)
+ pkt_inter_epg_220_to_222 * 65)
#
# A uni-directional contract from EPG 220 -> 221
#
- c1 = VppGbpContract(self, 220, 221, 0xffffffff)
+ c1 = VppGbpContract(self, 220, 221, 0)
c1.add_vpp_config()
- self.send_and_expect(self.pg0,
- pkt_inter_epg_220_to_221 * 65,
- self.pg2)
- self.send_and_assert_no_replies(self.pg2,
- pkt_inter_epg_221_to_220 * 65)
+ self.send_and_expect_bridged(self.pg0,
+ pkt_inter_epg_220_to_221 * 65,
+ self.pg2)
+ self.send_and_assert_no_replies(self.pg0,
+ pkt_inter_epg_220_to_222 * 65)
#
# contract for the return direction
#
- c2 = VppGbpContract(self, 221, 220, 0xffffffff)
+ c2 = VppGbpContract(self, 221, 220, 0)
c2.add_vpp_config()
- self.send_and_expect(self.pg0,
- pkt_inter_epg_220_to_221 * 65,
- self.pg2)
- self.send_and_expect(self.pg2,
- pkt_inter_epg_221_to_220 * 65,
- self.pg0)
+ self.send_and_expect_bridged(self.pg0,
+ pkt_inter_epg_220_to_221 * 65,
+ self.pg2)
+ self.send_and_expect_bridged(self.pg2,
+ pkt_inter_epg_221_to_220 * 65,
+ self.pg0)
#
# check that inter group is still disabled for the groups
@@ -350,19 +875,165 @@ class TestGBP(VppTestCase):
self.send_and_assert_no_replies(self.pg0,
pkt_inter_epg_220_to_222 * 65)
+ #
+ # A uni-directional contract from EPG 220 -> 222 'L3 routed'
+ #
+ c3 = VppGbpContract(self, 220, 222, 0)
+ c3.add_vpp_config()
+
self.logger.info(self.vapi.cli("sh gbp contract"))
+ self.send_and_expect_routed(self.pg0,
+ pkt_inter_epg_220_to_222 * 65,
+ self.pg3,
+ self.router_mac)
+
#
# remove both contracts, traffic stops in both directions
#
c2.remove_vpp_config()
c1.remove_vpp_config()
+ c3.remove_vpp_config()
self.send_and_assert_no_replies(self.pg2,
pkt_inter_epg_221_to_220 * 65)
self.send_and_assert_no_replies(self.pg0,
pkt_inter_epg_220_to_221 * 65)
- self.send_and_expect(self.pg0, pkt_intra_epg * 65, self.pg1)
+ self.send_and_expect_bridged(self.pg0, pkt_intra_epg * 65, self.pg1)
+
+ #
+ # EPs to the outside world
+ #
+
+ # in the EP's RD an external subnet via the NAT EPG's recirc
+ se1 = VppGbpSubnet(self, 0, "0.0.0.0", 0,
+ is_internal=False,
+ sw_if_index=recirc_nat.recirc.sw_if_index,
+ epg=epg_nat.epg)
+ se1.add_vpp_config()
+ se2 = VppGbpSubnet(self, 0, "11.0.0.0", 8,
+ is_internal=False,
+ sw_if_index=recirc_nat.recirc.sw_if_index,
+ epg=epg_nat.epg)
+ se2.add_vpp_config()
+ # in the NAT RD an external subnet via the NAT EPG's uplink
+ se3 = VppGbpSubnet(self, 20, "0.0.0.0", 0,
+ is_internal=False,
+ sw_if_index=epg_nat.uplink.sw_if_index,
+ epg=epg_nat.epg)
+ se4 = VppGbpSubnet(self, 20, "11.0.0.0", 8,
+ is_internal=False,
+ sw_if_index=epg_nat.uplink.sw_if_index,
+ epg=epg_nat.epg)
+ se3.add_vpp_config()
+ se4.add_vpp_config()
+
+ self.logger.info(self.vapi.cli("sh ip fib 0.0.0.0/0"))
+ self.logger.info(self.vapi.cli("sh ip fib 11.0.0.1"))
+
+ pkt_inter_epg_220_to_global = (Ether(src=self.pg0.remote_mac,
+ dst=self.router_mac) /
+ IP(src=eps[0].ip, dst="1.1.1.1") /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+
+ # no policy yet
+ self.send_and_assert_no_replies(self.pg0,
+ pkt_inter_epg_220_to_global * 65)
+
+ c4 = VppGbpContract(self, 220, 333, 0)
+ c4.add_vpp_config()
+
+ self.send_and_expect_natted(self.pg0,
+ pkt_inter_epg_220_to_global * 65,
+ self.pg7,
+ "11.0.0.1")
+
+ pkt_inter_epg_220_from_global = (Ether(src=self.router_mac,
+ dst=self.pg0.remote_mac) /
+ IP(dst=eps[0].floating_ip,
+ src="1.1.1.1") /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+
+ self.send_and_assert_no_replies(self.pg7,
+ pkt_inter_epg_220_from_global * 65)
+
+ c5 = VppGbpContract(self, 333, 220, 0)
+ c5.add_vpp_config()
+
+ self.send_and_expect_unnatted(self.pg7,
+ pkt_inter_epg_220_from_global * 65,
+ self.pg0,
+ "10.0.0.1")
+
+ pkt_intra_epg_220_global = (Ether(src=self.pg0.remote_mac,
+ dst=self.router_mac) /
+ IP(src=eps[0].ip,
+ dst=eps[1].floating_ip) /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+
+ self.send_and_expect_double_natted(self.pg0,
+ pkt_intra_epg_220_global * 65,
+ self.pg1,
+ "11.0.0.1",
+ "10.0.0.2")
+
+ #
+ # cleanup
+ #
+ for ep in eps:
+ # del static mappings for each EP from the 10/8 to 11/8 network
+ if ep.af == AF_INET:
+ self.vapi.nat44_add_del_static_mapping(ep.ip_n,
+ ep.floating_ip_n,
+ vrf_id=0,
+ addr_only=1,
+ is_add=0)
+ # else:
+ # self.vapi.nat66_add_del_static_mapping(ep.ip_n,
+ # ep.floating_ip_n,
+ # vrf_id=0,
+ # is_add=0)
+
+ for epg in epgs:
+ # IP config on the BVI interfaces
+ self.vapi.sw_interface_add_del_address(epg.bvi.sw_if_index,
+ epg.bvi_ip4_n,
+ 32,
+ is_add=0)
+ self.logger.info(self.vapi.cli("sh int addr"))
+
+ epg.uplink.set_table_ip4(0)
+
+ if epg != epgs[0] and epg != epgs[3]:
+ epg.bvi.set_table_ip4(0)
+
+ self.vapi.nat44_interface_add_del_feature(epg.bvi.sw_if_index,
+ is_inside=1,
+ is_add=0)
+ # self.vapi.nat66_add_del_interface(epg.bvi.sw_if_index,
+ # is_inside=1,
+ # is_add=0)
+
+ for recirc in recircs:
+ recirc.recirc.set_table_ip4(0)
+
+ if recirc.is_ext:
+ self.vapi.nat44_interface_add_del_output_feature(
+ recirc.recirc.sw_if_index,
+ is_inside=0,
+ is_add=0)
+ else:
+ self.vapi.nat44_interface_add_del_feature(
+ recirc.recirc.sw_if_index,
+ is_inside=0,
+ is_add=0)
+ # self.vapi.nat66_add_del_interface(
+ # recirc.recirc.sw_if_index,
+ # is_inside=0,
+ # is_add=0)
if __name__ == '__main__':
diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py
index 24483fe82b8..ad887e8c079 100644
--- a/test/vpp_papi_provider.py
+++ b/test/vpp_papi_provider.py
@@ -668,6 +668,16 @@ class VppPapiProvider(object):
{'sw_if_index': sw_if_index,
'mtu': mtu})
+ def sw_interface_set_promiscuous(self, sw_if_index, enable):
+ """
+ :param sw_if_index:
+ :param enable:
+
+ """
+ return self.api(self.papi.sw_interface_set_promiscuous,
+ {'sw_if_index': sw_if_index,
+ 'enable': enable})
+
def sw_interface_set_mac_address(self, sw_if_index, mac):
return self.api(self.papi.sw_interface_set_mac_address,
{'sw_if_index': sw_if_index,
@@ -3238,6 +3248,59 @@ class VppPapiProvider(object):
""" GBP endpoint Dump """
return self.api(self.papi.gbp_endpoint_dump, {})
+ def gbp_endpoint_group_add_del(self, is_add, epg, bd,
+ ip4_rd,
+ ip6_rd,
+ uplink_sw_if_index):
+ """ GBP endpoint group Add/Del """
+ return self.api(self.papi.gbp_endpoint_group_add_del,
+ {'is_add': is_add,
+ 'epg': {
+ 'uplink_sw_if_index': uplink_sw_if_index,
+ 'bd_id': bd,
+ 'ip4_table_id': ip4_rd,
+ 'ip6_table_id': ip6_rd,
+ 'epg_id': epg}})
+
+ def gbp_endpoint_group_dump(self):
+ """ GBP endpoint group Dump """
+ return self.api(self.papi.gbp_endpoint_group_dump, {})
+
+ def gbp_recirc_add_del(self, is_add, sw_if_index, epg, is_ext):
+ """ GBP recirc Add/Del """
+ return self.api(self.papi.gbp_recirc_add_del,
+ {'is_add': is_add,
+ 'recirc': {
+ 'is_ext': is_ext,
+ 'sw_if_index': sw_if_index,
+ 'epg_id': epg}})
+
+ def gbp_recirc_dump(self):
+ """ GBP recirc Dump """
+ return self.api(self.papi.gbp_recirc_dump, {})
+
+ def gbp_subnet_add_del(self, is_add, table_id,
+ is_internal,
+ addr, addr_len,
+ sw_if_index=0xffffffff,
+ epg_id=0xffffffff,
+ is_ip6=False):
+ """ GBP Subnet Add/Del """
+ return self.api(self.papi.gbp_subnet_add_del,
+ {'is_add': is_add,
+ 'subnet': {
+ 'is_internal': is_internal,
+ 'is_ip6': is_ip6,
+ 'sw_if_index': sw_if_index,
+ 'epg_id': epg_id,
+ 'address': addr,
+ 'address_length': addr_len,
+ 'table_id': table_id}})
+
+ def gbp_subnet_dump(self):
+ """ GBP Subnet Dump """
+ return self.api(self.papi.gbp_subnet_dump, {})
+
def gbp_contract_add_del(self, is_add, src_epg, dst_epg, acl_index):
""" GBP contract Add/Del """
return self.api(self.papi.gbp_contract_add_del,