diff options
author | Benoît Ganne <bganne@cisco.com> | 2019-06-26 13:36:51 +0200 |
---|---|---|
committer | Neale Ranns <nranns@cisco.com> | 2019-07-02 14:19:07 +0000 |
commit | cfc7a107e6cb8be6e7c53a08e23a146c431c8e90 (patch) | |
tree | 2179e92193ce7453b6eb67ba94a3c881f998f4fc | |
parent | 2ec825937b7ac856f67d086ce6814dd21c5e9bd7 (diff) |
gbp: add anonymous l3-out external interfaces
So far, GBP l3-out packets classification & policy relied on programmed
EP. All traffic to/from l3-out must go through a known EP.
This patch introduces a new feature where l3-out next-hops are only
known by their subnets (l3-out prefixes). As there are no longer known
EPs to program, an interface must be configured as external anonymous
l3-out. Packets classification & policy on this interface will rely on
the external subnets programmed in the BD VRF.
Note that contrary to all other interfaces in a GBP BD, external
anonymous l3-out interfaces have BD L2 learning turned on and rely on
ARP/ND.
Type: feature
Change-Id: Ieedb29dff4e967d08c4301e82d06bff450a63e5f
Signed-off-by: Benoît Ganne <bganne@cisco.com>
-rw-r--r-- | src/plugins/gbp/gbp.api | 8 | ||||
-rw-r--r-- | src/plugins/gbp/gbp.h | 11 | ||||
-rw-r--r-- | src/plugins/gbp/gbp_api.c | 31 | ||||
-rw-r--r-- | src/plugins/gbp/gbp_api_print.h | 23 | ||||
-rw-r--r-- | src/plugins/gbp/gbp_classify.c | 8 | ||||
-rw-r--r-- | src/plugins/gbp/gbp_classify.h | 45 | ||||
-rw-r--r-- | src/plugins/gbp/gbp_classify_node.c | 216 | ||||
-rw-r--r-- | src/plugins/gbp/gbp_ext_itf.c | 114 | ||||
-rw-r--r-- | src/plugins/gbp/gbp_ext_itf.h | 3 | ||||
-rw-r--r-- | src/plugins/gbp/gbp_policy.c | 14 | ||||
-rw-r--r-- | src/plugins/gbp/gbp_policy_dpo.h | 29 | ||||
-rw-r--r-- | src/plugins/gbp/gbp_policy_node.c | 135 | ||||
-rw-r--r-- | src/vnet/l2/l2_input.h | 1 | ||||
-rw-r--r-- | src/vnet/l2/l2_output.h | 1 |
14 files changed, 483 insertions, 156 deletions
diff --git a/src/plugins/gbp/gbp.api b/src/plugins/gbp/gbp.api index f6775e7dcd7..d1e483d5e5d 100644 --- a/src/plugins/gbp/gbp.api +++ b/src/plugins/gbp/gbp.api @@ -419,6 +419,14 @@ define gbp_ext_itf_details vl_api_gbp_ext_itf_t ext_itf; }; +manual_print autoreply define gbp_ext_itf_anon_add_del +{ + u32 client_index; + u32 context; + u8 is_add; + vl_api_gbp_ext_itf_t ext_itf; +}; + /* * Local Variables: * eval: (c-set-style "gnu") diff --git a/src/plugins/gbp/gbp.h b/src/plugins/gbp/gbp.h index 35e02d26ca7..e194a9df82c 100644 --- a/src/plugins/gbp/gbp.h +++ b/src/plugins/gbp/gbp.h @@ -48,6 +48,15 @@ typedef struct extern gbp_main_t gbp_main; +typedef enum gbp_policy_type_t_ +{ + GBP_POLICY_PORT, + GBP_POLICY_MAC, + GBP_POLICY_LPM, + GBP_N_POLICY +#define GBP_N_POLICY GBP_N_POLICY +} gbp_policy_type_t; + /** * Grouping of global data for the GBP source EPG classification feature */ @@ -56,7 +65,7 @@ typedef struct gbp_policy_main_t_ /** * Next nodes for L2 output features */ - u32 l2_output_feat_next[2][32]; + u32 l2_output_feat_next[GBP_N_POLICY][32]; } gbp_policy_main_t; extern gbp_policy_main_t gbp_policy_main; diff --git a/src/plugins/gbp/gbp_api.c b/src/plugins/gbp/gbp_api.c index 8155a8ff0f1..010e3087eb3 100644 --- a/src/plugins/gbp/gbp_api.c +++ b/src/plugins/gbp/gbp_api.c @@ -82,7 +82,8 @@ _(GBP_CONTRACT_DUMP, gbp_contract_dump) \ _(GBP_VXLAN_TUNNEL_ADD, gbp_vxlan_tunnel_add) \ _(GBP_VXLAN_TUNNEL_DEL, gbp_vxlan_tunnel_del) \ - _(GBP_VXLAN_TUNNEL_DUMP, gbp_vxlan_tunnel_dump) + _(GBP_VXLAN_TUNNEL_DUMP, gbp_vxlan_tunnel_dump) \ + _(GBP_EXT_ITF_ANON_ADD_DEL, gbp_ext_itf_anon_add_del) gbp_main_t gbp_main; @@ -1146,6 +1147,34 @@ vl_api_gbp_vxlan_tunnel_dump_t_handler (vl_api_gbp_vxlan_tunnel_dump_t * mp) gbp_vxlan_walk (gbp_vxlan_tunnel_send_details, &ctx); } +static void +vl_api_gbp_ext_itf_anon_add_del_t_handler (vl_api_gbp_ext_itf_anon_add_del_t * + mp) +{ + vl_api_gbp_ext_itf_anon_add_del_reply_t *rmp; + u32 sw_if_index = ~0; + vl_api_gbp_ext_itf_t *ext_itf; + int rv = 0; + + ext_itf = &mp->ext_itf; + if (ext_itf) + sw_if_index = ntohl (ext_itf->sw_if_index); + + if (!vnet_sw_if_index_is_api_valid (sw_if_index)) + goto bad_sw_if_index; + + if (mp->is_add) + rv = gbp_ext_itf_anon_add (sw_if_index, + ntohl (ext_itf->bd_id), + ntohl (ext_itf->rd_id)); + else + rv = gbp_ext_itf_anon_delete (sw_if_index); + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_GBP_EXT_ITF_ANON_ADD_DEL_REPLY + GBP_MSG_BASE); +} + /* * gbp_api_hookup * Add vpe's API message handlers to the table. diff --git a/src/plugins/gbp/gbp_api_print.h b/src/plugins/gbp/gbp_api_print.h index 67cd30c7da7..b3afc94dc4b 100644 --- a/src/plugins/gbp/gbp_api_print.h +++ b/src/plugins/gbp/gbp_api_print.h @@ -340,6 +340,29 @@ vl_api_gbp_ext_itf_add_del_t_print (vl_api_gbp_ext_itf_add_del_t * a, return handle; } +static inline void * +vl_api_gbp_ext_itf_anon_add_del_t_print (vl_api_gbp_ext_itf_anon_add_del_t * + a, void *handle) +{ + u8 *s = 0; + + s = format (s, "SCRIPT: gbp_ext_itf_anon_add_del "); + if (a->is_add) + s = format (s, "add "); + else + s = format (s, "del "); + + s = format (s, "sw_if_index %d ", ntohl (a->ext_itf.sw_if_index)); + s = format (s, "bd_id %d ", ntohl (a->ext_itf.bd_id)); + s = format (s, "rd_id %d ", ntohl (a->ext_itf.rd_id)); + + s = format (s, "\n"); + + PRINT_S; + + return handle; +} + /* * fd.io coding-style-patch-verification: ON * diff --git a/src/plugins/gbp/gbp_classify.c b/src/plugins/gbp/gbp_classify.c index 5735f359af0..255db252871 100644 --- a/src/plugins/gbp/gbp_classify.c +++ b/src/plugins/gbp/gbp_classify.c @@ -49,6 +49,14 @@ gbp_src_classify_init (vlib_main_t * vm) l2input_get_feat_names (), em->l2_input_feat_next[GBP_SRC_CLASSIFY_LPM]); + node = vlib_get_node_by_name (vm, (u8 *) "l2-gbp-lpm-anon-classify"); + feat_bitmap_init_next_nodes (vm, + node->index, + L2INPUT_N_FEAT, + l2input_get_feat_names (), + em->l2_input_feat_next + [GBP_SRC_CLASSIFY_LPM_ANON]); + return 0; } diff --git a/src/plugins/gbp/gbp_classify.h b/src/plugins/gbp/gbp_classify.h index c0c1fd53dc5..ca7db94a2c0 100644 --- a/src/plugins/gbp/gbp_classify.h +++ b/src/plugins/gbp/gbp_classify.h @@ -19,16 +19,18 @@ #define __GBP_CLASSIFY_H__ #include <plugins/gbp/gbp.h> +#include <vnet/ethernet/arp_packet.h> typedef enum gbp_src_classify_type_t_ { GBP_SRC_CLASSIFY_NULL, GBP_SRC_CLASSIFY_PORT, GBP_SRC_CLASSIFY_LPM, + GBP_SRC_CLASSIFY_LPM_ANON, + GBP_SRC_N_CLASSIFY +#define GBP_SRC_N_CLASSIFY GBP_SRC_N_CLASSIFY } gbp_src_classify_type_t; -#define GBP_SRC_N_CLASSIFY (GBP_SRC_CLASSIFY_LPM + 1) - /** * Grouping of global data for the GBP source EPG classification feature */ @@ -42,6 +44,45 @@ typedef struct gbp_src_classify_main_t_ extern gbp_src_classify_main_t gbp_src_classify_main; +enum gbp_classify_get_ip_way +{ + GBP_CLASSIFY_GET_IP_SRC = 0, + GBP_CLASSIFY_GET_IP_DST = 1 +}; + +static_always_inline dpo_proto_t +gbp_classify_get_ip_address (const ethernet_header_t * eh0, + const ip4_address_t ** ip4, + const ip6_address_t ** ip6, + const enum gbp_classify_get_ip_way way) +{ + u16 etype = clib_net_to_host_u16 (eh0->type); + const void *l3h0 = eh0 + 1; + + if (ETHERNET_TYPE_VLAN == etype) + { + const ethernet_vlan_header_t *vh0 = + (ethernet_vlan_header_t *) (eh0 + 1); + etype = clib_net_to_host_u16 (vh0->type); + l3h0 = vh0 + 1; + } + + switch (etype) + { + case ETHERNET_TYPE_IP4: + *ip4 = &(&((const ip4_header_t *) l3h0)->src_address)[way]; + return DPO_PROTO_IP4; + case ETHERNET_TYPE_IP6: + *ip6 = &(&((const ip6_header_t *) l3h0)->src_address)[way]; + return DPO_PROTO_IP6; + case ETHERNET_TYPE_ARP: + *ip4 = &((ethernet_arp_header_t *) l3h0)->ip4_over_ethernet[way].ip4; + return DPO_PROTO_IP4; + } + + return DPO_PROTO_NONE; +} + #endif /* diff --git a/src/plugins/gbp/gbp_classify_node.c b/src/plugins/gbp/gbp_classify_node.c index 9ad2b06148d..a2058a21284 100644 --- a/src/plugins/gbp/gbp_classify_node.c +++ b/src/plugins/gbp/gbp_classify_node.c @@ -279,36 +279,6 @@ typedef enum gbp_lpm_classify_next_t_ GPB_LPM_CLASSIFY_DROP, } gbp_lpm_classify_next_t; -always_inline void -gbp_classify_get_src_ip_address (const ethernet_header_t * eh0, - const ip4_address_t ** ip4, - const ip6_address_t ** ip6) -{ - u16 etype = clib_net_to_host_u16 (eh0->type); - const void *l3h0 = eh0 + 1; - - if (ETHERNET_TYPE_VLAN == etype) - { - const ethernet_vlan_header_t *vh0 = - (ethernet_vlan_header_t *) (eh0 + 1); - etype = clib_net_to_host_u16 (vh0->type); - l3h0 = vh0 + 1; - } - - switch (etype) - { - case ETHERNET_TYPE_IP4: - *ip4 = &((const ip4_header_t *) l3h0)->src_address; - break; - case ETHERNET_TYPE_IP6: - *ip6 = &((const ip6_header_t *) l3h0)->src_address; - break; - case ETHERNET_TYPE_ARP: - *ip4 = &((ethernet_arp_header_t *) l3h0)->ip4_over_ethernet[0].ip4; - break; - } -} - /** * per-packet trace data */ @@ -333,6 +303,13 @@ format_gbp_lpm_classify_trace (u8 * s, va_list * args) return s; } +enum gbp_lpm_type +{ + GBP_LPM_RECIRC, + GBP_LPM_EPG, + GBP_LPM_ANON +}; + /* * Determine the SRC EPG from a LPM */ @@ -340,7 +317,8 @@ always_inline uword gbp_lpm_classify_inline (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame, - dpo_proto_t dproto, u8 is_recirc) + const dpo_proto_t dproto, + const enum gbp_lpm_type type) { gbp_src_classify_main_t *gscm = &gbp_src_classify_main; u32 n_left_from, *from, *to_next; @@ -366,8 +344,6 @@ gbp_lpm_classify_inline (vlib_main_t * vm, const ip4_address_t *ip4_0; const ip6_address_t *ip6_0; const gbp_recirc_t *gr0; - const dpo_id_t *dpo0; - load_balance_t *lb0; vlib_buffer_t *b0; sclass_t sclass0; @@ -397,10 +373,11 @@ gbp_lpm_classify_inline (vlib_main_t * vm, else if (DPO_PROTO_ETHERNET == dproto) { eh0 = vlib_buffer_get_current (b0); - gbp_classify_get_src_ip_address (eh0, &ip4_0, &ip6_0); + gbp_classify_get_ip_address (eh0, &ip4_0, &ip6_0, + GBP_CLASSIFY_GET_IP_SRC); } - if (is_recirc) + if (GBP_LPM_RECIRC == type) { gr0 = gbp_recirc_get (sw_if_index0); fib_index0 = gr0->gr_fib_index[dproto]; @@ -417,96 +394,105 @@ gbp_lpm_classify_inline (vlib_main_t * vm, goto trace; } - ge0 = gbp_endpoint_find_mac (eh0->src_address, - vnet_buffer (b0)->l2.bd_index); - - if (NULL == ge0) + if (GBP_LPM_ANON == type) { - /* packet must have come from an EP's mac */ - sclass0 = SCLASS_INVALID; - goto trace; - } - - fib_index0 = ge0->ge_fwd.gef_fib_index; - - if (~0 == fib_index0) - { - sclass0 = SCLASS_INVALID; - goto trace; - } - - if (ip4_0) - { - ge_lpm0 = gbp_endpoint_find_ip4 (ip4_0, fib_index0); - } - else if (ip6_0) - { - ge_lpm0 = gbp_endpoint_find_ip6 (ip6_0, fib_index0); + /* + * anonymous LPM classification: only honour LPM as no EP + * were programmed + */ + gbp_ext_itf_t *gei = gbp_ext_itf_get (sw_if_index0); + if (ip4_0) + fib_index0 = gei->gx_fib_index[DPO_PROTO_IP4]; + else if (ip6_0) + fib_index0 = gei->gx_fib_index[DPO_PROTO_IP6]; + else + { + /* not IP so no LPM classify possible */ + sclass0 = SCLASS_INVALID; + next0 = GPB_LPM_CLASSIFY_DROP; + goto trace; + } + next0 = vnet_l2_feature_next + (b0, gscm->l2_input_feat_next[GBP_SRC_CLASSIFY_LPM_ANON], + L2INPUT_FEAT_GBP_LPM_ANON_CLASSIFY); } else { - ge_lpm0 = NULL; - } + /* + * not an anonymous LPM classification: check it comes from + * an EP, and use EP RD info + */ + ge0 = gbp_endpoint_find_mac (eh0->src_address, + vnet_buffer (b0)->l2.bd_index); - next0 = vnet_l2_feature_next - (b0, gscm->l2_input_feat_next[GBP_SRC_CLASSIFY_LPM], - L2INPUT_FEAT_GBP_LPM_CLASSIFY); + if (NULL == ge0) + { + /* packet must have come from an EP's mac */ + sclass0 = SCLASS_INVALID; + goto trace; + } - /* - * if we found the EP by IP lookup, it must be from the EP - * not a network behind it - */ - if (NULL != ge_lpm0) - { - if (PREDICT_FALSE (ge0 != ge_lpm0)) + fib_index0 = ge0->ge_fwd.gef_fib_index; + + if (~0 == fib_index0) { - /* an EP spoofing another EP */ sclass0 = SCLASS_INVALID; - next0 = GPB_LPM_CLASSIFY_DROP; + goto trace; + } + + if (ip4_0) + { + ge_lpm0 = gbp_endpoint_find_ip4 (ip4_0, fib_index0); + } + else if (ip6_0) + { + ge_lpm0 = gbp_endpoint_find_ip6 (ip6_0, fib_index0); } else { - sclass0 = ge0->ge_fwd.gef_sclass; + ge_lpm0 = NULL; + } + + next0 = vnet_l2_feature_next + (b0, gscm->l2_input_feat_next[GBP_SRC_CLASSIFY_LPM], + L2INPUT_FEAT_GBP_LPM_CLASSIFY); + + /* + * if we found the EP by IP lookup, it must be from the EP + * not a network behind it + */ + if (NULL != ge_lpm0) + { + if (PREDICT_FALSE (ge0 != ge_lpm0)) + { + /* an EP spoofing another EP */ + sclass0 = SCLASS_INVALID; + next0 = GPB_LPM_CLASSIFY_DROP; + } + else + { + sclass0 = ge0->ge_fwd.gef_sclass; + } + goto trace; } - goto trace; } } - if (ip4_0) - { - lbi0 = ip4_fib_forwarding_lookup (fib_index0, ip4_0); - } - else if (ip6_0) + gpd0 = gbp_classify_get_gpd (ip4_0, ip6_0, fib_index0); + if (0 == gpd0) { - lbi0 = - ip6_fib_table_fwding_lookup (&ip6_main, fib_index0, ip6_0); - } - else - { - /* not IP so no LPM classify possible */ + /* could not classify => drop */ sclass0 = SCLASS_INVALID; next0 = GPB_LPM_CLASSIFY_DROP; goto trace; } - lb0 = load_balance_get (lbi0); - dpo0 = load_balance_get_bucket_i (lb0, 0); + + sclass0 = gpd0->gpd_sclass; /* all packets from an external network should not be learned by the * reciever. so set the Do-not-learn bit here */ vnet_buffer2 (b0)->gbp.flags = VXLAN_GBP_GPFLAGS_D; - if (gbp_policy_dpo_type == dpo0->dpoi_type) - { - gpd0 = gbp_policy_dpo_get (dpo0->dpoi_index); - sclass0 = gpd0->gpd_sclass; - } - else - { - /* could not classify => drop */ - sclass0 = SCLASS_INVALID; - goto trace; - } - trace: vnet_buffer2 (b0)->gbp.sclass = sclass0; @@ -537,21 +523,32 @@ VLIB_NODE_FN (gbp_ip4_lpm_classify_node) (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { - return (gbp_lpm_classify_inline (vm, node, frame, DPO_PROTO_IP4, 1)); + return (gbp_lpm_classify_inline + (vm, node, frame, DPO_PROTO_IP4, GBP_LPM_RECIRC)); } VLIB_NODE_FN (gbp_ip6_lpm_classify_node) (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { - return (gbp_lpm_classify_inline (vm, node, frame, DPO_PROTO_IP6, 1)); + return (gbp_lpm_classify_inline + (vm, node, frame, DPO_PROTO_IP6, GBP_LPM_RECIRC)); } VLIB_NODE_FN (gbp_l2_lpm_classify_node) (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { - return (gbp_lpm_classify_inline (vm, node, frame, DPO_PROTO_ETHERNET, 0)); + return (gbp_lpm_classify_inline + (vm, node, frame, DPO_PROTO_ETHERNET, GBP_LPM_EPG)); +} + +VLIB_NODE_FN (gbp_l2_lpm_anon_classify_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return (gbp_lpm_classify_inline + (vm, node, frame, DPO_PROTO_ETHERNET, GBP_LPM_ANON)); } /* *INDENT-OFF* */ @@ -594,6 +591,19 @@ VLIB_REGISTER_NODE (gbp_l2_lpm_classify_node) = { }, }; +VLIB_REGISTER_NODE (gbp_l2_lpm_anon_classify_node) = { + .name = "l2-gbp-lpm-anon-classify", + .vector_size = sizeof (u32), + .format_trace = format_gbp_lpm_classify_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = 0, + .n_next_nodes = 1, + .next_nodes = { + [GPB_LPM_CLASSIFY_DROP] = "error-drop" + }, +}; + VNET_FEATURE_INIT (gbp_ip4_lpm_classify_feat_node, static) = { .arc_name = "ip4-unicast", diff --git a/src/plugins/gbp/gbp_ext_itf.c b/src/plugins/gbp/gbp_ext_itf.c index be2d614e1fa..89bcb3da49e 100644 --- a/src/plugins/gbp/gbp_ext_itf.c +++ b/src/plugins/gbp/gbp_ext_itf.c @@ -130,6 +130,120 @@ gbp_ext_itf_delete (u32 sw_if_index) return (VNET_API_ERROR_NO_SUCH_ENTRY); } +int +gbp_ext_itf_anon_add (u32 sw_if_index, u32 bd_id, u32 rd_id) +{ + int rv = gbp_ext_itf_add (sw_if_index, bd_id, rd_id); + if (rv) + return rv; + /* add interface to the BD */ + index_t itf = gbp_itf_add_and_lock (sw_if_index, bd_id); + /* setup GBP L2 features on this interface */ + gbp_itf_set_l2_input_feature (itf, 0, + L2INPUT_FEAT_GBP_LPM_ANON_CLASSIFY | + L2INPUT_FEAT_LEARN); + gbp_itf_set_l2_output_feature (itf, 0, L2OUTPUT_FEAT_GBP_POLICY_LPM); + return 0; +} + +int +gbp_ext_itf_anon_delete (u32 sw_if_index) +{ + int rv = gbp_ext_itf_delete (sw_if_index); + if (rv) + return rv; + gbp_itf_unlock (sw_if_index); + return 0; +} + +static clib_error_t * +gbp_ext_itf_add_del_cli (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + u32 sw_if_index = ~0, bd_id = ~0, rd_id = ~0; + int add = 1, anon = 0; + 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")) + add = 0; + else + if (unformat + (line_input, "%U", unformat_vnet_sw_interface, vnet_get_main (), + &sw_if_index)) + ; + else if (unformat (line_input, "bd %d", &bd_id)) + ; + else if (unformat (line_input, "rd %d", &rd_id)) + ; + else if (unformat (line_input, "anon-l3-out")) + anon = 1; + else + return clib_error_return (0, "unknown input `%U'", + format_unformat_error, line_input); + } + unformat_free (line_input); + + if (~0 == sw_if_index) + return clib_error_return (0, "interface must be specified"); + + if (add) + { + if (~0 == bd_id) + return clib_error_return (0, "BD-ID must be specified"); + if (~0 == rd_id) + return clib_error_return (0, "RD-ID must be specified"); + if (anon) + rv = gbp_ext_itf_anon_add (sw_if_index, bd_id, rd_id); + else + rv = gbp_ext_itf_add (sw_if_index, bd_id, rd_id); + } + else + { + if (anon) + rv = gbp_ext_itf_anon_delete (sw_if_index); + else + rv = gbp_ext_itf_delete (sw_if_index); + } + + switch (rv) + { + case 0: + return 0; + case VNET_API_ERROR_ENTRY_ALREADY_EXISTS: + return clib_error_return (0, "interface already exists"); + case VNET_API_ERROR_NO_SUCH_ENTRY: /* fallthrough */ + case VNET_API_ERROR_INVALID_SW_IF_INDEX: + return clib_error_return (0, "unknown interface"); + default: + return clib_error_return (0, "error %d", rv); + } + + /* never reached */ + return 0; +} + +/*? + * Add Group Based Interface as anonymous L3out interface + * + * @cliexpar + * @cliexstart{gbp interface [del] anon-l3out <interface> bd <ID>} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_itf_anon_l3out_add_del_node, static) = { + .path = "gbp ext-itf", + .short_help = "gbp ext-itf [del] <interface> bd <ID> rd <ID> [anon-l3-out]\n", + .function = gbp_ext_itf_add_del_cli, +}; +/* *INDENT-ON* */ + void gbp_ext_itf_walk (gbp_ext_itf_cb_t cb, void *ctx) { diff --git a/src/plugins/gbp/gbp_ext_itf.h b/src/plugins/gbp/gbp_ext_itf.h index dafcd08a2e4..d1b0f983447 100644 --- a/src/plugins/gbp/gbp_ext_itf.h +++ b/src/plugins/gbp/gbp_ext_itf.h @@ -52,6 +52,9 @@ typedef struct gpb_ext_itf_t_ extern int gbp_ext_itf_add (u32 sw_if_index, u32 bd_id, u32 rd_id); extern int gbp_ext_itf_delete (u32 sw_if_index); +extern int gbp_ext_itf_anon_add (u32 sw_if_index, u32 bd_id, u32 rd_id); +extern int gbp_ext_itf_anon_delete (u32 sw_if_index); + extern u8 *format_gbp_ext_itf (u8 * s, va_list * args); typedef walk_rc_t (*gbp_ext_itf_cb_t) (gbp_ext_itf_t * gbpe, void *ctx); diff --git a/src/plugins/gbp/gbp_policy.c b/src/plugins/gbp/gbp_policy.c index fbdf3946d1d..0f26701bd19 100644 --- a/src/plugins/gbp/gbp_policy.c +++ b/src/plugins/gbp/gbp_policy.c @@ -24,21 +24,27 @@ gbp_policy_init (vlib_main_t * vm) gbp_policy_main_t *gpm = &gbp_policy_main; clib_error_t *error = 0; - vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "gbp-policy-port"); - /* Initialize the feature next-node indexes */ + vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "gbp-policy-port"); feat_bitmap_init_next_nodes (vm, node->index, L2OUTPUT_N_FEAT, l2output_get_feat_names (), - gpm->l2_output_feat_next[1]); + gpm->l2_output_feat_next[GBP_POLICY_PORT]); node = vlib_get_node_by_name (vm, (u8 *) "gbp-policy-mac"); feat_bitmap_init_next_nodes (vm, node->index, L2OUTPUT_N_FEAT, l2output_get_feat_names (), - gpm->l2_output_feat_next[0]); + gpm->l2_output_feat_next[GBP_POLICY_MAC]); + + node = vlib_get_node_by_name (vm, (u8 *) "gbp-policy-lpm"); + feat_bitmap_init_next_nodes (vm, + node->index, + L2OUTPUT_N_FEAT, + l2output_get_feat_names (), + gpm->l2_output_feat_next[GBP_POLICY_LPM]); return error; } diff --git a/src/plugins/gbp/gbp_policy_dpo.h b/src/plugins/gbp/gbp_policy_dpo.h index 6b4f8c57fd0..1dc11ab2e32 100644 --- a/src/plugins/gbp/gbp_policy_dpo.h +++ b/src/plugins/gbp/gbp_policy_dpo.h @@ -17,6 +17,9 @@ #define __GBP_POLICY_DPO_H__ #include <vnet/dpo/dpo.h> +#include <vnet/dpo/load_balance.h> +#include <vnet/fib/ip4_fib.h> +#include <vnet/fib/ip6_fib.h> /** * @brief @@ -81,6 +84,32 @@ gbp_policy_dpo_get (index_t index) return (pool_elt_at_index (gbp_policy_dpo_pool, index)); } +static_always_inline const gbp_policy_dpo_t * +gbp_classify_get_gpd (const ip4_address_t * ip4, const ip6_address_t * ip6, + const u32 fib_index) +{ + const gbp_policy_dpo_t *gpd; + const dpo_id_t *dpo; + const load_balance_t *lb; + u32 lbi; + + if (ip4) + lbi = ip4_fib_forwarding_lookup (fib_index, ip4); + else if (ip6) + lbi = ip6_fib_table_fwding_lookup (&ip6_main, fib_index, ip6); + else + return 0; + + lb = load_balance_get (lbi); + dpo = load_balance_get_bucket_i (lb, 0); + + if (dpo->dpoi_type != gbp_policy_dpo_type) + return 0; + + gpd = gbp_policy_dpo_get (dpo->dpoi_index); + return gpd; +} + /* * fd.io coding-style-patch-verification: ON * diff --git a/src/plugins/gbp/gbp_policy_node.c b/src/plugins/gbp/gbp_policy_node.c index 26f7e9b8c59..10f5956033e 100644 --- a/src/plugins/gbp/gbp_policy_node.c +++ b/src/plugins/gbp/gbp_policy_node.c @@ -14,8 +14,10 @@ */ #include <plugins/gbp/gbp.h> +#include <plugins/gbp/gbp_classify.h> #include <plugins/gbp/gbp_policy_dpo.h> #include <plugins/gbp/gbp_bridge_domain.h> +#include <plugins/gbp/gbp_ext_itf.h> #include <vnet/vxlan-gbp/vxlan_gbp_packet.h> #include <vnet/vxlan-gbp/vxlan_gbp.h> @@ -109,10 +111,34 @@ gbp_policy_is_ethertype_allowed (const gbp_contract_t * gc0, u16 ethertype) return (0); } +static_always_inline gbp_policy_next_t +gbp_policy_l2_feature_next (gbp_policy_main_t * gpm, vlib_buffer_t * b, + const gbp_policy_type_t type) +{ + u32 feat_bit; + + switch (type) + { + case GBP_POLICY_PORT: + feat_bit = L2OUTPUT_FEAT_GBP_POLICY_PORT; + break; + case GBP_POLICY_MAC: + feat_bit = L2OUTPUT_FEAT_GBP_POLICY_MAC; + break; + case GBP_POLICY_LPM: + feat_bit = L2OUTPUT_FEAT_GBP_POLICY_LPM; + break; + default: + return GBP_POLICY_NEXT_DROP; + } + + return vnet_l2_feature_next (b, gpm->l2_output_feat_next[type], feat_bit); +} + static uword gbp_policy_inline (vlib_main_t * vm, vlib_node_runtime_t * node, - vlib_frame_t * frame, u8 is_port_based) + vlib_frame_t * frame, const gbp_policy_type_t type) { gbp_main_t *gm = &gbp_main; gbp_policy_main_t *gpm = &gbp_policy_main; @@ -174,12 +200,7 @@ gbp_policy_inline (vlib_main_t * vm, */ if (vnet_buffer2 (b0)->gbp.flags & VXLAN_GBP_GPFLAGS_A) { - next0 = vnet_l2_feature_next (b0, - gpm->l2_output_feat_next - [is_port_based], - (is_port_based ? - L2OUTPUT_FEAT_GBP_POLICY_PORT : - L2OUTPUT_FEAT_GBP_POLICY_MAC)); + next0 = gbp_policy_l2_feature_next (gpm, b0, type); n_allow_a_bit++; key0.as_u64 = ~0; goto trace; @@ -188,20 +209,40 @@ gbp_policy_inline (vlib_main_t * vm, /* * determine the src and dst EPG */ - if (is_port_based) - ge0 = gbp_endpoint_find_itf (sw_if_index0); - else - ge0 = gbp_endpoint_find_mac (h0->dst_address, - vnet_buffer (b0)->l2.bd_index); - if (NULL != ge0) + key0.gck_dst = SCLASS_INVALID; + + if (GBP_POLICY_LPM == type) { - key0.gck_dst = ge0->ge_fwd.gef_sclass; - key0.gck_scope = - gbp_bridge_domain_get_scope (vnet_buffer (b0)->l2.bd_index); + const ip4_address_t *ip4 = 0; + const ip6_address_t *ip6 = 0; + const dpo_proto_t proto = + gbp_classify_get_ip_address (h0, &ip4, &ip6, + GBP_CLASSIFY_GET_IP_DST); + if (PREDICT_TRUE (DPO_PROTO_NONE != proto)) + { + const gbp_ext_itf_t *ext_itf = + gbp_ext_itf_get (sw_if_index0); + const gbp_policy_dpo_t *gpd = + gbp_classify_get_gpd (ip4, ip6, + ext_itf->gx_fib_index[proto]); + if (gpd) + key0.gck_dst = gpd->gpd_sclass; + } } else { + if (GBP_POLICY_PORT == type) + ge0 = gbp_endpoint_find_itf (sw_if_index0); + else + ge0 = gbp_endpoint_find_mac (h0->dst_address, + vnet_buffer (b0)->l2.bd_index); + if (NULL != ge0) + key0.gck_dst = ge0->ge_fwd.gef_sclass; + } + + if (SCLASS_INVALID == key0.gck_dst) + { /* If you cannot determine the destination EP then drop */ b0->error = node->errors[GBP_POLICY_ERROR_DROP_NO_DCLASS]; goto trace; @@ -215,13 +256,7 @@ gbp_policy_inline (vlib_main_t * vm, /* * intra-epg allowed */ - next0 = - vnet_l2_feature_next (b0, - gpm->l2_output_feat_next - [is_port_based], - (is_port_based ? - L2OUTPUT_FEAT_GBP_POLICY_PORT : - L2OUTPUT_FEAT_GBP_POLICY_MAC)); + next0 = gbp_policy_l2_feature_next (gpm, b0, type); vnet_buffer2 (b0)->gbp.flags |= VXLAN_GBP_GPFLAGS_A; n_allow_intra++; } @@ -230,18 +265,15 @@ gbp_policy_inline (vlib_main_t * vm, /* * sclass or dclass 1 allowed */ - next0 = - vnet_l2_feature_next (b0, - gpm->l2_output_feat_next - [is_port_based], - (is_port_based ? - L2OUTPUT_FEAT_GBP_POLICY_PORT : - L2OUTPUT_FEAT_GBP_POLICY_MAC)); + next0 = gbp_policy_l2_feature_next (gpm, b0, type); vnet_buffer2 (b0)->gbp.flags |= VXLAN_GBP_GPFLAGS_A; n_allow_sclass_1++; } else { + key0.gck_scope = + gbp_bridge_domain_get_scope (vnet_buffer (b0)-> + l2.bd_index); gci0 = gbp_contract_find (&key0); if (INDEX_INVALID != gci0) @@ -321,13 +353,9 @@ gbp_policy_inline (vlib_main_t * vm, switch (gu->gu_action) { case GBP_RULE_PERMIT: - next0 = vnet_l2_feature_next - (b0, - gpm->l2_output_feat_next - [is_port_based], - (is_port_based ? - L2OUTPUT_FEAT_GBP_POLICY_PORT : - L2OUTPUT_FEAT_GBP_POLICY_MAC)); + next0 = + gbp_policy_l2_feature_next (gpm, b0, + type); break; case GBP_RULE_DENY: next0 = GBP_POLICY_NEXT_DROP; @@ -368,12 +396,7 @@ gbp_policy_inline (vlib_main_t * vm, * 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[is_port_based], - (is_port_based ? - L2OUTPUT_FEAT_GBP_POLICY_PORT : - L2OUTPUT_FEAT_GBP_POLICY_MAC)); + next0 = gbp_policy_l2_feature_next (gpm, b0, type); } trace: @@ -413,14 +436,21 @@ VLIB_NODE_FN (gbp_policy_port_node) (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { - return (gbp_policy_inline (vm, node, frame, 1)); + return (gbp_policy_inline (vm, node, frame, GBP_POLICY_PORT)); } VLIB_NODE_FN (gbp_policy_mac_node) (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { - return (gbp_policy_inline (vm, node, frame, 0)); + return (gbp_policy_inline (vm, node, frame, GBP_POLICY_MAC)); +} + +VLIB_NODE_FN (gbp_policy_lpm_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return (gbp_policy_inline (vm, node, frame, GBP_POLICY_LPM)); } /* packet trace format function */ @@ -470,6 +500,21 @@ VLIB_REGISTER_NODE (gbp_policy_mac_node) = { }, }; +VLIB_REGISTER_NODE (gbp_policy_lpm_node) = { + .name = "gbp-policy-lpm", + .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_DROP] = "error-drop", + }, +}; + /* *INDENT-ON* */ /* diff --git a/src/vnet/l2/l2_input.h b/src/vnet/l2/l2_input.h index ce9a7d5f0cd..677186bbdf3 100644 --- a/src/vnet/l2/l2_input.h +++ b/src/vnet/l2/l2_input.h @@ -112,6 +112,7 @@ l2input_bd_config (u32 bd_index) _(LEARN, "l2-learn") \ _(L2_EMULATION, "l2-emulation") \ _(GBP_LEARN, "gbp-learn-l2") \ + _(GBP_LPM_ANON_CLASSIFY, "l2-gbp-lpm-anon-classify") \ _(GBP_NULL_CLASSIFY, "gbp-null-classify") \ _(GBP_SRC_CLASSIFY, "gbp-src-classify") \ _(GBP_LPM_CLASSIFY, "l2-gbp-lpm-classify") \ diff --git a/src/vnet/l2/l2_output.h b/src/vnet/l2/l2_output.h index 74d2829839f..1cc1e738841 100644 --- a/src/vnet/l2/l2_output.h +++ b/src/vnet/l2/l2_output.h @@ -81,6 +81,7 @@ extern vlib_node_registration_t l2output_node; #define foreach_l2output_feat \ _(OUTPUT, "interface-output") \ _(SPAN, "span-l2-output") \ + _(GBP_POLICY_LPM, "gbp-policy-lpm") \ _(GBP_POLICY_PORT, "gbp-policy-port") \ _(GBP_POLICY_MAC, "gbp-policy-mac") \ _(CFM, "feature-bitmap-drop") \ |