blob: a9e9225ae72d4395b293afa0ef0cd0ab18824649 [file] [log] [blame]
/*
* 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>
#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:
*/