From 25b049484fcf9161edb2c19250066b893c38c264 Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Wed, 4 Apr 2018 09:34:50 -0700 Subject: [PATCH] GBP V2 update the GBP plugin to implement the full NAT feature set of opflex agent Change-Id: Ic06a039c889445ed0b9087fa1f292634192b0f8d Signed-off-by: Neale Ranns --- src/plugins/gbp.am | 13 +- src/plugins/gbp/gbp.api | 90 ++- src/plugins/gbp/gbp.c | 782 -------------------- src/plugins/gbp/gbp.h | 104 +-- src/plugins/gbp/gbp_all_api_h.h | 2 +- src/plugins/gbp/gbp_api.c | 230 +++++- src/plugins/gbp/gbp_classify.c | 276 +++++++ src/plugins/gbp/gbp_contract.c | 163 +++++ src/plugins/gbp/gbp_contract.h | 102 +++ src/plugins/gbp/gbp_endpoint.c | 393 ++++++++++ src/plugins/gbp/gbp_endpoint.h | 111 +++ src/plugins/gbp/gbp_endpoint_group.c | 263 +++++++ src/plugins/gbp/gbp_endpoint_group.h | 132 ++++ src/plugins/gbp/gbp_fwd.c | 195 +++++ src/plugins/gbp/gbp_fwd_dpo.c | 308 ++++++++ src/plugins/gbp/gbp_fwd_dpo.h | 62 ++ src/plugins/gbp/gbp_msg_enum.h | 2 +- src/plugins/gbp/gbp_policy.c | 231 ++++++ src/plugins/gbp/gbp_policy_dpo.c | 537 ++++++++++++++ src/plugins/gbp/gbp_policy_dpo.h | 70 ++ src/plugins/gbp/gbp_recirc.c | 217 ++++++ src/plugins/gbp/gbp_recirc.h | 74 ++ src/plugins/gbp/gbp_subnet.c | 176 +++++ src/plugins/gbp/gbp_subnet.h | 41 ++ src/plugins/gbp/gbp_types.h | 32 + src/vnet/buffer.h | 8 +- src/vnet/ethernet/arp.c | 15 +- src/vnet/ethernet/arp_packet.h | 5 +- src/vnet/fib/fib_table.c | 11 + src/vnet/fib/fib_table.h | 15 + src/vnet/ip/ip6.h | 5 +- src/vnet/ip/ip6_neighbor.c | 14 +- src/vnet/l2/l2_input.c | 3 +- src/vnet/l2/l2_input.h | 3 + src/vnet/l2/l2_output.h | 1 + src/vpp-api/vom/Makefile.am | 15 +- src/vpp-api/vom/acl_l2_rule.cpp | 2 +- src/vpp-api/vom/acl_l2_rule.hpp | 2 +- src/vpp-api/vom/acl_l3_rule.cpp | 2 +- src/vpp-api/vom/acl_l3_rule.hpp | 2 +- src/vpp-api/vom/acl_types.hpp | 13 +- src/vpp-api/vom/bridge_domain.cpp | 56 +- src/vpp-api/vom/bridge_domain.hpp | 67 +- src/vpp-api/vom/bridge_domain_cmds.cpp | 16 +- src/vpp-api/vom/bridge_domain_cmds.hpp | 17 +- src/vpp-api/vom/gbp_endpoint.cpp | 44 +- src/vpp-api/vom/gbp_endpoint.hpp | 24 +- src/vpp-api/vom/gbp_endpoint_cmds.cpp | 5 +- src/vpp-api/vom/gbp_endpoint_cmds.hpp | 2 + src/vpp-api/vom/gbp_endpoint_group.cpp | 198 ++++++ src/vpp-api/vom/gbp_endpoint_group.hpp | 205 ++++++ src/vpp-api/vom/gbp_endpoint_group_cmds.cpp | 147 ++++ src/vpp-api/vom/gbp_endpoint_group_cmds.hpp | 139 ++++ src/vpp-api/vom/gbp_recirc.cpp | 200 ++++++ src/vpp-api/vom/gbp_recirc.hpp | 209 ++++++ src/vpp-api/vom/gbp_recirc_cmds.cpp | 142 ++++ src/vpp-api/vom/gbp_recirc_cmds.hpp | 137 ++++ src/vpp-api/vom/gbp_subnet.cpp | 231 ++++++ src/vpp-api/vom/gbp_subnet.hpp | 218 ++++++ src/vpp-api/vom/gbp_subnet_cmds.cpp | 160 +++++ src/vpp-api/vom/gbp_subnet_cmds.hpp | 144 ++++ src/vpp-api/vom/interface.cpp | 12 +- src/vpp-api/vom/interface_types.cpp | 3 +- src/vpp-api/vom/nat_binding_cmds.hpp | 232 ++++++ src/vpp-api/vom/nat_static.cpp | 50 +- src/vpp-api/vom/nat_static.hpp | 8 +- src/vpp-api/vom/nat_static_cmds.cpp | 119 ++++ src/vpp-api/vom/nat_static_cmds.hpp | 105 +++ src/vpp-api/vom/route.cpp | 2 +- src/vpp-api/vom/types.cpp | 3 - src/vpp-api/vom/types.hpp | 2 +- test/ext/vom_test.cpp | 27 +- test/test_gbp.py | 1029 ++++++++++++++++++++++----- test/vpp_papi_provider.py | 63 ++ 74 files changed, 7570 insertions(+), 1168 deletions(-) delete mode 100644 src/plugins/gbp/gbp.c create mode 100644 src/plugins/gbp/gbp_classify.c create mode 100644 src/plugins/gbp/gbp_contract.c create mode 100644 src/plugins/gbp/gbp_contract.h create mode 100644 src/plugins/gbp/gbp_endpoint.c create mode 100644 src/plugins/gbp/gbp_endpoint.h create mode 100644 src/plugins/gbp/gbp_endpoint_group.c create mode 100644 src/plugins/gbp/gbp_endpoint_group.h create mode 100644 src/plugins/gbp/gbp_fwd.c create mode 100644 src/plugins/gbp/gbp_fwd_dpo.c create mode 100644 src/plugins/gbp/gbp_fwd_dpo.h create mode 100644 src/plugins/gbp/gbp_policy.c create mode 100644 src/plugins/gbp/gbp_policy_dpo.c create mode 100644 src/plugins/gbp/gbp_policy_dpo.h create mode 100644 src/plugins/gbp/gbp_recirc.c create mode 100644 src/plugins/gbp/gbp_recirc.h create mode 100644 src/plugins/gbp/gbp_subnet.c create mode 100644 src/plugins/gbp/gbp_subnet.h create mode 100644 src/plugins/gbp/gbp_types.h create mode 100644 src/vpp-api/vom/gbp_endpoint_group.cpp create mode 100644 src/vpp-api/vom/gbp_endpoint_group.hpp create mode 100644 src/vpp-api/vom/gbp_endpoint_group_cmds.cpp create mode 100644 src/vpp-api/vom/gbp_endpoint_group_cmds.hpp create mode 100644 src/vpp-api/vom/gbp_recirc.cpp create mode 100644 src/vpp-api/vom/gbp_recirc.hpp create mode 100644 src/vpp-api/vom/gbp_recirc_cmds.cpp create mode 100644 src/vpp-api/vom/gbp_recirc_cmds.hpp create mode 100644 src/vpp-api/vom/gbp_subnet.cpp create mode 100644 src/vpp-api/vom/gbp_subnet.hpp create mode 100644 src/vpp-api/vom/gbp_subnet_cmds.cpp create mode 100644 src/vpp-api/vom/gbp_subnet_cmds.hpp 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 - -/** - * 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] epg ip } - * @cliexend - ?*/ -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (gbp_endpoint_cli_node, static) = { - .path = "gbp endpoint", - .short_help = "gbp endpoint [del] epg ip ", - .function = gbp_endpoint_cli, -}; - -/*? - * Configure a GBP Contract - * - * @cliexpar - * @cliexstart{set gbp contract [del] src-epg dst-epg acl-index } - * @cliexend - ?*/ -VLIB_CLI_COMMAND (gbp_contract_cli_node, static) = { - .path = "gbp contract", - .short_help = "gbp contract [del] src-epg dst-epg acl-index ", - .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 -#include -#include - -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 +#include +#include +#include +#include +#include #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 { @@ -162,6 +167,223 @@ vl_api_gbp_endpoint_dump_t_handler (vl_api_gbp_endpoint_dump_t * mp) gbp_endpoint_walk (gbp_endpoint_send_details, &ctx); } +static void + vl_api_gbp_endpoint_group_add_del_t_handler + (vl_api_gbp_endpoint_group_add_del_t * mp) +{ + vl_api_gbp_endpoint_group_add_del_reply_t *rmp; + u32 uplink_sw_if_index; + int rv = 0; + + uplink_sw_if_index = ntohl (mp->epg.uplink_sw_if_index); + if (!vnet_sw_if_index_is_api_valid (uplink_sw_if_index)) + goto bad_sw_if_index; + + if (mp->is_add) + { + rv = gbp_endpoint_group_add (ntohl (mp->epg.epg_id), + ntohl (mp->epg.bd_id), + ntohl (mp->epg.ip4_table_id), + ntohl (mp->epg.ip6_table_id), + uplink_sw_if_index); + } + else + { + gbp_endpoint_group_delete (ntohl (mp->epg.epg_id)); + } + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_GBP_ENDPOINT_GROUP_ADD_DEL_REPLY + GBP_MSG_BASE); +} + +static void +vl_api_gbp_subnet_add_del_t_handler (vl_api_gbp_subnet_add_del_t * mp) +{ + vl_api_gbp_subnet_add_del_reply_t *rmp; + int rv = 0; + fib_prefix_t pfx = { + .fp_len = mp->subnet.address_length, + .fp_proto = (mp->subnet.is_ip6 ? FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4), + }; + + if (mp->subnet.is_ip6) + clib_memcpy (&pfx.fp_addr.ip6, mp->subnet.address, + sizeof (pfx.fp_addr.ip6)); + else + clib_memcpy (&pfx.fp_addr.ip4, mp->subnet.address, + sizeof (pfx.fp_addr.ip4)); + + rv = gbp_subnet_add_del (ntohl (mp->subnet.table_id), + &pfx, + ntohl (mp->subnet.sw_if_index), + ntohl (mp->subnet.epg_id), + mp->is_add, mp->subnet.is_internal); + + REPLY_MACRO (VL_API_GBP_SUBNET_ADD_DEL_REPLY + GBP_MSG_BASE); +} + +static int +gbp_subnet_send_details (u32 table_id, + const fib_prefix_t * pfx, + u32 sw_if_index, + epg_id_t epg, u8 is_internal, void *args) +{ + vl_api_gbp_subnet_details_t *mp; + gbp_walk_ctx_t *ctx; + + ctx = args; + mp = vl_msg_api_alloc (sizeof (*mp)); + if (!mp) + return 1; + + memset (mp, 0, sizeof (*mp)); + mp->_vl_msg_id = ntohs (VL_API_GBP_SUBNET_DETAILS + GBP_MSG_BASE); + mp->context = ctx->context; + + mp->subnet.is_internal = is_internal; + mp->subnet.sw_if_index = ntohl (sw_if_index); + mp->subnet.epg_id = ntohl (epg); + mp->subnet.is_ip6 = (pfx->fp_proto == FIB_PROTOCOL_IP6); + mp->subnet.address_length = pfx->fp_len; + mp->subnet.table_id = ntohl (table_id); + if (mp->subnet.is_ip6) + clib_memcpy (&mp->subnet.address, + &pfx->fp_addr.ip6, sizeof (pfx->fp_addr.ip6)); + else + clib_memcpy (&mp->subnet.address, + &pfx->fp_addr.ip4, sizeof (pfx->fp_addr.ip4)); + + + vl_api_send_msg (ctx->reg, (u8 *) mp); + + return (1); +} + +static void +vl_api_gbp_subnet_dump_t_handler (vl_api_gbp_subnet_dump_t * mp) +{ + vl_api_registration_t *reg; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + gbp_walk_ctx_t ctx = { + .reg = reg, + .context = mp->context, + }; + + gbp_subnet_walk (gbp_subnet_send_details, &ctx); +} + +static int +gbp_endpoint_group_send_details (gbp_endpoint_group_t * gepg, void *args) +{ + vl_api_gbp_endpoint_group_details_t *mp; + gbp_walk_ctx_t *ctx; + + ctx = args; + mp = vl_msg_api_alloc (sizeof (*mp)); + if (!mp) + return 1; + + memset (mp, 0, sizeof (*mp)); + mp->_vl_msg_id = ntohs (VL_API_GBP_ENDPOINT_GROUP_DETAILS + GBP_MSG_BASE); + mp->context = ctx->context; + + mp->epg.uplink_sw_if_index = ntohl (gepg->gepg_uplink_sw_if_index); + mp->epg.epg_id = ntohl (gepg->gepg_id); + mp->epg.bd_id = ntohl (gepg->gepg_bd); + mp->epg.ip4_table_id = ntohl (gepg->gepg_rd[FIB_PROTOCOL_IP4]); + mp->epg.ip6_table_id = ntohl (gepg->gepg_rd[FIB_PROTOCOL_IP6]); + + vl_api_send_msg (ctx->reg, (u8 *) mp); + + return (1); +} + +static void +vl_api_gbp_endpoint_group_dump_t_handler (vl_api_gbp_endpoint_group_dump_t * + mp) +{ + vl_api_registration_t *reg; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + gbp_walk_ctx_t ctx = { + .reg = reg, + .context = mp->context, + }; + + gbp_endpoint_group_walk (gbp_endpoint_group_send_details, &ctx); +} + +static void +vl_api_gbp_recirc_add_del_t_handler (vl_api_gbp_recirc_add_del_t * mp) +{ + vl_api_gbp_recirc_add_del_reply_t *rmp; + u32 sw_if_index; + int rv = 0; + + sw_if_index = ntohl (mp->recirc.sw_if_index); + if (!vnet_sw_if_index_is_api_valid (sw_if_index)) + goto bad_sw_if_index; + + if (mp->is_add) + gbp_recirc_add (sw_if_index, + ntohl (mp->recirc.epg_id), mp->recirc.is_ext); + else + gbp_recirc_delete (sw_if_index); + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_GBP_RECIRC_ADD_DEL_REPLY + GBP_MSG_BASE); +} + +static int +gbp_recirc_send_details (gbp_recirc_t * gr, void *args) +{ + vl_api_gbp_recirc_details_t *mp; + gbp_walk_ctx_t *ctx; + + ctx = args; + mp = vl_msg_api_alloc (sizeof (*mp)); + if (!mp) + return 1; + + memset (mp, 0, sizeof (*mp)); + mp->_vl_msg_id = ntohs (VL_API_GBP_RECIRC_DETAILS + GBP_MSG_BASE); + mp->context = ctx->context; + + mp->recirc.epg_id = ntohl (gr->gr_epg); + mp->recirc.sw_if_index = ntohl (gr->gr_sw_if_index); + mp->recirc.is_ext = ntohl (gr->gr_is_ext); + + vl_api_send_msg (ctx->reg, (u8 *) mp); + + return (1); +} + +static void +vl_api_gbp_recirc_dump_t_handler (vl_api_gbp_recirc_dump_t * mp) +{ + vl_api_registration_t *reg; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + gbp_walk_ctx_t ctx = { + .reg = reg, + .context = mp->context, + }; + + gbp_recirc_walk (gbp_recirc_send_details, &ctx); +} + static void vl_api_gbp_contract_add_del_t_handler (vl_api_gbp_contract_add_del_t * mp) { diff --git a/src/plugins/gbp/gbp_classify.c b/src/plugins/gbp/gbp_classify.c new file mode 100644 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 + +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 + +/** + * 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 dst-epg acl-index } + * @cliexend + ?*/ +VLIB_CLI_COMMAND (gbp_contract_cli_node, static) = +{ +.path = "gbp contract",.short_help = + "gbp contract [del] src-epg dst-epg acl-index ",.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 + +/** + * 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 +#include + +#include + +/** + * 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] epg ip } + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_endpoint_cli_node, static) = { + .path = "gbp endpoint", + .short_help = "gbp endpoint [del] epg 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 +#include + +/** + * 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 +#include + +#include +#include + +/** + * 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 bd } + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_endpoint_group_cli_node, static) = { + .path = "gbp endpoint-group", + .short_help = "gbp endpoint-group [del] epg bd ", + .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 + +#include + +/** + * 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 + +/** + * 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 +#include + +#include + +/** + * 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 + +/** + * @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 + +/** + * 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 +#include +#include + +#include +#include +#include +#include + +/** + * 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 + +/** + * @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 +#include +#include + +#include +#include + +/** + * 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 +#include + +/** + * 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 +#include +#include + +#include +#include + +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 + +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 + +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 @@ -1033,6 +1033,17 @@ fib_table_get_table_id_for_sw_if_index (fib_protocol_t proto, return ((NULL != fib_table ? fib_table->ft_table_id : ~0)); } +u32 +fib_table_get_table_id (u32 fib_index, + fib_protocol_t proto) +{ + fib_table_t *fib_table; + + fib_table = fib_table_get(fib_index, proto); + + return ((NULL != fib_table ? fib_table->ft_table_id : ~0)); +} + u32 fib_table_find (fib_protocol_t proto, u32 table_id) 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 @@ -643,6 +643,21 @@ extern u32 fib_table_get_index_for_sw_if_index(fib_protocol_t proto, extern u32 fib_table_get_table_id_for_sw_if_index(fib_protocol_t proto, u32 sw_if_index); +/** + * @brief + * Get the Table-ID of the FIB from protocol and index + * + * @param fib_index + * The FIB index + * + * @paran proto + * The protocol of the FIB (and thus the entries therein) + * + * @return fib_index + * The tableID of the FIB + */ +extern u32 fib_table_get_table_id(u32 fib_index, fib_protocol_t proto); + /** * @brief * Get the index of the FIB for a Table-ID. This DOES NOT create the 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 @@ -25,16 +25,6 @@ namespace ACL { */ struct action_t : public enum_base { - /** - * Constructor - */ - action_t(int v, const std::string s); - - /** - * Destructor - */ - ~action_t() = default; - /** * Permit and Reflexive */ @@ -60,6 +50,9 @@ struct action_t : public enum_base *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(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(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(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 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 @@ -51,6 +51,51 @@ public: learning_mode_t(int v, const std::string& s); }; + /** + * Bridge Domain ARP termination mode + */ + struct arp_term_mode_t : enum_base + { + 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 + { + 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 + { + const static flood_mode_t ON; + const static flood_mode_t OFF; + + private: + /** + * Private constructor taking the value and the string name + */ + flood_mode_t(int v, const std::string& s); + }; + /** * The value of the defaultbridge domain */ @@ -60,7 +105,10 @@ public: * Construct a new object matching the desried state */ bridge_domain(uint32_t id, - const learning_mode_t& lmode = learning_mode_t::ON); + const learning_mode_t& lmode = learning_mode_t::ON, + const arp_term_mode_t& amode = arp_term_mode_t::ON, + const flood_mode_t& fmode = flood_mode_t::ON, + const mac_age_mode_t& mmode = mac_age_mode_t::OFF); /** * Copy Constructor @@ -179,10 +227,25 @@ private: HW::item 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 */ 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& 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& 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 itf = interface::find(payload.endpoint.sw_if_index); + std::shared_ptr 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 +#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 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& 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& 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::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::find_or_add(const gbp_endpoint_group& temp) +{ + return (m_db.find_or_add(temp.key(), temp)); +} + +std::shared_ptr +gbp_endpoint_group::find(const key_t& k) +{ + return (m_db.find(k)); +} + +std::shared_ptr +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 cmd = + std::make_shared(); + + HW::enqueue(cmd); + HW::write(); + + for (auto& record : *cmd) { + auto& payload = record.get_payload(); + + std::shared_ptr itf = + interface::find(payload.epg.uplink_sw_if_index); + std::shared_ptr rd = + route_domain::find(payload.epg.ip4_table_id); + std::shared_ptr 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 singular() const; + + /** + * Find the instnace of the bridge_domain domain in the OM + */ + static std::shared_ptr 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 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; + + /** + * Sweep/reap the object if still stale + */ + void sweep(void); + + /** + * HW configuration for the result of creating the endpoint_group + */ + HW::item m_hw; + + /** + * The EPG ID + */ + epg_id_t m_epg_id; + + /** + * The uplink interface for the endpoint group + */ + std::shared_ptr m_itf; + + /** + * The route-domain the EPG uses + */ + std::shared_ptr m_rd; + + /** + * The bridge-domain the EPG uses + */ + std::shared_ptr m_bd; + + /** + * A map of all bridge_domains + */ + static singular_db 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& 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& 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 + +namespace VOM { +namespace gbp_endpoint_group_cmds { + +/** +* A command class that creates or updates the GBP endpoint_group +*/ +class create_cmd + : public rpc_cmd, rc_t, vapi::Gbp_endpoint_group_add_del> +{ +public: + /** + * Constructor + */ + create_cmd(HW::item& 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, rc_t, vapi::Gbp_endpoint_group_add_del> +{ +public: + /** + * Constructor + */ + delete_cmd(HW::item& 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 +{ +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 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(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::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::find_or_add(const gbp_recirc& temp) +{ + return (m_db.find_or_add(temp.key(), temp)); +} + +std::shared_ptr +gbp_recirc::find(const key_t& k) +{ + return (m_db.find(k)); +} + +std::shared_ptr +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 cmd = + std::make_shared(); + + HW::enqueue(cmd); + HW::write(); + + for (auto& record : *cmd) { + auto& payload = record.get_payload(); + + std::shared_ptr itf = + interface::find(payload.recirc.sw_if_index); + std::shared_ptr 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 + { + /** + * 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 singular() const; + + /** + * Find the instnace of the recirc interface in the OM + */ + static std::shared_ptr 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 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; + + /** + * Sweep/reap the object if still stale + */ + void sweep(void); + + /** + * HW configuration for the result of creating the recirc + */ + HW::item m_hw; + + /** + * The interface the recirc is attached to. + */ + std::shared_ptr 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 m_epg; + + /** + * A map of all bridge_domains + */ + static singular_db 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& 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& 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 + +namespace VOM { +namespace gbp_recirc_cmds { + +/** +* A command class that creates or updates the GBP recirc +*/ +class create_cmd + : public rpc_cmd, rc_t, vapi::Gbp_recirc_add_del> +{ +public: + /** + * Constructor + */ + create_cmd(HW::item& 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, rc_t, vapi::Gbp_recirc_add_del> +{ +public: + /** + * Constructor + */ + delete_cmd(HW::item& 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 +{ +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 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(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::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::find_or_add(const gbp_subnet& temp) +{ + return (m_db.find_or_add(temp.key(), temp)); +} + +std::shared_ptr +gbp_subnet::find(const key_t& k) +{ + return (m_db.find(k)); +} + +std::shared_ptr +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 cmd = + std::make_shared(); + + 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 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 itf = + interface::find(payload.subnet.sw_if_index); + std::shared_ptr epg = + gbp_endpoint_group::find(payload.subnet.epg_id); + + if (itf && epg) { + std::shared_ptr 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 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 singular() const; + + /** + * Find the instnace of the bridge_domain domain in the OM + */ + static std::shared_ptr 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 + { + /** + * 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 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; + + /** + * Sweep/reap the object if still stale + */ + void sweep(void); + + /** + * HW configuration for the result of creating the subnet + */ + HW::item m_hw; + + /** + * the route domain the prefix is in + */ + std::shared_ptr 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 m_recirc; + + /** + * The EPG the subnet is in + */ + std::shared_ptr m_epg; + + /** + * A map of all bridge_domains + */ + static singular_db 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& 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& 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 + +namespace VOM { +namespace gbp_subnet_cmds { + +/** +* A command class that creates or updates the GBP subnet +*/ +class create_cmd + : public rpc_cmd, rc_t, vapi::Gbp_subnet_add_del> +{ +public: + /** + * Constructor + */ + create_cmd(HW::item& 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, rc_t, vapi::Gbp_subnet_add_del> +{ +public: + /** + * Constructor + */ + delete_cmd(HW::item& 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 +{ +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 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& interface::mk_create_cmd(std::queue& 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 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 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 item; }; +///// +/** +* A functor class that binds a NAT configuration to an input interface +*/ +class bind_66_input_cmd + : public rpc_cmd, rc_t, vapi::Nat66_add_del_interface> +{ +public: + /** + * Constructor + */ + bind_66_input_cmd(HW::item& 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, rc_t, vapi::Nat66_add_del_interface> +{ +public: + /** + * Constructor + */ + unbind_66_input_cmd(HW::item& 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, */ +/* rc_t, */ +/* vapi::Nat66_interface_add_del_output_feature> */ +/* { */ +/* public: */ +/* /\** */ +/* * Constructor */ +/* *\/ */ +/* bind_66_output_cmd(HW::item& 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, */ +/* rc_t, */ +/* vapi::Nat66_interface_add_del_output_feature> */ +/* { */ +/* public: */ +/* /\** */ +/* * Constructor */ +/* *\/ */ +/* unbind_66_output_cmd(HW::item& 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 +{ +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 item; +}; + +/** + * A cmd class that Dumps all the nat_statics + */ +/* class dump_output_66_cmd */ +/* : public dump_cmd */ +/* { */ +/* 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 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::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 cmd = + std::shared_ptr cmd44 = std::make_shared(); - 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 cmd66 = + std::make_shared(); + + 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 @@ -143,6 +143,125 @@ dump_44_cmd::issue(connection& con) std::string dump_44_cmd::to_string() const +{ + return ("nat-44-static-dump"); +} + +create_66_cmd::create_66_cmd(HW::item& 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& 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 item; }; +/** + * A command class that creates NAT 66 static mapping + */ +class create_66_cmd + : public rpc_cmd, rc_t, vapi::Nat66_add_del_static_mapping> +{ +public: + /** + * Constructor + */ + create_66_cmd(HW::item& 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, rc_t, vapi::Nat66_add_del_static_mapping> +{ +public: + /** + * Constructor + */ + delete_66_cmd(HW::item& 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 +{ +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 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(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 /** * 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 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 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 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, -- 2.16.6