X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fplugins%2Fgbp%2Fgbp_subnet.c;h=2ef3fcfd534d0b6fcc9a947b9ff355f8123e9a5f;hb=59f71132e;hp=3819a51595bcea4d22b83236c2e91809bac35509;hpb=c5d4317ad0425d01893dbb4afad9f16c2cde077d;p=vpp.git diff --git a/src/plugins/gbp/gbp_subnet.c b/src/plugins/gbp/gbp_subnet.c index 3819a51595b..2ef3fcfd534 100644 --- a/src/plugins/gbp/gbp_subnet.c +++ b/src/plugins/gbp/gbp_subnet.c @@ -16,21 +16,121 @@ #include #include #include +#include #include #include +/** + * a key for the DB + */ +typedef struct gbp_subnet_key_t_ +{ + fib_prefix_t gsk_pfx; + u32 gsk_fib_index; +} gbp_subnet_key_t; + +/** + * Subnet + */ +typedef struct gbp_subnet_t_ +{ + gbp_subnet_key_t *gs_key; + gbp_subnet_type_t gs_type; + index_t gs_rd; + + union + { + struct + { + sclass_t gs_sclass; + u32 gs_sw_if_index; + } gs_stitched_external; + struct + { + sclass_t gs_sclass; + } gs_l3_out; + }; + + fib_node_index_t gs_fei; +} gbp_subnet_t; + +/** + * A DB of the subnets; key={pfx,fib-index} + */ +uword *gbp_subnet_db; + +/** + * pool of subnets + */ +gbp_subnet_t *gbp_subnet_pool; + +static fib_source_t gbp_fib_source; + +static index_t +gbp_subnet_db_find (u32 fib_index, const fib_prefix_t * pfx) +{ + gbp_subnet_key_t key = { + .gsk_pfx = *pfx, + .gsk_fib_index = fib_index, + }; + uword *p; + + p = hash_get_mem (gbp_subnet_db, &key); + + if (NULL != p) + return p[0]; + + return (INDEX_INVALID); +} + +static void +gbp_subnet_db_add (u32 fib_index, const fib_prefix_t * pfx, gbp_subnet_t * gs) +{ + gbp_subnet_key_t *key; + + key = clib_mem_alloc (sizeof (*key)); + + clib_memcpy (&(key->gsk_pfx), pfx, sizeof (*pfx)); + key->gsk_fib_index = fib_index; + + hash_set_mem (gbp_subnet_db, key, (gs - gbp_subnet_pool)); + + gs->gs_key = key; +} + +static void +gbp_subnet_db_del (gbp_subnet_t * gs) +{ + hash_unset_mem (gbp_subnet_db, gs->gs_key); + + clib_mem_free (gs->gs_key); + gs->gs_key = NULL; +} + + static int -gbp_internal_subnet_add (u32 fib_index, const fib_prefix_t * pfx) +gbp_subnet_transport_add (gbp_subnet_t * gs) { dpo_id_t gfd = DPO_INVALID; + gbp_route_domain_t *grd; + fib_protocol_t fproto; + + fproto = gs->gs_key->gsk_pfx.fp_proto; + grd = gbp_route_domain_get (gs->gs_rd); - gbp_fwd_dpo_add_or_lock (fib_proto_to_dpo (pfx->fp_proto), &gfd); + if (~0 == grd->grd_uu_sw_if_index[fproto]) + return (VNET_API_ERROR_INVALID_SW_IF_INDEX); - fib_table_entry_special_dpo_update (fib_index, - pfx, - FIB_SOURCE_PLUGIN_HI, - FIB_ENTRY_FLAG_EXCLUSIVE, &gfd); + gs->gs_fei = fib_table_entry_update_one_path (gs->gs_key->gsk_fib_index, + &gs->gs_key->gsk_pfx, + gbp_fib_source, + FIB_ENTRY_FLAG_NONE, + fib_proto_to_dpo (fproto), + &ADJ_BCAST_ADDR, + grd->grd_uu_sw_if_index + [fproto], ~0, 1, NULL, + FIB_ROUTE_PATH_FLAG_NONE); dpo_reset (&gfd); @@ -38,21 +138,43 @@ gbp_internal_subnet_add (u32 fib_index, const fib_prefix_t * pfx) } static int -gbp_external_subnet_add (u32 fib_index, - const fib_prefix_t * pfx, - u32 sw_if_index, epg_id_t epg) +gbp_subnet_internal_add (gbp_subnet_t * gs) +{ + dpo_id_t gfd = DPO_INVALID; + + gbp_fwd_dpo_add_or_lock (fib_proto_to_dpo (gs->gs_key->gsk_pfx.fp_proto), + &gfd); + + gs->gs_fei = fib_table_entry_special_dpo_update (gs->gs_key->gsk_fib_index, + &gs->gs_key->gsk_pfx, + gbp_fib_source, + FIB_ENTRY_FLAG_EXCLUSIVE, + &gfd); + + dpo_reset (&gfd); + + return (0); +} + +static int +gbp_subnet_external_add (gbp_subnet_t * gs, u32 sw_if_index, sclass_t sclass) { dpo_id_t gpd = DPO_INVALID; - gbp_policy_dpo_add_or_lock (fib_proto_to_dpo (pfx->fp_proto), - epg, sw_if_index, &gpd); + gs->gs_stitched_external.gs_sclass = sclass; + gs->gs_stitched_external.gs_sw_if_index = sw_if_index; + + gbp_policy_dpo_add_or_lock (fib_proto_to_dpo (gs->gs_key->gsk_pfx.fp_proto), + gbp_route_domain_get_scope (gs->gs_rd), + gs->gs_stitched_external.gs_sclass, + gs->gs_stitched_external.gs_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); + gs->gs_fei = fib_table_entry_special_dpo_update (gs->gs_key->gsk_fib_index, + &gs->gs_key->gsk_pfx, + gbp_fib_source, + (FIB_ENTRY_FLAG_EXCLUSIVE | + FIB_ENTRY_FLAG_LOOSE_URPF_EXEMPT), + &gpd); dpo_reset (&gpd); @@ -60,113 +182,413 @@ gbp_external_subnet_add (u32 fib_index, } static int -gbp_subnet_del (u32 fib_index, const fib_prefix_t * pfx) +gbp_subnet_l3_out_add (gbp_subnet_t * gs, sclass_t sclass, int is_anon) { - fib_table_entry_delete (fib_index, pfx, FIB_SOURCE_PLUGIN_HI); + fib_entry_flag_t flags; + dpo_id_t gpd = DPO_INVALID; + + gs->gs_l3_out.gs_sclass = sclass; + + gbp_policy_dpo_add_or_lock (fib_proto_to_dpo (gs->gs_key->gsk_pfx.fp_proto), + gbp_route_domain_get_scope (gs->gs_rd), + gs->gs_l3_out.gs_sclass, ~0, &gpd); + + flags = FIB_ENTRY_FLAG_INTERPOSE; + if (is_anon) + flags |= FIB_ENTRY_FLAG_COVERED_INHERIT; + + gs->gs_fei = fib_table_entry_special_dpo_add (gs->gs_key->gsk_fib_index, + &gs->gs_key->gsk_pfx, + FIB_SOURCE_SPECIAL, + flags, &gpd); + + dpo_reset (&gpd); return (0); } +static void +gbp_subnet_del_i (index_t gsi) +{ + gbp_subnet_t *gs; + + gs = pool_elt_at_index (gbp_subnet_pool, gsi); + + fib_table_entry_delete_index (gs->gs_fei, + (GBP_SUBNET_L3_OUT == gs->gs_type + || GBP_SUBNET_ANON_L3_OUT == + gs->gs_type) ? FIB_SOURCE_SPECIAL : + gbp_fib_source); + + gbp_subnet_db_del (gs); + gbp_route_domain_unlock (gs->gs_rd); + + pool_put (gbp_subnet_pool, gs); +} + 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) +gbp_subnet_del (u32 rd_id, const fib_prefix_t * pfx) { + gbp_route_domain_t *grd; + index_t gsi, grdi; u32 fib_index; - fib_index = fib_table_find (pfx->fp_proto, table_id); + grdi = gbp_route_domain_find (rd_id); - if (~0 == fib_index) + if (~0 == grdi) 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)); + grd = gbp_route_domain_get (grdi); + fib_index = grd->grd_fib_index[pfx->fp_proto]; + + gsi = gbp_subnet_db_find (fib_index, pfx); - return (gbp_subnet_del (fib_index, pfx)); + if (INDEX_INVALID == gsi) + return (VNET_API_ERROR_NO_SUCH_ENTRY); + + gbp_subnet_del_i (gsi); + + return (0); } -typedef struct gbp_subnet_fib_table_walk_ctx_t_ +int +gbp_subnet_add (u32 rd_id, + const fib_prefix_t * pfx, + gbp_subnet_type_t type, u32 sw_if_index, sclass_t sclass) { - gbp_subnet_cb_t cb; - void *ctx; -} gbp_subnet_fib_table_walk_ctx_t; + gbp_route_domain_t *grd; + index_t grdi, gsi; + gbp_subnet_t *gs; + u32 fib_index; + int rv; + + switch (type) + { + case GBP_SUBNET_TRANSPORT: + case GBP_SUBNET_STITCHED_INTERNAL: + case GBP_SUBNET_STITCHED_EXTERNAL: + case GBP_SUBNET_L3_OUT: + case GBP_SUBNET_ANON_L3_OUT: + break; + default: + return (VNET_API_ERROR_INCORRECT_ADJACENCY_TYPE); + } -static fib_table_walk_rc_t -gbp_subnet_fib_table_walk (fib_node_index_t fei, void *arg) + grdi = gbp_route_domain_find_and_lock (rd_id); + + if (~0 == grdi) + return (VNET_API_ERROR_NO_SUCH_FIB); + + grd = gbp_route_domain_get (grdi); + fib_index = grd->grd_fib_index[pfx->fp_proto]; + + gsi = gbp_subnet_db_find (fib_index, pfx); + + /* + * this is an update if the subnet already exists, so remove the old + */ + if (INDEX_INVALID != gsi) + gbp_subnet_del_i (gsi); + + rv = -2; + + pool_get (gbp_subnet_pool, gs); + + gs->gs_type = type; + gs->gs_rd = grdi; + gbp_subnet_db_add (fib_index, pfx, gs); + + switch (type) + { + case GBP_SUBNET_STITCHED_INTERNAL: + rv = gbp_subnet_internal_add (gs); + break; + case GBP_SUBNET_STITCHED_EXTERNAL: + rv = gbp_subnet_external_add (gs, sw_if_index, sclass); + break; + case GBP_SUBNET_TRANSPORT: + rv = gbp_subnet_transport_add (gs); + break; + case GBP_SUBNET_L3_OUT: + rv = gbp_subnet_l3_out_add (gs, sclass, 0 /* is_anon */ ); + break; + case GBP_SUBNET_ANON_L3_OUT: + rv = gbp_subnet_l3_out_add (gs, sclass, 1 /* is_anon */ ); + break; + } + + return (rv); +} + +static clib_error_t * +gbp_subnet_add_del_cli (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) { - gbp_subnet_fib_table_walk_ctx_t *ctx = arg; - const fib_prefix_t *pfx; - const dpo_id_t *dpo; - u32 table_id; + unformat_input_t _line_input, *line_input = &_line_input; + vnet_main_t *vnm = vnet_get_main (); + fib_prefix_t pfx = {.fp_addr = ip46_address_initializer }; + int length; + u32 rd_id = ~0; + u32 sw_if_index = ~0; + gbp_subnet_type_t type = ~0; + u32 sclass = ~0; + int is_add = 1; + int rv; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "del")) + is_add = 0; + else if (unformat (line_input, "rd %d", &rd_id)) + ; + else + if (unformat + (line_input, "prefix %U/%d", unformat_ip4_address, + &pfx.fp_addr.ip4, &length)) + pfx.fp_proto = FIB_PROTOCOL_IP4; + else + if (unformat + (line_input, "prefix %U/%d", unformat_ip6_address, + &pfx.fp_addr.ip6, &length)) + pfx.fp_proto = FIB_PROTOCOL_IP6; + else if (unformat (line_input, "type transport")) + type = GBP_SUBNET_TRANSPORT; + else if (unformat (line_input, "type stitched-internal")) + type = GBP_SUBNET_STITCHED_INTERNAL; + else if (unformat (line_input, "type stitched-external")) + type = GBP_SUBNET_STITCHED_EXTERNAL; + else if (unformat (line_input, "type anon-l3-out")) + type = GBP_SUBNET_ANON_L3_OUT; + else if (unformat (line_input, "type l3-out")) + type = GBP_SUBNET_L3_OUT; + else + if (unformat_user + (line_input, unformat_vnet_sw_interface, vnm, &sw_if_index)) + ; + else if (unformat (line_input, "sclass %u", &sclass)) + ; + else + return clib_error_return (0, "unknown input `%U'", + format_unformat_error, line_input); + } + unformat_free (line_input); + + pfx.fp_len = length; - pfx = fib_entry_get_prefix (fei); - table_id = fib_table_get_table_id (fib_entry_get_fib_index (fei), - pfx->fp_proto); - dpo = fib_entry_contribute_ip_forwarding (fei); + if (is_add) + rv = gbp_subnet_add (rd_id, &pfx, type, sw_if_index, sclass); + else + rv = gbp_subnet_del (rd_id, &pfx); - if (DPO_LOAD_BALANCE == dpo->dpoi_type) + switch (rv) { - 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* */ - } + case 0: + return 0; + case VNET_API_ERROR_NO_SUCH_FIB: + return clib_error_return (0, "no such FIB"); } - return (FIB_TABLE_WALK_CONTINUE); + return clib_error_return (0, "unknown error %d", rv); } +/*? + * Add Group Based Policy Subnets + * + * @cliexpar + * @cliexstart{gbp subnet [del] rd prefix type [] [sclass ]} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_subnet_add_del, static) = { + .path = "gbp subnet", + .short_help = "gbp subnet [del] rd prefix type [] [sclass ]\n", + .function = gbp_subnet_add_del_cli, +}; +/* *INDENT-ON* */ + + + void gbp_subnet_walk (gbp_subnet_cb_t cb, void *ctx) { - fib_table_t *fib_table; + gbp_route_domain_t *grd; + gbp_subnet_t *gs; + u32 sw_if_index; + sclass_t sclass; - gbp_subnet_fib_table_walk_ctx_t wctx = { - .cb = cb, - .ctx = ctx, - }; + sclass = SCLASS_INVALID; + sw_if_index = ~0; /* *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, + pool_foreach (gs, gbp_subnet_pool, ({ - fib_table_walk(fib_table->ft_index, - FIB_PROTOCOL_IP6, - gbp_subnet_fib_table_walk, - &wctx); + grd = gbp_route_domain_get(gs->gs_rd); + + switch (gs->gs_type) + { + case GBP_SUBNET_STITCHED_INTERNAL: + case GBP_SUBNET_TRANSPORT: + /* use defaults above */ + break; + case GBP_SUBNET_STITCHED_EXTERNAL: + sw_if_index = gs->gs_stitched_external.gs_sw_if_index; + sclass = gs->gs_stitched_external.gs_sclass; + break; + case GBP_SUBNET_L3_OUT: + case GBP_SUBNET_ANON_L3_OUT: + sclass = gs->gs_l3_out.gs_sclass; + break; + } + + if (WALK_STOP == cb (grd->grd_id, &gs->gs_key->gsk_pfx, + gs->gs_type, sw_if_index, sclass, ctx)) + break; })); /* *INDENT-ON* */ } +typedef enum gsb_subnet_show_flags_t_ +{ + GBP_SUBNET_SHOW_BRIEF, + GBP_SUBNET_SHOW_DETAILS, +} gsb_subnet_show_flags_t; + +static u8 * +format_gbp_subnet_type (u8 * s, va_list * args) +{ + gbp_subnet_type_t type = va_arg (*args, gbp_subnet_type_t); + + switch (type) + { + case GBP_SUBNET_STITCHED_INTERNAL: + return (format (s, "stitched-internal")); + case GBP_SUBNET_STITCHED_EXTERNAL: + return (format (s, "stitched-external")); + case GBP_SUBNET_TRANSPORT: + return (format (s, "transport")); + case GBP_SUBNET_L3_OUT: + return (format (s, "l3-out")); + case GBP_SUBNET_ANON_L3_OUT: + return (format (s, "anon-l3-out")); + } + + return (format (s, "unknown")); +} + +u8 * +format_gbp_subnet (u8 * s, va_list * args) +{ + index_t gsi = va_arg (*args, index_t); + gsb_subnet_show_flags_t flags = va_arg (*args, gsb_subnet_show_flags_t); + gbp_subnet_t *gs; + u32 table_id; + + gs = pool_elt_at_index (gbp_subnet_pool, gsi); + + table_id = fib_table_get_table_id (gs->gs_key->gsk_fib_index, + gs->gs_key->gsk_pfx.fp_proto); + + s = format (s, "[%d] tbl:%d %U %U", gsi, table_id, + format_fib_prefix, &gs->gs_key->gsk_pfx, + format_gbp_subnet_type, gs->gs_type); + + switch (gs->gs_type) + { + case GBP_SUBNET_STITCHED_INTERNAL: + case GBP_SUBNET_TRANSPORT: + break; + case GBP_SUBNET_STITCHED_EXTERNAL: + s = format (s, " {sclass:%d %U}", gs->gs_stitched_external.gs_sclass, + format_vnet_sw_if_index_name, + vnet_get_main (), gs->gs_stitched_external.gs_sw_if_index); + break; + case GBP_SUBNET_L3_OUT: + case GBP_SUBNET_ANON_L3_OUT: + s = format (s, " {sclass:%d}", gs->gs_l3_out.gs_sclass); + break; + } + + switch (flags) + { + case GBP_SUBNET_SHOW_DETAILS: + { + s = format (s, "\n %U", format_fib_entry, gs->gs_fei, + FIB_ENTRY_FORMAT_DETAIL); + } + case GBP_SUBNET_SHOW_BRIEF: + break; + } + return (s); +} + +static clib_error_t * +gbp_subnet_show (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + u32 gsi; + + gsi = INDEX_INVALID; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "%d", &gsi)) + ; + else + break; + } + + if (INDEX_INVALID != gsi) + { + vlib_cli_output (vm, "%U", format_gbp_subnet, gsi, + GBP_SUBNET_SHOW_DETAILS); + } + else + { + /* *INDENT-OFF* */ + pool_foreach_index(gsi, gbp_subnet_pool, + ({ + vlib_cli_output (vm, "%U", format_gbp_subnet, gsi, + GBP_SUBNET_SHOW_BRIEF); + })); + /* *INDENT-ON* */ + } + + return (NULL); +} + +/*? + * Show Group Based Policy Subnets + * + * @cliexpar + * @cliexstart{show gbp subnet} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_subnet_show_node, static) = { + .path = "show gbp subnet", + .short_help = "show gbp subnet\n", + .function = gbp_subnet_show, +}; +/* *INDENT-ON* */ + +static clib_error_t * +gbp_subnet_init (vlib_main_t * vm) +{ + gbp_subnet_db = hash_create_mem (0, + sizeof (gbp_subnet_key_t), sizeof (u32)); + gbp_fib_source = fib_source_allocate ("gbp-subnet", + FIB_SOURCE_PRIORITY_HI, + FIB_SOURCE_BH_SIMPLE); + + return (NULL); +} + +VLIB_INIT_FUNCTION (gbp_subnet_init); + /* * fd.io coding-style-patch-verification: ON *