/* * 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_itf.h> #include <plugins/gbp/gbp_bridge_domain.h> #include <plugins/gbp/gbp_route_domain.h> #include <vnet/ip/ip.h> #define foreach_gbp_itf_mode \ _(L2, "l2") \ _(L3, "L3") typedef enum gbp_ift_mode_t_ { #define _(s,v) GBP_ITF_MODE_##s, foreach_gbp_itf_mode #undef _ } gbp_itf_mode_t; /** * Attributes and configurations attached to interfaces by GBP */ typedef struct gbp_itf_t_ { /** * Number of references to this interface */ u32 gi_locks; /** * The interface this wrapper is managing */ u32 gi_sw_if_index; /** * The mode of the interface */ gbp_itf_mode_t gi_mode; /** * Users of this interface - this is encoded in the user's handle */ u32 *gi_users; /** * L2/L3 Features configured by each user */ u32 *gi_input_fbs; u32 gi_input_fb; u32 *gi_output_fbs; u32 gi_output_fb; /** * function to call when the interface is deleted. */ gbp_itf_free_fn_t gi_free_fn; union { /** * GBP BD or RD index */ u32 gi_gbi; index_t gi_gri; }; } gbp_itf_t; static gbp_itf_t *gbp_itf_pool; static uword *gbp_itf_db; static const char *gbp_itf_feat_bit_pos_to_arc[] = { #define _(s,v,a) [GBP_ITF_L3_FEAT_POS_##s] = a, foreach_gdb_l3_feature #undef _ }; static const char *gbp_itf_feat_bit_pos_to_feat[] = { #define _(s,v,a) [GBP_ITF_L3_FEAT_POS_##s] = v, foreach_gdb_l3_feature #undef _ }; u8 * format_gbp_itf_l3_feat (u8 * s, va_list * args) { gbp_itf_l3_feat_t flags = va_arg (*args, gbp_itf_l3_feat_t); #define _(a, b, c) \ if (flags & GBP_ITF_L3_FEAT_##a) \ s = format (s, "%s ", b); foreach_gdb_l3_feature #undef _ return (s); } void gbp_itf_hdl_reset (gbp_itf_hdl_t * gh) { *gh = GBP_ITF_HDL_INVALID; } bool gbp_itf_hdl_is_valid (gbp_itf_hdl_t gh) { return (gh.gh_which != GBP_ITF_HDL_INVALID.gh_which); } static gbp_itf_t * gbp_itf_get (index_t gii) { if (pool_is_free_index (gbp_itf_pool, gii)) return (NULL); return (pool_elt_at_index (gbp_itf_pool, gii)); } static gbp_itf_t * gbp_itf_find (u32 sw_if_index) { uword *p; p = hash_get (gbp_itf_db, sw_if_index); if (NULL != p) return (gbp_itf_get (p[0])); return (NULL); } static gbp_itf_t * gbp_itf_find_hdl (gbp_itf_hdl_t gh) { return (gbp_itf_find (gh.gh_which)); } u32 gbp_itf_get_sw_if_index (gbp_itf_hdl_t hdl) { return (hdl.gh_which); } static gbp_itf_hdl_t gbp_itf_mk_hdl (gbp_itf_t * gi) { gbp_itf_hdl_t gh; u32 *useri; pool_get (gi->gi_users, useri); *useri = 0; gh.gh_who = useri - gi->gi_users; gh.gh_which = gi->gi_sw_if_index; return (gh); } static gbp_itf_hdl_t gbp_itf_l2_add_and_lock_i (u32 sw_if_index, index_t gbi, gbp_itf_free_fn_t ff) { gbp_itf_t *gi; gi = gbp_itf_find (sw_if_index); if (NULL == gi) { pool_get_zero (gbp_itf_pool, gi); gi->gi_sw_if_index = sw_if_index; gi->gi_gbi = gbi; gi->gi_mode = GBP_ITF_MODE_L2; gi->gi_free_fn = ff; gbp_bridge_domain_itf_add (gi->gi_gbi, gi->gi_sw_if_index, L2_BD_PORT_TYPE_NORMAL); hash_set (gbp_itf_db, gi->gi_sw_if_index, gi - gbp_itf_pool); } gi->gi_locks++; return (gbp_itf_mk_hdl (gi)); } gbp_itf_hdl_t gbp_itf_l2_add_and_lock (u32 sw_if_index, index_t gbi) { return (gbp_itf_l2_add_and_lock_i (sw_if_index, gbi, NULL)); } gbp_itf_hdl_t gbp_itf_l2_add_and_lock_w_free (u32 sw_if_index, index_t gbi, gbp_itf_free_fn_t ff) { return (gbp_itf_l2_add_and_lock_i (sw_if_index, gbi, ff)); } gbp_itf_hdl_t gbp_itf_l3_add_and_lock_i (u32 sw_if_index, index_t gri, gbp_itf_free_fn_t ff) { gbp_itf_t *gi; gi = gbp_itf_find (sw_if_index); if (NULL == gi) { const gbp_route_domain_t *grd; fib_protocol_t fproto; pool_get_zero (gbp_itf_pool, gi); gi->gi_sw_if_index = sw_if_index; gi->gi_mode = GBP_ITF_MODE_L3; gi->gi_gri = gri; gi->gi_free_fn = ff; grd = gbp_route_domain_get (gi->gi_gri); ip4_sw_interface_enable_disable (gi->gi_sw_if_index, 1); ip6_sw_interface_enable_disable (gi->gi_sw_if_index, 1); FOR_EACH_FIB_IP_PROTOCOL (fproto) ip_table_bind (fproto, gi->gi_sw_if_index, grd->grd_table_id[fproto], 1); hash_set (gbp_itf_db, gi->gi_sw_if_index, gi - gbp_itf_pool); } gi->gi_locks++; return (gbp_itf_mk_hdl (gi)); } gbp_itf_hdl_t gbp_itf_l3_add_and_lock (u32 sw_if_index, index_t gri) { return (gbp_itf_l3_add_and_lock_i (sw_if_index, gri, NULL)); } gbp_itf_hdl_t gbp_itf_l3_add_and_lock_w_free (u32 sw_if_index, index_t gri, gbp_itf_free_fn_t ff) { return (gbp_itf_l3_add_and_lock_i (sw_if_index, gri, ff)); } void gbp_itf_lock (gbp_itf_hdl_t gh) { gbp_itf_t *gi; if (!gbp_itf_hdl_is_valid (gh)) return; gi = gbp_itf_find_hdl (gh); gi->gi_locks++; } gbp_itf_hdl_t gbp_itf_clone_and_lock (gbp_itf_hdl_t gh) { gbp_itf_t *gi; if (!gbp_itf_hdl_is_valid (gh)) return (GBP_ITF_HDL_INVALID); gi = gbp_itf_find_hdl (gh); gi->gi_locks++; return (gbp_itf_mk_hdl (gi)); } void gbp_itf_unlock (gbp_itf_hdl_t * gh) { gbp_itf_t *gi; if (!gbp_itf_hdl_is_valid (*gh)) return; gi = gbp_itf_find_hdl (*gh); ASSERT (gi->gi_locks > 0); gi->gi_locks--; if (0 == gi->gi_locks) { if (GBP_ITF_MODE_L2 == gi->gi_mode) { gbp_itf_l2_set_input_feature (*gh, L2INPUT_FEAT_NONE); gbp_itf_l2_set_output_feature (*gh, L2OUTPUT_FEAT_NONE); gbp_bridge_domain_itf_del (gi->gi_gbi, gi->gi_sw_if_index, L2_BD_PORT_TYPE_NORMAL); } else { fib_protocol_t fproto; gbp_itf_l3_set_input_feature (*gh, GBP_ITF_L3_FEAT_NONE); FOR_EACH_FIB_IP_PROTOCOL (fproto) ip_table_bind (fproto, gi->gi_sw_if_index, 0, 0); ip4_sw_interface_enable_disable (gi->gi_sw_if_index, 0); ip6_sw_interface_enable_disable (gi->gi_sw_if_index, 0); } hash_unset (gbp_itf_db, gi->gi_sw_if_index); if (gi->gi_free_fn) gi->gi_free_fn (gi->gi_sw_if_index); pool_free (gi->gi_users); vec_free (gi->gi_input_fbs); vec_free (gi->gi_output_fbs); memset (gi, 0, sizeof (*gi)); } gbp_itf_hdl_reset (gh); } void gbp_itf_l3_set_input_feature (gbp_itf_hdl_t gh, gbp_itf_l3_feat_t feats) { u32 diff_fb, new_fb, *fb, feat; gbp_itf_t *gi; gi = gbp_itf_find_hdl (gh); if (NULL == gi || GBP_ITF_MODE_L3 != gi->gi_mode) return; vec_validate (gi->gi_input_fbs, gh.gh_who); gi->gi_input_fbs[gh.gh_who] = feats; new_fb = 0; vec_foreach (fb, gi->gi_input_fbs) { new_fb |= *fb; } /* add new features */ diff_fb = (gi->gi_input_fb ^ new_fb) & new_fb; /* *INDENT-OFF* */ foreach_set_bit (feat, diff_fb, ({ vnet_feature_enable_disable (gbp_itf_feat_bit_pos_to_arc[feat], gbp_itf_feat_bit_pos_to_feat[feat], gi->gi_sw_if_index, 1, 0, 0); })); /* *INDENT-ON* */ /* remove unneeded features */ diff_fb = (gi->gi_input_fb ^ new_fb) & gi->gi_input_fb; /* *INDENT-OFF* */ foreach_set_bit (feat, diff_fb, ({ vnet_feature_enable_disable (gbp_itf_feat_bit_pos_to_arc[feat], gbp_itf_feat_bit_pos_to_feat[feat], gi->gi_sw_if_index, 0, 0, 0); })); /* *INDENT-ON* */ gi->gi_input_fb = new_fb; } void gbp_itf_l2_set_input_feature (gbp_itf_hdl_t gh, l2input_feat_masks_t feats) { u32 diff_fb, new_fb, *fb, feat; gbp_itf_t *gi; gi = gbp_itf_find_hdl (gh); if (NULL == gi || GBP_ITF_MODE_L2 != gi->gi_mode) { ASSERT (0); return; } vec_validate (gi->gi_input_fbs, gh.gh_who); gi->gi_input_fbs[gh.gh_who] = feats; new_fb = 0; vec_foreach (fb, gi->gi_input_fbs) { new_fb |= *fb; } /* add new features */ diff_fb = (gi->gi_input_fb ^ new_fb) & new_fb; /* *INDENT-OFF* */ foreach_set_bit (feat, diff_fb, ({ l2input_intf_bitmap_enable (gi->gi_sw_if_index, (1 << feat), 1); })); /* *INDENT-ON* */ /* remove unneeded features */ diff_fb = (gi->gi_input_fb ^ new_fb) & gi->gi_input_fb; /* *INDENT-OFF* */ foreach_set_bit (feat, diff_fb, ({ l2input_intf_bitmap_enable (gi->gi_sw_if_index, (1 << feat), 0); })); /* *INDENT-ON* */ gi->gi_input_fb = new_fb; } void gbp_itf_l2_set_output_feature (gbp_itf_hdl_t gh, l2output_feat_masks_t feats) { u32 diff_fb, new_fb, *fb, feat; gbp_itf_t *gi; gi = gbp_itf_find_hdl (gh); if (NULL == gi || GBP_ITF_MODE_L2 != gi->gi_mode) { ASSERT (0); return; } vec_validate (gi->gi_output_fbs, gh.gh_who); gi->gi_output_fbs[gh.gh_who] = feats; new_fb = 0; vec_foreach (fb, gi->gi_output_fbs) { new_fb |= *fb; } /* add new features */ diff_fb = (gi->gi_output_fb ^ new_fb) & new_fb; /* *INDENT-OFF* */ foreach_set_bit (feat, diff_fb, ({ l2output_intf_bitmap_enable (gi->gi_sw_if_index, (1 << feat), 1); })); /* *INDENT-ON* */ /* remove unneeded features */ diff_fb = (gi->gi_output_fb ^ new_fb) & gi->gi_output_fb; /* *INDENT-OFF* */ foreach_set_bit (feat, diff_fb, ({ l2output_intf_bitmap_enable (gi->gi_sw_if_index, (1 << feat), 0); })); /* *INDENT-ON* */ gi->gi_output_fb = new_fb; } static u8 * format_gbp_itf_mode (u8 * s, va_list * args) { gbp_itf_mode_t mode = va_arg (*args, gbp_itf_mode_t); switch (mode) { #define _(a,v) \ case GBP_ITF_MODE_##a: \ return format(s, "%s", v); foreach_gbp_itf_mode #undef _ } return (s); } static u8 * format_gbp_itf (u8 * s, va_list * args) { index_t gii = va_arg (*args, index_t); gbp_itf_t *gi; if (INDEX_INVALID == gii) return (format (s, "unset")); gi = gbp_itf_get (gii); s = format (s, "%U locks:%d mode:%U ", format_vnet_sw_if_index_name, vnet_get_main (), gi->gi_sw_if_index, gi->gi_locks, format_gbp_itf_mode, gi->gi_mode); if (GBP_ITF_MODE_L2 == gi->gi_mode) s = format (s, "gbp-bd:%d input-feats:[%U] output-feats:[%U]", gi->gi_gbi, format_l2_input_features, gi->gi_input_fb, 0, format_l2_output_features, gi->gi_output_fb, 0); else s = format (s, "gbp-rd:%d input-feats:[%U] output-feats:[%U]", gi->gi_gbi, format_gbp_itf_l3_feat, gi->gi_input_fb, format_gbp_itf_l3_feat, gi->gi_output_fb); return (s); } u8 * format_gbp_itf_hdl (u8 * s, va_list * args) { gbp_itf_hdl_t gh = va_arg (*args, gbp_itf_hdl_t); gbp_itf_t *gi; gi = gbp_itf_find_hdl (gh); if (NULL == gi) return format (s, "INVALID"); return (format (s, "%U", format_gbp_itf, gi - gbp_itf_pool)); } static clib_error_t * gbp_itf_show (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { u32 gii; vlib_cli_output (vm, "Interfaces:"); /* *INDENT-OFF* */ pool_foreach_index (gii, gbp_itf_pool) { vlib_cli_output (vm, " [%d] %U", gii, format_gbp_itf, gii); } /* *INDENT-ON* */ return (NULL); } /*? * Show Group Based Interfaces * * @cliexpar * @cliexstart{show gbp contract} * @cliexend ?*/ /* *INDENT-OFF* */ VLIB_CLI_COMMAND (gbp_contract_show_node, static) = { .path = "show gbp interface", .short_help = "show gbp interface\n", .function = gbp_itf_show, }; /* *INDENT-ON* */ /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */