diff options
Diffstat (limited to 'src/plugins')
25 files changed, 3720 insertions, 886 deletions
diff --git a/src/plugins/gbp.am b/src/plugins/gbp.am index 644d08ccb49..7271a0de45c 100644 --- a/src/plugins/gbp.am +++ b/src/plugins/gbp.am @@ -13,8 +13,17 @@ vppplugins_LTLIBRARIES += gbp_plugin.la -gbp_plugin_la_SOURCES = \ - gbp/gbp.c \ +gbp_plugin_la_SOURCES = \ + gbp/gbp_subnet.c \ + gbp/gbp_contract.c \ + gbp/gbp_endpoint.c \ + gbp/gbp_endpoint_group.c \ + gbp/gbp_classify.c \ + gbp/gbp_recirc.c \ + gbp/gbp_policy.c \ + gbp/gbp_policy_dpo.c \ + gbp/gbp_fwd.c \ + gbp/gbp_fwd_dpo.c \ gbp/gbp_api.c API_FILES += gbp/gbp.api diff --git a/src/plugins/gbp/gbp.api b/src/plugins/gbp/gbp.api index 491ad363c75..b2b32e90d4b 100644 --- a/src/plugins/gbp/gbp.api +++ b/src/plugins/gbp/gbp.api @@ -1,6 +1,6 @@ /* Hey Emacs use -*- mode: C -*- */ /* - * Copyright (c) 2016 Cisco and/or its affiliates. + * Copyright (c) 2018 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -27,6 +27,7 @@ typeonly define gbp_endpoint u32 epg_id; u8 is_ip6; u8 address[16]; + u8 mac[6]; }; autoreply define gbp_endpoint_add_del @@ -49,6 +50,93 @@ define gbp_endpoint_details vl_api_gbp_endpoint_t endpoint; }; +typeonly define gbp_endpoint_group +{ + u32 epg_id; + u32 bd_id; + u32 ip4_table_id; + u32 ip6_table_id; + u32 uplink_sw_if_index; +}; + +autoreply define gbp_endpoint_group_add_del +{ + u32 client_index; + u32 context; + u8 is_add; + vl_api_gbp_endpoint_group_t epg; +}; + +define gbp_endpoint_group_dump +{ + u32 client_index; + u32 context; +}; + +define gbp_endpoint_group_details +{ + u32 context; + vl_api_gbp_endpoint_group_t epg; +}; + +typeonly define gbp_recirc +{ + u32 epg_id; + u32 sw_if_index; + u8 is_ext; +}; + +autoreply define gbp_recirc_add_del +{ + u32 client_index; + u32 context; + u8 is_add; + vl_api_gbp_recirc_t recirc; +}; + +define gbp_recirc_dump +{ + u32 client_index; + u32 context; +}; + +define gbp_recirc_details +{ + u32 context; + vl_api_gbp_recirc_t recirc; +}; + +typeonly define gbp_subnet +{ + u32 table_id; + u32 sw_if_index; + u32 epg_id; + u8 is_ip6; + u8 is_internal; + u8 address_length; + u8 address[16]; +}; + +autoreply define gbp_subnet_add_del +{ + u32 client_index; + u32 context; + u8 is_add; + vl_api_gbp_subnet_t subnet; +}; + +define gbp_subnet_dump +{ + u32 client_index; + u32 context; +}; + +define gbp_subnet_details +{ + u32 context; + vl_api_gbp_subnet_t subnet; +}; + typeonly define gbp_contract { u32 src_epg; diff --git a/src/plugins/gbp/gbp.c b/src/plugins/gbp/gbp.c deleted file mode 100644 index 0b87d0b4d89..00000000000 --- a/src/plugins/gbp/gbp.c +++ /dev/null @@ -1,782 +0,0 @@ -/* - * gbp.h : Group Based Policy - * - * Copyright (c) 2013 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <plugins/gbp/gbp.h> - -/** - * IP4 destintion address to destination EPG mapping table - */ -typedef struct gbp_ip4_to_epg_db_t_ -{ - /** - * use a simple hash table - */ - uword *g4ie_hash; -} gbp_ip4_to_epg_db_t; - -static gbp_ip4_to_epg_db_t gbp_ip4_to_epg_db; - -/** - * IP6 destintion address to destination EPG mapping table - */ -typedef struct gbp_ip6_to_epg_db_t_ -{ - /** - * use a memroy hash table - */ - uword *g6ie_hash; -} gbp_ip6_to_epg_db_t; - -static gbp_ip6_to_epg_db_t gbp_ip6_to_epg_db; - -/** - * Result of a interface to EPG mapping. - * multiple Endpoints can occur on the same interface, so this - * mapping needs to be reference counted. - */ -typedef struct gbp_itf_t_ -{ - epg_id_t gi_epg; - u32 gi_ref_count; -} gbp_itf_t; - -const static gbp_itf_t ITF_INVALID = { - .gi_epg = EPG_INVALID, - .gi_ref_count = 0, -}; - -/** - * Interface to source EPG DB - a per-interface vector - */ -typedef struct gbp_itf_to_epg_db_t_ -{ - gbp_itf_t *gte_vec; -} gbp_itf_to_epg_db_t; - -static gbp_itf_to_epg_db_t gbp_itf_to_epg_db; - -/** - * Pool of GBP endpoints - */ -static gbp_endpoint_t *gbp_endpoint_pool; - -/** - * DB of endpoints - */ -static uword *gbp_endpoint_db; - -/** - * EPG src,dst pair to ACL mapping table, aka contract DB - */ -typedef struct gbp_contract_db_t_ -{ - /** - * We can form a u64 key from the pair, so use a simple hash table - */ - uword *gc_hash; -} gbp_contract_db_t; - -/** - * Since contract DB instance - */ -static gbp_contract_db_t gbp_contract_db; - -static void -gbp_ip_epg_update (const ip46_address_t * ip, epg_id_t epg_id) -{ - /* - * we are dealing only with addresses here so this limited - * is_ip4 check is ok - */ - if (ip46_address_is_ip4 (ip)) - { - hash_set (gbp_ip4_to_epg_db.g4ie_hash, ip->ip4.as_u32, epg_id); - } - else - { - hash_set_mem (gbp_ip6_to_epg_db.g6ie_hash, &ip->ip6, epg_id); - } -} - -static void -gbp_ip_epg_delete (const ip46_address_t * ip) -{ - if (ip46_address_is_ip4 (ip)) - { - hash_unset (gbp_ip4_to_epg_db.g4ie_hash, ip->ip4.as_u32); - } - else - { - hash_unset_mem (gbp_ip6_to_epg_db.g6ie_hash, &ip->ip6); - } -} - -static void -gbp_itf_epg_update (u32 sw_if_index, epg_id_t src_epg) -{ - vec_validate_init_empty (gbp_itf_to_epg_db.gte_vec, - sw_if_index, ITF_INVALID); - - if (0 == gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count) - { - vnet_feature_enable_disable ("ip4-unicast", "gbp4", - sw_if_index, 1, NULL, 0); - vnet_feature_enable_disable ("ip6-unicast", "gbp6", - sw_if_index, 1, NULL, 0); - } - gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg = src_epg; - gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count++; -} - -static void -gbp_itf_epg_delete (u32 sw_if_index) -{ - if (vec_len (gbp_itf_to_epg_db.gte_vec) <= sw_if_index) - return; - - if (1 == gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count) - { - gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg = EPG_INVALID; - - vnet_feature_enable_disable ("ip4-unicast", "gbp4", - sw_if_index, 0, NULL, 0); - vnet_feature_enable_disable ("ip6-unicast", "gbp6", - sw_if_index, 0, NULL, 0); - } - gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count--; -} - -void -gbp_endpoint_update (u32 sw_if_index, - const ip46_address_t * ip, epg_id_t epg_id) -{ - gbp_endpoint_key_t key = { - .gek_ip = *ip, - .gek_sw_if_index = sw_if_index, - }; - gbp_endpoint_t *gbpe; - uword *p; - - p = hash_get_mem (gbp_endpoint_db, &key); - - if (p) - { - gbpe = pool_elt_at_index (gbp_endpoint_pool, p[0]); - } - else - { - pool_get (gbp_endpoint_pool, gbpe); - - gbpe->ge_key = clib_mem_alloc (sizeof (gbp_endpoint_key_t)); - clib_memcpy (gbpe->ge_key, &key, sizeof (gbp_endpoint_key_t)); - - hash_set_mem (gbp_endpoint_db, gbpe->ge_key, gbpe - gbp_endpoint_pool); - } - - gbpe->ge_epg_id = epg_id; - - gbp_itf_epg_update (gbpe->ge_key->gek_sw_if_index, gbpe->ge_epg_id); - gbp_ip_epg_update (&gbpe->ge_key->gek_ip, gbpe->ge_epg_id); -} - -void -gbp_endpoint_delete (u32 sw_if_index, const ip46_address_t * ip) -{ - gbp_endpoint_key_t key = { - .gek_ip = *ip, - .gek_sw_if_index = sw_if_index, - }; - gbp_endpoint_t *gbpe; - uword *p; - - p = hash_get_mem (gbp_endpoint_db, &key); - - if (p) - { - gbpe = pool_elt_at_index (gbp_endpoint_pool, p[0]); - - hash_unset_mem (gbp_endpoint_db, gbpe->ge_key); - - gbp_itf_epg_delete (gbpe->ge_key->gek_sw_if_index); - gbp_ip_epg_delete (&gbpe->ge_key->gek_ip); - - clib_mem_free (gbpe->ge_key); - - pool_put (gbp_endpoint_pool, gbpe); - } -} - -void -gbp_endpoint_walk (gbp_endpoint_cb_t cb, void *ctx) -{ - gbp_endpoint_t *gbpe; - - /* *INDENT-OFF* */ - pool_foreach(gbpe, gbp_endpoint_pool, - { - if (!cb(gbpe, ctx)) - break; - }); - /* *INDENT-ON* */ -} - -void -gbp_contract_update (epg_id_t src_epg, epg_id_t dst_epg, u32 acl_index) -{ - gbp_contract_key_t key = { - .gck_src = src_epg, - .gck_dst = dst_epg, - }; - - hash_set (gbp_contract_db.gc_hash, key.as_u64, acl_index); -} - -void -gbp_contract_delete (epg_id_t src_epg, epg_id_t dst_epg) -{ - gbp_contract_key_t key = { - .gck_src = src_epg, - .gck_dst = dst_epg, - }; - - hash_unset (gbp_contract_db.gc_hash, key.as_u64); -} - -void -gbp_contract_walk (gbp_contract_cb_t cb, void *ctx) -{ - gbp_contract_key_t key; - u32 acl_index; - - /* *INDENT-OFF* */ - hash_foreach(key.as_u64, acl_index, gbp_contract_db.gc_hash, - ({ - gbp_contract_t gbpc = { - .gc_key = key, - .gc_acl_index = acl_index, - }; - - if (!cb(&gbpc, ctx)) - break; - })); - /* *INDENT-ON* */ -} - -static clib_error_t * -gbp_endpoint_cli (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - vnet_main_t *vnm = vnet_get_main (); - epg_id_t epg_id = EPG_INVALID; - ip46_address_t ip = { }; - u32 sw_if_index = ~0; - u8 add = 1; - - while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (input, "%U", unformat_vnet_sw_interface, - vnm, &sw_if_index)) - ; - else if (unformat (input, "add")) - add = 1; - else if (unformat (input, "del")) - add = 0; - else if (unformat (input, "epg %d", &epg_id)) - ; - else if (unformat (input, "ip %U", unformat_ip4_address, &ip.ip4)) - ; - else if (unformat (input, "ip %U", unformat_ip6_address, &ip.ip6)) - ; - else - break; - } - - if (~0 == sw_if_index) - return clib_error_return (0, "interface must be specified"); - if (EPG_INVALID == epg_id) - return clib_error_return (0, "EPG-ID must be specified"); - if (ip46_address_is_zero (&ip)) - return clib_error_return (0, "IP address must be specified"); - - if (add) - gbp_endpoint_update (sw_if_index, &ip, epg_id); - else - gbp_endpoint_delete (sw_if_index, &ip); - - return (NULL); -} - -static clib_error_t * -gbp_contract_cli (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - epg_id_t src_epg_id = EPG_INVALID, dst_epg_id = EPG_INVALID; - u32 acl_index = ~0; - u8 add = 1; - - while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (input, "add")) - add = 1; - else if (unformat (input, "del")) - add = 0; - else if (unformat (input, "src-epg %d", &src_epg_id)) - ; - else if (unformat (input, "dst-epg %d", &dst_epg_id)) - ; - else if (unformat (input, "acl-index %d", &acl_index)) - ; - else - break; - } - - if (EPG_INVALID == src_epg_id) - return clib_error_return (0, "Source EPG-ID must be specified"); - if (EPG_INVALID == dst_epg_id) - return clib_error_return (0, "Destination EPG-ID must be specified"); - - if (add) - { - gbp_contract_update (src_epg_id, dst_epg_id, acl_index); - } - else - { - gbp_contract_delete (src_epg_id, dst_epg_id); - } - - return (NULL); -} - -/*? - * Configure a GBP Endpoint - * - * @cliexpar - * @cliexstart{set gbp endpoint [del] <interface> epg <ID> ip <IP>} - * @cliexend - ?*/ -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (gbp_endpoint_cli_node, static) = { - .path = "gbp endpoint", - .short_help = "gbp endpoint [del] <interface> epg <ID> ip <IP>", - .function = gbp_endpoint_cli, -}; - -/*? - * Configure a GBP Contract - * - * @cliexpar - * @cliexstart{set gbp contract [del] src-epg <ID> dst-epg <ID> acl-index <ACL>} - * @cliexend - ?*/ -VLIB_CLI_COMMAND (gbp_contract_cli_node, static) = { - .path = "gbp contract", - .short_help = "gbp contract [del] src-epg <ID> dst-epg <ID> acl-index <ACL>", - .function = gbp_contract_cli, -}; -/* *INDENT-ON* */ - -static int -gbp_endpoint_show_one (gbp_endpoint_t * gbpe, void *ctx) -{ - vnet_main_t *vnm = vnet_get_main (); - vlib_main_t *vm; - - vm = ctx; - vlib_cli_output (vm, " {%U, %U} -> %d", - format_vnet_sw_if_index_name, vnm, - gbpe->ge_key->gek_sw_if_index, - format_ip46_address, &gbpe->ge_key->gek_ip, IP46_TYPE_ANY, - gbpe->ge_epg_id); - - return (1); -} - -static clib_error_t * -gbp_endpoint_show (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - vnet_main_t *vnm = vnet_get_main (); - ip46_address_t ip, *ipp; - epg_id_t epg_id; - u32 sw_if_index; - - vlib_cli_output (vm, "Endpoints:"); - gbp_endpoint_walk (gbp_endpoint_show_one, vm); - - vlib_cli_output (vm, "\nSource interface to EPG:"); - - vec_foreach_index (sw_if_index, gbp_itf_to_epg_db.gte_vec) - { - if (EPG_INVALID != gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg) - { - vlib_cli_output (vm, " %U -> %d", - format_vnet_sw_if_index_name, vnm, sw_if_index, - gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg); - } - } - - vlib_cli_output (vm, "\nDestination IP4 to EPG:"); - - /* *INDENT-OFF* */ - hash_foreach (ip.ip4.as_u32, epg_id, gbp_ip4_to_epg_db.g4ie_hash, - { - vlib_cli_output (vm, " %U -> %d", format_ip46_address, &ip, - IP46_TYPE_IP4, epg_id); - }); - /* *INDENT-ON* */ - - vlib_cli_output (vm, "\nDestination IP6 to EPG:"); - - /* *INDENT-OFF* */ - hash_foreach_mem (ipp, epg_id, gbp_ip6_to_epg_db.g6ie_hash, - { - vlib_cli_output (vm, " %U -> %d", format_ip46_address, ipp, - IP46_TYPE_IP6, epg_id); - }); - /* *INDENT-ON* */ - - return (NULL); -} - -static clib_error_t * -gbp_contract_show (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - gbp_contract_key_t key; - epg_id_t epg_id; - - vlib_cli_output (vm, "Contracts:"); - - /* *INDENT-OFF* */ - hash_foreach (key.as_u64, epg_id, gbp_contract_db.gc_hash, - { - vlib_cli_output (vm, " {%d,%d} -> %d", key.gck_src, - key.gck_dst, epg_id); - }); - /* *INDENT-ON* */ - - return (NULL); -} - -/*? - * Show Group Based Policy Endpoints and derived information - * - * @cliexpar - * @cliexstart{show gbp endpoint} - * @cliexend - ?*/ -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (gbp_endpoint_show_node, static) = { - .path = "show gbp endpoint", - .short_help = "show gbp endpoint\n", - .function = gbp_endpoint_show, -}; -/* *INDENT-ON* */ - -/*? - * Show Group Based Policy Contracts - * - * @cliexpar - * @cliexstart{show gbp contract} - * @cliexend - ?*/ -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (gbp_contract_show_node, static) = { - .path = "show gbp contract", - .short_help = "show gbp contract\n", - .function = gbp_contract_show, -}; -/* *INDENT-ON* */ - -#define foreach_gbp \ - _(DENY, "deny") - -typedef enum -{ -#define _(sym,str) GBP_ERROR_##sym, - foreach_gbp -#undef _ - GBP_N_ERROR, -} gbp_error_t; - -static char *gbp_error_strings[] = { -#define _(sym,string) string, - foreach_gbp -#undef _ -}; - -typedef enum -{ -#define _(sym,str) GBP_NEXT_##sym, - foreach_gbp -#undef _ - GBP_N_NEXT, -} gbp_next_t; - -/** - * per-packet trace data - */ -typedef struct gbp_trace_t_ -{ - /* per-pkt trace data */ - epg_id_t src_epg; - epg_id_t dst_epg; - u32 acl_index; -} gbp_trace_t; - -static inline uword -gbp_inline (vlib_main_t * vm, - vlib_node_runtime_t * node, vlib_frame_t * frame, int is_ip6) -{ - u32 n_left_from, *from, *to_next; - gbp_next_t next_index; - - next_index = 0; - n_left_from = frame->n_vectors; - from = vlib_frame_vector_args (frame); - - while (n_left_from > 0) - { - u32 n_left_to_next; - - vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); - - while (n_left_from > 0 && n_left_to_next > 0) - { - vlib_buffer_t *b0; - u32 sw_if_index0; - gbp_next_t next0; - u32 bi0; - ip4_header_t *ip4_0; - ip6_header_t *ip6_0; - gbp_contract_key_t key0; - u32 acl_index0; - uword *p; - - bi0 = from[0]; - to_next[0] = bi0; - from += 1; - to_next += 1; - n_left_from -= 1; - n_left_to_next -= 1; - - /* deny by default */ - next0 = GBP_NEXT_DENY; - - b0 = vlib_get_buffer (vm, bi0); - if (is_ip6) - ip6_0 = vlib_buffer_get_current (b0); - else - ip4_0 = vlib_buffer_get_current (b0); - sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; - - /* - * determine the src and dst EPG - */ - key0.gck_src = gbp_itf_to_epg_db.gte_vec[sw_if_index0].gi_epg; - - if (is_ip6) - p = hash_get_mem (gbp_ip6_to_epg_db.g6ie_hash, - &ip6_0->dst_address); - else - p = hash_get (gbp_ip4_to_epg_db.g4ie_hash, - ip4_0->dst_address.as_u32); - - if (NULL != p) - { - key0.gck_dst = p[0]; - - /* - * If the src and dst are the same, then let it through - */ - if (key0.gck_dst == key0.gck_src) - { - vnet_feature_next (sw_if_index0, &next0, b0); - acl_index0 = ~0; - } - else - { - /* - * find this src,dst pair in the egp->acl DB - */ - p = hash_get (gbp_contract_db.gc_hash, key0.as_u64); - - if (NULL != p) - { - acl_index0 = p[0]; - - /* - * the ACL index stored is NULL, this means any-any so let it pass - */ - if (~0 == acl_index0) - { - vnet_feature_next (sw_if_index0, &next0, b0); - } - else - { - /* - * TODO tests against the ACL - */ - /* - * ACL tables are not available outside of ACL plugin - * until then bypass the ACL to next node - */ - vnet_feature_next (sw_if_index0, &next0, b0); - } - } - else - { - /* - * no ACL to apply for packets between these two EPGs. - * GBP is a whitelist model, so no ACL implies deny, which - * is the default result - */ - acl_index0 = ~0; - } - } - } - else - { - /* - * cannot determine the destinaiotn EPG, so we cannot enforce policy - * on this node. permit. - */ - vnet_feature_next (sw_if_index0, &next0, b0); - - key0.gck_dst = ~0; - acl_index0 = ~0; - } - - if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && - (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - gbp_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); - t->src_epg = key0.gck_src; - t->dst_epg = key0.gck_dst; - t->acl_index = acl_index0; - } - - /* verify speculative enqueue, maybe switch current next frame */ - vlib_validate_buffer_enqueue_x1 (vm, node, next_index, - to_next, n_left_to_next, - bi0, next0); - } - - vlib_put_next_frame (vm, node, next_index, n_left_to_next); - } - - return frame->n_vectors; -} - -/* packet trace format function */ -static u8 * -format_gbp_trace (u8 * s, va_list * args) -{ - CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); - CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); - gbp_trace_t *t = va_arg (*args, gbp_trace_t *); - - s = format (s, "gbp: src:%d dst:%d acl:%d", - t->src_epg, t->dst_epg, t->acl_index); - - return s; -} - -static inline uword -gbp_4 (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) -{ - return (gbp_inline (vm, node, frame, 0)); -} - -static inline uword -gbp_6 (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) -{ - return (gbp_inline (vm, node, frame, 1)); -} - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (gbp_4_node) = { - .function = gbp_4, - .name = "gbp4", - .vector_size = sizeof (u32), - .format_trace = format_gbp_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - - .n_errors = ARRAY_LEN(gbp_error_strings), - .error_strings = gbp_error_strings, - - .n_next_nodes = GBP_N_NEXT, - - .next_nodes = { - [GBP_NEXT_DENY] = "ip4-drop", - }, -}; - -VLIB_NODE_FUNCTION_MULTIARCH (gbp_4_node, gbp_4); - -VNET_FEATURE_INIT (gbp_4_node, static) = { - .arc_name = "ip4-unicast", - .node_name = "gbp4", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"), -}; - -VLIB_REGISTER_NODE (gbp_6_node) = { - .function = gbp_6, - .name = "gbp6", - .vector_size = sizeof (u32), - .format_trace = format_gbp_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - - .n_errors = ARRAY_LEN(gbp_error_strings), - .error_strings = gbp_error_strings, - - .n_next_nodes = GBP_N_NEXT, - - .next_nodes = { - [GBP_NEXT_DENY] = "ip6-drop", - }, -}; - -VLIB_NODE_FUNCTION_MULTIARCH (gbp_6_node, gbp_6); - -VNET_FEATURE_INIT (gbp_6_node, static) = { - .arc_name = "ip6-unicast", - .node_name = "gbp6", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip6-fa"), -}; -/* *INDENT-ON* */ - -static clib_error_t * -gbp_init (vlib_main_t * vm) -{ - gbp_endpoint_db = hash_create_mem (0, - sizeof (gbp_endpoint_key_t), - sizeof (u32)); - gbp_ip6_to_epg_db.g6ie_hash = - hash_create_mem (0, sizeof (ip6_address_t), sizeof (u32)); - return 0; -} - -VLIB_INIT_FUNCTION (gbp_init); - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/gbp/gbp.h b/src/plugins/gbp/gbp.h index 334a7438f53..8672fd3b092 100644 --- a/src/plugins/gbp/gbp.h +++ b/src/plugins/gbp/gbp.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Cisco and/or its affiliates. + * Copyright (c) 2018 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -28,101 +28,15 @@ * */ -#ifndef included_vnet_gbp_h -#define included_vnet_gbp_h +#ifndef __GBP_H__ +#define __GBP_H__ -#include <vlib/vlib.h> -#include <vnet/vnet.h> -#include <vnet/ip/ip.h> - -typedef u32 epg_id_t; -#define EPG_INVALID (~0) - -/** - * The key for an Endpoint - */ -typedef struct gbp_endpoint_key_t_ -{ - /** - * The interface on which the EP is connected - */ - u32 gek_sw_if_index; - - /** - * The IP[46] address of the endpoint - */ - ip46_address_t gek_ip; -} gbp_endpoint_key_t; - -/** - * A Group Based Policy Endpoint. - * This is typcially a VM on the local compute node for which policy must be - * locally applied - */ -typedef struct gbp_endpoint_t_ -{ - /** - * The endpoint's interface and IP address - */ - gbp_endpoint_key_t *ge_key; - - /** - * The endpoint's designated EPG - */ - epg_id_t ge_epg_id; -} gbp_endpoint_t; - -extern void gbp_endpoint_update (u32 sw_if_index, - const ip46_address_t * ip, epg_id_t epg_id); -extern void gbp_endpoint_delete (u32 sw_if_index, const ip46_address_t * ip); - -typedef int (*gbp_endpoint_cb_t) (gbp_endpoint_t * gbpe, void *ctx); -extern void gbp_endpoint_walk (gbp_endpoint_cb_t bgpe, void *ctx); - - -/** - * The key for an Contract - */ -typedef struct gbp_contract_key_t_ -{ - union - { - struct - { - /** - * source and destination EPGs for which the ACL applies - */ - epg_id_t gck_src; - epg_id_t gck_dst; - }; - u64 as_u64; - }; -} gbp_contract_key_t; - -/** - * A Group Based Policy Contract. - * Determines the ACL that applies to traffic pass between two endpoint groups - */ -typedef struct gbp_contract_t_ -{ - /** - * source and destination EPGs - */ - gbp_contract_key_t gc_key; - - /** - * The ACL to apply for packets from the source to the destination EPG - */ - u32 gc_acl_index;; -} gbp_contract_t; - - -extern void gbp_contract_update (epg_id_t src_epg, - epg_id_t dst_epg, u32 acl_index); -extern void gbp_contract_delete (epg_id_t src_epg, epg_id_t dst_epg); - -typedef int (*gbp_contract_cb_t) (gbp_contract_t * gbpe, void *ctx); -extern void gbp_contract_walk (gbp_contract_cb_t bgpe, void *ctx); +#include <plugins/gbp/gbp_types.h> +#include <plugins/gbp/gbp_endpoint.h> +#include <plugins/gbp/gbp_endpoint_group.h> +#include <plugins/gbp/gbp_contract.h> +#include <plugins/gbp/gbp_subnet.h> +#include <plugins/gbp/gbp_recirc.h> #endif diff --git a/src/plugins/gbp/gbp_all_api_h.h b/src/plugins/gbp/gbp_all_api_h.h index a65d9fcd164..3093309e80f 100644 --- a/src/plugins/gbp/gbp_all_api_h.h +++ b/src/plugins/gbp/gbp_all_api_h.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Cisco and/or its affiliates. + * Copyright (c) 2018 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/src/plugins/gbp/gbp_api.c b/src/plugins/gbp/gbp_api.c index 998d5d9bdbb..88b2cd1cea9 100644 --- a/src/plugins/gbp/gbp_api.c +++ b/src/plugins/gbp/gbp_api.c @@ -1,8 +1,6 @@ /* *------------------------------------------------------------------ - * gbp_api.c - layer 2 emulation api - * - * Copyright (c) 2016 Cisco and/or its affiliates. + * Copyright (c) 2018 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -56,6 +54,12 @@ #define foreach_gbp_api_msg \ _(GBP_ENDPOINT_ADD_DEL, gbp_endpoint_add_del) \ _(GBP_ENDPOINT_DUMP, gbp_endpoint_dump) \ + _(GBP_SUBNET_ADD_DEL, gbp_subnet_add_del) \ + _(GBP_SUBNET_DUMP, gbp_subnet_dump) \ + _(GBP_ENDPOINT_GROUP_ADD_DEL, gbp_endpoint_group_add_del) \ + _(GBP_ENDPOINT_GROUP_DUMP, gbp_endpoint_group_dump) \ + _(GBP_RECIRC_ADD_DEL, gbp_recirc_add_del) \ + _(GBP_RECIRC_DUMP, gbp_recirc_dump) \ _(GBP_CONTRACT_ADD_DEL, gbp_contract_add_del) \ _(GBP_CONTRACT_DUMP, gbp_contract_dump) @@ -94,7 +98,8 @@ vl_api_gbp_endpoint_add_del_t_handler (vl_api_gbp_endpoint_add_del_t * mp) if (mp->is_add) { - gbp_endpoint_update (sw_if_index, &ip, ntohl (mp->endpoint.epg_id)); + rv = + gbp_endpoint_update (sw_if_index, &ip, ntohl (mp->endpoint.epg_id)); } else { @@ -163,6 +168,223 @@ vl_api_gbp_endpoint_dump_t_handler (vl_api_gbp_endpoint_dump_t * mp) } static void + vl_api_gbp_endpoint_group_add_del_t_handler + (vl_api_gbp_endpoint_group_add_del_t * mp) +{ + vl_api_gbp_endpoint_group_add_del_reply_t *rmp; + u32 uplink_sw_if_index; + int rv = 0; + + uplink_sw_if_index = ntohl (mp->epg.uplink_sw_if_index); + if (!vnet_sw_if_index_is_api_valid (uplink_sw_if_index)) + goto bad_sw_if_index; + + if (mp->is_add) + { + rv = gbp_endpoint_group_add (ntohl (mp->epg.epg_id), + ntohl (mp->epg.bd_id), + ntohl (mp->epg.ip4_table_id), + ntohl (mp->epg.ip6_table_id), + uplink_sw_if_index); + } + else + { + gbp_endpoint_group_delete (ntohl (mp->epg.epg_id)); + } + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_GBP_ENDPOINT_GROUP_ADD_DEL_REPLY + GBP_MSG_BASE); +} + +static void +vl_api_gbp_subnet_add_del_t_handler (vl_api_gbp_subnet_add_del_t * mp) +{ + vl_api_gbp_subnet_add_del_reply_t *rmp; + int rv = 0; + fib_prefix_t pfx = { + .fp_len = mp->subnet.address_length, + .fp_proto = (mp->subnet.is_ip6 ? FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4), + }; + + if (mp->subnet.is_ip6) + clib_memcpy (&pfx.fp_addr.ip6, mp->subnet.address, + sizeof (pfx.fp_addr.ip6)); + else + clib_memcpy (&pfx.fp_addr.ip4, mp->subnet.address, + sizeof (pfx.fp_addr.ip4)); + + rv = gbp_subnet_add_del (ntohl (mp->subnet.table_id), + &pfx, + ntohl (mp->subnet.sw_if_index), + ntohl (mp->subnet.epg_id), + mp->is_add, mp->subnet.is_internal); + + REPLY_MACRO (VL_API_GBP_SUBNET_ADD_DEL_REPLY + GBP_MSG_BASE); +} + +static int +gbp_subnet_send_details (u32 table_id, + const fib_prefix_t * pfx, + u32 sw_if_index, + epg_id_t epg, u8 is_internal, void *args) +{ + vl_api_gbp_subnet_details_t *mp; + gbp_walk_ctx_t *ctx; + + ctx = args; + mp = vl_msg_api_alloc (sizeof (*mp)); + if (!mp) + return 1; + + memset (mp, 0, sizeof (*mp)); + mp->_vl_msg_id = ntohs (VL_API_GBP_SUBNET_DETAILS + GBP_MSG_BASE); + mp->context = ctx->context; + + mp->subnet.is_internal = is_internal; + mp->subnet.sw_if_index = ntohl (sw_if_index); + mp->subnet.epg_id = ntohl (epg); + mp->subnet.is_ip6 = (pfx->fp_proto == FIB_PROTOCOL_IP6); + mp->subnet.address_length = pfx->fp_len; + mp->subnet.table_id = ntohl (table_id); + if (mp->subnet.is_ip6) + clib_memcpy (&mp->subnet.address, + &pfx->fp_addr.ip6, sizeof (pfx->fp_addr.ip6)); + else + clib_memcpy (&mp->subnet.address, + &pfx->fp_addr.ip4, sizeof (pfx->fp_addr.ip4)); + + + vl_api_send_msg (ctx->reg, (u8 *) mp); + + return (1); +} + +static void +vl_api_gbp_subnet_dump_t_handler (vl_api_gbp_subnet_dump_t * mp) +{ + vl_api_registration_t *reg; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + gbp_walk_ctx_t ctx = { + .reg = reg, + .context = mp->context, + }; + + gbp_subnet_walk (gbp_subnet_send_details, &ctx); +} + +static int +gbp_endpoint_group_send_details (gbp_endpoint_group_t * gepg, void *args) +{ + vl_api_gbp_endpoint_group_details_t *mp; + gbp_walk_ctx_t *ctx; + + ctx = args; + mp = vl_msg_api_alloc (sizeof (*mp)); + if (!mp) + return 1; + + memset (mp, 0, sizeof (*mp)); + mp->_vl_msg_id = ntohs (VL_API_GBP_ENDPOINT_GROUP_DETAILS + GBP_MSG_BASE); + mp->context = ctx->context; + + mp->epg.uplink_sw_if_index = ntohl (gepg->gepg_uplink_sw_if_index); + mp->epg.epg_id = ntohl (gepg->gepg_id); + mp->epg.bd_id = ntohl (gepg->gepg_bd); + mp->epg.ip4_table_id = ntohl (gepg->gepg_rd[FIB_PROTOCOL_IP4]); + mp->epg.ip6_table_id = ntohl (gepg->gepg_rd[FIB_PROTOCOL_IP6]); + + vl_api_send_msg (ctx->reg, (u8 *) mp); + + return (1); +} + +static void +vl_api_gbp_endpoint_group_dump_t_handler (vl_api_gbp_endpoint_group_dump_t * + mp) +{ + vl_api_registration_t *reg; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + gbp_walk_ctx_t ctx = { + .reg = reg, + .context = mp->context, + }; + + gbp_endpoint_group_walk (gbp_endpoint_group_send_details, &ctx); +} + +static void +vl_api_gbp_recirc_add_del_t_handler (vl_api_gbp_recirc_add_del_t * mp) +{ + vl_api_gbp_recirc_add_del_reply_t *rmp; + u32 sw_if_index; + int rv = 0; + + sw_if_index = ntohl (mp->recirc.sw_if_index); + if (!vnet_sw_if_index_is_api_valid (sw_if_index)) + goto bad_sw_if_index; + + if (mp->is_add) + gbp_recirc_add (sw_if_index, + ntohl (mp->recirc.epg_id), mp->recirc.is_ext); + else + gbp_recirc_delete (sw_if_index); + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_GBP_RECIRC_ADD_DEL_REPLY + GBP_MSG_BASE); +} + +static int +gbp_recirc_send_details (gbp_recirc_t * gr, void *args) +{ + vl_api_gbp_recirc_details_t *mp; + gbp_walk_ctx_t *ctx; + + ctx = args; + mp = vl_msg_api_alloc (sizeof (*mp)); + if (!mp) + return 1; + + memset (mp, 0, sizeof (*mp)); + mp->_vl_msg_id = ntohs (VL_API_GBP_RECIRC_DETAILS + GBP_MSG_BASE); + mp->context = ctx->context; + + mp->recirc.epg_id = ntohl (gr->gr_epg); + mp->recirc.sw_if_index = ntohl (gr->gr_sw_if_index); + mp->recirc.is_ext = ntohl (gr->gr_is_ext); + + vl_api_send_msg (ctx->reg, (u8 *) mp); + + return (1); +} + +static void +vl_api_gbp_recirc_dump_t_handler (vl_api_gbp_recirc_dump_t * mp) +{ + vl_api_registration_t *reg; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + gbp_walk_ctx_t ctx = { + .reg = reg, + .context = mp->context, + }; + + gbp_recirc_walk (gbp_recirc_send_details, &ctx); +} + +static void vl_api_gbp_contract_add_del_t_handler (vl_api_gbp_contract_add_del_t * mp) { vl_api_gbp_contract_add_del_reply_t *rmp; diff --git a/src/plugins/gbp/gbp_classify.c b/src/plugins/gbp/gbp_classify.c new file mode 100644 index 00000000000..859d4f95173 --- /dev/null +++ b/src/plugins/gbp/gbp_classify.c @@ -0,0 +1,276 @@ +/* + * gbp.h : Group Based Policy + * + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <plugins/gbp/gbp.h> + +typedef enum gbp_src_classify_type_t_ +{ + GBP_SRC_CLASSIFY_NULL, + GBP_SRC_CLASSIFY_PORT, +} gbp_src_classify_type_t; + +#define GBP_SRC_N_CLASSIFY (GBP_SRC_CLASSIFY_PORT + 1) + +/** + * Grouping of global data for the GBP source EPG classification feature + */ +typedef struct gbp_src_classify_main_t_ +{ + /** + * Next nodes for L2 output features + */ + u32 l2_input_feat_next[GBP_SRC_N_CLASSIFY][32]; +} gbp_src_classify_main_t; + +static gbp_src_classify_main_t gbp_src_classify_main; + +/** + * per-packet trace data + */ +typedef struct gbp_classify_trace_t_ +{ + /* per-pkt trace data */ + epg_id_t src_epg; +} gbp_classify_trace_t; + +/* + * determine the SRC EPG form the input port + */ +always_inline uword +gbp_classify_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, + gbp_src_classify_type_t type, u8 is_l3) +{ + gbp_src_classify_main_t *gscm = &gbp_src_classify_main; + u32 n_left_from, *from, *to_next; + u32 next_index; + + next_index = 0; + n_left_from = frame->n_vectors; + from = vlib_frame_vector_args (frame); + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 next0, bi0, src_epg, sw_if_index0; + vlib_buffer_t *b0; + + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + + if (GBP_SRC_CLASSIFY_NULL == type) + { + src_epg = ~0; + next0 = + vnet_l2_feature_next (b0, gscm->l2_input_feat_next[type], + L2INPUT_FEAT_GBP_NULL_CLASSIFY); + } + else + { + src_epg = gbp_port_to_epg (sw_if_index0); + if (is_l3) + { + /* + * Go straight to looukp, do not pass go, do not collect $200 + */ + next0 = 0; + } + else + { + next0 = + vnet_l2_feature_next (b0, gscm->l2_input_feat_next[type], + L2INPUT_FEAT_GBP_SRC_CLASSIFY); + } + } + + vnet_buffer2 (b0)->gbp.src_epg = src_epg; + + if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED))) + { + gbp_classify_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->src_epg = src_epg; + } + + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + return frame->n_vectors; +} + +static uword +gbp_src_classify (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return (gbp_classify_inline (vm, node, frame, GBP_SRC_CLASSIFY_PORT, 0)); +} + +static uword +gbp_null_classify (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return (gbp_classify_inline (vm, node, frame, GBP_SRC_CLASSIFY_NULL, 0)); +} + +static uword +gbp_ip4_src_classify (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return (gbp_classify_inline (vm, node, frame, 0, 1)); +} + +static uword +gbp_ip6_src_classify (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return (gbp_classify_inline (vm, node, frame, 0, 1)); +} + + +/* packet trace format function */ +static u8 * +format_gbp_classify_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + gbp_classify_trace_t *t = va_arg (*args, gbp_classify_trace_t *); + + s = format (s, "src-epg:%d", t->src_epg); + + return s; +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (gbp_null_classify_node) = { + .function = gbp_null_classify, + .name = "gbp-null-classify", + .vector_size = sizeof (u32), + .format_trace = format_gbp_classify_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = 0, + .n_next_nodes = 0, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (gbp_null_classify_node, gbp_null_classify); + +VLIB_REGISTER_NODE (gbp_src_classify_node) = { + .function = gbp_src_classify, + .name = "gbp-src-classify", + .vector_size = sizeof (u32), + .format_trace = format_gbp_classify_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = 0, + .n_next_nodes = 0, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (gbp_src_classify_node, gbp_src_classify); + +VLIB_REGISTER_NODE (gbp_ip4_src_classify_node) = { + .function = gbp_ip4_src_classify, + .name = "ip4-gbp-src-classify", + .vector_size = sizeof (u32), + .format_trace = format_gbp_classify_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = 0, + .n_next_nodes = 1, + .next_nodes = { + [0] = "ip4-lookup" + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (gbp_ip4_src_classify_node, gbp_ip4_src_classify); + +VLIB_REGISTER_NODE (gbp_ip6_src_classify_node) = { + .function = gbp_ip6_src_classify, + .name = "ip6-gbp-src-classify", + .vector_size = sizeof (u32), + .format_trace = format_gbp_classify_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = 0, + .n_next_nodes = 1, + .next_nodes = { + [0] = "ip6-lookup" + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (gbp_ip6_src_classify_node, gbp_ip6_src_classify); + +VNET_FEATURE_INIT (gbp_ip4_src_classify_feat_node, static) = +{ + .arc_name = "ip4-unicast", + .node_name = "ip4-gbp-src-classify", + .runs_before = VNET_FEATURES ("nat44-out2in"), +}; +VNET_FEATURE_INIT (gbp_ip6_src_classify_feat_node, static) = +{ + .arc_name = "ip6-unicast", + .node_name = "ip6-gbp-src-classify", + .runs_before = VNET_FEATURES ("nat66-out2in"), +}; + +static clib_error_t * +gbp_src_classify_init (vlib_main_t * vm) +{ + gbp_src_classify_main_t *em = &gbp_src_classify_main; + + /* Initialize the feature next-node indexes */ + feat_bitmap_init_next_nodes (vm, + gbp_src_classify_node.index, + L2INPUT_N_FEAT, + l2input_get_feat_names (), + em->l2_input_feat_next[GBP_SRC_CLASSIFY_NULL]); + feat_bitmap_init_next_nodes (vm, + gbp_null_classify_node.index, + L2INPUT_N_FEAT, + l2input_get_feat_names (), + em->l2_input_feat_next[GBP_SRC_CLASSIFY_PORT]); + + return 0; +} + +VLIB_INIT_FUNCTION (gbp_src_classify_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/gbp/gbp_contract.c b/src/plugins/gbp/gbp_contract.c new file mode 100644 index 00000000000..71d8bcf4671 --- /dev/null +++ b/src/plugins/gbp/gbp_contract.c @@ -0,0 +1,163 @@ +/* + * gbp.h : Group Based Policy + * + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <plugins/gbp/gbp.h> + +/** + * Single contract DB instance + */ +gbp_contract_db_t gbp_contract_db; + +void +gbp_contract_update (epg_id_t src_epg, epg_id_t dst_epg, u32 acl_index) +{ + gbp_contract_key_t key = { + .gck_src = src_epg, + .gck_dst = dst_epg, + }; + + hash_set (gbp_contract_db.gc_hash, key.as_u64, acl_index); +} + +void +gbp_contract_delete (epg_id_t src_epg, epg_id_t dst_epg) +{ + gbp_contract_key_t key = { + .gck_src = src_epg, + .gck_dst = dst_epg, + }; + + hash_unset (gbp_contract_db.gc_hash, key.as_u64); +} + +void +gbp_contract_walk (gbp_contract_cb_t cb, void *ctx) +{ + gbp_contract_key_t key; + u32 acl_index; + + /* *INDENT-OFF* */ + hash_foreach(key.as_u64, acl_index, gbp_contract_db.gc_hash, + ({ + gbp_contract_t gbpc = { + .gc_key = key, + .gc_acl_index = acl_index, + }; + + if (!cb(&gbpc, ctx)) + break; + })); + /* *INDENT-ON* */ +} + +static clib_error_t * +gbp_contract_cli (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + epg_id_t src_epg_id = EPG_INVALID, dst_epg_id = EPG_INVALID; + u32 acl_index = ~0; + u8 add = 1; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "add")) + add = 1; + else if (unformat (input, "del")) + add = 0; + else if (unformat (input, "src-epg %d", &src_epg_id)) + ; + else if (unformat (input, "dst-epg %d", &dst_epg_id)) + ; + else if (unformat (input, "acl-index %d", &acl_index)) + ; + else + break; + } + + if (EPG_INVALID == src_epg_id) + return clib_error_return (0, "Source EPG-ID must be specified"); + if (EPG_INVALID == dst_epg_id) + return clib_error_return (0, "Destination EPG-ID must be specified"); + + if (add) + { + gbp_contract_update (src_epg_id, dst_epg_id, acl_index); + } + else + { + gbp_contract_delete (src_epg_id, dst_epg_id); + } + + return (NULL); +} + +/*? + * Configure a GBP Contract + * + * @cliexpar + * @cliexstart{set gbp contract [del] src-epg <ID> dst-epg <ID> acl-index <ACL>} + * @cliexend + ?*/ +VLIB_CLI_COMMAND (gbp_contract_cli_node, static) = +{ +.path = "gbp contract",.short_help = + "gbp contract [del] src-epg <ID> dst-epg <ID> acl-index <ACL>",.function + = gbp_contract_cli,}; +/* *INDENT-ON* */ + +static clib_error_t * +gbp_contract_show (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + gbp_contract_key_t key; + epg_id_t epg_id; + + vlib_cli_output (vm, "Contracts:"); + + /* *INDENT-OFF* */ + hash_foreach (key.as_u64, epg_id, gbp_contract_db.gc_hash, + { + vlib_cli_output (vm, " {%d,%d} -> %d", key.gck_src, + key.gck_dst, epg_id); + }); + /* *INDENT-ON* */ + + return (NULL); +} + +/*? + * Show Group Based Policy Contracts + * + * @cliexpar + * @cliexstart{show gbp contract} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_contract_show_node, static) = { + .path = "show gbp contract", + .short_help = "show gbp contract\n", + .function = gbp_contract_show, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/gbp/gbp_contract.h b/src/plugins/gbp/gbp_contract.h new file mode 100644 index 00000000000..1964098adc3 --- /dev/null +++ b/src/plugins/gbp/gbp_contract.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __GBP_CONTRACT_H__ +#define __GBP_CONTRACT_H__ + +#include <plugins/gbp/gbp_types.h> + +/** + * The key for an Contract + */ +typedef struct gbp_contract_key_t_ +{ + union + { + struct + { + /** + * source and destination EPGs for which the ACL applies + */ + epg_id_t gck_src; + epg_id_t gck_dst; + }; + u64 as_u64; + }; +} gbp_contract_key_t; + +/** + * A Group Based Policy Contract. + * Determines the ACL that applies to traffic pass between two endpoint groups + */ +typedef struct gbp_contract_t_ +{ + /** + * source and destination EPGs + */ + gbp_contract_key_t gc_key; + + /** + * The ACL to apply for packets from the source to the destination EPG + */ + u32 gc_acl_index;; +} gbp_contract_t; + +/** + * EPG src,dst pair to ACL mapping table, aka contract DB + */ +typedef struct gbp_contract_db_t_ +{ + /** + * We can form a u64 key from the pair, so use a simple hash table + */ + uword *gc_hash; +} gbp_contract_db_t; + +extern void gbp_contract_update (epg_id_t src_epg, + epg_id_t dst_epg, u32 acl_index); +extern void gbp_contract_delete (epg_id_t src_epg, epg_id_t dst_epg); + +typedef int (*gbp_contract_cb_t) (gbp_contract_t * gbpe, void *ctx); +extern void gbp_contract_walk (gbp_contract_cb_t bgpe, void *ctx); + + +/** + * DP functions and databases + */ +extern gbp_contract_db_t gbp_contract_db; + +always_inline u32 +gbp_acl_lookup (gbp_contract_key_t * key) +{ + uword *p; + + p = hash_get (gbp_contract_db.gc_hash, key->as_u64); + + if (NULL != p) + return (p[0]); + + return (~0); +} + +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/gbp/gbp_endpoint.c b/src/plugins/gbp/gbp_endpoint.c new file mode 100644 index 00000000000..91505daee2a --- /dev/null +++ b/src/plugins/gbp/gbp_endpoint.c @@ -0,0 +1,393 @@ +/* + * gbp.h : Group Based Policy + * + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <plugins/gbp/gbp_endpoint.h> +#include <plugins/gbp/gbp_endpoint_group.h> + +#include <vnet/ethernet/arp_packet.h> + +/** + * IP4 destintion address to destination EPG mapping table + */ +typedef struct gbp_ip4_to_epg_db_t_ +{ + /** + * use a simple hash table + */ + uword *g4ie_hash; +} gbp_ip4_to_epg_db_t; + +static gbp_ip4_to_epg_db_t gbp_ip4_to_epg_db; + +/** + * IP6 destintion address to destination EPG mapping table + */ +typedef struct gbp_ip6_to_epg_db_t_ +{ + /** + * use a memroy hash table + */ + uword *g6ie_hash; +} gbp_ip6_to_epg_db_t; + +static gbp_ip6_to_epg_db_t gbp_ip6_to_epg_db; + + +const static gbp_itf_t ITF_INVALID = { + .gi_epg = EPG_INVALID, + .gi_ref_count = 0, +}; + +gbp_itf_to_epg_db_t gbp_itf_to_epg_db; + +/** + * Pool of GBP endpoints + */ +static gbp_endpoint_t *gbp_endpoint_pool; + +/** + * DB of endpoints + */ +static uword *gbp_endpoint_db; + +static void +gbp_ip_epg_update (const ip46_address_t * ip, epg_id_t epg_id) +{ + /* + * we are dealing only with addresses here so this limited + * is_ip4 check is ok + */ + if (ip46_address_is_ip4 (ip)) + { + hash_set (gbp_ip4_to_epg_db.g4ie_hash, ip->ip4.as_u32, epg_id); + } + else + { + hash_set_mem (gbp_ip6_to_epg_db.g6ie_hash, &ip->ip6, epg_id); + } +} + +static void +gbp_ip_epg_delete (const ip46_address_t * ip) +{ + if (ip46_address_is_ip4 (ip)) + { + hash_unset (gbp_ip4_to_epg_db.g4ie_hash, ip->ip4.as_u32); + } + else + { + hash_unset_mem (gbp_ip6_to_epg_db.g6ie_hash, &ip->ip6); + } +} + +void +gbp_itf_epg_update (u32 sw_if_index, epg_id_t src_epg, u8 do_policy) +{ + vec_validate_init_empty (gbp_itf_to_epg_db.gte_vec, + sw_if_index, ITF_INVALID); + + if (0 == gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count) + { + l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_SRC_CLASSIFY, + 1); + l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_FWD, 1); + if (do_policy) + l2output_intf_bitmap_enable (sw_if_index, L2OUTPUT_FEAT_GBP_POLICY, + 1); + } + gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg = src_epg; + gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count++; +} + +void +gbp_itf_epg_delete (u32 sw_if_index) +{ + if (vec_len (gbp_itf_to_epg_db.gte_vec) <= sw_if_index) + return; + + if (1 == gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count) + { + gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg = EPG_INVALID; + + l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_SRC_CLASSIFY, + 0); + l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_FWD, 0); + l2output_intf_bitmap_enable (sw_if_index, L2OUTPUT_FEAT_GBP_POLICY, 0); + } + gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count--; +} + +int +gbp_endpoint_update (u32 sw_if_index, + const ip46_address_t * ip, epg_id_t epg_id) +{ + gbp_endpoint_key_t key = { + .gek_ip = *ip, + .gek_sw_if_index = sw_if_index, + }; + gbp_endpoint_group_t *gepg; + gbp_endpoint_t *gbpe; + uword *p; + + gepg = gbp_endpoint_group_find (epg_id); + + if (NULL == gepg) + return (VNET_API_ERROR_NO_SUCH_ENTRY); + + p = hash_get_mem (gbp_endpoint_db, &key); + + if (p) + { + gbpe = pool_elt_at_index (gbp_endpoint_pool, p[0]); + } + else + { + pool_get (gbp_endpoint_pool, gbpe); + + gbpe->ge_key = clib_mem_alloc (sizeof (gbp_endpoint_key_t)); + clib_memcpy (gbpe->ge_key, &key, sizeof (gbp_endpoint_key_t)); + + hash_set_mem (gbp_endpoint_db, gbpe->ge_key, gbpe - gbp_endpoint_pool); + } + + gbpe->ge_epg_id = epg_id; + + gbp_itf_epg_update (gbpe->ge_key->gek_sw_if_index, gbpe->ge_epg_id, 1); + + if (!ip46_address_is_zero (&gbpe->ge_key->gek_ip)) + gbp_ip_epg_update (&gbpe->ge_key->gek_ip, gbpe->ge_epg_id); + + /* + * send a gratuitous ARP on the EPG's uplink. this is done so that if + * this EP has moved from some other place in the 'fabric', upstream + * devices are informed + */ + if (ip46_address_is_ip4 (&gbpe->ge_key->gek_ip)) + send_ip4_garp_w_addr (vlib_get_main (), + &gbpe->ge_key->gek_ip.ip4, + vnet_get_sup_hw_interface + (vnet_get_main (), gepg->gepg_uplink_sw_if_index)); + else + send_ip6_na_w_addr (vlib_get_main (), + &gbpe->ge_key->gek_ip.ip6, + vnet_get_sup_hw_interface + (vnet_get_main (), gepg->gepg_uplink_sw_if_index)); + + return (0); +} + +void +gbp_endpoint_delete (u32 sw_if_index, const ip46_address_t * ip) +{ + gbp_endpoint_key_t key = { + .gek_ip = *ip, + .gek_sw_if_index = sw_if_index, + }; + gbp_endpoint_t *gbpe; + uword *p; + + p = hash_get_mem (gbp_endpoint_db, &key); + + if (p) + { + gbpe = pool_elt_at_index (gbp_endpoint_pool, p[0]); + + hash_unset_mem (gbp_endpoint_db, gbpe->ge_key); + + gbp_itf_epg_delete (gbpe->ge_key->gek_sw_if_index); + if (!ip46_address_is_zero (&gbpe->ge_key->gek_ip)) + gbp_ip_epg_delete (&gbpe->ge_key->gek_ip); + + clib_mem_free (gbpe->ge_key); + + pool_put (gbp_endpoint_pool, gbpe); + } +} + +void +gbp_endpoint_walk (gbp_endpoint_cb_t cb, void *ctx) +{ + gbp_endpoint_t *gbpe; + + /* *INDENT-OFF* */ + pool_foreach(gbpe, gbp_endpoint_pool, + { + if (!cb(gbpe, ctx)) + break; + }); + /* *INDENT-ON* */ +} + +static clib_error_t * +gbp_endpoint_cli (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + vnet_main_t *vnm = vnet_get_main (); + epg_id_t epg_id = EPG_INVALID; + ip46_address_t ip = { }; + u32 sw_if_index = ~0; + u8 add = 1; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "%U", unformat_vnet_sw_interface, + vnm, &sw_if_index)) + ; + else if (unformat (input, "add")) + add = 1; + else if (unformat (input, "del")) + add = 0; + else if (unformat (input, "epg %d", &epg_id)) + ; + else if (unformat (input, "ip %U", unformat_ip4_address, &ip.ip4)) + ; + else if (unformat (input, "ip %U", unformat_ip6_address, &ip.ip6)) + ; + else + break; + } + + if (~0 == sw_if_index) + return clib_error_return (0, "interface must be specified"); + if (EPG_INVALID == epg_id) + return clib_error_return (0, "EPG-ID must be specified"); + if (ip46_address_is_zero (&ip)) + return clib_error_return (0, "IP address must be specified"); + + if (add) + gbp_endpoint_update (sw_if_index, &ip, epg_id); + else + gbp_endpoint_delete (sw_if_index, &ip); + + return (NULL); +} + + +/*? + * Configure a GBP Endpoint + * + * @cliexpar + * @cliexstart{set gbp endpoint [del] <interface> epg <ID> ip <IP>} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_endpoint_cli_node, static) = { + .path = "gbp endpoint", + .short_help = "gbp endpoint [del] <interface> epg <ID> ip <IP>", + .function = gbp_endpoint_cli, +}; +/* *INDENT-ON* */ + +static int +gbp_endpoint_show_one (gbp_endpoint_t * gbpe, void *ctx) +{ + vnet_main_t *vnm = vnet_get_main (); + vlib_main_t *vm; + + vm = ctx; + vlib_cli_output (vm, " {%U, %U} -> %d", + format_vnet_sw_if_index_name, vnm, + gbpe->ge_key->gek_sw_if_index, + format_ip46_address, &gbpe->ge_key->gek_ip, IP46_TYPE_ANY, + gbpe->ge_epg_id); + + return (1); +} + +static clib_error_t * +gbp_endpoint_show (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + vnet_main_t *vnm = vnet_get_main (); + ip46_address_t ip, *ipp; + epg_id_t epg_id; + u32 sw_if_index; + + vlib_cli_output (vm, "Endpoints:"); + gbp_endpoint_walk (gbp_endpoint_show_one, vm); + + vlib_cli_output (vm, "\nSource interface to EPG:"); + + vec_foreach_index (sw_if_index, gbp_itf_to_epg_db.gte_vec) + { + if (EPG_INVALID != gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg) + { + vlib_cli_output (vm, " %U -> %d", + format_vnet_sw_if_index_name, vnm, sw_if_index, + gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg); + } + } + + vlib_cli_output (vm, "\nDestination IP4 to EPG:"); + + /* *INDENT-OFF* */ + hash_foreach (ip.ip4.as_u32, epg_id, gbp_ip4_to_epg_db.g4ie_hash, + { + vlib_cli_output (vm, " %U -> %d", format_ip46_address, &ip, + IP46_TYPE_IP4, epg_id); + }); + /* *INDENT-ON* */ + + vlib_cli_output (vm, "\nDestination IP6 to EPG:"); + + /* *INDENT-OFF* */ + hash_foreach_mem (ipp, epg_id, gbp_ip6_to_epg_db.g6ie_hash, + { + vlib_cli_output (vm, " %U -> %d", format_ip46_address, ipp, + IP46_TYPE_IP6, epg_id); + }); + /* *INDENT-ON* */ + + return (NULL); +} + + +/*? + * Show Group Based Policy Endpoints and derived information + * + * @cliexpar + * @cliexstart{show gbp endpoint} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_endpoint_show_node, static) = { + .path = "show gbp endpoint", + .short_help = "show gbp endpoint\n", + .function = gbp_endpoint_show, +}; +/* *INDENT-ON* */ + +static clib_error_t * +gbp_endpoint_init (vlib_main_t * vm) +{ + gbp_endpoint_db = hash_create_mem (0, + sizeof (gbp_endpoint_key_t), + sizeof (u32)); + gbp_ip6_to_epg_db.g6ie_hash = + hash_create_mem (0, sizeof (ip6_address_t), sizeof (u32)); + return 0; +} + +VLIB_INIT_FUNCTION (gbp_endpoint_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/gbp/gbp_endpoint.h b/src/plugins/gbp/gbp_endpoint.h new file mode 100644 index 00000000000..000c211cfa1 --- /dev/null +++ b/src/plugins/gbp/gbp_endpoint.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __GBP_ENDPOINT_H__ +#define __GBP_ENDPOINT_H__ + +#include <plugins/gbp/gbp_types.h> +#include <vnet/ip/ip.h> + +/** + * The key for an Endpoint + */ +typedef struct gbp_endpoint_key_t_ +{ + /** + * The interface on which the EP is connected + */ + u32 gek_sw_if_index; + + /** + * The IP[46] address of the endpoint + */ + ip46_address_t gek_ip; +} gbp_endpoint_key_t; + +/** + * A Group Based Policy Endpoint. + * This is typcially a VM on the local compute node for which policy must be + * locally applied + */ +typedef struct gbp_endpoint_t_ +{ + /** + * The endpoint's interface and IP address + */ + gbp_endpoint_key_t *ge_key; + + /** + * The endpoint's designated EPG + */ + epg_id_t ge_epg_id; +} gbp_endpoint_t; + +/** + * Result of a interface to EPG mapping. + * multiple Endpoints can occur on the same interface, so this + * mapping needs to be reference counted. + */ +typedef struct gbp_itf_t_ +{ + epg_id_t gi_epg; + u32 gi_ref_count; +} gbp_itf_t; + +/** + * Interface to source EPG DB - a per-interface vector + */ +typedef struct gbp_itf_to_epg_db_t_ +{ + gbp_itf_t *gte_vec; +} gbp_itf_to_epg_db_t; + +extern int gbp_endpoint_update (u32 sw_if_index, + const ip46_address_t * ip, epg_id_t epg_id); +extern void gbp_endpoint_delete (u32 sw_if_index, const ip46_address_t * ip); + +typedef int (*gbp_endpoint_cb_t) (gbp_endpoint_t * gbpe, void *ctx); +extern void gbp_endpoint_walk (gbp_endpoint_cb_t cb, void *ctx); + +/** + * Port to EPG mapping management + */ +extern void gbp_itf_epg_update (u32 sw_if_index, epg_id_t src_epg, + u8 do_policy); +extern void gbp_itf_epg_delete (u32 sw_if_index); + +/** + * DP functions and databases + */ +extern gbp_itf_to_epg_db_t gbp_itf_to_epg_db; + +/** + * Get the source EPG for a port/interface + */ +always_inline u32 +gbp_port_to_epg (u32 sw_if_index) +{ + return (gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg); +} + +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/gbp/gbp_endpoint_group.c b/src/plugins/gbp/gbp_endpoint_group.c new file mode 100644 index 00000000000..ed312d305a1 --- /dev/null +++ b/src/plugins/gbp/gbp_endpoint_group.c @@ -0,0 +1,263 @@ +/* + * gbp.h : Group Based Policy + * + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <plugins/gbp/gbp_endpoint_group.h> +#include <plugins/gbp/gbp_endpoint.h> + +#include <vnet/dpo/dvr_dpo.h> +#include <vnet/fib/fib_table.h> + +/** + * Pool of GBP endpoint_groups + */ +gbp_endpoint_group_t *gbp_endpoint_group_pool; + +/** + * DB of endpoint_groups + */ +gbp_endpoint_group_db_t gbp_endpoint_group_db; + +gbp_endpoint_group_t * +gbp_endpoint_group_find (epg_id_t epg_id) +{ + uword *p; + + p = hash_get (gbp_endpoint_group_db.gepg_hash, epg_id); + + if (NULL != p) + return (pool_elt_at_index (gbp_endpoint_group_pool, p[0])); + + return (NULL); +} + +int +gbp_endpoint_group_add (epg_id_t epg_id, + u32 bd_id, + u32 ip4_table_id, + u32 ip6_table_id, u32 uplink_sw_if_index) +{ + gbp_endpoint_group_t *gepg; + + gepg = gbp_endpoint_group_find (epg_id); + + if (NULL == gepg) + { + fib_protocol_t fproto; + + pool_get (gbp_endpoint_group_pool, gepg); + memset (gepg, 0, sizeof (*gepg)); + + gepg->gepg_id = epg_id; + gepg->gepg_bd = bd_id; + gepg->gepg_rd[FIB_PROTOCOL_IP4] = ip4_table_id; + gepg->gepg_rd[FIB_PROTOCOL_IP6] = ip6_table_id; + gepg->gepg_uplink_sw_if_index = uplink_sw_if_index; + + /* + * an egress DVR dpo for internal subnets to use when sending + * on the uplink interface + */ + FOR_EACH_FIB_IP_PROTOCOL (fproto) + { + gepg->gepg_fib_index[fproto] = + fib_table_find_or_create_and_lock (fproto, + gepg->gepg_rd[fproto], + FIB_SOURCE_PLUGIN_HI); + + if (~0 == gepg->gepg_fib_index[fproto]) + { + return (VNET_API_ERROR_NO_SUCH_FIB); + } + + dvr_dpo_add_or_lock (uplink_sw_if_index, + fib_proto_to_dpo (fproto), + &gepg->gepg_dpo[fproto]); + } + + /* + * packets direct from the uplink have had policy applied + */ + l2input_intf_bitmap_enable (gepg->gepg_uplink_sw_if_index, + L2INPUT_FEAT_GBP_NULL_CLASSIFY, 1); + + hash_set (gbp_endpoint_group_db.gepg_hash, + gepg->gepg_id, gepg - gbp_endpoint_group_pool); + + } + + return (0); +} + +void +gbp_endpoint_group_delete (epg_id_t epg_id) +{ + gbp_endpoint_group_t *gepg; + uword *p; + + p = hash_get (gbp_endpoint_group_db.gepg_hash, epg_id); + + if (NULL != p) + { + fib_protocol_t fproto; + + gepg = pool_elt_at_index (gbp_endpoint_group_pool, p[0]); + + l2input_intf_bitmap_enable (gepg->gepg_uplink_sw_if_index, + L2INPUT_FEAT_GBP_NULL_CLASSIFY, 0); + + FOR_EACH_FIB_IP_PROTOCOL (fproto) + { + dpo_reset (&gepg->gepg_dpo[fproto]); + fib_table_unlock (gepg->gepg_fib_index[fproto], + fproto, FIB_SOURCE_PLUGIN_HI); + } + + hash_unset (gbp_endpoint_group_db.gepg_hash, epg_id); + + pool_put (gbp_endpoint_group_pool, gepg); + } +} + +void +gbp_endpoint_group_walk (gbp_endpoint_group_cb_t cb, void *ctx) +{ + gbp_endpoint_group_t *gbpe; + + /* *INDENT-OFF* */ + pool_foreach(gbpe, gbp_endpoint_group_pool, + { + if (!cb(gbpe, ctx)) + break; + }); + /* *INDENT-ON* */ +} + +static clib_error_t * +gbp_endpoint_group_cli (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + vnet_main_t *vnm = vnet_get_main (); + epg_id_t epg_id = EPG_INVALID; + u32 uplink_sw_if_index = ~0; + u32 bd_id = ~0; + u32 rd_id = ~0; + u8 add = 1; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "%U", unformat_vnet_sw_interface, + vnm, &uplink_sw_if_index)) + ; + else if (unformat (input, "add")) + add = 1; + else if (unformat (input, "del")) + add = 0; + else if (unformat (input, "epg %d", &epg_id)) + ; + else if (unformat (input, "bd %d", &bd_id)) + ; + else if (unformat (input, "rd %d", &rd_id)) + ; + else + break; + } + + if (EPG_INVALID == epg_id) + return clib_error_return (0, "EPG-ID must be specified"); + + if (add) + { + if (~0 == uplink_sw_if_index) + return clib_error_return (0, "interface must be specified"); + if (~0 == bd_id) + return clib_error_return (0, "Bridge-domain must be specified"); + if (~0 == rd_id) + return clib_error_return (0, "route-domain must be specified"); + + gbp_endpoint_group_add (epg_id, bd_id, rd_id, rd_id, + uplink_sw_if_index); + } + else + gbp_endpoint_group_delete (epg_id); + + return (NULL); +} + +/*? + * Configure a GBP Endpoint Group + * + * @cliexpar + * @cliexstart{set gbp endpoint-group [del] epg <ID> bd <ID> <interface>} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_endpoint_group_cli_node, static) = { + .path = "gbp endpoint-group", + .short_help = "gbp endpoint-group [del] epg <ID> bd <ID> <interface>", + .function = gbp_endpoint_group_cli, +}; + +static int +gbp_endpoint_group_show_one (gbp_endpoint_group_t *gepg, void *ctx) +{ + vnet_main_t *vnm = vnet_get_main (); + vlib_main_t *vm; + + vm = ctx; + vlib_cli_output (vm, " %d, bd:%d, ip4:%d ip6:%d uplink:%U", + gepg->gepg_id, + gepg->gepg_bd, + gepg->gepg_rd[FIB_PROTOCOL_IP4], + gepg->gepg_rd[FIB_PROTOCOL_IP6], + format_vnet_sw_if_index_name, vnm, gepg->gepg_uplink_sw_if_index); + + return (1); +} + +static clib_error_t * +gbp_endpoint_group_show (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + vlib_cli_output (vm, "Endpoint-Groups:"); + gbp_endpoint_group_walk (gbp_endpoint_group_show_one, vm); + + return (NULL); +} + + +/*? + * Show Group Based Policy Endpoint_Groups and derived information + * + * @cliexpar + * @cliexstart{show gbp endpoint_group} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_endpoint_group_show_node, static) = { + .path = "show gbp endpoint-group", + .short_help = "show gbp endpoint-group\n", + .function = gbp_endpoint_group_show, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/gbp/gbp_endpoint_group.h b/src/plugins/gbp/gbp_endpoint_group.h new file mode 100644 index 00000000000..f71e5f5d70b --- /dev/null +++ b/src/plugins/gbp/gbp_endpoint_group.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __GBP_ENDPOINT_GROUP_H__ +#define __GBP_ENDPOINT_GROUP_H__ + +#include <plugins/gbp/gbp_types.h> + +#include <vnet/fib/fib_types.h> + +/** + * An Endpoint Group representation + */ +typedef struct gpb_endpoint_group_t_ +{ + /** + * ID + */ + epg_id_t gepg_id; + + /** + * Bridge-domain ID the EPG is in + */ + u32 gepg_bd; + + /** + * route-domain/IP-table ID the EPG is in + */ + u32 gepg_rd[FIB_PROTOCOL_IP_MAX]; + + /** + * resulting FIB indices + */ + u32 gepg_fib_index[FIB_PROTOCOL_IP_MAX]; + + /** + * Is the EPG an external/NAT + */ + u8 gepg_is_ext; + + /** + * the uplink interface dedicated to the EPG + */ + u32 gepg_uplink_sw_if_index; + + /** + * The DPO used in the L3 path for forwarding internal subnets + */ + dpo_id_t gepg_dpo[FIB_PROTOCOL_IP_MAX]; +} gbp_endpoint_group_t; + +/** + * EPG DB, key'd on EGP-ID + */ +typedef struct gbp_endpoint_group_db_t_ +{ + uword *gepg_hash; +} gbp_endpoint_group_db_t; + +extern int gbp_endpoint_group_add (epg_id_t epg_id, + u32 bd_id, + u32 ip4_table_id, + u32 ip6_table_id, u32 uplink_sw_if_index); +extern void gbp_endpoint_group_delete (epg_id_t epg_id); + +typedef int (*gbp_endpoint_group_cb_t) (gbp_endpoint_group_t * gbpe, + void *ctx); +extern void gbp_endpoint_group_walk (gbp_endpoint_group_cb_t bgpe, void *ctx); + +extern gbp_endpoint_group_t *gbp_endpoint_group_find (epg_id_t epg_id); + +/** + * DP functions and databases + */ +extern gbp_endpoint_group_db_t gbp_endpoint_group_db; +extern gbp_endpoint_group_t *gbp_endpoint_group_pool; + +always_inline u32 +gbp_epg_itf_lookup (epg_id_t epg) +{ + uword *p; + + p = hash_get (gbp_endpoint_group_db.gepg_hash, epg); + + if (NULL != p) + { + gbp_endpoint_group_t *gepg; + + gepg = pool_elt_at_index (gbp_endpoint_group_pool, p[0]); + return (gepg->gepg_uplink_sw_if_index); + } + return (~0); +} + +always_inline const dpo_id_t * +gbp_epg_dpo_lookup (epg_id_t epg, fib_protocol_t fproto) +{ + uword *p; + + p = hash_get (gbp_endpoint_group_db.gepg_hash, epg); + + if (NULL != p) + { + gbp_endpoint_group_t *gepg; + + gepg = pool_elt_at_index (gbp_endpoint_group_pool, p[0]); + return (&gepg->gepg_dpo[fproto]); + } + return (NULL); +} + +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/gbp/gbp_fwd.c b/src/plugins/gbp/gbp_fwd.c new file mode 100644 index 00000000000..fec5703d014 --- /dev/null +++ b/src/plugins/gbp/gbp_fwd.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <plugins/gbp/gbp.h> + +/** + * Grouping of global data for the GBP source EPG classification feature + */ +typedef struct gbp_fwd_main_t_ +{ + /** + * Next nodes for L2 output features + */ + u32 l2_input_feat_next[32]; +} gbp_fwd_main_t; + +static gbp_fwd_main_t gbp_fwd_main; + +#define foreach_gbp_fwd \ + _(DROP, "drop") \ + _(OUTPUT, "output") + +typedef enum +{ +#define _(sym,str) GBP_FWD_ERROR_##sym, + foreach_gbp_fwd +#undef _ + GBP_FWD_N_ERROR, +} gbp_fwd_error_t; + +static char *gbp_fwd_error_strings[] = { +#define _(sym,string) string, + foreach_gbp_fwd +#undef _ +}; + +typedef enum +{ +#define _(sym,str) GBP_FWD_NEXT_##sym, + foreach_gbp_fwd +#undef _ + GBP_FWD_N_NEXT, +} gbp_fwd_next_t; + +/** + * per-packet trace data + */ +typedef struct gbp_fwd_trace_t_ +{ + /* per-pkt trace data */ + epg_id_t src_epg; + u32 sw_if_index; +} gbp_fwd_trace_t; + +static uword +gbp_fwd (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next; + u32 next_index; + + next_index = 0; + n_left_from = frame->n_vectors; + from = vlib_frame_vector_args (frame); + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0, sw_if_index0, src_epg; + gbp_fwd_next_t next0; + vlib_buffer_t *b0; + + next0 = GBP_FWD_NEXT_DROP; + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + + /* + * lookup the uplink based on src EPG + */ + src_epg = vnet_buffer2 (b0)->gbp.src_epg; + + sw_if_index0 = gbp_epg_itf_lookup (src_epg); + + if (~0 != sw_if_index0) + { + vnet_buffer (b0)->sw_if_index[VLIB_TX] = sw_if_index0; + + next0 = GBP_FWD_NEXT_OUTPUT; + } + /* + * else + * don't know the uplink interface for this EPG => drop + */ + + if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED))) + { + gbp_fwd_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->src_epg = src_epg; + t->sw_if_index = sw_if_index0; + } + + /* verify speculative enqueue, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + return frame->n_vectors; +} + +/* packet trace format function */ +static u8 * +format_gbp_fwd_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + gbp_fwd_trace_t *t = va_arg (*args, gbp_fwd_trace_t *); + + s = format (s, "src-epg:%d", t->src_epg); + + return s; +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (gbp_fwd_node) = { + .function = gbp_fwd, + .name = "gbp-fwd", + .vector_size = sizeof (u32), + .format_trace = format_gbp_fwd_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(gbp_fwd_error_strings), + .error_strings = gbp_fwd_error_strings, + + .n_next_nodes = GBP_FWD_N_NEXT, + + .next_nodes = { + [GBP_FWD_NEXT_DROP] = "error-drop", + [GBP_FWD_NEXT_OUTPUT] = "l2-output", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (gbp_fwd_node, gbp_fwd); + +/* *INDENT-ON* */ + +static clib_error_t * +gbp_fwd_init (vlib_main_t * vm) +{ + gbp_fwd_main_t *gpm = &gbp_fwd_main; + + /* Initialize the feature next-node indices */ + feat_bitmap_init_next_nodes (vm, + gbp_fwd_node.index, + L2INPUT_N_FEAT, + l2input_get_feat_names (), + gpm->l2_input_feat_next); + + return 0; +} + +VLIB_INIT_FUNCTION (gbp_fwd_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/gbp/gbp_fwd_dpo.c b/src/plugins/gbp/gbp_fwd_dpo.c new file mode 100644 index 00000000000..e3dba5fa527 --- /dev/null +++ b/src/plugins/gbp/gbp_fwd_dpo.c @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <plugins/gbp/gbp.h> +#include <plugins/gbp/gbp_fwd_dpo.h> + +#include <vnet/ethernet/ethernet.h> + +/** + * The 'DB' of GBP FWD DPOs. + * There is one per-proto + */ +static index_t gbp_fwd_dpo_db[DPO_PROTO_NUM] = { INDEX_INVALID }; + +/** + * DPO type registered for these GBP FWD + */ +static dpo_type_t gbp_fwd_dpo_type; + +/** + * @brief pool of all interface DPOs + */ +gbp_fwd_dpo_t *gbp_fwd_dpo_pool; + +static gbp_fwd_dpo_t * +gbp_fwd_dpo_alloc (void) +{ + gbp_fwd_dpo_t *gfd; + + pool_get (gbp_fwd_dpo_pool, gfd); + + return (gfd); +} + +static inline gbp_fwd_dpo_t * +gbp_fwd_dpo_get_from_dpo (const dpo_id_t * dpo) +{ + ASSERT (gbp_fwd_dpo_type == dpo->dpoi_type); + + return (gbp_fwd_dpo_get (dpo->dpoi_index)); +} + +static inline index_t +gbp_fwd_dpo_get_index (gbp_fwd_dpo_t * gfd) +{ + return (gfd - gbp_fwd_dpo_pool); +} + +static void +gbp_fwd_dpo_lock (dpo_id_t * dpo) +{ + gbp_fwd_dpo_t *gfd; + + gfd = gbp_fwd_dpo_get_from_dpo (dpo); + gfd->gfd_locks++; +} + +static void +gbp_fwd_dpo_unlock (dpo_id_t * dpo) +{ + gbp_fwd_dpo_t *gfd; + + gfd = gbp_fwd_dpo_get_from_dpo (dpo); + gfd->gfd_locks--; + + if (0 == gfd->gfd_locks) + { + gbp_fwd_dpo_db[gfd->gfd_proto] = INDEX_INVALID; + pool_put (gbp_fwd_dpo_pool, gfd); + } +} + +void +gbp_fwd_dpo_add_or_lock (dpo_proto_t dproto, dpo_id_t * dpo) +{ + gbp_fwd_dpo_t *gfd; + + if (INDEX_INVALID == gbp_fwd_dpo_db[dproto]) + { + gfd = gbp_fwd_dpo_alloc (); + + gfd->gfd_proto = dproto; + + gbp_fwd_dpo_db[dproto] = gbp_fwd_dpo_get_index (gfd); + } + else + { + gfd = gbp_fwd_dpo_get (gbp_fwd_dpo_db[dproto]); + } + + dpo_set (dpo, gbp_fwd_dpo_type, dproto, gbp_fwd_dpo_get_index (gfd)); +} + +u8 * +format_gbp_fwd_dpo (u8 * s, va_list * ap) +{ + index_t index = va_arg (*ap, index_t); + CLIB_UNUSED (u32 indent) = va_arg (*ap, u32); + gbp_fwd_dpo_t *gfd = gbp_fwd_dpo_get (index); + + return (format (s, "gbp-fwd-dpo: %U", format_dpo_proto, gfd->gfd_proto)); +} + +const static dpo_vft_t gbp_fwd_dpo_vft = { + .dv_lock = gbp_fwd_dpo_lock, + .dv_unlock = gbp_fwd_dpo_unlock, + .dv_format = format_gbp_fwd_dpo, +}; + +/** + * @brief The per-protocol VLIB graph nodes that are assigned to a glean + * object. + * + * this means that these graph nodes are ones from which a glean is the + * parent object in the DPO-graph. + */ +const static char *const gbp_fwd_dpo_ip4_nodes[] = { + "ip4-gbp-fwd-dpo", + NULL, +}; + +const static char *const gbp_fwd_dpo_ip6_nodes[] = { + "ip6-gbp-fwd-dpo", + NULL, +}; + +const static char *const *const gbp_fwd_dpo_nodes[DPO_PROTO_NUM] = { + [DPO_PROTO_IP4] = gbp_fwd_dpo_ip4_nodes, + [DPO_PROTO_IP6] = gbp_fwd_dpo_ip6_nodes, +}; + +dpo_type_t +gbp_fwd_dpo_get_type (void) +{ + return (gbp_fwd_dpo_type); +} + +static clib_error_t * +gbp_fwd_dpo_module_init (vlib_main_t * vm) +{ + dpo_proto_t dproto; + + FOR_EACH_DPO_PROTO (dproto) + { + gbp_fwd_dpo_db[dproto] = INDEX_INVALID; + } + + gbp_fwd_dpo_type = dpo_register_new_type (&gbp_fwd_dpo_vft, + gbp_fwd_dpo_nodes); + + return (NULL); +} + +VLIB_INIT_FUNCTION (gbp_fwd_dpo_module_init); + +typedef struct gbp_fwd_dpo_trace_t_ +{ + u32 src_epg; + u32 dpo_index; +} gbp_fwd_dpo_trace_t; + +typedef enum +{ + GBP_FWD_DROP, + GBP_FWD_FWD, + GBP_FWD_N_NEXT, +} gbp_fwd_next_t; + +always_inline uword +gbp_fwd_dpo_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame, fib_protocol_t fproto) +{ + u32 n_left_from, next_index, *from, *to_next; + + from = vlib_frame_vector_args (from_frame); + n_left_from = from_frame->n_vectors; + + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + const dpo_id_t *next_dpo0; + vlib_buffer_t *b0; + epg_id_t src_epg0; + u32 bi0, next0; + + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + + src_epg0 = vnet_buffer2 (b0)->gbp.src_epg; + next_dpo0 = gbp_epg_dpo_lookup (src_epg0, fproto); + + if (PREDICT_TRUE (NULL != next_dpo0)) + { + vnet_buffer (b0)->ip.adj_index[VLIB_TX] = next_dpo0->dpoi_index; + next0 = GBP_FWD_FWD; + } + else + { + next0 = GBP_FWD_DROP; + } + + if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) + { + gbp_fwd_dpo_trace_t *tr; + + tr = vlib_add_trace (vm, node, b0, sizeof (*tr)); + tr->src_epg = src_epg0; + tr->dpo_index = (NULL != next_dpo0 ? + next_dpo0->dpoi_index : ~0); + } + + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, + n_left_to_next, bi0, next0); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + return from_frame->n_vectors; +} + +static u8 * +format_gbp_fwd_dpo_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + gbp_fwd_dpo_trace_t *t = va_arg (*args, gbp_fwd_dpo_trace_t *); + + s = format (s, " epg:%d dpo:%d", t->src_epg, t->dpo_index); + + return s; +} + +static uword +ip4_gbp_fwd_dpo (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * from_frame) +{ + return (gbp_fwd_dpo_inline (vm, node, from_frame, FIB_PROTOCOL_IP4)); +} + +static uword +ip6_gbp_fwd_dpo (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * from_frame) +{ + return (gbp_fwd_dpo_inline (vm, node, from_frame, FIB_PROTOCOL_IP6)); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (ip4_gbp_fwd_dpo_node) = { + .function = ip4_gbp_fwd_dpo, + .name = "ip4-gbp-fwd-dpo", + .vector_size = sizeof (u32), + .format_trace = format_gbp_fwd_dpo_trace, + .n_next_nodes = GBP_FWD_N_NEXT, + .next_nodes = + { + [GBP_FWD_DROP] = "ip4-drop", + [GBP_FWD_FWD] = "ip4-dvr-dpo", + } +}; +VLIB_REGISTER_NODE (ip6_gbp_fwd_dpo_node) = { + .function = ip6_gbp_fwd_dpo, + .name = "ip6-gbp-fwd-dpo", + .vector_size = sizeof (u32), + .format_trace = format_gbp_fwd_dpo_trace, + .n_next_nodes = GBP_FWD_N_NEXT, + .next_nodes = + { + [GBP_FWD_DROP] = "ip6-drop", + [GBP_FWD_FWD] = "ip6-dvr-dpo", + } +}; + +VLIB_NODE_FUNCTION_MULTIARCH (ip4_gbp_fwd_dpo_node, ip4_gbp_fwd_dpo) +VLIB_NODE_FUNCTION_MULTIARCH (ip6_gbp_fwd_dpo_node, ip6_gbp_fwd_dpo) +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/gbp/gbp_fwd_dpo.h b/src/plugins/gbp/gbp_fwd_dpo.h new file mode 100644 index 00000000000..6092d6241b5 --- /dev/null +++ b/src/plugins/gbp/gbp_fwd_dpo.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __GBP_FWD_DPO_H__ +#define __GBP_FWD_DPO_H__ + +#include <vnet/dpo/dpo.h> + +/** + * @brief + * The GBP FWD DPO. Used in the L3 path to select the correct EPG uplink + * based on the source EPG. + */ +typedef struct gbp_fwd_dpo_t_ +{ + /** + * The protocol of packets using this DPO + */ + dpo_proto_t gfd_proto; + + /** + * number of locks. + */ + u16 gfd_locks; +} gbp_fwd_dpo_t; + +extern void gbp_fwd_dpo_add_or_lock (dpo_proto_t dproto, dpo_id_t * dpo); + +extern dpo_type_t gbp_fwd_dpo_get_type (void); + +/** + * @brief pool of all interface DPOs + */ +extern gbp_fwd_dpo_t *gbp_fwd_dpo_pool; + +static inline gbp_fwd_dpo_t * +gbp_fwd_dpo_get (index_t index) +{ + return (pool_elt_at_index (gbp_fwd_dpo_pool, index)); +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ + +#endif diff --git a/src/plugins/gbp/gbp_msg_enum.h b/src/plugins/gbp/gbp_msg_enum.h index 6f134a84027..c6f34f92005 100644 --- a/src/plugins/gbp/gbp_msg_enum.h +++ b/src/plugins/gbp/gbp_msg_enum.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Cisco and/or its affiliates. + * Copyright (c) 2018 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/src/plugins/gbp/gbp_policy.c b/src/plugins/gbp/gbp_policy.c new file mode 100644 index 00000000000..8f3fc76a019 --- /dev/null +++ b/src/plugins/gbp/gbp_policy.c @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <plugins/gbp/gbp.h> + +/** + * Grouping of global data for the GBP source EPG classification feature + */ +typedef struct gbp_policy_main_t_ +{ + /** + * Next nodes for L2 output features + */ + u32 l2_output_feat_next[32]; +} gbp_policy_main_t; + +static gbp_policy_main_t gbp_policy_main; + +#define foreach_gbp_policy \ + _(DENY, "deny") + +typedef enum +{ +#define _(sym,str) GBP_ERROR_##sym, + foreach_gbp_policy +#undef _ + GBP_POLICY_N_ERROR, +} gbp_policy_error_t; + +static char *gbp_policy_error_strings[] = { +#define _(sym,string) string, + foreach_gbp_policy +#undef _ +}; + +typedef enum +{ +#define _(sym,str) GBP_POLICY_NEXT_##sym, + foreach_gbp_policy +#undef _ + GBP_POLICY_N_NEXT, +} gbp_policy_next_t; + +/** + * per-packet trace data + */ +typedef struct gbp_policy_trace_t_ +{ + /* per-pkt trace data */ + epg_id_t src_epg; + epg_id_t dst_epg; + u32 acl_index; +} gbp_policy_trace_t; + +static uword +gbp_policy (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + gbp_policy_main_t *gpm = &gbp_policy_main; + u32 n_left_from, *from, *to_next; + u32 next_index; + + next_index = 0; + n_left_from = frame->n_vectors; + from = vlib_frame_vector_args (frame); + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + gbp_policy_next_t next0; + gbp_contract_key_t key0; + u32 bi0, sw_if_index0; + vlib_buffer_t *b0; + u32 acl_index0; + + next0 = GBP_POLICY_NEXT_DENY; + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + + /* + * determine the src and dst EPG + */ + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX]; + key0.gck_dst = gbp_port_to_epg (sw_if_index0); + key0.gck_src = vnet_buffer2 (b0)->gbp.src_epg; + acl_index0 = ~0; + + if (~0 != key0.gck_src) + { + if (PREDICT_FALSE (key0.gck_src == key0.gck_dst)) + { + /* + * intra-epg allowed + */ + next0 = vnet_l2_feature_next (b0, gpm->l2_output_feat_next, + L2OUTPUT_FEAT_GBP_POLICY); + } + else + { + acl_index0 = gbp_acl_lookup (&key0); + + if (~0 != acl_index0) + { + /* + * TODO tests against the ACL + */ + /* + * ACL tables are not available outside of ACL plugin + * until then bypass the ACL to next node + */ + next0 = + vnet_l2_feature_next (b0, gpm->l2_output_feat_next, + L2OUTPUT_FEAT_GBP_POLICY); + } + } + } + else + { + /* + * the src EPG is not set when the packet arrives on an EPG + * uplink interface and we do not need to apply policy + */ + next0 = vnet_l2_feature_next (b0, gpm->l2_output_feat_next, + L2OUTPUT_FEAT_GBP_POLICY); + } + + if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED))) + { + gbp_policy_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->src_epg = key0.gck_src; + t->dst_epg = key0.gck_dst; + t->acl_index = acl_index0; + } + + /* verify speculative enqueue, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + return frame->n_vectors; +} + +/* packet trace format function */ +static u8 * +format_gbp_policy_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + gbp_policy_trace_t *t = va_arg (*args, gbp_policy_trace_t *); + + s = + format (s, "src:%d, dst:%d, acl:%d", t->src_epg, t->dst_epg, + t->acl_index); + + return s; +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (gbp_policy_node) = { + .function = gbp_policy, + .name = "gbp-policy", + .vector_size = sizeof (u32), + .format_trace = format_gbp_policy_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(gbp_policy_error_strings), + .error_strings = gbp_policy_error_strings, + + .n_next_nodes = GBP_POLICY_N_NEXT, + + .next_nodes = { + [GBP_POLICY_NEXT_DENY] = "error-drop", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (gbp_policy_node, gbp_policy); + +/* *INDENT-ON* */ + +static clib_error_t * +gbp_policy_init (vlib_main_t * vm) +{ + gbp_policy_main_t *gpm = &gbp_policy_main; + + /* Initialize the feature next-node indexes */ + feat_bitmap_init_next_nodes (vm, + gbp_policy_node.index, + L2OUTPUT_N_FEAT, + l2output_get_feat_names (), + gpm->l2_output_feat_next); + + return 0; +} + +VLIB_INIT_FUNCTION (gbp_policy_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/gbp/gbp_policy_dpo.c b/src/plugins/gbp/gbp_policy_dpo.c new file mode 100644 index 00000000000..bfe5974f807 --- /dev/null +++ b/src/plugins/gbp/gbp_policy_dpo.c @@ -0,0 +1,537 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <plugins/gbp/gbp.h> +#include <plugins/gbp/gbp_policy_dpo.h> +#include <plugins/gbp/gbp_recirc.h> + +#include <vnet/dpo/dvr_dpo.h> +#include <vnet/fib/ip4_fib.h> +#include <vnet/fib/ip6_fib.h> +#include <vnet/dpo/load_balance.h> + +/** + * DPO pool + */ +static gbp_policy_dpo_t *gbp_policy_dpo_pool; + +/** + * DPO type registered for these GBP FWD + */ +static dpo_type_t gbp_policy_dpo_type; + +static inline gbp_policy_dpo_t * +gbp_policy_dpo_get_i (index_t index) +{ + return (pool_elt_at_index (gbp_policy_dpo_pool, index)); +} + +gbp_policy_dpo_t * +gbp_policy_dpo_get (index_t index) +{ + return (gbp_policy_dpo_get_i (index)); +} + +static gbp_policy_dpo_t * +gbp_policy_dpo_alloc (void) +{ + gbp_policy_dpo_t *gpd; + + pool_get (gbp_policy_dpo_pool, gpd); + + return (gpd); +} + +static inline gbp_policy_dpo_t * +gbp_policy_dpo_get_from_dpo (const dpo_id_t * dpo) +{ + ASSERT (gbp_policy_dpo_type == dpo->dpoi_type); + + return (gbp_policy_dpo_get_i (dpo->dpoi_index)); +} + +static inline index_t +gbp_policy_dpo_get_index (gbp_policy_dpo_t * gpd) +{ + return (gpd - gbp_policy_dpo_pool); +} + +static void +gbp_policy_dpo_lock (dpo_id_t * dpo) +{ + gbp_policy_dpo_t *gpd; + + gpd = gbp_policy_dpo_get_from_dpo (dpo); + gpd->gpd_locks++; +} + +static void +gbp_policy_dpo_unlock (dpo_id_t * dpo) +{ + gbp_policy_dpo_t *gpd; + + gpd = gbp_policy_dpo_get_from_dpo (dpo); + gpd->gpd_locks--; + + if (0 == gpd->gpd_locks) + { + dpo_reset (&gpd->gpd_dpo); + pool_put (gbp_policy_dpo_pool, gpd); + } +} + +void +gbp_policy_dpo_add_or_lock (dpo_proto_t dproto, + epg_id_t epg, u32 sw_if_index, dpo_id_t * dpo) +{ + gbp_policy_dpo_t *gpd; + dpo_id_t parent = DPO_INVALID; + + gpd = gbp_policy_dpo_alloc (); + memset (gpd, 0, sizeof (*gpd)); + + gpd->gpd_proto = dproto; + gpd->gpd_sw_if_index = sw_if_index; + gpd->gpd_epg = epg; + + /* + * stack on the DVR DPO for the output interface + */ + dvr_dpo_add_or_lock (sw_if_index, dproto, &parent); + + dpo_stack (gbp_policy_dpo_type, dproto, &gpd->gpd_dpo, &parent); + + dpo_set (dpo, gbp_policy_dpo_type, dproto, gbp_policy_dpo_get_index (gpd)); +} + +u8 * +format_gbp_policy_dpo (u8 * s, va_list * ap) +{ + index_t index = va_arg (*ap, index_t); + u32 indent = va_arg (*ap, u32); + gbp_policy_dpo_t *gpd = gbp_policy_dpo_get_i (index); + vnet_main_t *vnm = vnet_get_main (); + + s = format (s, "gbp-policy-dpo: %U, epg:%d out:%U", + format_dpo_proto, gpd->gpd_proto, + gpd->gpd_epg, + format_vnet_sw_if_index_name, vnm, gpd->gpd_sw_if_index); + s = format (s, "\n%U", format_white_space, indent + 2); + s = format (s, "%U", format_dpo_id, &gpd->gpd_dpo, indent + 4); + + return (s); +} + +const static dpo_vft_t gbp_policy_dpo_vft = { + .dv_lock = gbp_policy_dpo_lock, + .dv_unlock = gbp_policy_dpo_unlock, + .dv_format = format_gbp_policy_dpo, +}; + +/** + * @brief The per-protocol VLIB graph nodes that are assigned to a glean + * object. + * + * this means that these graph nodes are ones from which a glean is the + * parent object in the DPO-graph. + */ +const static char *const gbp_policy_dpo_ip4_nodes[] = { + "ip4-gbp-policy-dpo", + NULL, +}; + +const static char *const gbp_policy_dpo_ip6_nodes[] = { + "ip6-gbp-policy-dpo", + NULL, +}; + +const static char *const *const gbp_policy_dpo_nodes[DPO_PROTO_NUM] = { + [DPO_PROTO_IP4] = gbp_policy_dpo_ip4_nodes, + [DPO_PROTO_IP6] = gbp_policy_dpo_ip6_nodes, +}; + +dpo_type_t +gbp_policy_dpo_get_type (void) +{ + return (gbp_policy_dpo_type); +} + +static clib_error_t * +gbp_policy_dpo_module_init (vlib_main_t * vm) +{ + gbp_policy_dpo_type = dpo_register_new_type (&gbp_policy_dpo_vft, + gbp_policy_dpo_nodes); + + return (NULL); +} + +VLIB_INIT_FUNCTION (gbp_policy_dpo_module_init); + +typedef struct gbp_policy_dpo_trace_t_ +{ + u32 src_epg; + u32 dst_epg; + u32 acl_index; +} gbp_policy_dpo_trace_t; + +typedef enum +{ + GBP_POLICY_DROP, + GBP_POLICY_N_NEXT, +} gbp_policy_next_t; + +always_inline uword +gbp_policy_dpo_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame, fib_protocol_t fproto) +{ + u32 n_left_from, next_index, *from, *to_next; + + from = vlib_frame_vector_args (from_frame); + n_left_from = from_frame->n_vectors; + + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + const gbp_policy_dpo_t *gpd0; + u32 bi0, next0, acl_index0; + gbp_contract_key_t key0; + vlib_buffer_t *b0; + + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + next0 = GBP_POLICY_DROP; + acl_index0 = ~0; + + b0 = vlib_get_buffer (vm, bi0); + gpd0 = + gbp_policy_dpo_get_i (vnet_buffer (b0)->ip.adj_index[VLIB_TX]); + vnet_buffer (b0)->ip.adj_index[VLIB_TX] = gpd0->gpd_dpo.dpoi_index; + + key0.gck_src = vnet_buffer2 (b0)->gbp.src_epg; + key0.gck_dst = gpd0->gpd_epg; + + if (~0 != key0.gck_src) + { + if (PREDICT_FALSE (key0.gck_src == key0.gck_dst)) + { + /* + * intra-epg allowed + */ + next0 = gpd0->gpd_dpo.dpoi_next_node; + } + else + { + acl_index0 = gbp_acl_lookup (&key0); + + if (~0 != acl_index0) + { + /* + * TODO tests against the ACL + */ + /* + * ACL tables are not available outside of ACL plugin + * until then bypass the ACL to next node + */ + next0 = gpd0->gpd_dpo.dpoi_next_node; + } + } + } + else + { + /* + * the src EPG is not set when the packet arrives on an EPG + * uplink interface and we do not need to apply policy + */ + next0 = gpd0->gpd_dpo.dpoi_next_node; + } + + if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) + { + gbp_policy_dpo_trace_t *tr; + + tr = vlib_add_trace (vm, node, b0, sizeof (*tr)); + tr->src_epg = key0.gck_src; + tr->dst_epg = key0.gck_dst; + tr->acl_index = acl_index0; + } + + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, + n_left_to_next, bi0, next0); + } + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + return from_frame->n_vectors; +} + +static u8 * +format_gbp_policy_dpo_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + gbp_policy_dpo_trace_t *t = va_arg (*args, gbp_policy_dpo_trace_t *); + + s = format (s, " src-epg:%d dst-epg:%d acl-index:%d", + t->src_epg, t->dst_epg, t->acl_index); + + return s; +} + +static uword +ip4_gbp_policy_dpo (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * from_frame) +{ + return (gbp_policy_dpo_inline (vm, node, from_frame, FIB_PROTOCOL_IP4)); +} + +static uword +ip6_gbp_policy_dpo (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * from_frame) +{ + return (gbp_policy_dpo_inline (vm, node, from_frame, FIB_PROTOCOL_IP6)); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (ip4_gbp_policy_dpo_node) = { + .function = ip4_gbp_policy_dpo, + .name = "ip4-gbp-policy-dpo", + .vector_size = sizeof (u32), + .format_trace = format_gbp_policy_dpo_trace, + .n_next_nodes = GBP_POLICY_N_NEXT, + .next_nodes = + { + [GBP_POLICY_DROP] = "ip4-drop", + } +}; +VLIB_REGISTER_NODE (ip6_gbp_policy_dpo_node) = { + .function = ip6_gbp_policy_dpo, + .name = "ip6-gbp-policy-dpo", + .vector_size = sizeof (u32), + .format_trace = format_gbp_policy_dpo_trace, + .n_next_nodes = GBP_POLICY_N_NEXT, + .next_nodes = + { + [GBP_POLICY_DROP] = "ip6-drop", + } +}; + +VLIB_NODE_FUNCTION_MULTIARCH (ip4_gbp_policy_dpo_node, ip4_gbp_policy_dpo) +VLIB_NODE_FUNCTION_MULTIARCH (ip6_gbp_policy_dpo_node, ip6_gbp_policy_dpo) +/* *INDENT-ON* */ + + /** + * per-packet trace data + */ +typedef struct gbp_classify_trace_t_ +{ + /* per-pkt trace data */ + epg_id_t src_epg; +} gbp_classify_trace_t; + +typedef enum gbp_lpm_classify_next_t_ +{ + GPB_LPM_CLASSIFY_DROP, +} gbp_lpm_classify_next_t; + +/* + * Determine the SRC EPG from a LPM + */ +always_inline uword +gbp_lpm_classify_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, fib_protocol_t fproto) +{ + u32 n_left_from, *from, *to_next; + u32 next_index; + + next_index = 0; + n_left_from = frame->n_vectors; + from = vlib_frame_vector_args (frame); + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0, sw_if_index0, fib_index0, lbi0; + gbp_lpm_classify_next_t next0; + const gbp_policy_dpo_t *gpd0; + const gbp_recirc_t *gr0; + const dpo_id_t *dpo0; + load_balance_t *lb0; + ip4_header_t *ip4_0; + ip6_header_t *ip6_0; + vlib_buffer_t *b0; + epg_id_t src_epg0; + + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + next0 = GPB_LPM_CLASSIFY_DROP; + + b0 = vlib_get_buffer (vm, bi0); + + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + gr0 = gbp_recirc_get (sw_if_index0); + fib_index0 = gr0->gr_fib_index[fproto]; + + if (FIB_PROTOCOL_IP4 == fproto) + { + ip4_0 = vlib_buffer_get_current (b0); + lbi0 = ip4_fib_forwarding_lookup (fib_index0, + &ip4_0->src_address); + } + else + { + ip6_0 = vlib_buffer_get_current (b0); + lbi0 = ip6_fib_table_fwding_lookup (&ip6_main, fib_index0, + &ip6_0->src_address); + } + + lb0 = load_balance_get (lbi0); + dpo0 = load_balance_get_bucket_i (lb0, 0); + + if (gbp_policy_dpo_type == dpo0->dpoi_type) + { + gpd0 = gbp_policy_dpo_get_i (dpo0->dpoi_index); + src_epg0 = gpd0->gpd_epg; + vnet_feature_next (vnet_buffer (b0)->sw_if_index[VLIB_RX], + &next0, b0); + } + else + { + /* could not classify => drop */ + src_epg0 = 0; + } + + vnet_buffer2 (b0)->gbp.src_epg = src_epg0; + + if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED))) + { + gbp_classify_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->src_epg = src_epg0; + } + + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + return frame->n_vectors; +} + +static uword +gbp_ip4_lpm_classify (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return (gbp_lpm_classify_inline (vm, node, frame, FIB_PROTOCOL_IP4)); +} + +static uword +gbp_ip6_lpm_classify (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return (gbp_lpm_classify_inline (vm, node, frame, FIB_PROTOCOL_IP6)); +} + + /* packet trace format function */ +static u8 * +format_gbp_classify_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + gbp_classify_trace_t *t = va_arg (*args, gbp_classify_trace_t *); + + s = format (s, "src-epg:%d", t->src_epg); + + return s; +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (gbp_ip4_lpm_classify_node) = { + .function = gbp_ip4_lpm_classify, + .name = "ip4-gbp-lpm-classify", + .vector_size = sizeof (u32), + .format_trace = format_gbp_classify_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = 0, + .n_next_nodes = 1, + .next_nodes = { + [GPB_LPM_CLASSIFY_DROP] = "ip4-drop" + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (gbp_ip4_lpm_classify_node, gbp_ip4_lpm_classify); + +VLIB_REGISTER_NODE (gbp_ip6_lpm_classify_node) = { + .function = gbp_ip6_lpm_classify, + .name = "ip6-gpb-lpm-classify", + .vector_size = sizeof (u32), + .format_trace = format_gbp_classify_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = 0, + .n_next_nodes = 1, + .next_nodes = { + [GPB_LPM_CLASSIFY_DROP] = "ip6-drop" + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (gbp_ip6_lpm_classify_node, gbp_ip6_lpm_classify); + +VNET_FEATURE_INIT (gbp_ip4_lpm_classify_feat_node, static) = +{ + .arc_name = "ip4-unicast", + .node_name = "ip4-gbp-lpm-classify", + .runs_before = VNET_FEATURES ("nat44-out2in"), +}; +VNET_FEATURE_INIT (gbp_ip6_lpm_classify_feat_node, static) = +{ + .arc_name = "ip6-unicast", + .node_name = "ip6-gbp-lpm-classify", + .runs_before = VNET_FEATURES ("nat66-out2in"), +}; + +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/gbp/gbp_policy_dpo.h b/src/plugins/gbp/gbp_policy_dpo.h new file mode 100644 index 00000000000..d6a8986d622 --- /dev/null +++ b/src/plugins/gbp/gbp_policy_dpo.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __GBP_POLICY_DPO_H__ +#define __GBP_POLICY_DPO_H__ + +#include <vnet/dpo/dpo.h> + +/** + * @brief + * The GBP FWD DPO. Used in the L3 path to select the correct EPG uplink + * based on the source EPG. + */ +typedef struct gbp_policy_dpo_t_ +{ + /** + * The protocol of packets using this DPO + */ + dpo_proto_t gpd_proto; + + /** + * EPG + */ + epg_id_t gpd_epg; + + /** + * output sw_if_index + */ + u32 gpd_sw_if_index; + + /** + * number of locks. + */ + u16 gpd_locks; + + /** + * Stacked DPO on DVR of output interface + */ + dpo_id_t gpd_dpo; +} gbp_policy_dpo_t; + +extern void gbp_policy_dpo_add_or_lock (dpo_proto_t dproto, + epg_id_t epg, + u32 sw_if_index, dpo_id_t * dpo); + +extern gbp_policy_dpo_t *gbp_policy_dpo_get (index_t index); + +extern dpo_type_t gbp_policy_dpo_get_type (void); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ + +#endif diff --git a/src/plugins/gbp/gbp_recirc.c b/src/plugins/gbp/gbp_recirc.c new file mode 100644 index 00000000000..1debbe406f3 --- /dev/null +++ b/src/plugins/gbp/gbp_recirc.c @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <plugins/gbp/gbp_recirc.h> +#include <plugins/gbp/gbp_endpoint_group.h> +#include <plugins/gbp/gbp_endpoint.h> + +#include <vnet/dpo/dvr_dpo.h> +#include <vnet/fib/fib_table.h> + +/** + * Pool of GBP recircs + */ +gbp_recirc_t *gbp_recirc_pool; + +/** + * Recirc configs keyed by sw_if_index + */ +index_t *gbp_recirc_db; + +int +gbp_recirc_add (u32 sw_if_index, epg_id_t epg_id, u8 is_ext) +{ + gbp_recirc_t *gr; + index_t gri; + + vec_validate_init_empty (gbp_recirc_db, sw_if_index, INDEX_INVALID); + + gri = gbp_recirc_db[sw_if_index]; + + if (INDEX_INVALID == gri) + { + gbp_endpoint_group_t *gepg; + fib_protocol_t fproto; + + pool_get (gbp_recirc_pool, gr); + memset (gr, 0, sizeof (*gr)); + gri = gr - gbp_recirc_pool; + + gr->gr_epg = epg_id; + gr->gr_is_ext = is_ext; + gr->gr_sw_if_index = sw_if_index; + + /* + * IP enable the recirc interface + */ + ip4_sw_interface_enable_disable (gr->gr_sw_if_index, 1); + ip6_sw_interface_enable_disable (gr->gr_sw_if_index, 1); + + /* + * cache the FIB indicies of the EPG + */ + gepg = gbp_endpoint_group_find (gr->gr_epg); + + if (NULL == gepg) + return (VNET_API_ERROR_NO_SUCH_ENTRY); + + FOR_EACH_FIB_IP_PROTOCOL (fproto) + { + gr->gr_fib_index[fproto] = gepg->gepg_fib_index[fproto]; + } + + /* + * Packets on the recirculation interface are subjet to src-EPG + * classification. Recirc interfaces are L2-emulation mode. + * for internal EPGs this is via an LPM on all external subnets. + * for external EPGs this is via a port mapping. + */ + if (gr->gr_is_ext) + { + /* + * recirc is for post-NAT translation packets going into + * the external EPG, these are classified to the NAT EPG + * based on its port + */ + gbp_itf_epg_update (gr->gr_sw_if_index, gr->gr_epg, 0); + vnet_feature_enable_disable ("ip4-unicast", + "ip4-gbp-src-classify", + gr->gr_sw_if_index, 1, 0, 0); + vnet_feature_enable_disable ("ip6-unicast", + "ip6-gbp-src-classify", + gr->gr_sw_if_index, 1, 0, 0); + } + else + { + /* + * recirc is for pre-NAT translation packets coming from + * the external EPG, these are classified based on a LPM + * in the EPG's route-domain + */ + vnet_feature_enable_disable ("ip4-unicast", + "ip4-gbp-lpm-classify", + gr->gr_sw_if_index, 1, 0, 0); + vnet_feature_enable_disable ("ip6-unicast", + "ip6-gbp-lpm-classify", + gr->gr_sw_if_index, 1, 0, 0); + } + + gbp_recirc_db[sw_if_index] = gri; + } + + return (0); +} + +void +gbp_recirc_delete (u32 sw_if_index) +{ + gbp_recirc_t *gr; + index_t gri; + + gri = gbp_recirc_db[sw_if_index]; + + if (INDEX_INVALID != gri) + { + gr = pool_elt_at_index (gbp_recirc_pool, gri); + + if (gr->gr_is_ext) + { + gbp_itf_epg_delete (gr->gr_sw_if_index); + vnet_feature_enable_disable ("ip4-unicast", + "ip4-gbp-src-classify", + gr->gr_sw_if_index, 0, 0, 0); + vnet_feature_enable_disable ("ip6-unicast", + "ip6-gbp-src-classify", + gr->gr_sw_if_index, 0, 0, 0); + } + else + { + vnet_feature_enable_disable ("ip4-unicast", + "ip4-gbp-lpm-classify", + gr->gr_sw_if_index, 0, 0, 0); + vnet_feature_enable_disable ("ip6-unicast", + "ip6-gbp-lpm-classify", + gr->gr_sw_if_index, 0, 0, 0); + } + + ip4_sw_interface_enable_disable (gr->gr_sw_if_index, 0); + ip6_sw_interface_enable_disable (gr->gr_sw_if_index, 0); + + gbp_recirc_db[sw_if_index] = INDEX_INVALID; + pool_put (gbp_recirc_pool, gr); + } +} + +void +gbp_recirc_walk (gbp_recirc_cb_t cb, void *ctx) +{ + gbp_recirc_t *gbpe; + + /* *INDENT-OFF* */ + pool_foreach(gbpe, gbp_recirc_pool, + { + if (!cb(gbpe, ctx)) + break; + }); + /* *INDENT-ON* */ +} + +static int +gbp_recirc_show_one (gbp_recirc_t * gr, void *ctx) +{ + vnet_main_t *vnm = vnet_get_main (); + vlib_main_t *vm; + + vm = ctx; + vlib_cli_output (vm, " %U, epg:%d, ext:%d", + format_vnet_sw_if_index_name, vnm, + gr->gr_sw_if_index, gr->gr_epg, gr->gr_is_ext); + + return (1); +} + +static clib_error_t * +gbp_recirc_show (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + vlib_cli_output (vm, "Recirculation-Interfaces:"); + gbp_recirc_walk (gbp_recirc_show_one, vm); + + return (NULL); +} + + +/*? + * Show Group Based Policy Recircs and derived information + * + * @cliexpar + * @cliexstart{show gbp recirc} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_recirc_show_node, static) = { + .path = "show gbp recirc", + .short_help = "show gbp recirc\n", + .function = gbp_recirc_show, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/gbp/gbp_recirc.h b/src/plugins/gbp/gbp_recirc.h new file mode 100644 index 00000000000..10f3da631c1 --- /dev/null +++ b/src/plugins/gbp/gbp_recirc.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __GBP_RECIRC_H__ +#define __GBP_RECIRC_H__ + +#include <plugins/gbp/gbp_types.h> +#include <vnet/fib/fib_types.h> + +/** + * An Endpoint Group representation + */ +typedef struct gpb_recirc_t_ +{ + /** + * EPG ID that packets will classify to when they arrive on this recirc + */ + epg_id_t gr_epg; + + /** + * FIB indices the EPG is mapped to + */ + u32 gr_fib_index[FIB_PROTOCOL_IP_MAX]; + + /** + * Is the interface for packets post-NAT translatoin (i.e. ext) + * or pre-NAT ranslation (i.e. internal) + */ + u8 gr_is_ext; + + /** + */ + u32 gr_sw_if_index; + +} gbp_recirc_t; + +extern int gbp_recirc_add (u32 sw_if_index, epg_id_t epg_id, u8 is_ext); +extern void gbp_recirc_delete (u32 sw_if_index); + +typedef int (*gbp_recirc_cb_t) (gbp_recirc_t * gbpe, void *ctx); +extern void gbp_recirc_walk (gbp_recirc_cb_t bgpe, void *ctx); + +/** + * Data plane functions + */ +extern gbp_recirc_t *gbp_recirc_pool; +extern index_t *gbp_recirc_db; + +always_inline const gbp_recirc_t * +gbp_recirc_get (u32 sw_if_index) +{ + return (pool_elt_at_index (gbp_recirc_pool, gbp_recirc_db[sw_if_index])); +} +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/gbp/gbp_subnet.c b/src/plugins/gbp/gbp_subnet.c new file mode 100644 index 00000000000..b6990844cd3 --- /dev/null +++ b/src/plugins/gbp/gbp_subnet.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <plugins/gbp/gbp.h> +#include <plugins/gbp/gbp_fwd_dpo.h> +#include <plugins/gbp/gbp_policy_dpo.h> + +#include <vnet/fib/fib_table.h> +#include <vnet/dpo/load_balance.h> + +static int +gbp_internal_subnet_add (u32 fib_index, const fib_prefix_t * pfx) +{ + dpo_id_t gfd = DPO_INVALID; + + gbp_fwd_dpo_add_or_lock (fib_proto_to_dpo (pfx->fp_proto), &gfd); + + fib_table_entry_special_dpo_update (fib_index, + pfx, + FIB_SOURCE_PLUGIN_HI, + FIB_ENTRY_FLAG_EXCLUSIVE, &gfd); + + dpo_reset (&gfd); + + return (0); +} + +static int +gbp_external_subnet_add (u32 fib_index, + const fib_prefix_t * pfx, + u32 sw_if_index, epg_id_t epg) +{ + dpo_id_t gpd = DPO_INVALID; + + gbp_policy_dpo_add_or_lock (fib_proto_to_dpo (pfx->fp_proto), + epg, sw_if_index, &gpd); + + fib_table_entry_special_dpo_update (fib_index, + pfx, + FIB_SOURCE_PLUGIN_HI, + (FIB_ENTRY_FLAG_EXCLUSIVE | + FIB_ENTRY_FLAG_LOOSE_URPF_EXEMPT), + &gpd); + + dpo_reset (&gpd); + + return (0); +} + +static int +gbp_subnet_del (u32 fib_index, const fib_prefix_t * pfx) +{ + fib_table_entry_delete (fib_index, pfx, FIB_SOURCE_PLUGIN_HI); + + return (0); +} + +int +gbp_subnet_add_del (u32 table_id, + const fib_prefix_t * pfx, + u32 sw_if_index, epg_id_t epg, u8 is_add, u8 is_internal) +{ + u32 fib_index; + + fib_index = fib_table_find (pfx->fp_proto, table_id); + + if (~0 == fib_index) + return (VNET_API_ERROR_NO_SUCH_FIB); + + if (is_internal && is_add) + return (gbp_internal_subnet_add (fib_index, pfx)); + else if (!is_internal && is_add) + return (gbp_external_subnet_add (fib_index, pfx, sw_if_index, epg)); + + return (gbp_subnet_del (fib_index, pfx)); +} + +typedef struct gbp_subnet_fib_table_walk_ctx_t_ +{ + gbp_subnet_cb_t cb; + void *ctx; +} gbp_subnet_fib_table_walk_ctx_t; + +static fib_table_walk_rc_t +gbp_subnet_fib_table_walk (fib_node_index_t fei, void *arg) +{ + gbp_subnet_fib_table_walk_ctx_t *ctx = arg; + const dpo_id_t *dpo; + fib_prefix_t pfx; + u32 table_id; + + fib_entry_get_prefix (fei, &pfx); + table_id = fib_table_get_table_id (fib_entry_get_fib_index (fei), + pfx.fp_proto); + dpo = fib_entry_contribute_ip_forwarding (fei); + + if (DPO_LOAD_BALANCE == dpo->dpoi_type) + { + dpo = load_balance_get_bucket (dpo->dpoi_index, 0); + + if (dpo->dpoi_type == gbp_policy_dpo_get_type ()) + { + gbp_policy_dpo_t *gpd; + + gpd = gbp_policy_dpo_get (dpo->dpoi_index); + + /* *INDENT-OFF* */ + ctx->cb (table_id, &pfx, + gpd->gpd_sw_if_index, + gpd->gpd_epg, + 0, // is_internal + ctx->ctx); + /* *INDENT-ON* */ + } + else if (dpo->dpoi_type == gbp_fwd_dpo_get_type ()) + { + /* *INDENT-OFF* */ + ctx->cb (table_id, &pfx, + ~0, // sw_if_index + ~0, // epg + 1, // is_internal + ctx->ctx); + /* *INDENT-ON* */ + } + } + + return (FIB_TABLE_WALK_CONTINUE); +} + +void +gbp_subnet_walk (gbp_subnet_cb_t cb, void *ctx) +{ + fib_table_t *fib_table; + + gbp_subnet_fib_table_walk_ctx_t wctx = { + .cb = cb, + .ctx = ctx, + }; + + /* *INDENT-OFF* */ + pool_foreach (fib_table, ip4_main.fibs, + ({ + fib_table_walk(fib_table->ft_index, + FIB_PROTOCOL_IP4, + gbp_subnet_fib_table_walk, + &wctx); + })); + pool_foreach (fib_table, ip6_main.fibs, + ({ + fib_table_walk(fib_table->ft_index, + FIB_PROTOCOL_IP6, + gbp_subnet_fib_table_walk, + &wctx); + })); + /* *INDENT-ON* */ +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/gbp/gbp_subnet.h b/src/plugins/gbp/gbp_subnet.h new file mode 100644 index 00000000000..24b4f3a5816 --- /dev/null +++ b/src/plugins/gbp/gbp_subnet.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __GBP_SUBNET_H__ +#define __GBP_SUBNET_H__ + +#include <plugins/gbp/gbp_types.h> + +extern int gbp_subnet_add_del (u32 table_id, + const fib_prefix_t * pfx, + u32 sw_if_index, + epg_id_t epg, u8 is_add, u8 is_internal); + + +typedef int (*gbp_subnet_cb_t) (u32 table_id, + const fib_prefix_t * pfx, + u32 sw_if_index, + epg_id_t epg, u8 is_internal, void *ctx); +extern void gbp_subnet_walk (gbp_subnet_cb_t cb, void *ctx); + +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/gbp/gbp_types.h b/src/plugins/gbp/gbp_types.h new file mode 100644 index 00000000000..fa337451028 --- /dev/null +++ b/src/plugins/gbp/gbp_types.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __GBP_TYPES_H__ +#define __GBP_TYPES_H__ + +#include <vnet/vnet.h> + +typedef u32 epg_id_t; +#define EPG_INVALID (~0) + +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ |