diff options
Diffstat (limited to 'extras')
62 files changed, 23921 insertions, 0 deletions
diff --git a/extras/deprecated/plugins/gbp/CMakeLists.txt b/extras/deprecated/plugins/gbp/CMakeLists.txt new file mode 100644 index 00000000000..95f664ff08e --- /dev/null +++ b/extras/deprecated/plugins/gbp/CMakeLists.txt @@ -0,0 +1,54 @@ +# 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. + +add_vpp_plugin(gbp + SOURCES + gbp_api.c + gbp_bridge_domain.c + gbp_classify.c + gbp_classify_node.c + gbp_contract.c + gbp_endpoint.c + gbp_endpoint_group.c + gbp_ext_itf.c + gbp_fwd.c + gbp_fwd_dpo.c + gbp_fwd_node.c + gbp_itf.c + gbp_learn.c + gbp_learn_node.c + gbp_policy.c + gbp_policy_dpo.c + gbp_policy_node.c + gbp_recirc.c + gbp_route_domain.c + gbp_scanner.c + gbp_subnet.c + gbp_vxlan.c + gbp_vxlan_node.c + + MULTIARCH_SOURCES + gbp_classify_node.c + gbp_fwd_dpo.c + gbp_fwd_node.c + gbp_learn_node.c + gbp_policy_dpo.c + gbp_policy_node.c + gbp_vxlan_node.c + + API_FILES + gbp.api + + INSTALL_HEADERS + gbp.h +) diff --git a/extras/deprecated/plugins/gbp/gbp.api b/extras/deprecated/plugins/gbp/gbp.api new file mode 100644 index 00000000000..525e70536bd --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp.api @@ -0,0 +1,470 @@ +/* Hey Emacs use -*- mode: C -*- */ +/* + * 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. + */ + +option version = "2.0.0"; + +import "vnet/ip/ip_types.api"; +import "vnet/ethernet/ethernet_types.api"; +import "vnet/interface_types.api"; + +enum gbp_bridge_domain_flags +{ + GBP_BD_API_FLAG_NONE = 0, + GBP_BD_API_FLAG_DO_NOT_LEARN = 1, + GBP_BD_API_FLAG_UU_FWD_DROP = 2, + GBP_BD_API_FLAG_MCAST_DROP = 4, + GBP_BD_API_FLAG_UCAST_ARP = 8, +}; + +typedef gbp_bridge_domain +{ + u32 bd_id; + u32 rd_id; + vl_api_gbp_bridge_domain_flags_t flags; + vl_api_interface_index_t bvi_sw_if_index; + vl_api_interface_index_t uu_fwd_sw_if_index; + vl_api_interface_index_t bm_flood_sw_if_index; +}; + + autoreply define gbp_bridge_domain_add +{ + option status="in_progress"; + u32 client_index; + u32 context; + vl_api_gbp_bridge_domain_t bd; +}; + autoreply define gbp_bridge_domain_del +{ + option status="in_progress"; + u32 client_index; + u32 context; + u32 bd_id; +}; +autoreply define gbp_bridge_domain_dump +{ + option status="in_progress"; + u32 client_index; + u32 context; +}; +define gbp_bridge_domain_details +{ + option status="in_progress"; + u32 context; + vl_api_gbp_bridge_domain_t bd; +}; + +typedef u16 gbp_scope; + +typedef gbp_route_domain +{ + u32 rd_id; + u32 ip4_table_id; + u32 ip6_table_id; + vl_api_interface_index_t ip4_uu_sw_if_index; + vl_api_interface_index_t ip6_uu_sw_if_index; + vl_api_gbp_scope_t scope; +}; + + autoreply define gbp_route_domain_add +{ + option status="in_progress"; + u32 client_index; + u32 context; + vl_api_gbp_route_domain_t rd; +}; + autoreply define gbp_route_domain_del +{ + option status="in_progress"; + u32 client_index; + u32 context; + u32 rd_id; +}; +autoreply define gbp_route_domain_dump +{ + option status="in_progress"; + u32 client_index; + u32 context; +}; +define gbp_route_domain_details +{ + option status="in_progress"; + u32 context; + vl_api_gbp_route_domain_t rd; +}; + +/** \brief Endpoint + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ + +enum gbp_endpoint_flags +{ + GBP_API_ENDPOINT_FLAG_NONE = 0, + GBP_API_ENDPOINT_FLAG_BOUNCE = 0x1, + GBP_API_ENDPOINT_FLAG_REMOTE = 0x2, + GBP_API_ENDPOINT_FLAG_LEARNT = 0x4, + GBP_API_ENDPOINT_FLAG_EXTERNAL = 0x8, +}; + +typedef gbp_endpoint_tun +{ + vl_api_address_t src; + vl_api_address_t dst; +}; + +typedef gbp_endpoint +{ + vl_api_interface_index_t sw_if_index; + u16 sclass; + vl_api_gbp_endpoint_flags_t flags; + vl_api_mac_address_t mac; + vl_api_gbp_endpoint_tun_t tun; + u8 n_ips; + vl_api_address_t ips[n_ips]; +}; + + define gbp_endpoint_add +{ + option status="in_progress"; + u32 client_index; + u32 context; + vl_api_gbp_endpoint_t endpoint; +}; + +define gbp_endpoint_add_reply +{ + option status="in_progress"; + u32 context; + i32 retval; + u32 handle; +}; + + autoreply define gbp_endpoint_del +{ + option status="in_progress"; + u32 client_index; + u32 context; + u32 handle; +}; + +define gbp_endpoint_dump +{ + option status="in_progress"; + u32 client_index; + u32 context; +}; + +define gbp_endpoint_details +{ + option status="in_progress"; + u32 context; + f64 age; + u32 handle; + vl_api_gbp_endpoint_t endpoint; +}; + +typedef gbp_endpoint_retention +{ + u32 remote_ep_timeout; +}; + +typedef gbp_endpoint_group +{ + u32 vnid; + u16 sclass; + u32 bd_id; + u32 rd_id; + vl_api_interface_index_t uplink_sw_if_index; + vl_api_gbp_endpoint_retention_t retention; +}; + + autoreply define gbp_endpoint_group_add +{ + option status="in_progress"; + u32 client_index; + u32 context; + vl_api_gbp_endpoint_group_t epg; +}; + autoreply define gbp_endpoint_group_del +{ + option status="in_progress"; + u32 client_index; + u32 context; + u16 sclass; +}; + +define gbp_endpoint_group_dump +{ + option status="in_progress"; + u32 client_index; + u32 context; +}; + +define gbp_endpoint_group_details +{ + option status="in_progress"; + u32 context; + vl_api_gbp_endpoint_group_t epg; +}; + +typedef gbp_recirc +{ + vl_api_interface_index_t sw_if_index; + u16 sclass; + bool is_ext; +}; + + autoreply define gbp_recirc_add_del +{ + option status="in_progress"; + u32 client_index; + u32 context; + bool is_add; + vl_api_gbp_recirc_t recirc; +}; + +define gbp_recirc_dump +{ + option status="in_progress"; + u32 client_index; + u32 context; +}; + +define gbp_recirc_details +{ + option status="in_progress"; + u32 context; + vl_api_gbp_recirc_t recirc; +}; + +enum gbp_subnet_type +{ + GBP_API_SUBNET_TRANSPORT, + GBP_API_SUBNET_STITCHED_INTERNAL, + GBP_API_SUBNET_STITCHED_EXTERNAL, + GBP_API_SUBNET_L3_OUT, + GBP_API_SUBNET_ANON_L3_OUT, +}; + +typedef gbp_subnet +{ + u32 rd_id; + vl_api_interface_index_t sw_if_index [default= 0xffffffff]; + u16 sclass [default=0xffffffff]; + vl_api_gbp_subnet_type_t type; + vl_api_prefix_t prefix; +}; + + autoreply define gbp_subnet_add_del +{ + option status="in_progress"; + u32 client_index; + u32 context; + bool is_add; + vl_api_gbp_subnet_t subnet; +}; + +define gbp_subnet_dump +{ + option status="in_progress"; + u32 client_index; + u32 context; +}; + +define gbp_subnet_details +{ + option status="in_progress"; + u32 context; + vl_api_gbp_subnet_t subnet; +}; + +typedef gbp_next_hop +{ + vl_api_address_t ip; + vl_api_mac_address_t mac; + u32 bd_id; + u32 rd_id; +}; + +enum gbp_hash_mode +{ + GBP_API_HASH_MODE_SRC_IP, + GBP_API_HASH_MODE_DST_IP, + GBP_API_HASH_MODE_SYMMETRIC, +}; + +typedef gbp_next_hop_set +{ + vl_api_gbp_hash_mode_t hash_mode; + u8 n_nhs; + vl_api_gbp_next_hop_t nhs[8]; +}; + +enum gbp_rule_action +{ + GBP_API_RULE_PERMIT, + GBP_API_RULE_DENY, + GBP_API_RULE_REDIRECT, +}; + +typedef gbp_rule +{ + vl_api_gbp_rule_action_t action; + vl_api_gbp_next_hop_set_t nh_set; +}; + +typedef gbp_contract +{ + vl_api_gbp_scope_t scope; + u16 sclass; + u16 dclass; + u32 acl_index; + u8 n_ether_types; + u16 allowed_ethertypes[16]; + u8 n_rules; + vl_api_gbp_rule_t rules[n_rules]; +}; + + define gbp_contract_add_del +{ + option status="in_progress"; + u32 client_index; + u32 context; + bool is_add; + vl_api_gbp_contract_t contract; +}; +define gbp_contract_add_del_reply +{ + option status="in_progress"; + u32 context; + i32 retval; + u32 stats_index; +}; + +define gbp_contract_dump +{ + option status="in_progress"; + u32 client_index; + u32 context; +}; + +define gbp_contract_details +{ + option status="in_progress"; + u32 context; + vl_api_gbp_contract_t contract; +}; + +/** + * @brief Configure a 'base' tunnel from which learned tunnels + * are permitted to derive + * A base tunnel consists only of the VNI, any src,dst IP + * pair is thus allowed. + */ +enum gbp_vxlan_tunnel_mode +{ + GBP_VXLAN_TUNNEL_MODE_L2, + GBP_VXLAN_TUNNEL_MODE_L3, +}; + +typedef gbp_vxlan_tunnel +{ + u32 vni; + vl_api_gbp_vxlan_tunnel_mode_t mode; + u32 bd_rd_id; + vl_api_ip4_address_t src; +}; + + define gbp_vxlan_tunnel_add +{ + option status="in_progress"; + u32 client_index; + u32 context; + vl_api_gbp_vxlan_tunnel_t tunnel; +}; + +define gbp_vxlan_tunnel_add_reply +{ + option status="in_progress"; + u32 context; + i32 retval; + vl_api_interface_index_t sw_if_index; +}; + + autoreply define gbp_vxlan_tunnel_del +{ + option status="in_progress"; + u32 client_index; + u32 context; + u32 vni; +}; + +define gbp_vxlan_tunnel_dump +{ + option status="in_progress"; + u32 client_index; + u32 context; +}; + +define gbp_vxlan_tunnel_details +{ + option status="in_progress"; + u32 context; + vl_api_gbp_vxlan_tunnel_t tunnel; +}; + +enum gbp_ext_itf_flags +{ + GBP_API_EXT_ITF_F_NONE = 0, + GBP_API_EXT_ITF_F_ANON = 1, +}; + +typedef gbp_ext_itf +{ + vl_api_interface_index_t sw_if_index; + u32 bd_id; + u32 rd_id; + vl_api_gbp_ext_itf_flags_t flags; +}; + + autoreply define gbp_ext_itf_add_del +{ + option status="in_progress"; + u32 client_index; + u32 context; + bool is_add; + vl_api_gbp_ext_itf_t ext_itf; +}; + +define gbp_ext_itf_dump +{ + option status="in_progress"; + u32 client_index; + u32 context; +}; + +define gbp_ext_itf_details +{ + option status="in_progress"; + u32 context; + vl_api_gbp_ext_itf_t ext_itf; +}; + +/* + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp.h b/extras/deprecated/plugins/gbp/gbp.h new file mode 100644 index 00000000000..50039b3bdcf --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp.h @@ -0,0 +1,80 @@ +/* + * 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. + */ + +/** + * Group Base Policy (GBP) defines: + * - endpoints: typically a VM or container that is connected to the + * virtual switch/router (i.e. to VPP) + * - endpoint-group: (EPG) a collection of endpoints + * - policy: rules determining which traffic can pass between EPGs a.k.a + * a 'contract' + * + * Here, policy is implemented via an ACL. + * EPG classification for transit packets is determined by: + * - source EPG: from the packet's input interface + * - destination EPG: from the packet's destination IP address. + * + */ + +#ifndef __GBP_H__ +#define __GBP_H__ + +#include <plugins/acl/exports.h> + +#include <plugins/gbp/gbp_types.h> +#include <plugins/gbp/gbp_endpoint.h> +#include <plugins/gbp/gbp_endpoint_group.h> +#include <plugins/gbp/gbp_subnet.h> +#include <plugins/gbp/gbp_recirc.h> + +typedef struct +{ + u32 gbp_acl_user_id; + acl_plugin_methods_t acl_plugin; +} gbp_main_t; + +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 + */ +typedef struct gbp_policy_main_t_ +{ + /** + * Next nodes for L2 output features + */ + u32 l2_output_feat_next[GBP_N_POLICY][32]; +} gbp_policy_main_t; + +extern gbp_policy_main_t gbp_policy_main; + +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_api.c b/extras/deprecated/plugins/gbp/gbp_api.c new file mode 100644 index 00000000000..ab89172b1af --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_api.c @@ -0,0 +1,1154 @@ +/* + *------------------------------------------------------------------ + * 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 <vnet/vnet.h> +#include <vnet/plugin/plugin.h> + +#include <vnet/interface.h> +#include <vnet/api_errno.h> +#include <vnet/ip/ip_types_api.h> +#include <vnet/ethernet/ethernet_types_api.h> +#include <vpp/app/version.h> + +#include <gbp/gbp.h> +#include <gbp/gbp_learn.h> +#include <gbp/gbp_itf.h> +#include <gbp/gbp_vxlan.h> +#include <gbp/gbp_bridge_domain.h> +#include <gbp/gbp_route_domain.h> +#include <gbp/gbp_ext_itf.h> +#include <gbp/gbp_contract.h> + +#include <vlibapi/api.h> +#include <vlibmemory/api.h> + +/* define message IDs */ +#include <gbp/gbp.api_enum.h> +#include <gbp/gbp.api_types.h> +#include <vnet/format_fns.h> +#include <vlibapi/api_helper_macros.h> +#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) + +gbp_main_t gbp_main; + +static u16 msg_id_base; + +#define GBP_MSG_BASE msg_id_base + +static gbp_endpoint_flags_t +gbp_endpoint_flags_decode (vl_api_gbp_endpoint_flags_t v) +{ + gbp_endpoint_flags_t f = GBP_ENDPOINT_FLAG_NONE; + + v = ntohl (v); + + if (v & GBP_API_ENDPOINT_FLAG_BOUNCE) + f |= GBP_ENDPOINT_FLAG_BOUNCE; + if (v & GBP_API_ENDPOINT_FLAG_REMOTE) + f |= GBP_ENDPOINT_FLAG_REMOTE; + if (v & GBP_API_ENDPOINT_FLAG_LEARNT) + f |= GBP_ENDPOINT_FLAG_LEARNT; + if (v & GBP_API_ENDPOINT_FLAG_EXTERNAL) + f |= GBP_ENDPOINT_FLAG_EXTERNAL; + + return (f); +} + +static vl_api_gbp_endpoint_flags_t +gbp_endpoint_flags_encode (gbp_endpoint_flags_t f) +{ + vl_api_gbp_endpoint_flags_t v = 0; + + + if (f & GBP_ENDPOINT_FLAG_BOUNCE) + v |= GBP_API_ENDPOINT_FLAG_BOUNCE; + if (f & GBP_ENDPOINT_FLAG_REMOTE) + v |= GBP_API_ENDPOINT_FLAG_REMOTE; + if (f & GBP_ENDPOINT_FLAG_LEARNT) + v |= GBP_API_ENDPOINT_FLAG_LEARNT; + if (f & GBP_ENDPOINT_FLAG_EXTERNAL) + v |= GBP_API_ENDPOINT_FLAG_EXTERNAL; + + v = htonl (v); + + return (v); +} + +static void +vl_api_gbp_endpoint_add_t_handler (vl_api_gbp_endpoint_add_t * mp) +{ + vl_api_gbp_endpoint_add_reply_t *rmp; + gbp_endpoint_flags_t gef; + u32 sw_if_index, handle; + ip46_address_t *ips; + mac_address_t mac; + int rv = 0, ii; + + handle = INDEX_INVALID; + + VALIDATE_SW_IF_INDEX (&(mp->endpoint)); + + gef = gbp_endpoint_flags_decode (mp->endpoint.flags), ips = NULL; + sw_if_index = ntohl (mp->endpoint.sw_if_index); + + if (mp->endpoint.n_ips) + { + vec_validate (ips, mp->endpoint.n_ips - 1); + + vec_foreach_index (ii, ips) + { + ip_address_decode (&mp->endpoint.ips[ii], &ips[ii]); + } + } + mac_address_decode (mp->endpoint.mac, &mac); + + if (GBP_ENDPOINT_FLAG_REMOTE & gef) + { + ip46_address_t tun_src, tun_dst; + + ip_address_decode (&mp->endpoint.tun.src, &tun_src); + ip_address_decode (&mp->endpoint.tun.dst, &tun_dst); + + rv = gbp_endpoint_update_and_lock (GBP_ENDPOINT_SRC_CP, + sw_if_index, ips, &mac, + INDEX_INVALID, INDEX_INVALID, + ntohs (mp->endpoint.sclass), + gef, &tun_src, &tun_dst, &handle); + } + else + { + rv = gbp_endpoint_update_and_lock (GBP_ENDPOINT_SRC_CP, + sw_if_index, ips, &mac, + INDEX_INVALID, INDEX_INVALID, + ntohs (mp->endpoint.sclass), + gef, NULL, NULL, &handle); + } + vec_free (ips); + BAD_SW_IF_INDEX_LABEL; + + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_GBP_ENDPOINT_ADD_REPLY + GBP_MSG_BASE, + ({ + rmp->handle = htonl (handle); + })); + /* *INDENT-ON* */ +} + +static void +vl_api_gbp_endpoint_del_t_handler (vl_api_gbp_endpoint_del_t * mp) +{ + vl_api_gbp_endpoint_del_reply_t *rmp; + int rv = 0; + + gbp_endpoint_unlock (GBP_ENDPOINT_SRC_CP, ntohl (mp->handle)); + + REPLY_MACRO (VL_API_GBP_ENDPOINT_DEL_REPLY + GBP_MSG_BASE); +} + +typedef struct gbp_walk_ctx_t_ +{ + vl_api_registration_t *reg; + u32 context; +} gbp_walk_ctx_t; + +static walk_rc_t +gbp_endpoint_send_details (index_t gei, void *args) +{ + vl_api_gbp_endpoint_details_t *mp; + gbp_endpoint_loc_t *gel; + gbp_endpoint_fwd_t *gef; + gbp_endpoint_t *ge; + gbp_walk_ctx_t *ctx; + u8 n_ips, ii; + + ctx = args; + ge = gbp_endpoint_get (gei); + + n_ips = vec_len (ge->ge_key.gek_ips); + mp = vl_msg_api_alloc (sizeof (*mp) + (sizeof (*mp->endpoint.ips) * n_ips)); + if (!mp) + return 1; + + clib_memset (mp, 0, sizeof (*mp)); + mp->_vl_msg_id = ntohs (VL_API_GBP_ENDPOINT_DETAILS + GBP_MSG_BASE); + mp->context = ctx->context; + + gel = &ge->ge_locs[0]; + gef = &ge->ge_fwd; + + if (gbp_endpoint_is_remote (ge)) + { + mp->endpoint.sw_if_index = ntohl (gel->tun.gel_parent_sw_if_index); + ip_address_encode (&gel->tun.gel_src, IP46_TYPE_ANY, + &mp->endpoint.tun.src); + ip_address_encode (&gel->tun.gel_dst, IP46_TYPE_ANY, + &mp->endpoint.tun.dst); + } + else + { + mp->endpoint.sw_if_index = + ntohl (gbp_itf_get_sw_if_index (gef->gef_itf)); + } + mp->endpoint.sclass = ntohs (ge->ge_fwd.gef_sclass); + mp->endpoint.n_ips = n_ips; + mp->endpoint.flags = gbp_endpoint_flags_encode (gef->gef_flags); + mp->handle = htonl (gei); + mp->age = + clib_host_to_net_f64 (vlib_time_now (vlib_get_main ()) - + ge->ge_last_time); + mac_address_encode (&ge->ge_key.gek_mac, mp->endpoint.mac); + + vec_foreach_index (ii, ge->ge_key.gek_ips) + { + ip_address_encode (&ge->ge_key.gek_ips[ii].fp_addr, + IP46_TYPE_ANY, &mp->endpoint.ips[ii]); + } + + vl_api_send_msg (ctx->reg, (u8 *) mp); + + return (WALK_CONTINUE); +} + +static void +vl_api_gbp_endpoint_dump_t_handler (vl_api_gbp_endpoint_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_walk (gbp_endpoint_send_details, &ctx); +} + +static void +gbp_retention_decode (const vl_api_gbp_endpoint_retention_t * in, + gbp_endpoint_retention_t * out) +{ + out->remote_ep_timeout = ntohl (in->remote_ep_timeout); +} + +static void + vl_api_gbp_endpoint_group_add_t_handler + (vl_api_gbp_endpoint_group_add_t * mp) +{ + vl_api_gbp_endpoint_group_add_reply_t *rmp; + gbp_endpoint_retention_t retention; + int rv = 0; + + gbp_retention_decode (&mp->epg.retention, &retention); + + rv = gbp_endpoint_group_add_and_lock (ntohl (mp->epg.vnid), + ntohs (mp->epg.sclass), + ntohl (mp->epg.bd_id), + ntohl (mp->epg.rd_id), + ntohl (mp->epg.uplink_sw_if_index), + &retention); + + REPLY_MACRO (VL_API_GBP_ENDPOINT_GROUP_ADD_REPLY + GBP_MSG_BASE); +} + +static void + vl_api_gbp_endpoint_group_del_t_handler + (vl_api_gbp_endpoint_group_del_t * mp) +{ + vl_api_gbp_endpoint_group_del_reply_t *rmp; + int rv = 0; + + rv = gbp_endpoint_group_delete (ntohs (mp->sclass)); + + REPLY_MACRO (VL_API_GBP_ENDPOINT_GROUP_DEL_REPLY + GBP_MSG_BASE); +} + +static gbp_bridge_domain_flags_t +gbp_bridge_domain_flags_from_api (vl_api_gbp_bridge_domain_flags_t a) +{ + gbp_bridge_domain_flags_t g; + + g = GBP_BD_FLAG_NONE; + a = clib_net_to_host_u32 (a); + + if (a & GBP_BD_API_FLAG_DO_NOT_LEARN) + g |= GBP_BD_FLAG_DO_NOT_LEARN; + if (a & GBP_BD_API_FLAG_UU_FWD_DROP) + g |= GBP_BD_FLAG_UU_FWD_DROP; + if (a & GBP_BD_API_FLAG_MCAST_DROP) + g |= GBP_BD_FLAG_MCAST_DROP; + if (a & GBP_BD_API_FLAG_UCAST_ARP) + g |= GBP_BD_FLAG_UCAST_ARP; + + return (g); +} + +static void +vl_api_gbp_bridge_domain_add_t_handler (vl_api_gbp_bridge_domain_add_t * mp) +{ + vl_api_gbp_bridge_domain_add_reply_t *rmp; + int rv = 0; + + rv = gbp_bridge_domain_add_and_lock (ntohl (mp->bd.bd_id), + ntohl (mp->bd.rd_id), + gbp_bridge_domain_flags_from_api + (mp->bd.flags), + ntohl (mp->bd.bvi_sw_if_index), + ntohl (mp->bd.uu_fwd_sw_if_index), + ntohl (mp->bd.bm_flood_sw_if_index)); + + REPLY_MACRO (VL_API_GBP_BRIDGE_DOMAIN_ADD_REPLY + GBP_MSG_BASE); +} + +static void +vl_api_gbp_bridge_domain_del_t_handler (vl_api_gbp_bridge_domain_del_t * mp) +{ + vl_api_gbp_bridge_domain_del_reply_t *rmp; + int rv = 0; + + rv = gbp_bridge_domain_delete (ntohl (mp->bd_id)); + + REPLY_MACRO (VL_API_GBP_BRIDGE_DOMAIN_DEL_REPLY + GBP_MSG_BASE); +} + +static void +vl_api_gbp_route_domain_add_t_handler (vl_api_gbp_route_domain_add_t * mp) +{ + vl_api_gbp_route_domain_add_reply_t *rmp; + int rv = 0; + + rv = gbp_route_domain_add_and_lock (ntohl (mp->rd.rd_id), + ntohs (mp->rd.scope), + ntohl (mp->rd.ip4_table_id), + ntohl (mp->rd.ip6_table_id), + ntohl (mp->rd.ip4_uu_sw_if_index), + ntohl (mp->rd.ip6_uu_sw_if_index)); + + REPLY_MACRO (VL_API_GBP_ROUTE_DOMAIN_ADD_REPLY + GBP_MSG_BASE); +} + +static void +vl_api_gbp_route_domain_del_t_handler (vl_api_gbp_route_domain_del_t * mp) +{ + vl_api_gbp_route_domain_del_reply_t *rmp; + int rv = 0; + + rv = gbp_route_domain_delete (ntohl (mp->rd_id)); + + REPLY_MACRO (VL_API_GBP_ROUTE_DOMAIN_DEL_REPLY + GBP_MSG_BASE); +} + +static int +gub_subnet_type_from_api (vl_api_gbp_subnet_type_t a, gbp_subnet_type_t * t) +{ + a = clib_net_to_host_u32 (a); + + switch (a) + { + case GBP_API_SUBNET_TRANSPORT: + *t = GBP_SUBNET_TRANSPORT; + return (0); + case GBP_API_SUBNET_L3_OUT: + *t = GBP_SUBNET_L3_OUT; + return (0); + case GBP_API_SUBNET_ANON_L3_OUT: + *t = GBP_SUBNET_ANON_L3_OUT; + return (0); + case GBP_API_SUBNET_STITCHED_INTERNAL: + *t = GBP_SUBNET_STITCHED_INTERNAL; + return (0); + case GBP_API_SUBNET_STITCHED_EXTERNAL: + *t = GBP_SUBNET_STITCHED_EXTERNAL; + return (0); + } + + return (-1); +} + +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; + gbp_subnet_type_t type; + fib_prefix_t pfx; + int rv = 0; + + ip_prefix_decode (&mp->subnet.prefix, &pfx); + + rv = gub_subnet_type_from_api (mp->subnet.type, &type); + + if (0 != rv) + goto out; + + if (mp->is_add) + rv = gbp_subnet_add (ntohl (mp->subnet.rd_id), + &pfx, type, + ntohl (mp->subnet.sw_if_index), + ntohs (mp->subnet.sclass)); + else + rv = gbp_subnet_del (ntohl (mp->subnet.rd_id), &pfx); + +out: + REPLY_MACRO (VL_API_GBP_SUBNET_ADD_DEL_REPLY + GBP_MSG_BASE); +} + +static vl_api_gbp_subnet_type_t +gub_subnet_type_to_api (gbp_subnet_type_t t) +{ + vl_api_gbp_subnet_type_t a = 0; + + switch (t) + { + case GBP_SUBNET_TRANSPORT: + a = GBP_API_SUBNET_TRANSPORT; + break; + case GBP_SUBNET_STITCHED_INTERNAL: + a = GBP_API_SUBNET_STITCHED_INTERNAL; + break; + case GBP_SUBNET_STITCHED_EXTERNAL: + a = GBP_API_SUBNET_STITCHED_EXTERNAL; + break; + case GBP_SUBNET_L3_OUT: + a = GBP_API_SUBNET_L3_OUT; + break; + case GBP_SUBNET_ANON_L3_OUT: + a = GBP_API_SUBNET_ANON_L3_OUT; + break; + } + + a = clib_host_to_net_u32 (a); + + return (a); +} + +static walk_rc_t +gbp_subnet_send_details (u32 rd_id, + const fib_prefix_t * pfx, + gbp_subnet_type_t type, + u32 sw_if_index, sclass_t sclass, 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; + + clib_memset (mp, 0, sizeof (*mp)); + mp->_vl_msg_id = ntohs (VL_API_GBP_SUBNET_DETAILS + GBP_MSG_BASE); + mp->context = ctx->context; + + mp->subnet.type = gub_subnet_type_to_api (type); + mp->subnet.sw_if_index = ntohl (sw_if_index); + mp->subnet.sclass = ntohs (sclass); + mp->subnet.rd_id = ntohl (rd_id); + ip_prefix_encode (pfx, &mp->subnet.prefix); + + vl_api_send_msg (ctx->reg, (u8 *) mp); + + return (WALK_CONTINUE); +} + +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 * gg, 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; + + clib_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 (gg->gg_uplink_sw_if_index); + mp->epg.vnid = ntohl (gg->gg_vnid); + mp->epg.sclass = ntohs (gg->gg_sclass); + mp->epg.bd_id = ntohl (gbp_endpoint_group_get_bd_id (gg)); + mp->epg.rd_id = ntohl (gbp_route_domain_get_rd_id (gg->gg_rd)); + + 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 int +gbp_bridge_domain_send_details (gbp_bridge_domain_t * gb, void *args) +{ + vl_api_gbp_bridge_domain_details_t *mp; + gbp_route_domain_t *gr; + 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_BRIDGE_DOMAIN_DETAILS + GBP_MSG_BASE); + mp->context = ctx->context; + + gr = gbp_route_domain_get (gb->gb_rdi); + + mp->bd.bd_id = ntohl (gb->gb_bd_id); + mp->bd.rd_id = ntohl (gr->grd_id); + mp->bd.bvi_sw_if_index = ntohl (gb->gb_bvi_sw_if_index); + mp->bd.uu_fwd_sw_if_index = ntohl (gb->gb_uu_fwd_sw_if_index); + mp->bd.bm_flood_sw_if_index = + ntohl (gbp_itf_get_sw_if_index (gb->gb_bm_flood_itf)); + + vl_api_send_msg (ctx->reg, (u8 *) mp); + + return (1); +} + +static void +vl_api_gbp_bridge_domain_dump_t_handler (vl_api_gbp_bridge_domain_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_bridge_domain_walk (gbp_bridge_domain_send_details, &ctx); +} + +static int +gbp_route_domain_send_details (gbp_route_domain_t * grd, void *args) +{ + vl_api_gbp_route_domain_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_ROUTE_DOMAIN_DETAILS + GBP_MSG_BASE); + mp->context = ctx->context; + + mp->rd.rd_id = ntohl (grd->grd_id); + mp->rd.ip4_uu_sw_if_index = + ntohl (grd->grd_uu_sw_if_index[FIB_PROTOCOL_IP4]); + mp->rd.ip6_uu_sw_if_index = + ntohl (grd->grd_uu_sw_if_index[FIB_PROTOCOL_IP6]); + + vl_api_send_msg (ctx->reg, (u8 *) mp); + + return (1); +} + +static void +vl_api_gbp_route_domain_dump_t_handler (vl_api_gbp_route_domain_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_route_domain_walk (gbp_route_domain_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) + rv = gbp_recirc_add (sw_if_index, + ntohs (mp->recirc.sclass), mp->recirc.is_ext); + else + rv = gbp_recirc_delete (sw_if_index); + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_GBP_RECIRC_ADD_DEL_REPLY + GBP_MSG_BASE); +} + +static walk_rc_t +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 (WALK_STOP); + + clib_memset (mp, 0, sizeof (*mp)); + mp->_vl_msg_id = ntohs (VL_API_GBP_RECIRC_DETAILS + GBP_MSG_BASE); + mp->context = ctx->context; + + mp->recirc.sclass = ntohs (gr->gr_sclass); + mp->recirc.sw_if_index = ntohl (gr->gr_sw_if_index); + mp->recirc.is_ext = gr->gr_is_ext; + + vl_api_send_msg (ctx->reg, (u8 *) mp); + + return (WALK_CONTINUE); +} + +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_ext_itf_add_del_t_handler (vl_api_gbp_ext_itf_add_del_t * mp) +{ + vl_api_gbp_ext_itf_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_add (sw_if_index, + ntohl (ext_itf->bd_id), ntohl (ext_itf->rd_id), + ntohl (ext_itf->flags)); + else + rv = gbp_ext_itf_delete (sw_if_index); + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_GBP_EXT_ITF_ADD_DEL_REPLY + GBP_MSG_BASE); +} + +static walk_rc_t +gbp_ext_itf_send_details (gbp_ext_itf_t * gx, void *args) +{ + vl_api_gbp_ext_itf_details_t *mp; + gbp_walk_ctx_t *ctx; + + ctx = args; + mp = vl_msg_api_alloc (sizeof (*mp)); + if (!mp) + return (WALK_STOP); + + clib_memset (mp, 0, sizeof (*mp)); + mp->_vl_msg_id = ntohs (VL_API_GBP_EXT_ITF_DETAILS + GBP_MSG_BASE); + mp->context = ctx->context; + + mp->ext_itf.flags = ntohl (gx->gx_flags); + mp->ext_itf.bd_id = ntohl (gbp_bridge_domain_get_bd_id (gx->gx_bd)); + mp->ext_itf.rd_id = ntohl (gbp_route_domain_get_rd_id (gx->gx_rd)); + mp->ext_itf.sw_if_index = ntohl (gbp_itf_get_sw_if_index (gx->gx_itf)); + + vl_api_send_msg (ctx->reg, (u8 *) mp); + + return (WALK_CONTINUE); +} + +static void +vl_api_gbp_ext_itf_dump_t_handler (vl_api_gbp_ext_itf_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_ext_itf_walk (gbp_ext_itf_send_details, &ctx); +} + +static int +gbp_contract_rule_action_deocde (vl_api_gbp_rule_action_t in, + gbp_rule_action_t * out) +{ + in = clib_net_to_host_u32 (in); + + switch (in) + { + case GBP_API_RULE_PERMIT: + *out = GBP_RULE_PERMIT; + return (0); + case GBP_API_RULE_DENY: + *out = GBP_RULE_DENY; + return (0); + case GBP_API_RULE_REDIRECT: + *out = GBP_RULE_REDIRECT; + return (0); + } + + return (-1); +} + +static int +gbp_hash_mode_decode (vl_api_gbp_hash_mode_t in, gbp_hash_mode_t * out) +{ + in = clib_net_to_host_u32 (in); + + switch (in) + { + case GBP_API_HASH_MODE_SRC_IP: + *out = GBP_HASH_MODE_SRC_IP; + return (0); + case GBP_API_HASH_MODE_DST_IP: + *out = GBP_HASH_MODE_DST_IP; + return (0); + case GBP_API_HASH_MODE_SYMMETRIC: + *out = GBP_HASH_MODE_SYMMETRIC; + return (0); + } + + return (-2); +} + +static int +gbp_next_hop_decode (const vl_api_gbp_next_hop_t * in, index_t * gnhi) +{ + ip46_address_t ip; + mac_address_t mac; + index_t grd, gbd; + + gbd = gbp_bridge_domain_find_and_lock (ntohl (in->bd_id)); + + if (INDEX_INVALID == gbd) + return (VNET_API_ERROR_BD_NOT_MODIFIABLE); + + grd = gbp_route_domain_find_and_lock (ntohl (in->rd_id)); + + if (INDEX_INVALID == grd) + return (VNET_API_ERROR_NO_SUCH_FIB); + + ip_address_decode (&in->ip, &ip); + mac_address_decode (in->mac, &mac); + + *gnhi = gbp_next_hop_alloc (&ip, grd, &mac, gbd); + + return (0); +} + +static int +gbp_next_hop_set_decode (const vl_api_gbp_next_hop_set_t * in, + gbp_hash_mode_t * hash_mode, index_t ** out) +{ + + index_t *gnhis = NULL; + int rv; + u8 ii; + + rv = gbp_hash_mode_decode (in->hash_mode, hash_mode); + + if (0 != rv) + return rv; + + vec_validate (gnhis, in->n_nhs - 1); + + for (ii = 0; ii < in->n_nhs; ii++) + { + rv = gbp_next_hop_decode (&in->nhs[ii], &gnhis[ii]); + + if (0 != rv) + { + vec_free (gnhis); + break; + } + } + + *out = gnhis; + return (rv); +} + +static int +gbp_contract_rule_decode (const vl_api_gbp_rule_t * in, index_t * gui) +{ + gbp_hash_mode_t hash_mode; + gbp_rule_action_t action; + index_t *nhs = NULL; + int rv; + + rv = gbp_contract_rule_action_deocde (in->action, &action); + + if (0 != rv) + return rv; + + if (GBP_RULE_REDIRECT == action) + { + rv = gbp_next_hop_set_decode (&in->nh_set, &hash_mode, &nhs); + + if (0 != rv) + return (rv); + } + else + { + hash_mode = GBP_HASH_MODE_SRC_IP; + } + + *gui = gbp_rule_alloc (action, hash_mode, nhs); + + return (rv); +} + +static int +gbp_contract_rules_decode (u8 n_rules, + const vl_api_gbp_rule_t * rules, index_t ** out) +{ + index_t *guis = NULL; + int rv; + u8 ii; + + if (0 == n_rules) + { + *out = NULL; + return (0); + } + + vec_validate (guis, n_rules - 1); + + for (ii = 0; ii < n_rules; ii++) + { + rv = gbp_contract_rule_decode (&rules[ii], &guis[ii]); + + if (0 != rv) + { + index_t *gui; + vec_foreach (gui, guis) gbp_rule_free (*gui); + vec_free (guis); + return (rv); + } + } + + *out = guis; + return (rv); +} + +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; + u16 *allowed_ethertypes; + u32 stats_index = ~0; + index_t *rules; + int ii, rv = 0; + u8 n_et; + + if (mp->is_add) + { + rv = gbp_contract_rules_decode (mp->contract.n_rules, + mp->contract.rules, &rules); + if (0 != rv) + goto out; + + allowed_ethertypes = NULL; + + /* + * allowed ether types + */ + n_et = mp->contract.n_ether_types; + vec_validate (allowed_ethertypes, n_et - 1); + + for (ii = 0; ii < n_et; ii++) + { + /* leave the ether types in network order */ + allowed_ethertypes[ii] = mp->contract.allowed_ethertypes[ii]; + } + + rv = gbp_contract_update (ntohs (mp->contract.scope), + ntohs (mp->contract.sclass), + ntohs (mp->contract.dclass), + ntohl (mp->contract.acl_index), + rules, allowed_ethertypes, &stats_index); + } + else + rv = gbp_contract_delete (ntohs (mp->contract.scope), + ntohs (mp->contract.sclass), + ntohs (mp->contract.dclass)); + +out: + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_GBP_CONTRACT_ADD_DEL_REPLY + GBP_MSG_BASE, + ({ + rmp->stats_index = htonl (stats_index); + })); + /* *INDENT-ON* */ +} + +static int +gbp_contract_send_details (gbp_contract_t * gbpc, void *args) +{ + vl_api_gbp_contract_details_t *mp; + gbp_walk_ctx_t *ctx; + + ctx = args; + mp = vl_msg_api_alloc (sizeof (*mp)); + if (!mp) + return 1; + + clib_memset (mp, 0, sizeof (*mp)); + mp->_vl_msg_id = ntohs (VL_API_GBP_CONTRACT_DETAILS + GBP_MSG_BASE); + mp->context = ctx->context; + + mp->contract.sclass = ntohs (gbpc->gc_key.gck_src); + mp->contract.dclass = ntohs (gbpc->gc_key.gck_dst); + mp->contract.acl_index = ntohl (gbpc->gc_acl_index); + mp->contract.scope = ntohs (gbpc->gc_key.gck_scope); + + vl_api_send_msg (ctx->reg, (u8 *) mp); + + return (1); +} + +static void +vl_api_gbp_contract_dump_t_handler (vl_api_gbp_contract_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_contract_walk (gbp_contract_send_details, &ctx); +} + +static int +gbp_vxlan_tunnel_mode_2_layer (vl_api_gbp_vxlan_tunnel_mode_t mode, + gbp_vxlan_tunnel_layer_t * l) +{ + mode = clib_net_to_host_u32 (mode); + + switch (mode) + { + case GBP_VXLAN_TUNNEL_MODE_L2: + *l = GBP_VXLAN_TUN_L2; + return (0); + case GBP_VXLAN_TUNNEL_MODE_L3: + *l = GBP_VXLAN_TUN_L3; + return (0); + } + return (-1); +} + +static void +vl_api_gbp_vxlan_tunnel_add_t_handler (vl_api_gbp_vxlan_tunnel_add_t * mp) +{ + vl_api_gbp_vxlan_tunnel_add_reply_t *rmp; + gbp_vxlan_tunnel_layer_t layer; + ip4_address_t src; + u32 sw_if_index; + int rv = 0; + + ip4_address_decode (mp->tunnel.src, &src); + rv = gbp_vxlan_tunnel_mode_2_layer (mp->tunnel.mode, &layer); + + if (0 != rv) + goto out; + + rv = gbp_vxlan_tunnel_add (ntohl (mp->tunnel.vni), + layer, + ntohl (mp->tunnel.bd_rd_id), &src, &sw_if_index); + +out: + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_GBP_VXLAN_TUNNEL_ADD_REPLY + GBP_MSG_BASE, + ({ + rmp->sw_if_index = htonl (sw_if_index); + })); + /* *INDENT-ON* */ +} + +static void +vl_api_gbp_vxlan_tunnel_del_t_handler (vl_api_gbp_vxlan_tunnel_add_t * mp) +{ + vl_api_gbp_vxlan_tunnel_del_reply_t *rmp; + int rv = 0; + + rv = gbp_vxlan_tunnel_del (ntohl (mp->tunnel.vni)); + + REPLY_MACRO (VL_API_GBP_VXLAN_TUNNEL_DEL_REPLY + GBP_MSG_BASE); +} + +static vl_api_gbp_vxlan_tunnel_mode_t +gbp_vxlan_tunnel_layer_2_mode (gbp_vxlan_tunnel_layer_t layer) +{ + vl_api_gbp_vxlan_tunnel_mode_t mode = GBP_VXLAN_TUNNEL_MODE_L2; + + switch (layer) + { + case GBP_VXLAN_TUN_L2: + mode = GBP_VXLAN_TUNNEL_MODE_L2; + break; + case GBP_VXLAN_TUN_L3: + mode = GBP_VXLAN_TUNNEL_MODE_L3; + break; + } + mode = clib_host_to_net_u32 (mode); + + return (mode); +} + +static walk_rc_t +gbp_vxlan_tunnel_send_details (gbp_vxlan_tunnel_t * gt, void *args) +{ + vl_api_gbp_vxlan_tunnel_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 = htons (VL_API_GBP_VXLAN_TUNNEL_DETAILS + GBP_MSG_BASE); + mp->context = ctx->context; + + mp->tunnel.vni = htonl (gt->gt_vni); + mp->tunnel.mode = gbp_vxlan_tunnel_layer_2_mode (gt->gt_layer); + mp->tunnel.bd_rd_id = htonl (gt->gt_bd_rd_id); + + vl_api_send_msg (ctx->reg, (u8 *) mp); + + return (1); +} + +static void +vl_api_gbp_vxlan_tunnel_dump_t_handler (vl_api_gbp_vxlan_tunnel_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_vxlan_walk (gbp_vxlan_tunnel_send_details, &ctx); +} + +#include <gbp/gbp.api.c> +static clib_error_t * +gbp_init (vlib_main_t * vm) +{ + gbp_main_t *gbpm = &gbp_main; + + gbpm->gbp_acl_user_id = ~0; + + /* Ask for a correctly-sized block of API message decode slots */ + msg_id_base = setup_message_id_table (); + + return (NULL); +} + +VLIB_API_INIT_FUNCTION (gbp_init); + +/* *INDENT-OFF* */ +VLIB_PLUGIN_REGISTER () = { + .version = VPP_BUILD_VER, + .description = "Group Based Policy (GBP)", +}; +/* *INDENT-ON* */ + + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_bridge_domain.c b/extras/deprecated/plugins/gbp/gbp_bridge_domain.c new file mode 100644 index 00000000000..279169abb1d --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_bridge_domain.c @@ -0,0 +1,503 @@ +/* + * 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_bridge_domain.h> +#include <plugins/gbp/gbp_route_domain.h> +#include <plugins/gbp/gbp_endpoint.h> +#include <plugins/gbp/gbp_learn.h> +#include <plugins/gbp/gbp_itf.h> + +#include <vnet/dpo/dvr_dpo.h> +#include <vnet/fib/fib_table.h> +#include <vnet/l2/l2_input.h> +#include <vnet/l2/feat_bitmap.h> +#include <vnet/l2/l2_bvi.h> +#include <vnet/l2/l2_fib.h> + +/** + * Pool of GBP bridge_domains + */ +gbp_bridge_domain_t *gbp_bridge_domain_pool; + +/** + * DB of bridge_domains + */ +gbp_bridge_domain_db_t gbp_bridge_domain_db; + +/** + * Map of BD index to contract scope + */ +gbp_scope_t *gbp_scope_by_bd_index; + +/** + * logger + */ +vlib_log_class_t gb_logger; + +#define GBP_BD_DBG(...) \ + vlib_log_debug (gb_logger, __VA_ARGS__); + +index_t +gbp_bridge_domain_index (const gbp_bridge_domain_t * gbd) +{ + return (gbd - gbp_bridge_domain_pool); +} + +static void +gbp_bridge_domain_lock (index_t i) +{ + gbp_bridge_domain_t *gb; + + gb = gbp_bridge_domain_get (i); + gb->gb_locks++; +} + +u32 +gbp_bridge_domain_get_bd_id (index_t gbdi) +{ + gbp_bridge_domain_t *gb; + + gb = gbp_bridge_domain_get (gbdi); + + return (gb->gb_bd_id); +} + +static index_t +gbp_bridge_domain_find (u32 bd_id) +{ + uword *p; + + p = hash_get (gbp_bridge_domain_db.gbd_by_bd_id, bd_id); + + if (NULL != p) + return p[0]; + + return (INDEX_INVALID); +} + +index_t +gbp_bridge_domain_find_and_lock (u32 bd_id) +{ + uword *p; + + p = hash_get (gbp_bridge_domain_db.gbd_by_bd_id, bd_id); + + if (NULL != p) + { + gbp_bridge_domain_lock (p[0]); + return p[0]; + } + return (INDEX_INVALID); +} + +static void +gbp_bridge_domain_db_add (gbp_bridge_domain_t * gb) +{ + index_t gbi = gb - gbp_bridge_domain_pool; + + hash_set (gbp_bridge_domain_db.gbd_by_bd_id, gb->gb_bd_id, gbi); + vec_validate_init_empty (gbp_bridge_domain_db.gbd_by_bd_index, + gb->gb_bd_index, INDEX_INVALID); + gbp_bridge_domain_db.gbd_by_bd_index[gb->gb_bd_index] = gbi; +} + +static void +gbp_bridge_domain_db_remove (gbp_bridge_domain_t * gb) +{ + hash_unset (gbp_bridge_domain_db.gbd_by_bd_id, gb->gb_bd_id); + gbp_bridge_domain_db.gbd_by_bd_index[gb->gb_bd_index] = INDEX_INVALID; +} + +u8 * +format_gbp_bridge_domain_flags (u8 * s, va_list * args) +{ + gbp_bridge_domain_flags_t gf = va_arg (*args, gbp_bridge_domain_flags_t); + + if (gf) + { + if (gf & GBP_BD_FLAG_DO_NOT_LEARN) + s = format (s, "do-not-learn "); + if (gf & GBP_BD_FLAG_UU_FWD_DROP) + s = format (s, "uu-fwd-drop "); + if (gf & GBP_BD_FLAG_MCAST_DROP) + s = format (s, "mcast-drop "); + if (gf & GBP_BD_FLAG_UCAST_ARP) + s = format (s, "ucast-arp "); + } + else + { + s = format (s, "none"); + } + return (s); +} + +static u8 * +format_gbp_bridge_domain_ptr (u8 * s, va_list * args) +{ + gbp_bridge_domain_t *gb = va_arg (*args, gbp_bridge_domain_t *); + vnet_main_t *vnm = vnet_get_main (); + + if (NULL != gb) + s = + format (s, + "[%d] bd:[%d,%d], bvi:%U uu-flood:%U bm-flood:%U flags:%U locks:%d", + gb - gbp_bridge_domain_pool, gb->gb_bd_id, gb->gb_bd_index, + format_vnet_sw_if_index_name, vnm, gb->gb_bvi_sw_if_index, + format_vnet_sw_if_index_name, vnm, gb->gb_uu_fwd_sw_if_index, + format_gbp_itf_hdl, gb->gb_bm_flood_itf, + format_gbp_bridge_domain_flags, gb->gb_flags, gb->gb_locks); + else + s = format (s, "NULL"); + + return (s); +} + +u8 * +format_gbp_bridge_domain (u8 * s, va_list * args) +{ + index_t gbi = va_arg (*args, index_t); + + s = + format (s, "%U", format_gbp_bridge_domain_ptr, + gbp_bridge_domain_get (gbi)); + + return (s); +} + +int +gbp_bridge_domain_add_and_lock (u32 bd_id, + u32 rd_id, + gbp_bridge_domain_flags_t flags, + u32 bvi_sw_if_index, + u32 uu_fwd_sw_if_index, + u32 bm_flood_sw_if_index) +{ + gbp_bridge_domain_t *gb; + index_t gbi; + + gbi = gbp_bridge_domain_find (bd_id); + + if (INDEX_INVALID == gbi) + { + gbp_route_domain_t *gr; + u32 bd_index; + + bd_index = bd_find_index (&bd_main, bd_id); + + if (~0 == bd_index) + return (VNET_API_ERROR_BD_NOT_MODIFIABLE); + + bd_flags_t bd_flags = L2_NONE; + if (flags & GBP_BD_FLAG_UU_FWD_DROP) + bd_flags |= L2_UU_FLOOD; + if (flags & GBP_BD_FLAG_MCAST_DROP) + bd_flags |= L2_FLOOD; + + pool_get (gbp_bridge_domain_pool, gb); + memset (gb, 0, sizeof (*gb)); + + gbi = gb - gbp_bridge_domain_pool; + gb->gb_bd_id = bd_id; + gb->gb_bd_index = bd_index; + gb->gb_uu_fwd_sw_if_index = uu_fwd_sw_if_index; + gb->gb_bvi_sw_if_index = bvi_sw_if_index; + gbp_itf_hdl_reset (&gb->gb_bm_flood_itf); + gb->gb_locks = 1; + gb->gb_flags = flags; + gb->gb_rdi = gbp_route_domain_find_and_lock (rd_id); + + /* + * set the scope from the BD's RD's scope + */ + gr = gbp_route_domain_get (gb->gb_rdi); + vec_validate (gbp_scope_by_bd_index, gb->gb_bd_index); + gbp_scope_by_bd_index[gb->gb_bd_index] = gr->grd_scope; + + /* + * Set the BVI and uu-flood interfaces into the BD + */ + gbp_bridge_domain_itf_add (gbi, gb->gb_bvi_sw_if_index, + L2_BD_PORT_TYPE_BVI); + + if ((!(flags & GBP_BD_FLAG_UU_FWD_DROP) || + (flags & GBP_BD_FLAG_UCAST_ARP)) && + ~0 != gb->gb_uu_fwd_sw_if_index) + gbp_bridge_domain_itf_add (gbi, gb->gb_uu_fwd_sw_if_index, + L2_BD_PORT_TYPE_UU_FWD); + + if (!(flags & GBP_BD_FLAG_MCAST_DROP) && ~0 != bm_flood_sw_if_index) + { + gb->gb_bm_flood_itf = + gbp_itf_l2_add_and_lock (bm_flood_sw_if_index, gbi); + gbp_itf_l2_set_input_feature (gb->gb_bm_flood_itf, + L2INPUT_FEAT_GBP_LEARN); + } + + /* + * unset any flag(s) set above + */ + bd_set_flags (vlib_get_main (), bd_index, bd_flags, 0); + + if (flags & GBP_BD_FLAG_UCAST_ARP) + { + bd_flags = L2_ARP_UFWD; + bd_set_flags (vlib_get_main (), bd_index, bd_flags, 1); + } + + /* + * Add the BVI's MAC to the L2FIB + */ + l2fib_add_entry (vnet_sw_interface_get_hw_address + (vnet_get_main (), gb->gb_bvi_sw_if_index), + gb->gb_bd_index, gb->gb_bvi_sw_if_index, + (L2FIB_ENTRY_RESULT_FLAG_STATIC | + L2FIB_ENTRY_RESULT_FLAG_BVI)); + + gbp_bridge_domain_db_add (gb); + } + else + { + gb = gbp_bridge_domain_get (gbi); + gb->gb_locks++; + } + + GBP_BD_DBG ("add: %U", format_gbp_bridge_domain_ptr, gb); + + return (0); +} + +void +gbp_bridge_domain_itf_add (index_t gbdi, + u32 sw_if_index, l2_bd_port_type_t type) +{ + gbp_bridge_domain_t *gb; + + gb = gbp_bridge_domain_get (gbdi); + + set_int_l2_mode (vlib_get_main (), vnet_get_main (), MODE_L2_BRIDGE, + sw_if_index, gb->gb_bd_index, type, 0, 0); + /* + * adding an interface to the bridge enables learning on the + * interface. Disable learning on the interface by default for gbp + * interfaces + */ + l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_LEARN, 0); +} + +void +gbp_bridge_domain_itf_del (index_t gbdi, + u32 sw_if_index, l2_bd_port_type_t type) +{ + gbp_bridge_domain_t *gb; + + gb = gbp_bridge_domain_get (gbdi); + + set_int_l2_mode (vlib_get_main (), vnet_get_main (), MODE_L3, sw_if_index, + gb->gb_bd_index, type, 0, 0); +} + +void +gbp_bridge_domain_unlock (index_t gbdi) +{ + gbp_bridge_domain_t *gb; + + gb = gbp_bridge_domain_get (gbdi); + + gb->gb_locks--; + + if (0 == gb->gb_locks) + { + GBP_BD_DBG ("destroy: %U", format_gbp_bridge_domain_ptr, gb); + + l2fib_del_entry (vnet_sw_interface_get_hw_address + (vnet_get_main (), gb->gb_bvi_sw_if_index), + gb->gb_bd_index, gb->gb_bvi_sw_if_index); + + gbp_bridge_domain_itf_del (gbdi, gb->gb_bvi_sw_if_index, + L2_BD_PORT_TYPE_BVI); + if (~0 != gb->gb_uu_fwd_sw_if_index) + gbp_bridge_domain_itf_del (gbdi, gb->gb_uu_fwd_sw_if_index, + L2_BD_PORT_TYPE_UU_FWD); + gbp_itf_unlock (&gb->gb_bm_flood_itf); + + gbp_bridge_domain_db_remove (gb); + gbp_route_domain_unlock (gb->gb_rdi); + + pool_put (gbp_bridge_domain_pool, gb); + } +} + +int +gbp_bridge_domain_delete (u32 bd_id) +{ + index_t gbi; + + GBP_BD_DBG ("del: %d", bd_id); + gbi = gbp_bridge_domain_find (bd_id); + + if (INDEX_INVALID != gbi) + { + GBP_BD_DBG ("del: %U", format_gbp_bridge_domain, gbi); + gbp_bridge_domain_unlock (gbi); + + return (0); + } + + return (VNET_API_ERROR_NO_SUCH_ENTRY); +} + +void +gbp_bridge_domain_walk (gbp_bridge_domain_cb_t cb, void *ctx) +{ + gbp_bridge_domain_t *gbpe; + + /* *INDENT-OFF* */ + pool_foreach (gbpe, gbp_bridge_domain_pool) + { + if (!cb(gbpe, ctx)) + break; + } + /* *INDENT-ON* */ +} + +static clib_error_t * +gbp_bridge_domain_cli (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + vnet_main_t *vnm = vnet_get_main (); + gbp_bridge_domain_flags_t flags; + u32 bm_flood_sw_if_index = ~0; + u32 uu_fwd_sw_if_index = ~0; + u32 bd_id = ~0, rd_id = ~0; + u32 bvi_sw_if_index = ~0; + u8 add = 1; + + flags = GBP_BD_FLAG_NONE; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "bvi %U", unformat_vnet_sw_interface, + vnm, &bvi_sw_if_index)) + ; + else if (unformat (input, "uu-fwd %U", unformat_vnet_sw_interface, + vnm, &uu_fwd_sw_if_index)) + ; + else if (unformat (input, "bm-flood %U", unformat_vnet_sw_interface, + vnm, &bm_flood_sw_if_index)) + ; + else if (unformat (input, "add")) + add = 1; + else if (unformat (input, "del")) + add = 0; + else if (unformat (input, "flags %d", &flags)) + ; + else if (unformat (input, "bd %d", &bd_id)) + ; + else if (unformat (input, "rd %d", &rd_id)) + ; + else + break; + } + + 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 (add) + { + if (~0 == bvi_sw_if_index) + return clib_error_return (0, "interface must be specified"); + + gbp_bridge_domain_add_and_lock (bd_id, rd_id, + flags, + bvi_sw_if_index, + uu_fwd_sw_if_index, + bm_flood_sw_if_index); + } + else + gbp_bridge_domain_delete (bd_id); + + return (NULL); +} + +/*? + * Configure a GBP bridge-domain + * + * @cliexpar + * @cliexstart{gbp bridge-domain [del] bd <ID> bvi <interface> [uu-fwd <interface>] [bm-flood <interface>] [flags <flags>]} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_bridge_domain_cli_node, static) = { + .path = "gbp bridge-domain", + .short_help = "gbp bridge-domain [del] bd <ID> bvi <interface> [uu-fwd <interface>] [bm-flood <interface>] [flags <flags>]", + .function = gbp_bridge_domain_cli, +}; + +static int +gbp_bridge_domain_show_one (gbp_bridge_domain_t *gb, void *ctx) +{ + vlib_main_t *vm; + + vm = ctx; + vlib_cli_output (vm, " %U", format_gbp_bridge_domain_ptr, gb); + + return (1); +} + +static clib_error_t * +gbp_bridge_domain_show (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + vlib_cli_output (vm, "Bridge-Domains:"); + gbp_bridge_domain_walk (gbp_bridge_domain_show_one, vm); + + return (NULL); +} + + +/*? + * Show Group Based Policy Bridge_Domains and derived information + * + * @cliexpar + * @cliexstart{show gbp bridge_domain} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_bridge_domain_show_node, static) = { + .path = "show gbp bridge-domain", + .short_help = "show gbp bridge-domain\n", + .function = gbp_bridge_domain_show, +}; +/* *INDENT-ON* */ + +static clib_error_t * +gbp_bridge_domain_init (vlib_main_t * vm) +{ + gb_logger = vlib_log_register_class ("gbp", "bd"); + + return (NULL); +} + +VLIB_INIT_FUNCTION (gbp_bridge_domain_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_bridge_domain.h b/extras/deprecated/plugins/gbp/gbp_bridge_domain.h new file mode 100644 index 00000000000..0449240083c --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_bridge_domain.h @@ -0,0 +1,156 @@ +/* + * 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_BRIDGE_DOMAIN_H__ +#define __GBP_BRIDGE_DOMAIN_H__ + +#include <plugins/gbp/gbp_types.h> +#include <plugins/gbp/gbp_itf.h> + +#include <vnet/fib/fib_types.h> +#include <vnet/l2/l2_bd.h> + +/** + * Bridge Domain Flags + */ +typedef enum gbp_bridge_domain_flags_t_ +{ + GBP_BD_FLAG_NONE = 0, + GBP_BD_FLAG_DO_NOT_LEARN = (1 << 0), + GBP_BD_FLAG_UU_FWD_DROP = (1 << 1), + GBP_BD_FLAG_MCAST_DROP = (1 << 2), + GBP_BD_FLAG_UCAST_ARP = (1 << 3), +} gbp_bridge_domain_flags_t; + +/** + * A bridge Domain Representation. + * This is a standard bridge-domain plus all the attributes it must + * have to supprt the GBP model. + */ +typedef struct gbp_bridge_domain_t_ +{ + /** + * Bridge-domain ID + */ + u32 gb_bd_id; + u32 gb_bd_index; + + /** + * Index of the Route-domain this BD is associated with. This is used as the + * 'scope' of the packets for contract matching. + */ + u32 gb_rdi; + + /** + * Flags conttrolling behaviour + */ + gbp_bridge_domain_flags_t gb_flags; + + /** + * The BD's BVI interface (obligatory) + */ + u32 gb_bvi_sw_if_index; + + /** + * The BD's MAC spine-proxy interface (optional) + */ + u32 gb_uu_fwd_sw_if_index; + + /** + * The BD's interface to sned Broadcast and multicast packets + */ + gbp_itf_hdl_t gb_bm_flood_itf; + + /** + * The index of the BD's VNI interface on which packets from + * unkown endpoints arrive + */ + u32 gb_vni; + + /** + * locks/references to the BD so it does not get deleted (from the API) + * whilst it is still being used + */ + u32 gb_locks; +} gbp_bridge_domain_t; + +extern void gbp_bridge_domain_itf_add (index_t gbdi, + u32 sw_if_index, + l2_bd_port_type_t type); +extern void gbp_bridge_domain_itf_del (index_t gbdi, + u32 sw_if_index, + l2_bd_port_type_t type); + +extern int gbp_bridge_domain_add_and_lock (u32 bd_id, + u32 rd_id, + gbp_bridge_domain_flags_t flags, + u32 bvi_sw_if_index, + u32 uu_fwd_sw_if_index, + u32 bm_flood_sw_if_index); + +extern void gbp_bridge_domain_unlock (index_t gbi); +extern index_t gbp_bridge_domain_find_and_lock (u32 bd_id); +extern int gbp_bridge_domain_delete (u32 bd_id); +extern index_t gbp_bridge_domain_index (const gbp_bridge_domain_t *); +extern u32 gbp_bridge_domain_get_bd_id (index_t gbdi); + +typedef int (*gbp_bridge_domain_cb_t) (gbp_bridge_domain_t * gb, void *ctx); +extern void gbp_bridge_domain_walk (gbp_bridge_domain_cb_t bgpe, void *ctx); + +extern u8 *format_gbp_bridge_domain (u8 * s, va_list * args); +extern u8 *format_gbp_bridge_domain_flags (u8 * s, va_list * args); + +/** + * DB of bridge_domains + */ +typedef struct gbp_bridge_domain_db_t +{ + uword *gbd_by_bd_id; + index_t *gbd_by_bd_index; +} gbp_bridge_domain_db_t; + +extern gbp_bridge_domain_db_t gbp_bridge_domain_db; +extern gbp_bridge_domain_t *gbp_bridge_domain_pool; + +always_inline gbp_bridge_domain_t * +gbp_bridge_domain_get (index_t i) +{ + return (pool_elt_at_index (gbp_bridge_domain_pool, i)); +} + +always_inline gbp_bridge_domain_t * +gbp_bridge_domain_get_by_bd_index (u32 bd_index) +{ + return (gbp_bridge_domain_get + (gbp_bridge_domain_db.gbd_by_bd_index[bd_index])); +} + +extern gbp_scope_t *gbp_scope_by_bd_index; + +always_inline gbp_scope_t +gbp_bridge_domain_get_scope (u32 bd_index) +{ + return (gbp_scope_by_bd_index[bd_index]); +} + +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_classify.c b/extras/deprecated/plugins/gbp/gbp_classify.c new file mode 100644 index 00000000000..255db252871 --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_classify.c @@ -0,0 +1,71 @@ +/* + * 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> +#include <plugins/gbp/gbp_classify.h> +#include <vnet/l2/l2_input.h> + +gbp_src_classify_main_t gbp_src_classify_main; + +static clib_error_t * +gbp_src_classify_init (vlib_main_t * vm) +{ + gbp_src_classify_main_t *em = &gbp_src_classify_main; + + vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "gbp-src-classify"); + + /* Initialize the feature next-node indexes */ + feat_bitmap_init_next_nodes (vm, + node->index, + L2INPUT_N_FEAT, + l2input_get_feat_names (), + em->l2_input_feat_next[GBP_SRC_CLASSIFY_NULL]); + + node = vlib_get_node_by_name (vm, (u8 *) "gbp-null-classify"); + feat_bitmap_init_next_nodes (vm, + node->index, + L2INPUT_N_FEAT, + l2input_get_feat_names (), + em->l2_input_feat_next[GBP_SRC_CLASSIFY_PORT]); + + node = vlib_get_node_by_name (vm, (u8 *) "l2-gbp-lpm-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]); + + 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; +} + +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/extras/deprecated/plugins/gbp/gbp_classify.h b/extras/deprecated/plugins/gbp/gbp_classify.h new file mode 100644 index 00000000000..ca7db94a2c0 --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_classify.h @@ -0,0 +1,94 @@ +/* + * 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. + */ + +#ifndef __GBP_CLASSIFY_H__ +#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; + +/** + * 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; + +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 + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_classify_node.c b/extras/deprecated/plugins/gbp/gbp_classify_node.c new file mode 100644 index 00000000000..a2058a21284 --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_classify_node.c @@ -0,0 +1,628 @@ +/* + * 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> +#include <plugins/gbp/gbp_classify.h> +#include <plugins/gbp/gbp_policy_dpo.h> +#include <plugins/gbp/gbp_ext_itf.h> +#include <vnet/fib/ip4_fib.h> +#include <vnet/fib/ip6_fib.h> +#include <vnet/dpo/load_balance.h> +#include <vnet/l2/l2_input.h> +#include <vnet/l2/feat_bitmap.h> +#include <vnet/fib/fib_table.h> +#include <vnet/vxlan-gbp/vxlan_gbp_packet.h> +#include <vnet/ethernet/arp_packet.h> + +/** + * per-packet trace data + */ +typedef struct gbp_classify_trace_t_ +{ + /* per-pkt trace data */ + sclass_t sclass; +} 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, dpo_proto_t dproto) +{ + 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, sw_if_index0; + const gbp_endpoint_t *ge0; + vlib_buffer_t *b0; + sclass_t sclass0; + + 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]; + vnet_buffer2 (b0)->gbp.flags = VXLAN_GBP_GPFLAGS_NONE; + + if (GBP_SRC_CLASSIFY_NULL == type) + { + sclass0 = SCLASS_INVALID; + next0 = + vnet_l2_feature_next (b0, gscm->l2_input_feat_next[type], + L2INPUT_FEAT_GBP_NULL_CLASSIFY); + } + else + { + if (DPO_PROTO_ETHERNET == dproto) + { + const ethernet_header_t *h0; + + h0 = vlib_buffer_get_current (b0); + next0 = + vnet_l2_feature_next (b0, gscm->l2_input_feat_next[type], + L2INPUT_FEAT_GBP_SRC_CLASSIFY); + ge0 = gbp_endpoint_find_mac (h0->src_address, + vnet_buffer (b0)->l2.bd_index); + } + else if (DPO_PROTO_IP4 == dproto) + { + const ip4_header_t *h0; + + h0 = vlib_buffer_get_current (b0); + + ge0 = gbp_endpoint_find_ip4 + (&h0->src_address, + fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, + sw_if_index0)); + + + /* + * Go straight to looukp, do not pass go, do not collect $200 + */ + next0 = 0; + } + else if (DPO_PROTO_IP6 == dproto) + { + const ip6_header_t *h0; + + h0 = vlib_buffer_get_current (b0); + + ge0 = gbp_endpoint_find_ip6 + (&h0->src_address, + fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, + sw_if_index0)); + + + /* + * Go straight to lookup, do not pass go, do not collect $200 + */ + next0 = 0; + } + else + { + ge0 = NULL; + next0 = 0; + ASSERT (0); + } + + if (PREDICT_TRUE (NULL != ge0)) + sclass0 = ge0->ge_fwd.gef_sclass; + else + sclass0 = SCLASS_INVALID; + } + + vnet_buffer2 (b0)->gbp.sclass = sclass0; + + if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED))) + { + gbp_classify_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sclass = sclass0; + } + + 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; +} + +VLIB_NODE_FN (gbp_src_classify_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return (gbp_classify_inline (vm, node, frame, + GBP_SRC_CLASSIFY_PORT, DPO_PROTO_ETHERNET)); +} + +VLIB_NODE_FN (gbp_null_classify_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return (gbp_classify_inline (vm, node, frame, + GBP_SRC_CLASSIFY_NULL, DPO_PROTO_ETHERNET)); +} + +VLIB_NODE_FN (gbp_ip4_src_classify_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return (gbp_classify_inline (vm, node, frame, + GBP_SRC_CLASSIFY_PORT, DPO_PROTO_IP4)); +} + +VLIB_NODE_FN (gbp_ip6_src_classify_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return (gbp_classify_inline (vm, node, frame, + GBP_SRC_CLASSIFY_PORT, DPO_PROTO_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, "sclass:%d", t->sclass); + + return s; +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (gbp_null_classify_node) = { + .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_REGISTER_NODE (gbp_src_classify_node) = { + .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_REGISTER_NODE (gbp_ip4_src_classify_node) = { + .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_REGISTER_NODE (gbp_ip6_src_classify_node) = { + .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" + }, +}; + +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"), +}; + +/* *INDENT-ON* */ + +typedef enum gbp_lpm_classify_next_t_ +{ + GPB_LPM_CLASSIFY_DROP, +} gbp_lpm_classify_next_t; + +/** + * per-packet trace data + */ +typedef struct gbp_lpm_classify_trace_t_ +{ + sclass_t sclass; + index_t lbi; + ip46_address_t src; +} gbp_lpm_classify_trace_t; + +/* packet trace format function */ +static u8 * +format_gbp_lpm_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_lpm_classify_trace_t *t = va_arg (*args, gbp_lpm_classify_trace_t *); + + s = format (s, "sclass:%d lb:%d src:%U", + t->sclass, t->lbi, format_ip46_address, &t->src, IP46_TYPE_ANY); + + return s; +} + +enum gbp_lpm_type +{ + GBP_LPM_RECIRC, + GBP_LPM_EPG, + GBP_LPM_ANON +}; + +/* + * 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, + 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; + 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; + const gbp_endpoint_t *ge0, *ge_lpm0; + gbp_lpm_classify_next_t next0; + const ethernet_header_t *eh0; + const gbp_policy_dpo_t *gpd0; + const ip4_address_t *ip4_0; + const ip6_address_t *ip6_0; + const gbp_recirc_t *gr0; + vlib_buffer_t *b0; + sclass_t sclass0; + + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + ip4_0 = NULL; + ip6_0 = NULL; + next0 = GPB_LPM_CLASSIFY_DROP; + + lbi0 = ~0; + eh0 = NULL; + b0 = vlib_get_buffer (vm, bi0); + + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + vnet_buffer2 (b0)->gbp.flags = VXLAN_GBP_GPFLAGS_NONE; + + if (DPO_PROTO_IP4 == dproto) + ip4_0 = + &((ip4_header_t *) vlib_buffer_get_current (b0))->src_address; + else if (DPO_PROTO_IP6 == dproto) + ip6_0 = + &((ip6_header_t *) vlib_buffer_get_current (b0))->src_address; + else if (DPO_PROTO_ETHERNET == dproto) + { + eh0 = vlib_buffer_get_current (b0); + gbp_classify_get_ip_address (eh0, &ip4_0, &ip6_0, + GBP_CLASSIFY_GET_IP_SRC); + } + + if (GBP_LPM_RECIRC == type) + { + gr0 = gbp_recirc_get (sw_if_index0); + fib_index0 = gr0->gr_fib_index[dproto]; + ge0 = NULL; + + vnet_feature_next (&next0, b0); + } + else + { + if (NULL == eh0) + { + /* packet should be l2 */ + sclass0 = SCLASS_INVALID; + goto trace; + } + + if (GBP_LPM_ANON == type) + { + /* + * 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 + { + /* + * 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); + + if (NULL == ge0) + { + /* 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); + } + else + { + 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; + } + } + } + + gpd0 = gbp_classify_get_gpd (ip4_0, ip6_0, fib_index0); + if (0 == gpd0) + { + /* could not classify => drop */ + sclass0 = SCLASS_INVALID; + next0 = GPB_LPM_CLASSIFY_DROP; + goto trace; + } + + 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; + + trace: + vnet_buffer2 (b0)->gbp.sclass = sclass0; + + if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED))) + { + gbp_lpm_classify_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sclass = sclass0; + t->lbi = lbi0; + if (ip4_0) + t->src.ip4 = *ip4_0; + if (ip6_0) + t->src.ip6 = *ip6_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 frame->n_vectors; +} + +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, 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, 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, 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* */ +VLIB_REGISTER_NODE (gbp_ip4_lpm_classify_node) = { + .name = "ip4-gbp-lpm-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] = "ip4-drop" + }, +}; + +VLIB_REGISTER_NODE (gbp_ip6_lpm_classify_node) = { + .name = "ip6-gbp-lpm-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] = "ip6-drop" + }, +}; + +VLIB_REGISTER_NODE (gbp_l2_lpm_classify_node) = { + .name = "l2-gbp-lpm-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" + }, +}; + +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", + .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/extras/deprecated/plugins/gbp/gbp_contract.c b/extras/deprecated/plugins/gbp/gbp_contract.c new file mode 100644 index 00000000000..dd433f28a84 --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_contract.c @@ -0,0 +1,819 @@ +/* + * 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> +#include <plugins/gbp/gbp_bridge_domain.h> +#include <plugins/gbp/gbp_route_domain.h> +#include <plugins/gbp/gbp_policy_dpo.h> +#include <plugins/gbp/gbp_contract.h> + +#include <vnet/dpo/load_balance.h> +#include <vnet/dpo/drop_dpo.h> + +char *gbp_contract_error_strings[] = { +#define _(sym,string) string, + foreach_gbp_contract_error +#undef _ +}; + +/** + * Single contract DB instance + */ +gbp_contract_db_t gbp_contract_db; + +gbp_contract_t *gbp_contract_pool; + +vlib_log_class_t gc_logger; + +fib_node_type_t gbp_next_hop_fib_type; + +gbp_rule_t *gbp_rule_pool; +gbp_next_hop_t *gbp_next_hop_pool; + +#define GBP_CONTRACT_DBG(...) \ + vlib_log_notice (gc_logger, __VA_ARGS__); + +/* Adjacency packet/byte counters indexed by adjacency index. */ +vlib_combined_counter_main_t gbp_contract_permit_counters = { + .name = "gbp-contracts-permit", + .stat_segment_name = "/net/gbp/contract/permit", +}; + +vlib_combined_counter_main_t gbp_contract_drop_counters = { + .name = "gbp-contracts-drop", + .stat_segment_name = "/net/gbp/contract/drop", +}; + +index_t +gbp_rule_alloc (gbp_rule_action_t action, + gbp_hash_mode_t hash_mode, index_t * nhs) +{ + gbp_rule_t *gu; + + pool_get_zero (gbp_rule_pool, gu); + + gu->gu_hash_mode = hash_mode; + gu->gu_nhs = nhs; + gu->gu_action = action; + + return (gu - gbp_rule_pool); +} + +void +gbp_rule_free (index_t gui) +{ + pool_put_index (gbp_rule_pool, gui); +} + +index_t +gbp_next_hop_alloc (const ip46_address_t * ip, + index_t grd, const mac_address_t * mac, index_t gbd) +{ + fib_protocol_t fproto; + gbp_next_hop_t *gnh; + + pool_get_zero (gbp_next_hop_pool, gnh); + + fib_node_init (&gnh->gnh_node, gbp_next_hop_fib_type); + + ip46_address_copy (&gnh->gnh_ip, ip); + mac_address_copy (&gnh->gnh_mac, mac); + + gnh->gnh_rd = grd; + gnh->gnh_bd = gbd; + + FOR_EACH_FIB_IP_PROTOCOL (fproto) gnh->gnh_ai[fproto] = INDEX_INVALID; + + return (gnh - gbp_next_hop_pool); +} + +static inline gbp_next_hop_t * +gbp_next_hop_get (index_t gui) +{ + return (pool_elt_at_index (gbp_next_hop_pool, gui)); +} + +static void +gbp_contract_rules_free (index_t * rules) +{ + index_t *gui, *gnhi; + + vec_foreach (gui, rules) + { + gbp_policy_node_t pnode; + fib_protocol_t fproto; + gbp_next_hop_t *gnh; + gbp_rule_t *gu; + + gu = gbp_rule_get (*gui); + + FOR_EACH_GBP_POLICY_NODE (pnode) + { + FOR_EACH_FIB_IP_PROTOCOL (fproto) + { + dpo_reset (&gu->gu_dpo[pnode][fproto]); + dpo_reset (&gu->gu_dpo[pnode][fproto]); + } + } + + vec_foreach (gnhi, gu->gu_nhs) + { + fib_protocol_t fproto; + + gnh = gbp_next_hop_get (*gnhi); + gbp_bridge_domain_unlock (gnh->gnh_bd); + gbp_route_domain_unlock (gnh->gnh_rd); + gbp_endpoint_child_remove (gnh->gnh_ge, gnh->gnh_sibling); + gbp_endpoint_unlock (GBP_ENDPOINT_SRC_RR, gnh->gnh_ge); + + FOR_EACH_FIB_IP_PROTOCOL (fproto) + { + adj_unlock (gnh->gnh_ai[fproto]); + } + } + + gbp_rule_free (*gui); + } + vec_free (rules); +} + +static u8 * +format_gbp_next_hop (u8 * s, va_list * args) +{ + index_t gnhi = va_arg (*args, index_t); + gbp_next_hop_t *gnh; + + gnh = gbp_next_hop_get (gnhi); + + s = format (s, "%U, %U, %U EP:%d", + format_mac_address_t, &gnh->gnh_mac, + format_gbp_bridge_domain, gnh->gnh_bd, + format_ip46_address, &gnh->gnh_ip, IP46_TYPE_ANY, gnh->gnh_ge); + + return (s); +} + +u8 * +format_gbp_rule_action (u8 * s, va_list * args) +{ + gbp_rule_action_t action = va_arg (*args, gbp_rule_action_t); + + switch (action) + { +#define _(v,a) case GBP_RULE_##v: return (format (s, "%s", a)); + foreach_gbp_rule_action +#undef _ + } + + return (format (s, "unknown")); +} + +static u8 * +format_gbp_hash_mode (u8 * s, va_list * args) +{ + gbp_hash_mode_t hash_mode = va_arg (*args, gbp_hash_mode_t); + + switch (hash_mode) + { +#define _(v,a) case GBP_HASH_MODE_##v: return (format (s, "%s", a)); + foreach_gbp_hash_mode +#undef _ + } + + return (format (s, "unknown")); +} + +static u8 * +format_gbp_policy_node (u8 * s, va_list * args) +{ + gbp_policy_node_t action = va_arg (*args, gbp_policy_node_t); + + switch (action) + { +#define _(v,a) case GBP_POLICY_NODE_##v: return (format (s, "%s", a)); + foreach_gbp_policy_node +#undef _ + } + + return (format (s, "unknown")); +} + +static u8 * +format_gbp_rule (u8 * s, va_list * args) +{ + index_t gui = va_arg (*args, index_t); + gbp_policy_node_t pnode; + fib_protocol_t fproto; + gbp_rule_t *gu; + index_t *gnhi; + + gu = gbp_rule_get (gui); + s = format (s, "%U", format_gbp_rule_action, gu->gu_action); + + switch (gu->gu_action) + { + case GBP_RULE_PERMIT: + case GBP_RULE_DENY: + return (s); + case GBP_RULE_REDIRECT: + s = format (s, ", %U", format_gbp_hash_mode, gu->gu_hash_mode); + break; + } + + vec_foreach (gnhi, gu->gu_nhs) + { + s = format (s, "\n [%U]", format_gbp_next_hop, *gnhi); + } + + FOR_EACH_GBP_POLICY_NODE (pnode) + { + s = format (s, "\n policy-%U", format_gbp_policy_node, pnode); + + FOR_EACH_FIB_IP_PROTOCOL (fproto) + { + if (dpo_id_is_valid (&gu->gu_dpo[pnode][fproto])) + { + s = + format (s, "\n %U", format_dpo_id, + &gu->gu_dpo[pnode][fproto], 8); + } + } + } + + return (s); +} + +static void +gbp_contract_mk_adj (gbp_next_hop_t * gnh, fib_protocol_t fproto) +{ + ethernet_header_t *eth; + gbp_endpoint_t *ge; + index_t old_ai; + u8 *rewrite; + + old_ai = gnh->gnh_ai[fproto]; + rewrite = NULL; + vec_validate (rewrite, sizeof (*eth) - 1); + eth = (ethernet_header_t *) rewrite; + + GBP_CONTRACT_DBG ("...mk-adj: %U", format_gbp_next_hop, + gnh - gbp_next_hop_pool); + + ge = gbp_endpoint_get (gnh->gnh_ge); + + eth->type = clib_host_to_net_u16 ((fproto == FIB_PROTOCOL_IP4 ? + ETHERNET_TYPE_IP4 : ETHERNET_TYPE_IP6)); + mac_address_to_bytes (gbp_route_domain_get_local_mac (), eth->src_address); + mac_address_to_bytes (&gnh->gnh_mac, eth->dst_address); + + gnh->gnh_ai[fproto] = + adj_nbr_add_or_lock_w_rewrite (fproto, + fib_proto_to_link (fproto), + &gnh->gnh_ip, + gbp_itf_get_sw_if_index (ge-> + ge_fwd.gef_itf), + rewrite); + + adj_unlock (old_ai); +} + +static flow_hash_config_t +gbp_contract_mk_lb_hp (gbp_hash_mode_t gu_hash_mode) +{ + switch (gu_hash_mode) + { + case GBP_HASH_MODE_SRC_IP: + return IP_FLOW_HASH_SRC_ADDR; + case GBP_HASH_MODE_DST_IP: + return IP_FLOW_HASH_DST_ADDR; + case GBP_HASH_MODE_SYMMETRIC: + return (IP_FLOW_HASH_SRC_ADDR | IP_FLOW_HASH_DST_ADDR | + IP_FLOW_HASH_PROTO | IP_FLOW_HASH_SYMMETRIC); + } + + return 0; +} + +static void +gbp_contract_mk_lb (index_t gui, fib_protocol_t fproto) +{ + load_balance_path_t *paths = NULL; + gbp_policy_node_t pnode; + gbp_next_hop_t *gnh; + dpo_proto_t dproto; + gbp_rule_t *gu; + u32 ii; + + u32 policy_nodes[] = { + [GBP_POLICY_NODE_L2] = gbp_policy_port_node.index, + [GBP_POLICY_NODE_IP4] = ip4_gbp_policy_dpo_node.index, + [GBP_POLICY_NODE_IP6] = ip6_gbp_policy_dpo_node.index, + }; + + GBP_CONTRACT_DBG ("..mk-lb: %U", format_gbp_rule, gui); + + gu = gbp_rule_get (gui); + dproto = fib_proto_to_dpo (fproto); + + if (GBP_RULE_REDIRECT != gu->gu_action) + return; + + vec_foreach_index (ii, gu->gu_nhs) + { + gnh = gbp_next_hop_get (gu->gu_nhs[ii]); + + gbp_contract_mk_adj (gnh, FIB_PROTOCOL_IP4); + gbp_contract_mk_adj (gnh, FIB_PROTOCOL_IP6); + } + + FOR_EACH_GBP_POLICY_NODE (pnode) + { + vec_validate (paths, vec_len (gu->gu_nhs) - 1); + + vec_foreach_index (ii, gu->gu_nhs) + { + gnh = gbp_next_hop_get (gu->gu_nhs[ii]); + + paths[ii].path_index = FIB_NODE_INDEX_INVALID; + paths[ii].path_weight = 1; + dpo_set (&paths[ii].path_dpo, DPO_ADJACENCY, + dproto, gnh->gnh_ai[fproto]); + } + + if (!dpo_id_is_valid (&gu->gu_dpo[pnode][fproto])) + { + dpo_id_t dpo = DPO_INVALID; + + dpo_set (&dpo, DPO_LOAD_BALANCE, dproto, + load_balance_create (vec_len (paths), + dproto, + gbp_contract_mk_lb_hp + (gu->gu_hash_mode))); + dpo_stack_from_node (policy_nodes[pnode], &gu->gu_dpo[pnode][fproto], + &dpo); + dpo_reset (&dpo); + } + + load_balance_multipath_update (&gu->gu_dpo[pnode][fproto], + paths, LOAD_BALANCE_FLAG_NONE); + vec_free (paths); + } +} + +static void +gbp_contract_mk_one_lb (index_t gui) +{ + gbp_contract_mk_lb (gui, FIB_PROTOCOL_IP4); + gbp_contract_mk_lb (gui, FIB_PROTOCOL_IP6); +} + +static int +gbp_contract_next_hop_resolve (index_t gui, index_t gnhi) +{ + gbp_bridge_domain_t *gbd; + gbp_next_hop_t *gnh; + ip46_address_t *ips; + int rv; + + ips = NULL; + gnh = gbp_next_hop_get (gnhi); + gbd = gbp_bridge_domain_get (gnh->gnh_bd); + + gnh->gnh_gu = gui; + vec_add1 (ips, gnh->gnh_ip); + + /* + * source the endpoint this contract needs to forward via. + * give ofrwarding details via the spine proxy. if this EP is known + * to us, then since we source here with a low priority, the learned + * info will take precedenc. + */ + rv = gbp_endpoint_update_and_lock (GBP_ENDPOINT_SRC_RR, + gbd->gb_uu_fwd_sw_if_index, + ips, + &gnh->gnh_mac, + gnh->gnh_bd, gnh->gnh_rd, SCLASS_INVALID, + GBP_ENDPOINT_FLAG_NONE, NULL, NULL, + &gnh->gnh_ge); + + if (0 == rv) + { + gnh->gnh_sibling = gbp_endpoint_child_add (gnh->gnh_ge, + gbp_next_hop_fib_type, gnhi); + } + + GBP_CONTRACT_DBG ("..resolve: %d: %d: %U", gui, gnhi, format_gbp_next_hop, + gnhi); + + vec_free (ips); + return (rv); +} + +static void +gbp_contract_rule_resolve (index_t gui) +{ + gbp_rule_t *gu; + index_t *gnhi; + + gu = gbp_rule_get (gui); + + GBP_CONTRACT_DBG ("..resolve: %U", format_gbp_rule, gui); + + vec_foreach (gnhi, gu->gu_nhs) + { + gbp_contract_next_hop_resolve (gui, *gnhi); + } +} + +static void +gbp_contract_resolve (index_t * guis) +{ + index_t *gui; + + vec_foreach (gui, guis) + { + gbp_contract_rule_resolve (*gui); + } +} + +static void +gbp_contract_mk_lbs (index_t * guis) +{ + index_t *gui; + + vec_foreach (gui, guis) + { + gbp_contract_mk_one_lb (*gui); + } +} + +int +gbp_contract_update (gbp_scope_t scope, + sclass_t sclass, + sclass_t dclass, + u32 acl_index, + index_t * rules, + u16 * allowed_ethertypes, u32 * stats_index) +{ + gbp_main_t *gm = &gbp_main; + u32 *acl_vec = NULL; + gbp_contract_t *gc; + index_t gci; + uword *p; + + gbp_contract_key_t key = { + .gck_scope = scope, + .gck_src = sclass, + .gck_dst = dclass, + }; + + if (~0 == gm->gbp_acl_user_id) + { + acl_plugin_exports_init (&gm->acl_plugin); + gm->gbp_acl_user_id = + gm->acl_plugin.register_user_module ("GBP ACL", "src-epg", "dst-epg"); + } + + p = hash_get (gbp_contract_db.gc_hash, key.as_u64); + if (p != NULL) + { + gci = p[0]; + gc = gbp_contract_get (gci); + gbp_contract_rules_free (gc->gc_rules); + gbp_main.acl_plugin.put_lookup_context_index (gc->gc_lc_index); + gc->gc_rules = NULL; + vec_free (gc->gc_allowed_ethertypes); + } + else + { + pool_get_zero (gbp_contract_pool, gc); + gc->gc_key = key; + gci = gc - gbp_contract_pool; + hash_set (gbp_contract_db.gc_hash, key.as_u64, gci); + + vlib_validate_combined_counter (&gbp_contract_drop_counters, gci); + vlib_zero_combined_counter (&gbp_contract_drop_counters, gci); + vlib_validate_combined_counter (&gbp_contract_permit_counters, gci); + vlib_zero_combined_counter (&gbp_contract_permit_counters, gci); + } + + GBP_CONTRACT_DBG ("update: %U", format_gbp_contract, gci); + + gc->gc_rules = rules; + gc->gc_allowed_ethertypes = allowed_ethertypes; + gbp_contract_resolve (gc->gc_rules); + gbp_contract_mk_lbs (gc->gc_rules); + + gc->gc_acl_index = acl_index; + gc->gc_lc_index = + gm->acl_plugin.get_lookup_context_index (gm->gbp_acl_user_id, + sclass, dclass); + + vec_add1 (acl_vec, gc->gc_acl_index); + gm->acl_plugin.set_acl_vec_for_context (gc->gc_lc_index, acl_vec); + vec_free (acl_vec); + + *stats_index = gci; + + return (0); +} + +int +gbp_contract_delete (gbp_scope_t scope, sclass_t sclass, sclass_t dclass) +{ + gbp_contract_key_t key = { + .gck_scope = scope, + .gck_src = sclass, + .gck_dst = dclass, + }; + gbp_contract_t *gc; + uword *p; + + p = hash_get (gbp_contract_db.gc_hash, key.as_u64); + if (p != NULL) + { + gc = gbp_contract_get (p[0]); + + gbp_contract_rules_free (gc->gc_rules); + gbp_main.acl_plugin.put_lookup_context_index (gc->gc_lc_index); + vec_free (gc->gc_allowed_ethertypes); + + hash_unset (gbp_contract_db.gc_hash, key.as_u64); + pool_put (gbp_contract_pool, gc); + + return (0); + } + + return (VNET_API_ERROR_NO_SUCH_ENTRY); +} + +void +gbp_contract_walk (gbp_contract_cb_t cb, void *ctx) +{ + gbp_contract_t *gc; + + /* *INDENT-OFF* */ + pool_foreach (gc, gbp_contract_pool) + { + if (!cb(gc, ctx)) + break; + } + /* *INDENT-ON* */ +} + +static clib_error_t * +gbp_contract_cli (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + sclass_t sclass = SCLASS_INVALID, dclass = SCLASS_INVALID; + u32 acl_index = ~0, stats_index, scope; + 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, "scope %d", &scope)) + ; + else if (unformat (input, "sclass %d", &sclass)) + ; + else if (unformat (input, "dclass %d", &dclass)) + ; + else if (unformat (input, "acl-index %d", &acl_index)) + ; + else + break; + } + + if (SCLASS_INVALID == sclass) + return clib_error_return (0, "Source EPG-ID must be specified"); + if (SCLASS_INVALID == dclass) + return clib_error_return (0, "Destination EPG-ID must be specified"); + + if (add) + { + gbp_contract_update (scope, sclass, dclass, acl_index, + NULL, NULL, &stats_index); + } + else + { + gbp_contract_delete (scope, sclass, dclass); + } + + return (NULL); +} + +/*? + * Configure a GBP Contract + * + * @cliexpar + * @cliexstart{set gbp contract [del] src-epg <ID> dst-epg <ID> acl-index <ACL>} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +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 u8 * +format_gbp_contract_key (u8 * s, va_list * args) +{ + gbp_contract_key_t *gck = va_arg (*args, gbp_contract_key_t *); + + s = format (s, "{%d,%d,%d}", gck->gck_scope, gck->gck_src, gck->gck_dst); + + return (s); +} + +u8 * +format_gbp_contract (u8 * s, va_list * args) +{ + index_t gci = va_arg (*args, index_t); + vlib_counter_t counts; + gbp_contract_t *gc; + index_t *gui; + u16 *et; + + gc = gbp_contract_get (gci); + + s = format (s, "[%d] %U: acl-index:%d", + gci, format_gbp_contract_key, &gc->gc_key, gc->gc_acl_index); + + s = format (s, "\n rules:"); + vec_foreach (gui, gc->gc_rules) + { + s = format (s, "\n %d: %U", *gui, format_gbp_rule, *gui); + } + + s = format (s, "\n allowed-ethertypes:"); + s = format (s, "\n ["); + vec_foreach (et, gc->gc_allowed_ethertypes) + { + int host_et = clib_net_to_host_u16 (*et); + if (0 != host_et) + s = format (s, "0x%x, ", host_et); + } + s = format (s, "]"); + + s = format (s, "\n stats:"); + vlib_get_combined_counter (&gbp_contract_drop_counters, gci, &counts); + s = format (s, "\n drop:[%Ld:%Ld]", counts.packets, counts.bytes); + vlib_get_combined_counter (&gbp_contract_permit_counters, gci, &counts); + s = format (s, "\n permit:[%Ld:%Ld]", counts.packets, counts.bytes); + + s = format (s, "]"); + + return (s); +} + +static clib_error_t * +gbp_contract_show (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + gbp_contract_t *gc; + u32 src, dst; + index_t gci; + + src = dst = SCLASS_INVALID; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "src %d", &src)) + ; + else if (unformat (input, "dst %d", &dst)) + ; + else + break; + } + + vlib_cli_output (vm, "Contracts:"); + + /* *INDENT-OFF* */ + pool_foreach (gc, gbp_contract_pool) + { + gci = gc - gbp_contract_pool; + + if (SCLASS_INVALID != src && SCLASS_INVALID != dst) + { + if (gc->gc_key.gck_src == src && + gc->gc_key.gck_dst == dst) + vlib_cli_output (vm, " %U", format_gbp_contract, gci); + } + else if (SCLASS_INVALID != src) + { + if (gc->gc_key.gck_src == src) + vlib_cli_output (vm, " %U", format_gbp_contract, gci); + } + else if (SCLASS_INVALID != dst) + { + if (gc->gc_key.gck_dst == dst) + vlib_cli_output (vm, " %U", format_gbp_contract, gci); + } + else + vlib_cli_output (vm, " %U", format_gbp_contract, gci); + } + /* *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 [src <SRC>] [dst <DST>]\n", + .function = gbp_contract_show, +}; +/* *INDENT-ON* */ + +static fib_node_t * +gbp_next_hop_get_node (fib_node_index_t index) +{ + gbp_next_hop_t *gnh; + + gnh = gbp_next_hop_get (index); + + return (&gnh->gnh_node); +} + +static void +gbp_next_hop_last_lock_gone (fib_node_t * node) +{ + ASSERT (0); +} + +static gbp_next_hop_t * +gbp_next_hop_from_fib_node (fib_node_t * node) +{ + ASSERT (gbp_next_hop_fib_type == node->fn_type); + return ((gbp_next_hop_t *) node); +} + +static fib_node_back_walk_rc_t +gbp_next_hop_back_walk_notify (fib_node_t * node, + fib_node_back_walk_ctx_t * ctx) +{ + gbp_next_hop_t *gnh; + + gnh = gbp_next_hop_from_fib_node (node); + + gbp_contract_mk_one_lb (gnh->gnh_gu); + + return (FIB_NODE_BACK_WALK_CONTINUE); +} + +/* + * The FIB path's graph node virtual function table + */ +static const fib_node_vft_t gbp_next_hop_vft = { + .fnv_get = gbp_next_hop_get_node, + .fnv_last_lock = gbp_next_hop_last_lock_gone, + .fnv_back_walk = gbp_next_hop_back_walk_notify, + // .fnv_mem_show = fib_path_memory_show, +}; + +static clib_error_t * +gbp_contract_init (vlib_main_t * vm) +{ + gc_logger = vlib_log_register_class ("gbp", "con"); + gbp_next_hop_fib_type = fib_node_register_new_type (&gbp_next_hop_vft); + + return (NULL); +} + +VLIB_INIT_FUNCTION (gbp_contract_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_contract.h b/extras/deprecated/plugins/gbp/gbp_contract.h new file mode 100644 index 00000000000..1e74db60116 --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_contract.h @@ -0,0 +1,362 @@ +/* + * 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.h> +#include <plugins/gbp/gbp_types.h> + +#define foreach_gbp_contract_error \ + _(ALLOW_NO_SCLASS, "allow-no-sclass") \ + _(ALLOW_INTRA, "allow-intra-sclass") \ + _(ALLOW_A_BIT, "allow-a-bit-set") \ + _(ALLOW_SCLASS_1, "allow-sclass-1") \ + _(ALLOW_CONTRACT, "allow-contract") \ + _(DROP_CONTRACT, "drop-contract") \ + _(DROP_ETHER_TYPE, "drop-ether-type") \ + _(DROP_NO_CONTRACT, "drop-no-contract") \ + _(DROP_NO_DCLASS, "drop-no-dclass") \ + _(DROP_NO_RULE, "drop-no-rule") + +typedef enum +{ +#define _(sym,str) GBP_CONTRACT_ERROR_##sym, + foreach_gbp_contract_error +#undef _ + GBP_CONTRACT_N_ERROR, +#define GBP_CONTRACT_N_ERROR GBP_CONTRACT_N_ERROR +} gbp_contract_error_t; + +extern char *gbp_contract_error_strings[GBP_CONTRACT_N_ERROR]; + +/** + * The key for an Contract + */ +typedef struct gbp_contract_key_t_ +{ + union + { + struct + { + gbp_scope_t gck_scope; + /** + * source and destination EPGs for which the ACL applies + */ + sclass_t gck_src; + sclass_t gck_dst; + }; + u64 as_u64; + }; +} gbp_contract_key_t; + +typedef struct gbp_next_hop_t_ +{ + fib_node_t gnh_node; + ip46_address_t gnh_ip; + mac_address_t gnh_mac; + index_t gnh_gu; + index_t gnh_bd; + index_t gnh_rd; + u32 gnh_ge; + u32 gnh_sibling; + index_t gnh_ai[FIB_PROTOCOL_IP_MAX]; +} gbp_next_hop_t; + +#define foreach_gbp_hash_mode \ + _(SRC_IP, "src-ip") \ + _(DST_IP, "dst-ip") \ + _(SYMMETRIC, "symmetric") + +typedef enum gbp_hash_mode_t_ +{ +#define _(v,s) GBP_HASH_MODE_##v, + foreach_gbp_hash_mode +#undef _ +} gbp_hash_mode_t; + +#define foreach_gbp_rule_action \ + _(PERMIT, "permit") \ + _(DENY, "deny") \ + _(REDIRECT, "redirect") + +typedef enum gbp_rule_action_t_ +{ +#define _(v,s) GBP_RULE_##v, + foreach_gbp_rule_action +#undef _ +} gbp_rule_action_t; + +#define foreach_gbp_policy_node \ + _(L2, "L2") \ + _(IP4, "ip4") \ + _(IP6, "ip6") + +typedef enum gbp_policy_node_t_ +{ +#define _(v,s) GBP_POLICY_NODE_##v, + foreach_gbp_policy_node +#undef _ +} gbp_policy_node_t; +#define GBP_POLICY_N_NODES (GBP_POLICY_NODE_IP6+1) + +#define FOR_EACH_GBP_POLICY_NODE(pnode) \ + for (pnode = GBP_POLICY_NODE_L2; pnode < GBP_POLICY_N_NODES; pnode++) + +typedef struct gbp_rule_t_ +{ + gbp_rule_action_t gu_action; + gbp_hash_mode_t gu_hash_mode; + index_t *gu_nhs; + + /** + * DPO of the load-balance object used to redirect + */ + dpo_id_t gu_dpo[GBP_POLICY_N_NODES][FIB_PROTOCOL_IP_MAX]; +} gbp_rule_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; + + u32 gc_acl_index; + u32 gc_lc_index; + + /** + * The ACL to apply for packets from the source to the destination EPG + */ + index_t *gc_rules; + + /** + * An ethertype whitelist + */ + u16 *gc_allowed_ethertypes; +} 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 int gbp_contract_update (gbp_scope_t scope, + sclass_t sclass, + sclass_t dclass, + u32 acl_index, + index_t * rules, + u16 * allowed_ethertypes, u32 * stats_index); +extern int gbp_contract_delete (gbp_scope_t scope, sclass_t sclass, + sclass_t dclass); + +extern index_t gbp_rule_alloc (gbp_rule_action_t action, + gbp_hash_mode_t hash_mode, index_t * nhs); +extern void gbp_rule_free (index_t gui); +extern index_t gbp_next_hop_alloc (const ip46_address_t * ip, + index_t grd, + const mac_address_t * mac, index_t gbd); + +typedef int (*gbp_contract_cb_t) (gbp_contract_t * gbpe, void *ctx); +extern void gbp_contract_walk (gbp_contract_cb_t bgpe, void *ctx); + +extern u8 *format_gbp_rule_action (u8 * s, va_list * args); +extern u8 *format_gbp_contract (u8 * s, va_list * args); + +/** + * DP functions and databases + */ +extern gbp_contract_db_t gbp_contract_db; + +always_inline index_t +gbp_contract_find (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 (INDEX_INVALID); +} + +extern gbp_contract_t *gbp_contract_pool; + +always_inline gbp_contract_t * +gbp_contract_get (index_t gci) +{ + return (pool_elt_at_index (gbp_contract_pool, gci)); +} + +extern gbp_rule_t *gbp_rule_pool; + +always_inline gbp_rule_t * +gbp_rule_get (index_t gui) +{ + return (pool_elt_at_index (gbp_rule_pool, gui)); +} + +extern vlib_combined_counter_main_t gbp_contract_permit_counters; +extern vlib_combined_counter_main_t gbp_contract_drop_counters; + +typedef enum +{ + GBP_CONTRACT_APPLY_L2, + GBP_CONTRACT_APPLY_IP4, + GBP_CONTRACT_APPLY_IP6, +} gbp_contract_apply_type_t; + +static_always_inline gbp_rule_action_t +gbp_contract_apply (vlib_main_t * vm, gbp_main_t * gm, + gbp_contract_key_t * key, vlib_buffer_t * b, + gbp_rule_t ** rule, u32 * intra, u32 * sclass1, + u32 * acl_match, u32 * rule_match, + gbp_contract_error_t * err, + gbp_contract_apply_type_t type) +{ + fa_5tuple_opaque_t fa_5tuple; + const gbp_contract_t *contract; + index_t contract_index; + u32 acl_pos, trace_bitmap; + u16 etype; + u8 ip6, action; + + *rule = 0; + trace_bitmap = 0; + + if (key->gck_src == key->gck_dst) + { + /* intra-epg allowed */ + (*intra)++; + *err = GBP_CONTRACT_ERROR_ALLOW_INTRA; + return GBP_RULE_PERMIT; + } + + if (1 == key->gck_src || 1 == key->gck_dst) + { + /* sclass 1 allowed */ + (*sclass1)++; + *err = GBP_CONTRACT_ERROR_ALLOW_SCLASS_1; + return GBP_RULE_PERMIT; + } + + /* look for contract */ + contract_index = gbp_contract_find (key); + if (INDEX_INVALID == contract_index) + { + *err = GBP_CONTRACT_ERROR_DROP_NO_CONTRACT; + return GBP_RULE_DENY; + } + + contract = gbp_contract_get (contract_index); + + *err = GBP_CONTRACT_ERROR_DROP_CONTRACT; + + switch (type) + { + case GBP_CONTRACT_APPLY_IP4: + ip6 = 0; + break; + case GBP_CONTRACT_APPLY_IP6: + ip6 = 1; + break; + case GBP_CONTRACT_APPLY_L2: + { + /* check ethertype */ + etype = + ((u16 *) (vlib_buffer_get_current (b) + + vnet_buffer (b)->l2.l2_len))[-1]; + + if (~0 == vec_search (contract->gc_allowed_ethertypes, etype)) + { + *err = GBP_CONTRACT_ERROR_DROP_ETHER_TYPE; + goto contract_deny; + } + + switch (clib_net_to_host_u16 (etype)) + { + case ETHERNET_TYPE_IP4: + ip6 = 0; + break; + case ETHERNET_TYPE_IP6: + ip6 = 1; + break; + default: + goto contract_deny; + } + } + break; + } + + /* check ACL */ + action = 0; + acl_plugin_fill_5tuple_inline (gm->acl_plugin.p_acl_main, + contract->gc_lc_index, b, ip6, + GBP_CONTRACT_APPLY_L2 != type /* input */ , + GBP_CONTRACT_APPLY_L2 == type /* l2_path */ , + &fa_5tuple); + acl_plugin_match_5tuple_inline (gm->acl_plugin.p_acl_main, + contract->gc_lc_index, &fa_5tuple, ip6, + &action, &acl_pos, acl_match, rule_match, + &trace_bitmap); + if (action <= 0) + goto contract_deny; + + if (PREDICT_FALSE (*rule_match >= vec_len (contract->gc_rules))) + { + *err = GBP_CONTRACT_ERROR_DROP_NO_RULE; + goto contract_deny; + } + + *rule = gbp_rule_get (contract->gc_rules[*rule_match]); + switch ((*rule)->gu_action) + { + case GBP_RULE_PERMIT: + case GBP_RULE_REDIRECT: + *err = GBP_CONTRACT_ERROR_ALLOW_CONTRACT; + vlib_increment_combined_counter (&gbp_contract_permit_counters, + vm->thread_index, contract_index, 1, + vlib_buffer_length_in_chain (vm, b)); + return (*rule)->gu_action; + case GBP_RULE_DENY: + break; + } + +contract_deny: + vlib_increment_combined_counter (&gbp_contract_drop_counters, + vm->thread_index, contract_index, 1, + vlib_buffer_length_in_chain (vm, b)); + return GBP_RULE_DENY; +} + +#endif /* __GBP_CONTRACT_H__ */ +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_endpoint.c b/extras/deprecated/plugins/gbp/gbp_endpoint.c new file mode 100644 index 00000000000..b0cf64ced2d --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_endpoint.c @@ -0,0 +1,1597 @@ +/* + * 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 <plugins/gbp/gbp_itf.h> +#include <plugins/gbp/gbp_scanner.h> +#include <plugins/gbp/gbp_bridge_domain.h> +#include <plugins/gbp/gbp_route_domain.h> +#include <plugins/gbp/gbp_policy_dpo.h> +#include <plugins/gbp/gbp_vxlan.h> + +#include <vnet/l2/l2_input.h> +#include <vnet/l2/l2_output.h> +#include <vnet/l2/feat_bitmap.h> +#include <vnet/l2/l2_fib.h> +#include <vnet/fib/fib_table.h> +#include <vnet/ip-neighbor/ip_neighbor.h> +#include <vnet/ip-neighbor/ip4_neighbor.h> +#include <vnet/ip-neighbor/ip6_neighbor.h> +#include <vnet/fib/fib_walk.h> +#include <vnet/vxlan-gbp/vxlan_gbp.h> + +static const char *gbp_endpoint_attr_names[] = GBP_ENDPOINT_ATTR_NAMES; + +/** + * EP DBs + */ +gbp_ep_db_t gbp_ep_db; + +static fib_source_t gbp_fib_source_hi; +static fib_source_t gbp_fib_source_low; +static fib_node_type_t gbp_endpoint_fib_type; +static vlib_log_class_t gbp_ep_logger; + +#define GBP_ENDPOINT_DBG(...) \ + vlib_log_debug (gbp_ep_logger, __VA_ARGS__); + +#define GBP_ENDPOINT_INFO(...) \ + vlib_log_notice (gbp_ep_logger, __VA_ARGS__); + +/** + * Pool of GBP endpoints + */ +gbp_endpoint_t *gbp_endpoint_pool; + +/** + * A count of the number of dynamic entries + */ +static u32 gbp_n_learnt_endpoints; + +#define FOR_EACH_GBP_ENDPOINT_ATTR(_item) \ + for (_item = GBP_ENDPOINT_ATTR_FIRST; \ + _item < GBP_ENDPOINT_ATTR_LAST; \ + _item++) + +u8 * +format_gbp_endpoint_flags (u8 * s, va_list * args) +{ + gbp_endpoint_attr_t attr; + gbp_endpoint_flags_t flags = va_arg (*args, gbp_endpoint_flags_t); + + FOR_EACH_GBP_ENDPOINT_ATTR (attr) + { + if ((1 << attr) & flags) + { + s = format (s, "%s,", gbp_endpoint_attr_names[attr]); + } + } + + return (s); +} + +int +gbp_endpoint_is_remote (const gbp_endpoint_t * ge) +{ + return (! !(ge->ge_fwd.gef_flags & GBP_ENDPOINT_FLAG_REMOTE)); +} + +int +gbp_endpoint_is_local (const gbp_endpoint_t * ge) +{ + return (!(ge->ge_fwd.gef_flags & GBP_ENDPOINT_FLAG_REMOTE)); +} + +int +gbp_endpoint_is_external (const gbp_endpoint_t * ge) +{ + return (! !(ge->ge_fwd.gef_flags & GBP_ENDPOINT_FLAG_EXTERNAL)); +} + +int +gbp_endpoint_is_learnt (const gbp_endpoint_t * ge) +{ + if (0 == vec_len (ge->ge_locs)) + return 0; + + /* DP is the highest source so if present it will be first */ + return (ge->ge_locs[0].gel_src == GBP_ENDPOINT_SRC_DP); +} + +static void +gbp_endpoint_extract_key_mac_itf (const clib_bihash_kv_16_8_t * key, + mac_address_t * mac, u32 * sw_if_index) +{ + mac_address_from_u64 (mac, key->key[0]); + *sw_if_index = key->key[1]; +} + +static void +gbp_endpoint_extract_key_ip_itf (const clib_bihash_kv_24_8_t * key, + ip46_address_t * ip, u32 * sw_if_index) +{ + ip->as_u64[0] = key->key[0]; + ip->as_u64[1] = key->key[1]; + *sw_if_index = key->key[2]; +} + +gbp_endpoint_t * +gbp_endpoint_find_ip (const ip46_address_t * ip, u32 fib_index) +{ + clib_bihash_kv_24_8_t key, value; + int rv; + + gbp_endpoint_mk_key_ip (ip, fib_index, &key); + + rv = clib_bihash_search_24_8 (&gbp_ep_db.ged_by_ip_rd, &key, &value); + + if (0 != rv) + return NULL; + + return (gbp_endpoint_get (value.value)); +} + +static void +gbp_endpoint_add_itf (u32 sw_if_index, index_t gei) +{ + vec_validate_init_empty (gbp_ep_db.ged_by_sw_if_index, sw_if_index, ~0); + + gbp_ep_db.ged_by_sw_if_index[sw_if_index] = gei; +} + +static bool +gbp_endpoint_add_mac (const mac_address_t * mac, u32 bd_index, index_t gei) +{ + clib_bihash_kv_16_8_t key; + int rv; + + gbp_endpoint_mk_key_mac (mac->bytes, bd_index, &key); + key.value = gei; + + rv = clib_bihash_add_del_16_8 (&gbp_ep_db.ged_by_mac_bd, &key, 1); + + + return (0 == rv); +} + +static bool +gbp_endpoint_add_ip (const ip46_address_t * ip, u32 fib_index, index_t gei) +{ + clib_bihash_kv_24_8_t key; + int rv; + + gbp_endpoint_mk_key_ip (ip, fib_index, &key); + key.value = gei; + + rv = clib_bihash_add_del_24_8 (&gbp_ep_db.ged_by_ip_rd, &key, 1); + + return (0 == rv); +} + +static void +gbp_endpoint_del_mac (const mac_address_t * mac, u32 bd_index) +{ + clib_bihash_kv_16_8_t key; + + gbp_endpoint_mk_key_mac (mac->bytes, bd_index, &key); + + clib_bihash_add_del_16_8 (&gbp_ep_db.ged_by_mac_bd, &key, 0); +} + +static void +gbp_endpoint_del_ip (const ip46_address_t * ip, u32 fib_index) +{ + clib_bihash_kv_24_8_t key; + + gbp_endpoint_mk_key_ip (ip, fib_index, &key); + + clib_bihash_add_del_24_8 (&gbp_ep_db.ged_by_ip_rd, &key, 0); +} + +static index_t +gbp_endpoint_index (const gbp_endpoint_t * ge) +{ + return (ge - gbp_endpoint_pool); +} + +static int +gbp_endpoint_ip_is_equal (const fib_prefix_t * fp, const ip46_address_t * ip) +{ + return (ip46_address_is_equal (ip, &fp->fp_addr)); +} + +static void +gbp_endpoint_ips_update (gbp_endpoint_t * ge, + const ip46_address_t * ips, + const gbp_route_domain_t * grd) +{ + const ip46_address_t *ip; + index_t gei, grdi; + + gei = gbp_endpoint_index (ge); + grdi = gbp_route_domain_index (grd); + + ASSERT ((ge->ge_key.gek_grd == INDEX_INVALID) || + (ge->ge_key.gek_grd == grdi)); + + vec_foreach (ip, ips) + { + if (~0 == vec_search_with_function (ge->ge_key.gek_ips, ip, + gbp_endpoint_ip_is_equal)) + { + fib_prefix_t *pfx; + + vec_add2 (ge->ge_key.gek_ips, pfx, 1); + fib_prefix_from_ip46_addr (ip, pfx); + + gbp_endpoint_add_ip (&pfx->fp_addr, + grd->grd_fib_index[pfx->fp_proto], gei); + } + ge->ge_key.gek_grd = grdi; + } +} + +static gbp_endpoint_t * +gbp_endpoint_alloc (const ip46_address_t * ips, + const gbp_route_domain_t * grd, + const mac_address_t * mac, + const gbp_bridge_domain_t * gbd) +{ + gbp_endpoint_t *ge; + index_t gei; + + pool_get_zero (gbp_endpoint_pool, ge); + + fib_node_init (&ge->ge_node, gbp_endpoint_fib_type); + gei = gbp_endpoint_index (ge); + ge->ge_key.gek_gbd = + ge->ge_key.gek_grd = ge->ge_fwd.gef_fib_index = INDEX_INVALID; + gbp_itf_hdl_reset (&ge->ge_fwd.gef_itf); + ge->ge_last_time = vlib_time_now (vlib_get_main ()); + ge->ge_key.gek_gbd = gbp_bridge_domain_index (gbd); + + if (NULL != mac) + { + mac_address_copy (&ge->ge_key.gek_mac, mac); + gbp_endpoint_add_mac (mac, gbd->gb_bd_index, gei); + } + gbp_endpoint_ips_update (ge, ips, grd); + + return (ge); +} + +static int +gbp_endpoint_loc_is_equal (gbp_endpoint_loc_t * a, gbp_endpoint_loc_t * b) +{ + return (a->gel_src == b->gel_src); +} + +static int +gbp_endpoint_loc_cmp_for_sort (gbp_endpoint_loc_t * a, gbp_endpoint_loc_t * b) +{ + return (a->gel_src - b->gel_src); +} + +static gbp_endpoint_loc_t * +gbp_endpoint_loc_find (gbp_endpoint_t * ge, gbp_endpoint_src_t src) +{ + gbp_endpoint_loc_t gel = { + .gel_src = src, + }; + u32 pos; + + pos = vec_search_with_function (ge->ge_locs, &gel, + gbp_endpoint_loc_is_equal); + + if (~0 != pos) + return (&ge->ge_locs[pos]); + + return NULL; +} + +static int +gbp_endpoint_loc_unlock (gbp_endpoint_t * ge, gbp_endpoint_loc_t * gel) +{ + u32 pos; + + gel->gel_locks--; + + if (0 == gel->gel_locks) + { + pos = gel - ge->ge_locs; + + vec_del1 (ge->ge_locs, pos); + if (vec_len (ge->ge_locs) > 1) + vec_sort_with_function (ge->ge_locs, gbp_endpoint_loc_cmp_for_sort); + + /* This could be the last lock, so don't access the EP from + * this point on */ + fib_node_unlock (&ge->ge_node); + + return (1); + } + return (0); +} + +static void +gbp_endpoint_loc_destroy (gbp_endpoint_loc_t * gel) +{ + gbp_endpoint_group_unlock (gel->gel_epg); + gbp_itf_unlock (&gel->gel_itf); +} + +static gbp_endpoint_loc_t * +gbp_endpoint_loc_find_or_add (gbp_endpoint_t * ge, gbp_endpoint_src_t src) +{ + gbp_endpoint_loc_t gel = { + .gel_src = src, + .gel_epg = INDEX_INVALID, + .gel_itf = GBP_ITF_HDL_INVALID, + .gel_locks = 0, + }; + u32 pos; + + pos = vec_search_with_function (ge->ge_locs, &gel, + gbp_endpoint_loc_is_equal); + + if (~0 == pos) + { + vec_add1 (ge->ge_locs, gel); + + if (vec_len (ge->ge_locs) > 1) + { + vec_sort_with_function (ge->ge_locs, gbp_endpoint_loc_cmp_for_sort); + + pos = vec_search_with_function (ge->ge_locs, &gel, + gbp_endpoint_loc_is_equal); + } + else + pos = 0; + + /* + * it's the sources and children that lock the endpoints + */ + fib_node_lock (&ge->ge_node); + } + + return (&ge->ge_locs[pos]); +} + +/** + * Find an EP inthe DBs and check that if we find it in the L2 DB + * it has the same IPs as this update + */ +static int +gbp_endpoint_find_for_update (const ip46_address_t * ips, + const gbp_route_domain_t * grd, + const mac_address_t * mac, + const gbp_bridge_domain_t * gbd, + gbp_endpoint_t ** ge) +{ + gbp_endpoint_t *l2_ge, *l3_ge, *tmp; + + l2_ge = l3_ge = NULL; + + if (NULL != mac && !mac_address_is_zero (mac)) + { + ASSERT (gbd); + l2_ge = gbp_endpoint_find_mac (mac->bytes, gbd->gb_bd_index); + } + if (NULL != ips && !ip46_address_is_zero (ips)) + { + const ip46_address_t *ip; + fib_protocol_t fproto; + + ASSERT (grd); + vec_foreach (ip, ips) + { + fproto = fib_proto_from_ip46 (ip46_address_get_type (ip)); + + tmp = gbp_endpoint_find_ip (ip, grd->grd_fib_index[fproto]); + + if (NULL == tmp) + /* not found */ + continue; + else if (NULL == l3_ge) + /* first match against an IP address */ + l3_ge = tmp; + else if (tmp == l3_ge) + /* another match against IP address that is the same endpoint */ + continue; + else + { + /* + * a match agains a different endpoint. + * this means the KEY of the EP is changing which is not allowed + */ + return (-1); + } + } + } + + if (NULL == l2_ge && NULL == l3_ge) + /* not found */ + *ge = NULL; + else if (NULL == l2_ge) + /* found at L3 */ + *ge = l3_ge; + else if (NULL == l3_ge) + /* found at L2 */ + *ge = l2_ge; + else + { + /* found both L3 and L2 - they must be the same else the KEY + * is changing + */ + if (l2_ge == l3_ge) + *ge = l2_ge; + else + return (-1); + } + + return (0); +} + +static gbp_endpoint_src_t +gbp_endpoint_get_best_src (const gbp_endpoint_t * ge) +{ + if (0 == vec_len (ge->ge_locs)) + return (GBP_ENDPOINT_SRC_MAX); + + return (ge->ge_locs[0].gel_src); +} + +static void +gbp_endpoint_n_learned (int n) +{ + gbp_n_learnt_endpoints += n; + + if (n > 0 && 1 == gbp_n_learnt_endpoints) + { + vlib_process_signal_event (vlib_get_main (), + gbp_scanner_node.index, + GBP_ENDPOINT_SCAN_START, 0); + } + if (n < 0 && 0 == gbp_n_learnt_endpoints) + { + vlib_process_signal_event (vlib_get_main (), + gbp_scanner_node.index, + GBP_ENDPOINT_SCAN_STOP, 0); + } +} + +static void +gbp_endpoint_loc_update (const gbp_endpoint_t * ge, + gbp_endpoint_loc_t * gel, + const gbp_bridge_domain_t * gb, + u32 sw_if_index, + index_t ggi, + gbp_endpoint_flags_t flags, + const ip46_address_t * tun_src, + const ip46_address_t * tun_dst) +{ + int was_learnt, is_learnt; + + gel->gel_locks++; + was_learnt = ! !(gel->gel_flags & GBP_ENDPOINT_FLAG_REMOTE); + gel->gel_flags = flags; + is_learnt = ! !(gel->gel_flags & GBP_ENDPOINT_FLAG_REMOTE); + + gbp_endpoint_n_learned (is_learnt - was_learnt); + + /* + * update the EPG + */ + gbp_endpoint_group_lock (ggi); + gbp_endpoint_group_unlock (gel->gel_epg); + gel->gel_epg = ggi; + + if (gel->gel_flags & GBP_ENDPOINT_FLAG_REMOTE) + { + if (NULL != tun_src) + ip46_address_copy (&gel->tun.gel_src, tun_src); + if (NULL != tun_dst) + ip46_address_copy (&gel->tun.gel_dst, tun_dst); + + if (ip46_address_is_multicast (&gel->tun.gel_src)) + { + /* + * we learnt the EP from the multicast tunnel. + * Create a unicast TEP from the packet's source + * and the fixed address of the BD's parent tunnel + */ + const gbp_vxlan_tunnel_t *gt; + + gt = gbp_vxlan_tunnel_get (gb->gb_vni); + + if (NULL != gt) + { + ip46_address_copy (&gel->tun.gel_src, >->gt_src); + sw_if_index = gt->gt_sw_if_index; + } + } + + /* + * the input interface may be the parent GBP-vxlan interface, + * create a child vlxan-gbp tunnel and use that as the endpoint's + * interface. + */ + gbp_itf_hdl_t old = gel->gel_itf; + + switch (gbp_vxlan_tunnel_get_type (sw_if_index)) + { + case GBP_VXLAN_TEMPLATE_TUNNEL: + gel->tun.gel_parent_sw_if_index = sw_if_index; + gel->gel_itf = gbp_vxlan_tunnel_clone_and_lock (sw_if_index, + &gel->tun.gel_src, + &gel->tun.gel_dst); + break; + case VXLAN_GBP_TUNNEL: + gel->tun.gel_parent_sw_if_index = + vxlan_gbp_tunnel_get_parent (sw_if_index); + gel->gel_itf = vxlan_gbp_tunnel_lock_itf (sw_if_index); + break; + } + + gbp_itf_unlock (&old); + } + else + { + gel->gel_itf = gbp_itf_l2_add_and_lock (sw_if_index, + ge->ge_key.gek_gbd); + } +} + +static void +gbb_endpoint_fwd_reset (gbp_endpoint_t * ge) +{ + const gbp_route_domain_t *grd; + const gbp_bridge_domain_t *gbd; + gbp_endpoint_fwd_t *gef; + const fib_prefix_t *pfx; + index_t *ai; + + gbd = gbp_bridge_domain_get (ge->ge_key.gek_gbd); + gef = &ge->ge_fwd; + + vec_foreach (pfx, ge->ge_key.gek_ips) + { + u32 fib_index; + + grd = gbp_route_domain_get (ge->ge_key.gek_grd); + fib_index = grd->grd_fib_index[pfx->fp_proto]; + + bd_add_del_ip_mac (gbd->gb_bd_index, fib_proto_to_ip46 (pfx->fp_proto), + &pfx->fp_addr, &ge->ge_key.gek_mac, 0); + + /* + * remove a host route + */ + if (gbp_endpoint_is_remote (ge)) + { + fib_table_entry_special_remove (fib_index, pfx, gbp_fib_source_hi); + } + + fib_table_entry_delete (fib_index, pfx, gbp_fib_source_low); + } + vec_foreach (ai, gef->gef_adjs) + { + adj_unlock (*ai); + } + + if (gbp_itf_hdl_is_valid (gef->gef_itf)) + { + l2fib_del_entry (ge->ge_key.gek_mac.bytes, + gbd->gb_bd_index, + gbp_itf_get_sw_if_index (gef->gef_itf)); + } + + gbp_itf_unlock (&gef->gef_itf); + vec_free (gef->gef_adjs); +} + +static void +gbb_endpoint_fwd_recalc (gbp_endpoint_t * ge) +{ + const gbp_bridge_domain_t *gbd; + const gbp_endpoint_group_t *gg; + const gbp_route_domain_t *grd; + gbp_endpoint_loc_t *gel; + gbp_endpoint_fwd_t *gef; + const fib_prefix_t *pfx; + index_t gei; + + /* + * locations are sort in source priority order + */ + gei = gbp_endpoint_index (ge); + gel = &ge->ge_locs[0]; + gef = &ge->ge_fwd; + gbd = gbp_bridge_domain_get (ge->ge_key.gek_gbd); + + gef->gef_flags = gel->gel_flags; + + if (INDEX_INVALID != gel->gel_epg) + { + gg = gbp_endpoint_group_get (gel->gel_epg); + gef->gef_sclass = gg->gg_sclass; + } + else + { + gg = NULL; + } + + gef->gef_itf = gbp_itf_clone_and_lock (gel->gel_itf); + + if (!mac_address_is_zero (&ge->ge_key.gek_mac)) + { + gbp_itf_l2_set_input_feature (gef->gef_itf, L2INPUT_FEAT_GBP_FWD); + + if (gbp_endpoint_is_remote (ge) || gbp_endpoint_is_external (ge)) + { + /* + * bridged packets to external endpoints should be classifed + * based on the EP's/BD's EPG + */ + gbp_itf_l2_set_output_feature (gef->gef_itf, + L2OUTPUT_FEAT_GBP_POLICY_MAC); + } + else + { + gbp_endpoint_add_itf (gbp_itf_get_sw_if_index (gef->gef_itf), gei); + gbp_itf_l2_set_output_feature (gef->gef_itf, + L2OUTPUT_FEAT_GBP_POLICY_PORT); + } + l2fib_add_entry (ge->ge_key.gek_mac.bytes, + gbd->gb_bd_index, + gbp_itf_get_sw_if_index (gef->gef_itf), + L2FIB_ENTRY_RESULT_FLAG_STATIC); + } + + vec_foreach (pfx, ge->ge_key.gek_ips) + { + ethernet_header_t *eth; + u32 ip_sw_if_index; + u32 fib_index; + u8 *rewrite; + index_t ai; + + rewrite = NULL; + grd = gbp_route_domain_get (ge->ge_key.gek_grd); + fib_index = grd->grd_fib_index[pfx->fp_proto]; + gef->gef_fib_index = fib_index; + + bd_add_del_ip_mac (gbd->gb_bd_index, fib_proto_to_ip46 (pfx->fp_proto), + &pfx->fp_addr, &ge->ge_key.gek_mac, 1); + + /* + * add a host route via the EPG's BVI we need this because the + * adj fib does not install, due to cover refinement check, since + * the BVI's prefix is /32 + */ + vec_validate (rewrite, sizeof (*eth) - 1); + eth = (ethernet_header_t *) rewrite; + + eth->type = clib_host_to_net_u16 ((pfx->fp_proto == FIB_PROTOCOL_IP4 ? + ETHERNET_TYPE_IP4 : + ETHERNET_TYPE_IP6)); + + if (gbp_endpoint_is_remote (ge)) + { + /* + * for dynamic EPs we must add the IP adjacency via the learned + * tunnel since the BD will not contain the EP's MAC since it was + * L3 learned. The dst MAC address used is the 'BD's MAC'. + */ + ip_sw_if_index = gbp_itf_get_sw_if_index (gef->gef_itf); + + mac_address_to_bytes (gbp_route_domain_get_local_mac (), + eth->src_address); + mac_address_to_bytes (gbp_route_domain_get_remote_mac (), + eth->dst_address); + } + else + { + /* + * for the static EPs we add the IP adjacency via the BVI + * knowing that the BD has the MAC address to route to and + * that policy will be applied on egress to the EP's port + */ + ip_sw_if_index = gbd->gb_bvi_sw_if_index; + + clib_memcpy (eth->src_address, + vnet_sw_interface_get_hw_address (vnet_get_main (), + ip_sw_if_index), + sizeof (eth->src_address)); + mac_address_to_bytes (&ge->ge_key.gek_mac, eth->dst_address); + } + + fib_table_entry_path_add (fib_index, pfx, + gbp_fib_source_low, + FIB_ENTRY_FLAG_NONE, + fib_proto_to_dpo (pfx->fp_proto), + &pfx->fp_addr, ip_sw_if_index, + ~0, 1, NULL, FIB_ROUTE_PATH_FLAG_NONE); + + ai = adj_nbr_add_or_lock_w_rewrite (pfx->fp_proto, + fib_proto_to_link (pfx->fp_proto), + &pfx->fp_addr, + ip_sw_if_index, rewrite); + vec_add1 (gef->gef_adjs, ai); + + /* + * if the endpoint is external then routed packet to it must be + * classifed to the BD's EPG. but this will happen anyway with + * the GBP_MAC classification. + */ + + if (NULL != gg) + { + if (gbp_endpoint_is_remote (ge)) + { + dpo_id_t policy_dpo = DPO_INVALID; + + /* + * interpose a policy DPO from the endpoint so that policy + * is applied + */ + gbp_policy_dpo_add_or_lock (fib_proto_to_dpo (pfx->fp_proto), + grd->grd_scope, + gg->gg_sclass, ~0, &policy_dpo); + + fib_table_entry_special_dpo_add (fib_index, pfx, + gbp_fib_source_hi, + FIB_ENTRY_FLAG_INTERPOSE, + &policy_dpo); + dpo_reset (&policy_dpo); + } + + /* + * 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 (gbp_endpoint_is_local (ge) && ~0 != gg->gg_uplink_sw_if_index) + { + gbp_endpoint_add_itf (gbp_itf_get_sw_if_index (gef->gef_itf), + gei); + if (FIB_PROTOCOL_IP4 == pfx->fp_proto) + ip4_neighbor_advertise (vlib_get_main (), + vnet_get_main (), + gg->gg_uplink_sw_if_index, + &pfx->fp_addr.ip4); + else + ip6_neighbor_advertise (vlib_get_main (), + vnet_get_main (), + gg->gg_uplink_sw_if_index, + &pfx->fp_addr.ip6); + } + } + } + + if (gbp_endpoint_is_external (ge)) + { + gbp_itf_l2_set_input_feature (gef->gef_itf, + L2INPUT_FEAT_GBP_LPM_CLASSIFY); + } + else if (gbp_endpoint_is_local (ge)) + { + /* + * non-remote endpoints (i.e. those not arriving on iVXLAN + * tunnels) need to be classifed based on the the input interface. + * We enable the GBP-FWD feature only if the group has an uplink + * interface (on which the GBP-FWD feature would send UU traffic). + * External endpoints get classified based on an LPM match + */ + l2input_feat_masks_t feats = L2INPUT_FEAT_GBP_SRC_CLASSIFY; + + if (NULL != gg && ~0 != gg->gg_uplink_sw_if_index) + feats |= L2INPUT_FEAT_GBP_FWD; + gbp_itf_l2_set_input_feature (gef->gef_itf, feats); + } + + /* + * update children with the new forwarding info + */ + fib_node_back_walk_ctx_t bw_ctx = { + .fnbw_reason = FIB_NODE_BW_REASON_FLAG_EVALUATE, + .fnbw_flags = FIB_NODE_BW_FLAG_FORCE_SYNC, + }; + + fib_walk_sync (gbp_endpoint_fib_type, gei, &bw_ctx); +} + +int +gbp_endpoint_update_and_lock (gbp_endpoint_src_t src, + u32 sw_if_index, + const ip46_address_t * ips, + const mac_address_t * mac, + index_t gbdi, index_t grdi, + sclass_t sclass, + gbp_endpoint_flags_t flags, + const ip46_address_t * tun_src, + const ip46_address_t * tun_dst, u32 * handle) +{ + gbp_bridge_domain_t *gbd; + gbp_endpoint_group_t *gg; + gbp_endpoint_src_t best; + gbp_route_domain_t *grd; + gbp_endpoint_loc_t *gel; + gbp_endpoint_t *ge; + index_t ggi, gei; + int rv; + + if (~0 == sw_if_index) + return (VNET_API_ERROR_INVALID_SW_IF_INDEX); + + ge = NULL; + gg = NULL; + + /* + * we need to determine the bridge-domain, either from the EPG or + * the BD passed + */ + if (SCLASS_INVALID != sclass) + { + ggi = gbp_endpoint_group_find (sclass); + + if (INDEX_INVALID == ggi) + return (VNET_API_ERROR_NO_SUCH_ENTRY); + + gg = gbp_endpoint_group_get (ggi); + gbdi = gg->gg_gbd; + grdi = gg->gg_rd; + } + else + { + if (INDEX_INVALID == gbdi) + return (VNET_API_ERROR_NO_SUCH_ENTRY); + if (INDEX_INVALID == grdi) + return (VNET_API_ERROR_NO_SUCH_FIB); + ggi = INDEX_INVALID; + } + + gbd = gbp_bridge_domain_get (gbdi); + grd = gbp_route_domain_get (grdi); + rv = gbp_endpoint_find_for_update (ips, grd, mac, gbd, &ge); + + if (0 != rv) + return (rv); + + if (NULL == ge) + { + ge = gbp_endpoint_alloc (ips, grd, mac, gbd); + } + else + { + gbp_endpoint_ips_update (ge, ips, grd); + } + + best = gbp_endpoint_get_best_src (ge); + gei = gbp_endpoint_index (ge); + gel = gbp_endpoint_loc_find_or_add (ge, src); + + gbp_endpoint_loc_update (ge, gel, gbd, sw_if_index, ggi, flags, + tun_src, tun_dst); + + if (src <= best) + { + /* + * either the best source has been updated or we have a new best source + */ + gbb_endpoint_fwd_reset (ge); + gbb_endpoint_fwd_recalc (ge); + } + else + { + /* + * an update to a lower priority source, so we need do nothing + */ + } + + if (handle) + *handle = gei; + + GBP_ENDPOINT_INFO ("update: %U", format_gbp_endpoint, gei); + + return (0); +} + +void +gbp_endpoint_unlock (gbp_endpoint_src_t src, index_t gei) +{ + gbp_endpoint_loc_t *gel, gel_copy; + gbp_endpoint_src_t best; + gbp_endpoint_t *ge; + int removed; + + if (pool_is_free_index (gbp_endpoint_pool, gei)) + return; + + GBP_ENDPOINT_INFO ("delete: %U", format_gbp_endpoint, gei); + + ge = gbp_endpoint_get (gei); + + gel = gbp_endpoint_loc_find (ge, src); + + if (NULL == gel) + return; + + /* + * lock the EP so we can control when it is deleted + */ + fib_node_lock (&ge->ge_node); + best = gbp_endpoint_get_best_src (ge); + + /* + * copy the location info since we'll lose it when it's removed from + * the vector + */ + clib_memcpy (&gel_copy, gel, sizeof (gel_copy)); + + /* + * remove the source we no longer need + */ + removed = gbp_endpoint_loc_unlock (ge, gel); + + if (src == best) + { + /* + * we have removed the old best source => recalculate fwding + */ + if (0 == vec_len (ge->ge_locs)) + { + /* + * if there are no more sources left, then we need only release + * the fwding resources held and then this EP is gawn. + */ + gbb_endpoint_fwd_reset (ge); + } + else + { + /* + * else there are more sources. release the old and get new + * fwding objects + */ + gbb_endpoint_fwd_reset (ge); + gbb_endpoint_fwd_recalc (ge); + } + } + /* + * else + * we removed a lower priority source so we need to do nothing + */ + + /* + * clear up any resources held by the source + */ + if (removed) + gbp_endpoint_loc_destroy (&gel_copy); + + /* + * remove the lock taken above + */ + fib_node_unlock (&ge->ge_node); + /* + * We may have removed the last source and so this EP is now TOAST + * DO NOTHING BELOW HERE + */ +} + +u32 +gbp_endpoint_child_add (index_t gei, + fib_node_type_t type, fib_node_index_t index) +{ + return (fib_node_child_add (gbp_endpoint_fib_type, gei, type, index)); +} + +void +gbp_endpoint_child_remove (index_t gei, u32 sibling) +{ + return (fib_node_child_remove (gbp_endpoint_fib_type, gei, sibling)); +} + +typedef struct gbp_endpoint_flush_ctx_t_ +{ + u32 sw_if_index; + gbp_endpoint_src_t src; + index_t *geis; +} gbp_endpoint_flush_ctx_t; + +static walk_rc_t +gbp_endpoint_flush_cb (index_t gei, void *args) +{ + gbp_endpoint_flush_ctx_t *ctx = args; + gbp_endpoint_loc_t *gel; + gbp_endpoint_t *ge; + + ge = gbp_endpoint_get (gei); + gel = gbp_endpoint_loc_find (ge, ctx->src); + + if ((NULL != gel) && ctx->sw_if_index == gel->tun.gel_parent_sw_if_index) + { + vec_add1 (ctx->geis, gei); + } + + return (WALK_CONTINUE); +} + +/** + * remove all learnt endpoints using the interface + */ +void +gbp_endpoint_flush (gbp_endpoint_src_t src, u32 sw_if_index) +{ + gbp_endpoint_flush_ctx_t ctx = { + .sw_if_index = sw_if_index, + .src = src, + }; + index_t *gei; + + GBP_ENDPOINT_INFO ("flush: %U %U", + format_gbp_endpoint_src, src, + format_vnet_sw_if_index_name, vnet_get_main (), + sw_if_index); + gbp_endpoint_walk (gbp_endpoint_flush_cb, &ctx); + + vec_foreach (gei, ctx.geis) + { + gbp_endpoint_unlock (src, *gei); + } + + vec_free (ctx.geis); +} + +void +gbp_endpoint_walk (gbp_endpoint_cb_t cb, void *ctx) +{ + u32 index; + + /* *INDENT-OFF* */ + pool_foreach_index (index, gbp_endpoint_pool) + { + if (!cb(index, ctx)) + break; + } + /* *INDENT-ON* */ +} + +static clib_error_t * +gbp_endpoint_cli (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + ip46_address_t ip = ip46_address_initializer, *ips = NULL; + mac_address_t mac = ZERO_MAC_ADDRESS; + vnet_main_t *vnm = vnet_get_main (); + u32 sclass = SCLASS_INVALID; + u32 handle = INDEX_INVALID; + u32 sw_if_index = ~0; + u32 flags = GBP_ENDPOINT_FLAG_NONE; + u8 add = 1; + int rv; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + ip46_address_reset (&ip); + + 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, "sclass %d", &sclass)) + ; + else if (unformat (input, "handle %d", &handle)) + ; + else if (unformat (input, "ip %U", unformat_ip4_address, &ip.ip4)) + vec_add1 (ips, ip); + else if (unformat (input, "ip %U", unformat_ip6_address, &ip.ip6)) + vec_add1 (ips, ip); + else if (unformat (input, "mac %U", unformat_mac_address, &mac)) + ; + else if (unformat (input, "flags 0x%x", &flags)) + ; + else + break; + } + + if (add) + { + if (~0 == sw_if_index) + return clib_error_return (0, "interface must be specified"); + if (SCLASS_INVALID == sclass) + return clib_error_return (0, "SCLASS must be specified"); + + rv = + gbp_endpoint_update_and_lock (GBP_ENDPOINT_SRC_CP, + sw_if_index, ips, &mac, + INDEX_INVALID, INDEX_INVALID, + sclass, flags, NULL, NULL, &handle); + + if (rv) + return clib_error_return (0, "GBP Endpoint update returned %d", rv); + else + vlib_cli_output (vm, "handle %d\n", handle); + } + else + { + if (INDEX_INVALID == handle) + return clib_error_return (0, "handle must be specified"); + + gbp_endpoint_unlock (GBP_ENDPOINT_SRC_CP, handle); + } + + vec_free (ips); + + return (NULL); +} + +/*? + * Configure a GBP Endpoint + * + * @cliexpar + * @cliexstart{gbp endpoint del <handle> | [add] <interface> sclass <SCLASS> ip <IP> mac <MAC> [flags <flags>]} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_endpoint_cli_node, static) = { + .path = "gbp endpoint", + .short_help = "gbp endpoint del <handle> | [add] <interface> sclass <SCLASS> ip <IP> mac <MAC> [flags <flags>]", + .function = gbp_endpoint_cli, +}; +/* *INDENT-ON* */ + +u8 * +format_gbp_endpoint_src (u8 * s, va_list * args) +{ + gbp_endpoint_src_t action = va_arg (*args, gbp_endpoint_src_t); + + switch (action) + { +#define _(v,a) case GBP_ENDPOINT_SRC_##v: return (format (s, "%s", a)); + foreach_gbp_endpoint_src +#undef _ + } + + return (format (s, "unknown")); +} + +static u8 * +format_gbp_endpoint_fwd (u8 * s, va_list * args) +{ + gbp_endpoint_fwd_t *gef = va_arg (*args, gbp_endpoint_fwd_t *); + + s = format (s, "fwd:"); + s = format (s, "\n itf:[%U]", format_gbp_itf_hdl, gef->gef_itf); + if (GBP_ENDPOINT_FLAG_NONE != gef->gef_flags) + { + s = format (s, " flags:%U", format_gbp_endpoint_flags, gef->gef_flags); + } + + return (s); +} + +static u8 * +format_gbp_endpoint_key (u8 * s, va_list * args) +{ + gbp_endpoint_key_t *gek = va_arg (*args, gbp_endpoint_key_t *); + const fib_prefix_t *pfx; + + s = format (s, "ips:["); + + vec_foreach (pfx, gek->gek_ips) + { + s = format (s, "%U, ", format_fib_prefix, pfx); + } + s = format (s, "]"); + + s = format (s, " mac:%U", format_mac_address_t, &gek->gek_mac); + + return (s); +} + +static u8 * +format_gbp_endpoint_loc (u8 * s, va_list * args) +{ + gbp_endpoint_loc_t *gel = va_arg (*args, gbp_endpoint_loc_t *); + + s = format (s, "%U", format_gbp_endpoint_src, gel->gel_src); + s = format (s, "\n EPG:%d [%U]", gel->gel_epg, + format_gbp_itf_hdl, gel->gel_itf); + + if (GBP_ENDPOINT_FLAG_NONE != gel->gel_flags) + { + s = format (s, " flags:%U", format_gbp_endpoint_flags, gel->gel_flags); + } + if (GBP_ENDPOINT_FLAG_REMOTE & gel->gel_flags) + { + s = format (s, " tun:["); + s = format (s, "parent:%U", format_vnet_sw_if_index_name, + vnet_get_main (), gel->tun.gel_parent_sw_if_index); + s = format (s, " {%U,%U}]", + format_ip46_address, &gel->tun.gel_src, IP46_TYPE_ANY, + format_ip46_address, &gel->tun.gel_dst, IP46_TYPE_ANY); + } + + return (s); +} + +u8 * +format_gbp_endpoint (u8 * s, va_list * args) +{ + index_t gei = va_arg (*args, index_t); + gbp_endpoint_loc_t *gel; + gbp_endpoint_t *ge; + + ge = gbp_endpoint_get (gei); + + s = format (s, "[@%d] %U", gei, format_gbp_endpoint_key, &ge->ge_key); + s = format (s, " last-time:[%f]", ge->ge_last_time); + + vec_foreach (gel, ge->ge_locs) + { + s = format (s, "\n %U", format_gbp_endpoint_loc, gel); + } + s = format (s, "\n %U", format_gbp_endpoint_fwd, &ge->ge_fwd); + + return s; +} + +static walk_rc_t +gbp_endpoint_show_one (index_t gei, void *ctx) +{ + vlib_main_t *vm; + + vm = ctx; + vlib_cli_output (vm, " %U", format_gbp_endpoint, gei); + + return (WALK_CONTINUE); +} + +static int +gbp_endpoint_walk_ip_itf (clib_bihash_kv_24_8_t * kvp, void *arg) +{ + ip46_address_t ip; + vlib_main_t *vm; + u32 sw_if_index; + + vm = arg; + + gbp_endpoint_extract_key_ip_itf (kvp, &ip, &sw_if_index); + + vlib_cli_output (vm, " {%U, %U} -> %d", + format_ip46_address, &ip, IP46_TYPE_ANY, + format_vnet_sw_if_index_name, vnet_get_main (), + sw_if_index, kvp->value); + return (BIHASH_WALK_CONTINUE); +} + +static int +gbp_endpoint_walk_mac_itf (clib_bihash_kv_16_8_t * kvp, void *arg) +{ + mac_address_t mac; + vlib_main_t *vm; + u32 sw_if_index; + + vm = arg; + + gbp_endpoint_extract_key_mac_itf (kvp, &mac, &sw_if_index); + + vlib_cli_output (vm, " {%U, %U} -> %d", + format_mac_address_t, &mac, + format_vnet_sw_if_index_name, vnet_get_main (), + sw_if_index, kvp->value); + return (BIHASH_WALK_CONTINUE); +} + +static clib_error_t * +gbp_endpoint_show (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + u32 show_dbs, handle; + + handle = INDEX_INVALID; + show_dbs = 0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "%d", &handle)) + ; + else if (unformat (input, "db")) + show_dbs = 1; + else + break; + } + + if (INDEX_INVALID != handle) + { + vlib_cli_output (vm, "%U", format_gbp_endpoint, handle); + } + else if (show_dbs) + { + vlib_cli_output (vm, "\nDatabases:"); + clib_bihash_foreach_key_value_pair_24_8 (&gbp_ep_db.ged_by_ip_rd, + gbp_endpoint_walk_ip_itf, vm); + clib_bihash_foreach_key_value_pair_16_8 + (&gbp_ep_db.ged_by_mac_bd, gbp_endpoint_walk_mac_itf, vm); + } + else + { + vlib_cli_output (vm, "Endpoints:"); + gbp_endpoint_walk (gbp_endpoint_show_one, vm); + } + + 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 void +gbp_endpoint_check (index_t gei, f64 start_time) +{ + gbp_endpoint_group_t *gg; + gbp_endpoint_loc_t *gel; + gbp_endpoint_t *ge; + + ge = gbp_endpoint_get (gei); + gel = gbp_endpoint_loc_find (ge, GBP_ENDPOINT_SRC_DP); + + if (NULL != gel) + { + gg = gbp_endpoint_group_get (gel->gel_epg); + + if ((start_time - ge->ge_last_time) > + gg->gg_retention.remote_ep_timeout) + { + gbp_endpoint_unlock (GBP_ENDPOINT_SRC_DP, gei); + } + } +} + +static void +gbp_endpoint_scan_l2 (vlib_main_t * vm) +{ + clib_bihash_16_8_t *gte_table = &gbp_ep_db.ged_by_mac_bd; + f64 last_start, start_time, delta_t; + int i, j, k; + + if (!gte_table->instantiated) + return; + + delta_t = 0; + last_start = start_time = vlib_time_now (vm); + + for (i = 0; i < gte_table->nbuckets; i++) + { + clib_bihash_bucket_16_8_t *b; + clib_bihash_value_16_8_t *v; + + /* allow no more than 20us without a pause */ + delta_t = vlib_time_now (vm) - last_start; + if (delta_t > 20e-6) + { + /* suspend for 100 us */ + vlib_process_suspend (vm, 100e-6); + last_start = vlib_time_now (vm); + } + + b = clib_bihash_get_bucket_16_8 (gte_table, i); + if (clib_bihash_bucket_is_empty_16_8 (b)) + continue; + v = clib_bihash_get_value_16_8 (gte_table, b->offset); + + for (j = 0; j < (1 << b->log2_pages); j++) + { + for (k = 0; k < BIHASH_KVP_PER_PAGE; k++) + { + if (clib_bihash_is_free_16_8 (&v->kvp[k])) + continue; + + gbp_endpoint_check (v->kvp[k].value, start_time); + + /* + * Note: we may have just freed the bucket's backing + * storage, so check right here... + */ + if (clib_bihash_bucket_is_empty_16_8 (b)) + goto doublebreak; + } + v++; + } + doublebreak: + ; + } +} + +static void +gbp_endpoint_scan_l3 (vlib_main_t * vm) +{ + clib_bihash_24_8_t *gte_table = &gbp_ep_db.ged_by_ip_rd; + f64 last_start, start_time, delta_t; + int i, j, k; + + if (!gte_table->instantiated) + return; + + delta_t = 0; + last_start = start_time = vlib_time_now (vm); + + for (i = 0; i < gte_table->nbuckets; i++) + { + clib_bihash_bucket_24_8_t *b; + clib_bihash_value_24_8_t *v; + + /* allow no more than 20us without a pause */ + delta_t = vlib_time_now (vm) - last_start; + if (delta_t > 20e-6) + { + /* suspend for 100 us */ + vlib_process_suspend (vm, 100e-6); + last_start = vlib_time_now (vm); + } + + b = clib_bihash_get_bucket_24_8 (gte_table, i); + if (clib_bihash_bucket_is_empty_24_8 (b)) + continue; + v = clib_bihash_get_value_24_8 (gte_table, b->offset); + + for (j = 0; j < (1 << b->log2_pages); j++) + { + for (k = 0; k < BIHASH_KVP_PER_PAGE; k++) + { + if (clib_bihash_is_free_24_8 (&v->kvp[k])) + continue; + + gbp_endpoint_check (v->kvp[k].value, start_time); + + /* + * Note: we may have just freed the bucket's backing + * storage, so check right here... + */ + if (clib_bihash_bucket_is_empty_24_8 (b)) + goto doublebreak; + } + v++; + } + doublebreak: + ; + } +} + +void +gbp_endpoint_scan (vlib_main_t * vm) +{ + gbp_endpoint_scan_l2 (vm); + gbp_endpoint_scan_l3 (vm); +} + +static fib_node_t * +gbp_endpoint_get_node (fib_node_index_t index) +{ + gbp_endpoint_t *ge; + + ge = gbp_endpoint_get (index); + + return (&ge->ge_node); +} + +static gbp_endpoint_t * +gbp_endpoint_from_fib_node (fib_node_t * node) +{ + ASSERT (gbp_endpoint_fib_type == node->fn_type); + return ((gbp_endpoint_t *) node); +} + +static void +gbp_endpoint_last_lock_gone (fib_node_t * node) +{ + const gbp_bridge_domain_t *gbd; + const gbp_route_domain_t *grd; + const fib_prefix_t *pfx; + gbp_endpoint_t *ge; + + ge = gbp_endpoint_from_fib_node (node); + + ASSERT (0 == vec_len (ge->ge_locs)); + + gbd = gbp_bridge_domain_get (ge->ge_key.gek_gbd); + + /* + * we have removed the last source. this EP is toast + */ + if (INDEX_INVALID != ge->ge_key.gek_gbd) + { + gbp_endpoint_del_mac (&ge->ge_key.gek_mac, gbd->gb_bd_index); + } + vec_foreach (pfx, ge->ge_key.gek_ips) + { + grd = gbp_route_domain_get (ge->ge_key.gek_grd); + gbp_endpoint_del_ip (&pfx->fp_addr, grd->grd_fib_index[pfx->fp_proto]); + } + pool_put (gbp_endpoint_pool, ge); +} + +static fib_node_back_walk_rc_t +gbp_endpoint_back_walk_notify (fib_node_t * node, + fib_node_back_walk_ctx_t * ctx) +{ + ASSERT (0); + + return (FIB_NODE_BACK_WALK_CONTINUE); +} + +/* + * The FIB path's graph node virtual function table + */ +static const fib_node_vft_t gbp_endpoint_vft = { + .fnv_get = gbp_endpoint_get_node, + .fnv_last_lock = gbp_endpoint_last_lock_gone, + .fnv_back_walk = gbp_endpoint_back_walk_notify, + // .fnv_mem_show = fib_path_memory_show, +}; + +static clib_error_t * +gbp_endpoint_init (vlib_main_t * vm) +{ +#define GBP_EP_HASH_NUM_BUCKETS (2 * 1024) +#define GBP_EP_HASH_MEMORY_SIZE (1 << 20) + + clib_bihash_init_24_8 (&gbp_ep_db.ged_by_ip_rd, + "GBP Endpoints - IP/RD", + GBP_EP_HASH_NUM_BUCKETS, GBP_EP_HASH_MEMORY_SIZE); + + clib_bihash_init_16_8 (&gbp_ep_db.ged_by_mac_bd, + "GBP Endpoints - MAC/BD", + GBP_EP_HASH_NUM_BUCKETS, GBP_EP_HASH_MEMORY_SIZE); + + gbp_ep_logger = vlib_log_register_class ("gbp", "ep"); + gbp_endpoint_fib_type = fib_node_register_new_type (&gbp_endpoint_vft); + gbp_fib_source_hi = fib_source_allocate ("gbp-endpoint-hi", + FIB_SOURCE_PRIORITY_HI, + FIB_SOURCE_BH_SIMPLE); + gbp_fib_source_low = fib_source_allocate ("gbp-endpoint-low", + FIB_SOURCE_PRIORITY_LOW, + FIB_SOURCE_BH_SIMPLE); + + return (NULL); +} + +VLIB_INIT_FUNCTION (gbp_endpoint_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_endpoint.h b/extras/deprecated/plugins/gbp/gbp_endpoint.h new file mode 100644 index 00000000000..3155e7be4e0 --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_endpoint.h @@ -0,0 +1,376 @@ +/* + * 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 <plugins/gbp/gbp_itf.h> +#include <vnet/ip/ip.h> +#include <vnet/ethernet/mac_address.h> + +#include <vppinfra/bihash_16_8.h> +#include <vppinfra/bihash_template.h> +#include <vppinfra/bihash_24_8.h> +#include <vppinfra/bihash_template.h> + +/** + * Flags for each endpoint + */ +typedef enum gbp_endpoint_attr_t_ +{ + GBP_ENDPOINT_ATTR_FIRST = 0, + GBP_ENDPOINT_ATTR_BOUNCE = GBP_ENDPOINT_ATTR_FIRST, + GBP_ENDPOINT_ATTR_REMOTE, + GBP_ENDPOINT_ATTR_LEARNT, + GBP_ENDPOINT_ATTR_EXTERNAL, + GBP_ENDPOINT_ATTR_LAST, +} gbp_endpoint_attr_t; + +typedef enum gbp_endpoint_flags_t_ +{ + GBP_ENDPOINT_FLAG_NONE = 0, + GBP_ENDPOINT_FLAG_BOUNCE = (1 << GBP_ENDPOINT_ATTR_BOUNCE), + GBP_ENDPOINT_FLAG_REMOTE = (1 << GBP_ENDPOINT_ATTR_REMOTE), + GBP_ENDPOINT_FLAG_LEARNT = (1 << GBP_ENDPOINT_ATTR_LEARNT), + GBP_ENDPOINT_FLAG_EXTERNAL = (1 << GBP_ENDPOINT_ATTR_EXTERNAL), +} gbp_endpoint_flags_t; + +#define GBP_ENDPOINT_ATTR_NAMES { \ + [GBP_ENDPOINT_ATTR_BOUNCE] = "bounce", \ + [GBP_ENDPOINT_ATTR_REMOTE] = "remote", \ + [GBP_ENDPOINT_ATTR_LEARNT] = "learnt", \ + [GBP_ENDPOINT_ATTR_EXTERNAL] = "external", \ +} + +extern u8 *format_gbp_endpoint_flags (u8 * s, va_list * args); + +/** + * Sources of Endpoints in priority order. The best (lowest value) source + * provides the forwarding information. + * Data-plane takes preference because the CP data is not always complete, + * it may not have the sclass. + */ +#define foreach_gbp_endpoint_src \ + _(DP, "data-plane") \ + _(CP, "control-plane") \ + _(RR, "recursive-resolution") + +typedef enum gbp_endpoint_src_t_ +{ +#define _(v,s) GBP_ENDPOINT_SRC_##v, + foreach_gbp_endpoint_src +#undef _ +} gbp_endpoint_src_t; + +#define GBP_ENDPOINT_SRC_MAX (GBP_ENDPOINT_SRC_RR+1) + +extern u8 *format_gbp_endpoint_src (u8 * s, va_list * args); + +/** + * This is the identity of an endpoint, as such it is information + * about an endpoint that is idempotent. + * The ID is used to add the EP into the various data-bases for retrieval. + */ +typedef struct gbp_endpoint_key_t_ +{ + /** + * A vector of ip addresses that belong to the endpoint. + * Together with the route EPG's RD this forms the EP's L3 key + */ + fib_prefix_t *gek_ips; + + /** + * MAC address of the endpoint. + * Together with the route EPG's BD this forms the EP's L2 key + */ + mac_address_t gek_mac; + + /** + * Index of the Bridge-Domain + */ + index_t gek_gbd; + + /** + * Index of the Route-Domain + */ + index_t gek_grd; +} gbp_endpoint_key_t; + +/** + * Information about the location of the endpoint provided by a source + * of endpoints + */ +typedef struct gbp_endpoint_loc_t_ +{ + /** + * The source providing this location information + */ + gbp_endpoint_src_t gel_src; + + /** + * The interface on which the EP is connected + */ + gbp_itf_hdl_t gel_itf; + + /** + * Endpoint flags + */ + gbp_endpoint_flags_t gel_flags; + + /** + * Endpoint Group. + */ + index_t gel_epg; + + /** + * number of times this source has locked this + */ + u32 gel_locks; + + /** + * Tunnel info for remote endpoints + */ + struct + { + u32 gel_parent_sw_if_index; + ip46_address_t gel_src; + ip46_address_t gel_dst; + } tun; +} gbp_endpoint_loc_t; + +/** + * And endpoints current forwarding state + */ +typedef struct gbp_endpoint_fwd_t_ +{ + /** + * The interface on which the EP is connected + */ + gbp_itf_hdl_t gef_itf; + + /** + * The L3 adj, if created + */ + index_t *gef_adjs; + + /** + * Endpoint Group's sclass. cached for fast DP access. + */ + sclass_t gef_sclass; + + /** + * FIB index the EP is in + */ + u32 gef_fib_index; + + gbp_endpoint_flags_t gef_flags; +} gbp_endpoint_fwd_t; + +/** + * A Group Based Policy Endpoint. + * This is typically a VM or container. If the endpoint is local (i.e. on + * the same compute node as VPP) then there is one interface per-endpoint. + * If the EP is remote,e.g. reachable over a [vxlan] tunnel, then there + * will be multiple EPs reachable over the tunnel and they can be distinguished + * via either their MAC or IP Address[es]. + */ +typedef struct gbp_endpoint_t_ +{ + /** + * A FIB node that allows the tracking of children. + */ + fib_node_t ge_node; + + /** + * The key/ID of this EP + */ + gbp_endpoint_key_t ge_key; + + /** + * Location information provided by the various sources. + * These are sorted based on source priority. + */ + gbp_endpoint_loc_t *ge_locs; + + gbp_endpoint_fwd_t ge_fwd; + + /** + * The last time a packet from seen from this end point + */ + f64 ge_last_time; +} gbp_endpoint_t; + +extern u8 *format_gbp_endpoint (u8 * s, va_list * args); + +/** + * GBP Endpoint Databases + */ +typedef struct gbp_ep_by_ip_itf_db_t_ +{ + index_t *ged_by_sw_if_index; + clib_bihash_24_8_t ged_by_ip_rd; + clib_bihash_16_8_t ged_by_mac_bd; +} gbp_ep_db_t; + +extern int gbp_endpoint_update_and_lock (gbp_endpoint_src_t src, + u32 sw_if_index, + const ip46_address_t * ip, + const mac_address_t * mac, + index_t gbd, index_t grd, + sclass_t sclass, + gbp_endpoint_flags_t flags, + const ip46_address_t * tun_src, + const ip46_address_t * tun_dst, + u32 * handle); +extern void gbp_endpoint_unlock (gbp_endpoint_src_t src, index_t gbpei); +extern u32 gbp_endpoint_child_add (index_t gei, + fib_node_type_t type, + fib_node_index_t index); +extern void gbp_endpoint_child_remove (index_t gei, u32 sibling); + +typedef walk_rc_t (*gbp_endpoint_cb_t) (index_t gbpei, void *ctx); +extern void gbp_endpoint_walk (gbp_endpoint_cb_t cb, void *ctx); +extern void gbp_endpoint_scan (vlib_main_t * vm); +extern int gbp_endpoint_is_remote (const gbp_endpoint_t * ge); +extern int gbp_endpoint_is_local (const gbp_endpoint_t * ge); +extern int gbp_endpoint_is_external (const gbp_endpoint_t * ge); +extern int gbp_endpoint_is_learnt (const gbp_endpoint_t * ge); + + +extern void gbp_endpoint_flush (gbp_endpoint_src_t src, u32 sw_if_index); + +/** + * DP functions and databases + */ +extern gbp_ep_db_t gbp_ep_db; +extern gbp_endpoint_t *gbp_endpoint_pool; + +/** + * Get the endpoint from a port/interface + */ +always_inline gbp_endpoint_t * +gbp_endpoint_get (index_t gbpei) +{ + return (pool_elt_at_index (gbp_endpoint_pool, gbpei)); +} + +static_always_inline void +gbp_endpoint_mk_key_mac (const u8 * mac, + u32 bd_index, clib_bihash_kv_16_8_t * key) +{ + key->key[0] = ethernet_mac_address_u64 (mac); + key->key[1] = bd_index; +} + +static_always_inline gbp_endpoint_t * +gbp_endpoint_find_mac (const u8 * mac, u32 bd_index) +{ + clib_bihash_kv_16_8_t key, value; + int rv; + + gbp_endpoint_mk_key_mac (mac, bd_index, &key); + + rv = clib_bihash_search_16_8 (&gbp_ep_db.ged_by_mac_bd, &key, &value); + + if (0 != rv) + return NULL; + + return (gbp_endpoint_get (value.value)); +} + +static_always_inline void +gbp_endpoint_mk_key_ip (const ip46_address_t * ip, + u32 fib_index, clib_bihash_kv_24_8_t * key) +{ + key->key[0] = ip->as_u64[0]; + key->key[1] = ip->as_u64[1]; + key->key[2] = fib_index; +} + +static_always_inline void +gbp_endpoint_mk_key_ip4 (const ip4_address_t * ip, + u32 fib_index, clib_bihash_kv_24_8_t * key) +{ + const ip46_address_t a = { + .ip4 = *ip, + }; + gbp_endpoint_mk_key_ip (&a, fib_index, key); +} + +static_always_inline gbp_endpoint_t * +gbp_endpoint_find_ip4 (const ip4_address_t * ip, u32 fib_index) +{ + clib_bihash_kv_24_8_t key, value; + int rv; + + gbp_endpoint_mk_key_ip4 (ip, fib_index, &key); + + rv = clib_bihash_search_24_8 (&gbp_ep_db.ged_by_ip_rd, &key, &value); + + if (0 != rv) + return NULL; + + return (gbp_endpoint_get (value.value)); +} + +static_always_inline void +gbp_endpoint_mk_key_ip6 (const ip6_address_t * ip, + u32 fib_index, clib_bihash_kv_24_8_t * key) +{ + key->key[0] = ip->as_u64[0]; + key->key[1] = ip->as_u64[1]; + key->key[2] = fib_index; +} + +static_always_inline gbp_endpoint_t * +gbp_endpoint_find_ip6 (const ip6_address_t * ip, u32 fib_index) +{ + clib_bihash_kv_24_8_t key, value; + int rv; + + gbp_endpoint_mk_key_ip6 (ip, fib_index, &key); + + rv = clib_bihash_search_24_8 (&gbp_ep_db.ged_by_ip_rd, &key, &value); + + if (0 != rv) + return NULL; + + return (gbp_endpoint_get (value.value)); +} + +static_always_inline gbp_endpoint_t * +gbp_endpoint_find_itf (u32 sw_if_index) +{ + index_t gei; + + gei = gbp_ep_db.ged_by_sw_if_index[sw_if_index]; + + if (INDEX_INVALID != gei) + return (gbp_endpoint_get (gei)); + + return (NULL); +} + + +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_endpoint_group.c b/extras/deprecated/plugins/gbp/gbp_endpoint_group.c new file mode 100644 index 00000000000..b9044378e3b --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_endpoint_group.c @@ -0,0 +1,402 @@ +/* + * 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 <plugins/gbp/gbp_bridge_domain.h> +#include <plugins/gbp/gbp_route_domain.h> +#include <plugins/gbp/gbp_itf.h> + +#include <vnet/dpo/dvr_dpo.h> +#include <vnet/fib/fib_table.h> +#include <vnet/l2/l2_input.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; + +/** + * Map sclass to EPG + */ +uword *gbp_epg_sclass_db; + +vlib_log_class_t gg_logger; + +#define GBP_EPG_DBG(...) \ + vlib_log_debug (gg_logger, __VA_ARGS__); + +gbp_endpoint_group_t * +gbp_endpoint_group_get (index_t i) +{ + return (pool_elt_at_index (gbp_endpoint_group_pool, i)); +} + +void +gbp_endpoint_group_lock (index_t ggi) +{ + gbp_endpoint_group_t *gg; + + if (INDEX_INVALID == ggi) + return; + + gg = gbp_endpoint_group_get (ggi); + gg->gg_locks++; +} + +index_t +gbp_endpoint_group_find (sclass_t sclass) +{ + uword *p; + + p = hash_get (gbp_endpoint_group_db.gg_hash_sclass, sclass); + + if (NULL != p) + return p[0]; + + return (INDEX_INVALID); +} + +int +gbp_endpoint_group_add_and_lock (vnid_t vnid, + u16 sclass, + u32 bd_id, + u32 rd_id, + u32 uplink_sw_if_index, + const gbp_endpoint_retention_t * retention) +{ + gbp_endpoint_group_t *gg; + index_t ggi; + + ggi = gbp_endpoint_group_find (sclass); + + if (INDEX_INVALID == ggi) + { + fib_protocol_t fproto; + index_t gbi, grdi; + + gbi = gbp_bridge_domain_find_and_lock (bd_id); + + if (~0 == gbi) + return (VNET_API_ERROR_BD_NOT_MODIFIABLE); + + grdi = gbp_route_domain_find_and_lock (rd_id); + + if (~0 == grdi) + { + gbp_bridge_domain_unlock (gbi); + return (VNET_API_ERROR_NO_SUCH_FIB); + } + + pool_get_zero (gbp_endpoint_group_pool, gg); + + gg->gg_vnid = vnid; + gg->gg_rd = grdi; + gg->gg_gbd = gbi; + + gg->gg_uplink_sw_if_index = uplink_sw_if_index; + gbp_itf_hdl_reset (&gg->gg_uplink_itf); + gg->gg_locks = 1; + gg->gg_sclass = sclass; + gg->gg_retention = *retention; + + if (SCLASS_INVALID != gg->gg_sclass) + hash_set (gbp_epg_sclass_db, gg->gg_sclass, gg->gg_vnid); + + /* + * an egress DVR dpo for internal subnets to use when sending + * on the uplink interface + */ + if (~0 != gg->gg_uplink_sw_if_index) + { + FOR_EACH_FIB_IP_PROTOCOL (fproto) + { + dvr_dpo_add_or_lock (uplink_sw_if_index, + fib_proto_to_dpo (fproto), + &gg->gg_dpo[fproto]); + } + + /* + * Add the uplink to the BD + * packets direct from the uplink have had policy applied + */ + gg->gg_uplink_itf = + gbp_itf_l2_add_and_lock (gg->gg_uplink_sw_if_index, gbi); + + gbp_itf_l2_set_input_feature (gg->gg_uplink_itf, + L2INPUT_FEAT_GBP_NULL_CLASSIFY); + } + + hash_set (gbp_endpoint_group_db.gg_hash_sclass, + gg->gg_sclass, gg - gbp_endpoint_group_pool); + } + else + { + gg = gbp_endpoint_group_get (ggi); + gg->gg_locks++; + } + + GBP_EPG_DBG ("add: %U", format_gbp_endpoint_group, gg); + + return (0); +} + +void +gbp_endpoint_group_unlock (index_t ggi) +{ + gbp_endpoint_group_t *gg; + + if (INDEX_INVALID == ggi) + return; + + gg = gbp_endpoint_group_get (ggi); + + gg->gg_locks--; + + if (0 == gg->gg_locks) + { + fib_protocol_t fproto; + + gg = pool_elt_at_index (gbp_endpoint_group_pool, ggi); + + gbp_itf_unlock (&gg->gg_uplink_itf); + + FOR_EACH_FIB_IP_PROTOCOL (fproto) + { + dpo_reset (&gg->gg_dpo[fproto]); + } + gbp_bridge_domain_unlock (gg->gg_gbd); + gbp_route_domain_unlock (gg->gg_rd); + + if (SCLASS_INVALID != gg->gg_sclass) + hash_unset (gbp_epg_sclass_db, gg->gg_sclass); + hash_unset (gbp_endpoint_group_db.gg_hash_sclass, gg->gg_sclass); + + pool_put (gbp_endpoint_group_pool, gg); + } +} + +int +gbp_endpoint_group_delete (sclass_t sclass) +{ + index_t ggi; + + ggi = gbp_endpoint_group_find (sclass); + + if (INDEX_INVALID != ggi) + { + GBP_EPG_DBG ("del: %U", format_gbp_endpoint_group, + gbp_endpoint_group_get (ggi)); + gbp_endpoint_group_unlock (ggi); + + return (0); + } + + return (VNET_API_ERROR_NO_SUCH_ENTRY); +} + +u32 +gbp_endpoint_group_get_bd_id (const gbp_endpoint_group_t * gg) +{ + const gbp_bridge_domain_t *gb; + + gb = gbp_bridge_domain_get (gg->gg_gbd); + + return (gb->gb_bd_id); +} + +index_t +gbp_endpoint_group_get_fib_index (const gbp_endpoint_group_t * gg, + fib_protocol_t fproto) +{ + const gbp_route_domain_t *grd; + + grd = gbp_route_domain_get (gg->gg_rd); + + return (grd->grd_fib_index[fproto]); +} + +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) +{ + gbp_endpoint_retention_t retention = { 0 }; + vnid_t vnid = VNID_INVALID, sclass; + vnet_main_t *vnm = vnet_get_main (); + 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", &vnid)) + ; + else if (unformat (input, "sclass %d", &sclass)) + ; + else if (unformat (input, "bd %d", &bd_id)) + ; + else if (unformat (input, "rd %d", &rd_id)) + ; + else + break; + } + + if (VNID_INVALID == vnid) + return clib_error_return (0, "EPG-ID must be specified"); + + if (add) + { + 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_and_lock (vnid, sclass, bd_id, rd_id, + uplink_sw_if_index, &retention); + } + else + gbp_endpoint_group_delete (vnid); + + return (NULL); +} + +/*? + * Configure a GBP Endpoint Group + * + * @cliexpar + * @cliexstart{gbp endpoint-group [del] epg <ID> bd <ID> rd <ID> [sclass <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> rd <ID> [sclass <ID>] [<interface>]", + .function = gbp_endpoint_group_cli, +}; + +static u8 * +format_gbp_endpoint_retention (u8 * s, va_list * args) +{ + gbp_endpoint_retention_t *rt = va_arg (*args, gbp_endpoint_retention_t*); + + s = format (s, "[remote-EP-timeout:%d]", rt->remote_ep_timeout); + + return (s); +} + +u8 * +format_gbp_endpoint_group (u8 * s, va_list * args) +{ + gbp_endpoint_group_t *gg = va_arg (*args, gbp_endpoint_group_t*); + + if (NULL != gg) + s = format (s, "[%d] %d, sclass:%d bd:%d rd:%d uplink:%U retention:%U locks:%d", + gg - gbp_endpoint_group_pool, + gg->gg_vnid, + gg->gg_sclass, + gg->gg_gbd, + gg->gg_rd, + format_gbp_itf_hdl, gg->gg_uplink_itf, + format_gbp_endpoint_retention, &gg->gg_retention, + gg->gg_locks); + else + s = format (s, "NULL"); + + return (s); +} + +static int +gbp_endpoint_group_show_one (gbp_endpoint_group_t *gg, void *ctx) +{ + vlib_main_t *vm; + + vm = ctx; + vlib_cli_output (vm, " %U",format_gbp_endpoint_group, gg); + + 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* */ + +static clib_error_t * +gbp_endpoint_group_init (vlib_main_t * vm) +{ + gg_logger = vlib_log_register_class ("gbp", "epg"); + + return (NULL); +} + +VLIB_INIT_FUNCTION (gbp_endpoint_group_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_endpoint_group.h b/extras/deprecated/plugins/gbp/gbp_endpoint_group.h new file mode 100644 index 00000000000..c5fdff8463d --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_endpoint_group.h @@ -0,0 +1,166 @@ +/* + * 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 <plugins/gbp/gbp_itf.h> + +#include <vnet/fib/fib_types.h> + +/** + * Endpoint Retnetion Policy + */ +typedef struct gbp_endpoint_retention_t_ +{ + /** Aging timeout for remote endpoints */ + u32 remote_ep_timeout; +} gbp_endpoint_retention_t; + +/** + * An Endpoint Group representation + */ +typedef struct gpb_endpoint_group_t_ +{ + /** + * ID + */ + vnid_t gg_vnid; + + /** + * Sclass. Could be unset => ~0 + */ + u16 gg_sclass; + + /** + * Bridge-domain ID the EPG is in + */ + index_t gg_gbd; + + /** + * route-domain/IP-table ID the EPG is in + */ + index_t gg_rd; + + /** + * Is the EPG an external/NAT + */ + u8 gg_is_ext; + + /** + * the uplink interface dedicated to the EPG + */ + u32 gg_uplink_sw_if_index; + gbp_itf_hdl_t gg_uplink_itf; + + /** + * The DPO used in the L3 path for forwarding internal subnets + */ + dpo_id_t gg_dpo[FIB_PROTOCOL_IP_MAX]; + + /** + * Locks/references to this EPG + */ + u32 gg_locks; + + /** + * EP retention policy + */ + gbp_endpoint_retention_t gg_retention; +} gbp_endpoint_group_t; + +/** + * EPG DB, key'd on EGP-ID + */ +typedef struct gbp_endpoint_group_db_t_ +{ + uword *gg_hash_sclass; +} gbp_endpoint_group_db_t; + +extern int gbp_endpoint_group_add_and_lock (vnid_t vnid, + u16 sclass, + u32 bd_id, + u32 rd_id, + u32 uplink_sw_if_index, + const gbp_endpoint_retention_t * + retention); +extern index_t gbp_endpoint_group_find (sclass_t sclass); +extern int gbp_endpoint_group_delete (sclass_t sclass); +extern void gbp_endpoint_group_unlock (index_t index); +extern void gbp_endpoint_group_lock (index_t index); +extern u32 gbp_endpoint_group_get_bd_id (const gbp_endpoint_group_t *); + +extern gbp_endpoint_group_t *gbp_endpoint_group_get (index_t i); +extern index_t gbp_endpoint_group_get_fib_index (const gbp_endpoint_group_t * + gg, fib_protocol_t fproto); + +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 u8 *format_gbp_endpoint_group (u8 * s, va_list * args); + +/** + * DP functions and databases + */ +extern gbp_endpoint_group_db_t gbp_endpoint_group_db; +extern gbp_endpoint_group_t *gbp_endpoint_group_pool; +extern uword *gbp_epg_sclass_db; + +always_inline u32 +gbp_epg_itf_lookup_sclass (sclass_t sclass) +{ + uword *p; + + p = hash_get (gbp_endpoint_group_db.gg_hash_sclass, sclass); + + if (NULL != p) + { + gbp_endpoint_group_t *gg; + + gg = pool_elt_at_index (gbp_endpoint_group_pool, p[0]); + return (gg->gg_uplink_sw_if_index); + } + return (~0); +} + +always_inline const dpo_id_t * +gbp_epg_dpo_lookup (sclass_t sclass, fib_protocol_t fproto) +{ + uword *p; + + p = hash_get (gbp_endpoint_group_db.gg_hash_sclass, sclass); + + if (NULL != p) + { + gbp_endpoint_group_t *gg; + + gg = pool_elt_at_index (gbp_endpoint_group_pool, p[0]); + return (&gg->gg_dpo[fproto]); + } + return (NULL); +} + +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_ext_itf.c b/extras/deprecated/plugins/gbp/gbp_ext_itf.c new file mode 100644 index 00000000000..c5506661c2d --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_ext_itf.c @@ -0,0 +1,293 @@ +/* + * 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_ext_itf.h> +#include <plugins/gbp/gbp_bridge_domain.h> +#include <plugins/gbp/gbp_route_domain.h> +#include <plugins/gbp/gbp_itf.h> + +/** + * Pool of GBP ext_itfs + */ +gbp_ext_itf_t *gbp_ext_itf_pool; + +/** + * external interface configs keyed by sw_if_index + */ +index_t *gbp_ext_itf_db; + +#define GBP_EXT_ITF_ID 0x00000080 + +/** + * logger + */ +vlib_log_class_t gx_logger; + +#define GBP_EXT_ITF_DBG(...) \ + vlib_log_debug (gx_logger, __VA_ARGS__); + +u8 * +format_gbp_ext_itf (u8 * s, va_list * args) +{ + gbp_ext_itf_t *gx = va_arg (*args, gbp_ext_itf_t *); + + return (format (s, "%U%s in %U", + format_gbp_itf_hdl, gx->gx_itf, + (gx->gx_flags & GBP_EXT_ITF_F_ANON) ? " [anon]" : "", + format_gbp_bridge_domain, gx->gx_bd)); +} + +int +gbp_ext_itf_add (u32 sw_if_index, u32 bd_id, u32 rd_id, u32 flags) +{ + gbp_ext_itf_t *gx; + index_t gxi; + + vec_validate_init_empty (gbp_ext_itf_db, sw_if_index, INDEX_INVALID); + + gxi = gbp_ext_itf_db[sw_if_index]; + + if (INDEX_INVALID == gxi) + { + gbp_route_domain_t *gr; + fib_protocol_t fproto; + index_t gbi, gri; + + gbi = gbp_bridge_domain_find_and_lock (bd_id); + + if (INDEX_INVALID == gbi) + return (VNET_API_ERROR_NO_SUCH_ENTRY); + + gri = gbp_route_domain_find_and_lock (rd_id); + + if (INDEX_INVALID == gri) + { + gbp_bridge_domain_unlock (gbi); + return (VNET_API_ERROR_NO_SUCH_ENTRY); + } + + pool_get_zero (gbp_ext_itf_pool, gx); + gxi = gx - gbp_ext_itf_pool; + + gr = gbp_route_domain_get (gri); + + gx->gx_bd = gbi; + gx->gx_rd = gri; + gbp_itf_hdl_reset (&gx->gx_itf); + + FOR_EACH_FIB_IP_PROTOCOL (fproto) + { + gx->gx_fib_index[fproto] = + gr->grd_fib_index[fib_proto_to_dpo (fproto)]; + } + + if (flags & GBP_EXT_ITF_F_ANON) + { + /* add interface to the BD */ + gx->gx_itf = gbp_itf_l2_add_and_lock (sw_if_index, gbi); + + /* setup GBP L2 features on this interface */ + gbp_itf_l2_set_input_feature (gx->gx_itf, + L2INPUT_FEAT_GBP_LPM_ANON_CLASSIFY | + L2INPUT_FEAT_LEARN); + gbp_itf_l2_set_output_feature (gx->gx_itf, + L2OUTPUT_FEAT_GBP_POLICY_LPM); + } + + gx->gx_flags = flags; + + gbp_ext_itf_db[sw_if_index] = gxi; + + GBP_EXT_ITF_DBG ("add: %U", format_gbp_ext_itf, gx); + + return (0); + } + + return (VNET_API_ERROR_ENTRY_ALREADY_EXISTS); +} + +int +gbp_ext_itf_delete (u32 sw_if_index) +{ + gbp_ext_itf_t *gx; + index_t gxi; + + if (vec_len (gbp_ext_itf_db) <= sw_if_index) + return (VNET_API_ERROR_INVALID_SW_IF_INDEX); + + gxi = gbp_ext_itf_db[sw_if_index]; + + if (INDEX_INVALID != gxi) + { + gx = pool_elt_at_index (gbp_ext_itf_pool, gxi); + + GBP_EXT_ITF_DBG ("del: %U", format_gbp_ext_itf, gx); + + gbp_itf_unlock (&gx->gx_itf); + gbp_route_domain_unlock (gx->gx_rd); + gbp_bridge_domain_unlock (gx->gx_bd); + + gbp_ext_itf_db[sw_if_index] = INDEX_INVALID; + pool_put (gbp_ext_itf_pool, gx); + + return (0); + } + return (VNET_API_ERROR_NO_SUCH_ENTRY); +} + +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, flags = 0; + int 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")) + 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")) + flags |= GBP_EXT_ITF_F_ANON; + 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"); + rv = gbp_ext_itf_add (sw_if_index, bd_id, rd_id, flags); + } + 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) +{ + gbp_ext_itf_t *ge; + + /* *INDENT-OFF* */ + pool_foreach (ge, gbp_ext_itf_pool) + { + if (!cb(ge, ctx)) + break; + } + /* *INDENT-ON* */ +} + +static walk_rc_t +gbp_ext_itf_show_one (gbp_ext_itf_t * gx, void *ctx) +{ + vlib_cli_output (ctx, " %U", format_gbp_ext_itf, gx); + + return (WALK_CONTINUE); +} + +static clib_error_t * +gbp_ext_itf_show (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + vlib_cli_output (vm, "External-Interfaces:"); + gbp_ext_itf_walk (gbp_ext_itf_show_one, vm); + + return (NULL); +} + +/*? + * Show Group Based Policy external interface and derived information + * + * @cliexpar + * @cliexstart{show gbp ext-itf} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_ext_itf_show_node, static) = { + .path = "show gbp ext-itf", + .short_help = "show gbp ext-itf\n", + .function = gbp_ext_itf_show, +}; +/* *INDENT-ON* */ + +static clib_error_t * +gbp_ext_itf_init (vlib_main_t * vm) +{ + gx_logger = vlib_log_register_class ("gbp", "ext-itf"); + + return (NULL); +} + +VLIB_INIT_FUNCTION (gbp_ext_itf_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_ext_itf.h b/extras/deprecated/plugins/gbp/gbp_ext_itf.h new file mode 100644 index 00000000000..03b1992ca45 --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_ext_itf.h @@ -0,0 +1,92 @@ +/* + * 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_EXT_ITF_H__ +#define __GBP_EXT_ITF_H__ + +#include <gbp/gbp.h> + +enum +{ + GBP_EXT_ITF_F_NONE = 0, + GBP_EXT_ITF_F_ANON = 1 << 0, +}; + +/** + * An external interface maps directly to an oflex L3ExternalInterface. + * The special characteristics of an external interface is the way the source + * EPG is determined for input packets which, like a recirc interface, is via + * a LPM. + */ +typedef struct gpb_ext_itf_t_ +{ + /** + * The interface + */ + gbp_itf_hdl_t gx_itf; + + /** + * The BD this external interface is a member of + */ + index_t gx_bd; + + /** + * The RD this external interface is a member of + */ + index_t gx_rd; + + /** + * cached FIB indices from the RD + */ + u32 gx_fib_index[DPO_PROTO_NUM]; + + /** + * The associated flags + */ + u32 gx_flags; + +} gbp_ext_itf_t; + + +extern int gbp_ext_itf_add (u32 sw_if_index, u32 bd_id, u32 rd_id, u32 flags); +extern int gbp_ext_itf_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); +extern void gbp_ext_itf_walk (gbp_ext_itf_cb_t bgpe, void *ctx); + + +/** + * Exposed types for the data-plane + */ +extern gbp_ext_itf_t *gbp_ext_itf_pool; +extern index_t *gbp_ext_itf_db; + +always_inline gbp_ext_itf_t * +gbp_ext_itf_get (u32 sw_if_index) +{ + return (pool_elt_at_index (gbp_ext_itf_pool, gbp_ext_itf_db[sw_if_index])); +} + +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_fwd.c b/extras/deprecated/plugins/gbp/gbp_fwd.c new file mode 100644 index 00000000000..4ecc4779b92 --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_fwd.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 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 <vnet/l2/l2_input.h> +#include <plugins/gbp/gbp_learn.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; + +gbp_fwd_main_t gbp_fwd_main; + +static clib_error_t * +gbp_fwd_init (vlib_main_t * vm) +{ + gbp_fwd_main_t *gpm = &gbp_fwd_main; + vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "gbp-fwd"); + + /* Initialize the feature next-node indices */ + feat_bitmap_init_next_nodes (vm, + 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/extras/deprecated/plugins/gbp/gbp_fwd_dpo.c b/extras/deprecated/plugins/gbp/gbp_fwd_dpo.c new file mode 100644 index 00000000000..b1023f5e78f --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_fwd_dpo.c @@ -0,0 +1,306 @@ +/* + * 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> + + +#ifndef CLIB_MARCH_VARIANT +/** + * 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); +#endif /* CLIB_MARCH_VARIANT */ + +typedef struct gbp_fwd_dpo_trace_t_ +{ + u32 sclass; + 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; + sclass_t sclass0; + 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); + + sclass0 = vnet_buffer2 (b0)->gbp.sclass; + next_dpo0 = gbp_epg_dpo_lookup (sclass0, 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->sclass = sclass0; + 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, " sclass:%d dpo:%d", t->sclass, t->dpo_index); + + return s; +} + +VLIB_NODE_FN (ip4_gbp_fwd_dpo_node) (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)); +} + +VLIB_NODE_FN (ip6_gbp_fwd_dpo_node) (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) = { + .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) = { + .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", + } +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_fwd_dpo.h b/extras/deprecated/plugins/gbp/gbp_fwd_dpo.h new file mode 100644 index 00000000000..6092d6241b5 --- /dev/null +++ b/extras/deprecated/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/extras/deprecated/plugins/gbp/gbp_fwd_node.c b/extras/deprecated/plugins/gbp/gbp_fwd_node.c new file mode 100644 index 00000000000..6ea56fd8074 --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_fwd_node.c @@ -0,0 +1,163 @@ +/* + * 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 <vnet/l2/l2_input.h> + +#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 */ + sclass_t sclass; + u32 sw_if_index; +} gbp_fwd_trace_t; + +VLIB_NODE_FN (gbp_fwd_node) (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; + gbp_fwd_next_t next0; + vlib_buffer_t *b0; + sclass_t sclass0; + + 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 + */ + sclass0 = vnet_buffer2 (b0)->gbp.sclass; + + sw_if_index0 = gbp_epg_itf_lookup_sclass (sclass0); + + 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->sclass = sclass0; + 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, "sclass:%d", t->sclass); + + return s; +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (gbp_fwd_node) = { + .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", + }, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_itf.c b/extras/deprecated/plugins/gbp/gbp_itf.c new file mode 100644 index 00000000000..738a7ac2e39 --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_itf.c @@ -0,0 +1,574 @@ +/* + * 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]); + + 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); + + 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: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_itf.h b/extras/deprecated/plugins/gbp/gbp_itf.h new file mode 100644 index 00000000000..23a09b2a9ff --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_itf.h @@ -0,0 +1,97 @@ +/* + * 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_INTERFACE_H__ +#define __GBP_INTERFACE_H__ + +#include <vnet/l2/l2_input.h> +#include <vnet/l2/l2_output.h> +#include <vnet/dpo/dpo.h> + + +#define foreach_gdb_l3_feature \ + _(LEARN_IP4, "gbp-learn-ip4", "ip4-unicast") \ + _(LEARN_IP6, "gbp-learn-ip6", "ip6-unicast") + +typedef enum gbp_itf_l3_feat_pos_t_ +{ +#define _(s,v,a) GBP_ITF_L3_FEAT_POS_##s, + foreach_gdb_l3_feature +#undef _ +} gbp_itf_l3_feat_pos_t; + +typedef enum gbp_itf_l3_feat_t_ +{ + GBP_ITF_L3_FEAT_NONE, +#define _(s,v,a) GBP_ITF_L3_FEAT_##s = (1 << GBP_ITF_L3_FEAT_POS_##s), + foreach_gdb_l3_feature +#undef _ +} gbp_itf_l3_feat_t; + +#define GBP_ITF_L3_FEAT_LEARN (GBP_ITF_L3_FEAT_LEARN_IP4|GBP_ITF_L3_FEAT_LEARN_IP6) + +typedef struct gbp_itf_hdl_t_ +{ + union + { + struct + { + u32 gh_who; + u32 gh_which; + }; + }; +} gbp_itf_hdl_t; + +#define GBP_ITF_HDL_INIT {.gh_which = ~0} +const static gbp_itf_hdl_t GBP_ITF_HDL_INVALID = GBP_ITF_HDL_INIT; + +extern void gbp_itf_hdl_reset (gbp_itf_hdl_t * gh); +extern bool gbp_itf_hdl_is_valid (gbp_itf_hdl_t gh); + +typedef void (*gbp_itf_free_fn_t) (u32 sw_if_index); + +extern gbp_itf_hdl_t gbp_itf_l2_add_and_lock (u32 sw_if_index, u32 bd_index); +extern gbp_itf_hdl_t gbp_itf_l3_add_and_lock (u32 sw_if_index, index_t gri); +extern gbp_itf_hdl_t gbp_itf_l2_add_and_lock_w_free (u32 sw_if_index, + u32 bd_index, + gbp_itf_free_fn_t ff); +extern 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); + +extern void gbp_itf_unlock (gbp_itf_hdl_t * hdl); +extern void gbp_itf_lock (gbp_itf_hdl_t hdl); +extern gbp_itf_hdl_t gbp_itf_clone_and_lock (gbp_itf_hdl_t hdl); +extern u32 gbp_itf_get_sw_if_index (gbp_itf_hdl_t hdl); + +extern void gbp_itf_l2_set_input_feature (gbp_itf_hdl_t hdl, + l2input_feat_masks_t feats); +extern void gbp_itf_l2_set_output_feature (gbp_itf_hdl_t hdl, + l2output_feat_masks_t feats); + +extern void gbp_itf_l3_set_input_feature (gbp_itf_hdl_t hdl, + gbp_itf_l3_feat_t feats); + +extern u8 *format_gbp_itf_hdl (u8 * s, va_list * args); + +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_learn.c b/extras/deprecated/plugins/gbp/gbp_learn.c new file mode 100644 index 00000000000..af3a6fb52ac --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_learn.c @@ -0,0 +1,76 @@ +/* + * 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_learn.h> +#include <plugins/gbp/gbp_bridge_domain.h> + +#include <vnet/l2/l2_input.h> + +gbp_learn_main_t gbp_learn_main; + +void +gbp_learn_enable (u32 sw_if_index) +{ + vnet_feature_enable_disable ("ip4-unicast", + "gbp-learn-ip4", sw_if_index, 1, 0, 0); + vnet_feature_enable_disable ("ip6-unicast", + "gbp-learn-ip6", sw_if_index, 1, 0, 0); +} + +void +gbp_learn_disable (u32 sw_if_index) +{ + vnet_feature_enable_disable ("ip4-unicast", + "gbp-learn-ip4", sw_if_index, 0, 0, 0); + vnet_feature_enable_disable ("ip6-unicast", + "gbp-learn-ip6", sw_if_index, 0, 0, 0); +} + +static clib_error_t * +gbp_learn_init (vlib_main_t * vm) +{ + gbp_learn_main_t *glm = &gbp_learn_main; + vlib_thread_main_t *tm = &vlib_thread_main; + + vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "gbp-learn-l2"); + + /* Initialize the feature next-node indices */ + feat_bitmap_init_next_nodes (vm, + node->index, + L2INPUT_N_FEAT, + l2input_get_feat_names (), + glm->gl_l2_input_feat_next); + + throttle_init (&glm->gl_l2_throttle, + tm->n_vlib_mains, GBP_ENDPOINT_HASH_LEARN_RATE); + + throttle_init (&glm->gl_l3_throttle, + tm->n_vlib_mains, GBP_ENDPOINT_HASH_LEARN_RATE); + + glm->gl_logger = vlib_log_register_class ("gbp", "learn"); + + return 0; +} + +VLIB_INIT_FUNCTION (gbp_learn_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_learn.h b/extras/deprecated/plugins/gbp/gbp_learn.h new file mode 100644 index 00000000000..b4f3ae0a23d --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_learn.h @@ -0,0 +1,63 @@ +/* + * 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_LEARN_H__ +#define __GBP_LEARN_H__ + +#include <plugins/gbp/gbp.h> + +#include <vnet/util/throttle.h> + +/** + * The maximum learning rate per-hashed EP + */ +#define GBP_ENDPOINT_HASH_LEARN_RATE (1e-2) + +/** + * Grouping of global data for the GBP source EPG classification feature + */ +typedef struct gbp_learn_main_t_ +{ + /** + * Next nodes for L2 output features + */ + u32 gl_l2_input_feat_next[32]; + + /** + * logger - VLIB log class + */ + vlib_log_class_t gl_logger; + + /** + * throttles for the DP leanring + */ + throttle_t gl_l2_throttle; + throttle_t gl_l3_throttle; +} gbp_learn_main_t; + +extern gbp_learn_main_t gbp_learn_main; + +extern void gbp_learn_enable (u32 sw_if_index); +extern void gbp_learn_disable (u32 sw_if_index); + +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_learn_node.c b/extras/deprecated/plugins/gbp/gbp_learn_node.c new file mode 100644 index 00000000000..a6c54971956 --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_learn_node.c @@ -0,0 +1,718 @@ +/* + * 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_learn.h> +#include <plugins/gbp/gbp_bridge_domain.h> +#include <vlibmemory/api.h> + +#include <vnet/util/throttle.h> +#include <vnet/l2/l2_input.h> +#include <vnet/fib/fib_table.h> +#include <vnet/vxlan-gbp/vxlan_gbp_packet.h> +#include <vnet/ethernet/arp_packet.h> + +#define GBP_LEARN_DBG(...) \ + vlib_log_debug (gbp_learn_main.gl_logger, __VA_ARGS__); + +#define foreach_gbp_learn \ + _(DROP, "drop") + +typedef enum +{ +#define _(sym,str) GBP_LEARN_ERROR_##sym, + foreach_gbp_learn +#undef _ + GBP_LEARN_N_ERROR, +} gbp_learn_error_t; + +static char *gbp_learn_error_strings[] = { +#define _(sym,string) string, + foreach_gbp_learn +#undef _ +}; + +typedef enum +{ +#define _(sym,str) GBP_LEARN_NEXT_##sym, + foreach_gbp_learn +#undef _ + GBP_LEARN_N_NEXT, +} gbp_learn_next_t; + +typedef struct gbp_learn_l2_t_ +{ + ip46_address_t ip; + mac_address_t mac; + u32 sw_if_index; + u32 bd_index; + sclass_t sclass; + ip46_address_t outer_src; + ip46_address_t outer_dst; +} gbp_learn_l2_t; + + +static void +gbp_learn_l2_cp (const gbp_learn_l2_t * gl2) +{ + ip46_address_t *ips = NULL; + + GBP_LEARN_DBG ("L2 EP: %U %U, %d", + format_mac_address_t, &gl2->mac, + format_ip46_address, &gl2->ip, IP46_TYPE_ANY, gl2->sclass); + + if (!ip46_address_is_zero (&gl2->ip)) + vec_add1 (ips, gl2->ip); + + /* + * flip the source and dst, since that's how it was received, this API + * takes how it's sent + */ + gbp_endpoint_update_and_lock (GBP_ENDPOINT_SRC_DP, + gl2->sw_if_index, ips, + &gl2->mac, INDEX_INVALID, + INDEX_INVALID, gl2->sclass, + (GBP_ENDPOINT_FLAG_LEARNT | + GBP_ENDPOINT_FLAG_REMOTE), + &gl2->outer_dst, &gl2->outer_src, NULL); + vec_free (ips); +} + +static void +gbp_learn_l2_ip4_dp (const u8 * mac, const ip4_address_t * ip, + u32 bd_index, u32 sw_if_index, sclass_t sclass, + const ip4_address_t * outer_src, + const ip4_address_t * outer_dst) +{ + gbp_learn_l2_t gl2 = { + .sw_if_index = sw_if_index, + .bd_index = bd_index, + .sclass = sclass, + .ip.ip4 = *ip, + .outer_src.ip4 = *outer_src, + .outer_dst.ip4 = *outer_dst, + }; + mac_address_from_bytes (&gl2.mac, mac); + + vl_api_rpc_call_main_thread (gbp_learn_l2_cp, (u8 *) & gl2, sizeof (gl2)); +} + +static void +gbp_learn_l2_ip6_dp (const u8 * mac, const ip6_address_t * ip, + u32 bd_index, u32 sw_if_index, sclass_t sclass, + const ip4_address_t * outer_src, + const ip4_address_t * outer_dst) +{ + gbp_learn_l2_t gl2 = { + .sw_if_index = sw_if_index, + .bd_index = bd_index, + .sclass = sclass, + .ip.ip6 = *ip, + .outer_src.ip4 = *outer_src, + .outer_dst.ip4 = *outer_dst, + }; + mac_address_from_bytes (&gl2.mac, mac); + + vl_api_rpc_call_main_thread (gbp_learn_l2_cp, (u8 *) & gl2, sizeof (gl2)); +} + +static void +gbp_learn_l2_dp (const u8 * mac, u32 bd_index, u32 sw_if_index, + sclass_t sclass, + const ip4_address_t * outer_src, + const ip4_address_t * outer_dst) +{ + gbp_learn_l2_t gl2 = { + .sw_if_index = sw_if_index, + .bd_index = bd_index, + .sclass = sclass, + .outer_src.ip4 = *outer_src, + .outer_dst.ip4 = *outer_dst, + }; + mac_address_from_bytes (&gl2.mac, mac); + + vl_api_rpc_call_main_thread (gbp_learn_l2_cp, (u8 *) & gl2, sizeof (gl2)); +} + +/** + * per-packet trace data + */ +typedef struct gbp_learn_l2_trace_t_ +{ + /* per-pkt trace data */ + mac_address_t mac; + u32 sw_if_index; + u32 new; + u32 throttled; + u32 sclass; + u32 d_bit; + gbp_bridge_domain_flags_t gb_flags; +} gbp_learn_l2_trace_t; + +always_inline void +gbp_learn_get_outer (const ethernet_header_t * eh0, + ip4_address_t * outer_src, ip4_address_t * outer_dst) +{ + ip4_header_t *ip0; + u8 *buff; + + /* rewind back to the ivxlan header */ + buff = (u8 *) eh0; + buff -= (sizeof (vxlan_gbp_header_t) + + sizeof (udp_header_t) + sizeof (ip4_header_t)); + + ip0 = (ip4_header_t *) buff; + + *outer_src = ip0->src_address; + *outer_dst = ip0->dst_address; +} + +always_inline int +gbp_endpoint_update_required (const gbp_endpoint_t * ge0, + u32 rx_sw_if_index, sclass_t sclass) +{ + /* Conditions for [re]learning this EP */ + + /* 1. it doesn't have a dataplane source */ + if (!gbp_endpoint_is_learnt (ge0)) + return (!0); + + /* 2. has the input interface changed */ + if (gbp_itf_get_sw_if_index (ge0->ge_fwd.gef_itf) != rx_sw_if_index) + return (!0); + + /* 3. has the sclass changed */ + if (sclass != ge0->ge_fwd.gef_sclass) + return (!0); + + /* otherwise it's unchanged */ + return (0); +} + +VLIB_NODE_FN (gbp_learn_l2_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next, next_index, thread_index, seed; + gbp_learn_main_t *glm; + f64 time_now; + + glm = &gbp_learn_main; + next_index = 0; + n_left_from = frame->n_vectors; + from = vlib_frame_vector_args (frame); + time_now = vlib_time_now (vm); + thread_index = vm->thread_index; + + seed = throttle_seed (&glm->gl_l2_throttle, thread_index, time_now); + + 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) + { + ip4_address_t outer_src, outer_dst; + const ethernet_header_t *eh0; + u32 bi0, sw_if_index0, t0; + gbp_bridge_domain_t *gb0; + gbp_learn_next_t next0; + gbp_endpoint_t *ge0; + vlib_buffer_t *b0; + sclass_t sclass0; + + next0 = GBP_LEARN_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); + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + + eh0 = vlib_buffer_get_current (b0); + sclass0 = vnet_buffer2 (b0)->gbp.sclass; + + next0 = vnet_l2_feature_next (b0, glm->gl_l2_input_feat_next, + L2INPUT_FEAT_GBP_LEARN); + + ge0 = gbp_endpoint_find_mac (eh0->src_address, + vnet_buffer (b0)->l2.bd_index); + gb0 = + gbp_bridge_domain_get_by_bd_index (vnet_buffer (b0)->l2.bd_index); + + if ((vnet_buffer2 (b0)->gbp.flags & VXLAN_GBP_GPFLAGS_D) || + (gb0->gb_flags & GBP_BD_FLAG_DO_NOT_LEARN)) + { + t0 = 1; + goto trace; + } + + /* + * check for new EP or a moved EP + */ + if (NULL == ge0 || + gbp_endpoint_update_required (ge0, sw_if_index0, sclass0)) + { + /* + * use the last 4 bytes of the mac address as the hash for the EP + */ + t0 = throttle_check (&glm->gl_l2_throttle, thread_index, + *((u32 *) (eh0->src_address + 2)), seed); + if (!t0) + { + gbp_learn_get_outer (eh0, &outer_src, &outer_dst); + + if (outer_src.as_u32 == 0 || outer_dst.as_u32 == 0) + { + t0 = 2; + goto trace; + } + + switch (clib_net_to_host_u16 (eh0->type)) + { + case ETHERNET_TYPE_IP4: + { + const ip4_header_t *ip0; + + ip0 = (ip4_header_t *) (eh0 + 1); + + gbp_learn_l2_ip4_dp (eh0->src_address, + &ip0->src_address, + vnet_buffer (b0)->l2.bd_index, + sw_if_index0, sclass0, + &outer_src, &outer_dst); + + break; + } + case ETHERNET_TYPE_IP6: + { + const ip6_header_t *ip0; + + ip0 = (ip6_header_t *) (eh0 + 1); + + gbp_learn_l2_ip6_dp (eh0->src_address, + &ip0->src_address, + vnet_buffer (b0)->l2.bd_index, + sw_if_index0, sclass0, + &outer_src, &outer_dst); + + break; + } + case ETHERNET_TYPE_ARP: + { + const ethernet_arp_header_t *arp0; + + arp0 = (ethernet_arp_header_t *) (eh0 + 1); + + gbp_learn_l2_ip4_dp (eh0->src_address, + &arp0->ip4_over_ethernet[0].ip4, + vnet_buffer (b0)->l2.bd_index, + sw_if_index0, sclass0, + &outer_src, &outer_dst); + break; + } + default: + gbp_learn_l2_dp (eh0->src_address, + vnet_buffer (b0)->l2.bd_index, + sw_if_index0, sclass0, + &outer_src, &outer_dst); + break; + } + } + } + else + { + /* + * this update could happen simultaneoulsy from multiple workers + * but that's ok we are not interested in being very accurate. + */ + t0 = 0; + ge0->ge_last_time = time_now; + } + trace: + if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED))) + { + gbp_learn_l2_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + clib_memcpy_fast (t->mac.bytes, eh0->src_address, 6); + t->new = (NULL == ge0); + t->throttled = t0; + t->sw_if_index = sw_if_index0; + t->sclass = sclass0; + t->gb_flags = gb0->gb_flags; + t->d_bit = ! !(vnet_buffer2 (b0)->gbp.flags & + VXLAN_GBP_GPFLAGS_D); + } + + /* 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_learn_l2_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_learn_l2_trace_t *t = va_arg (*args, gbp_learn_l2_trace_t *); + + s = format (s, "new:%d throttled:%d d-bit:%d mac:%U itf:%d sclass:%d" + " gb-flags:%U", + t->new, t->throttled, t->d_bit, + format_mac_address_t, &t->mac, t->sw_if_index, t->sclass, + format_gbp_bridge_domain_flags, t->gb_flags); + + return s; +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (gbp_learn_l2_node) = { + .name = "gbp-learn-l2", + .vector_size = sizeof (u32), + .format_trace = format_gbp_learn_l2_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(gbp_learn_error_strings), + .error_strings = gbp_learn_error_strings, + + .n_next_nodes = GBP_LEARN_N_NEXT, + + .next_nodes = { + [GBP_LEARN_NEXT_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + +typedef struct gbp_learn_l3_t_ +{ + ip46_address_t ip; + u32 fib_index; + u32 sw_if_index; + sclass_t sclass; + ip46_address_t outer_src; + ip46_address_t outer_dst; +} gbp_learn_l3_t; + +static void +gbp_learn_l3_cp (const gbp_learn_l3_t * gl3) +{ + ip46_address_t *ips = NULL; + + GBP_LEARN_DBG ("L3 EP: %U, %d", format_ip46_address, &gl3->ip, + IP46_TYPE_ANY, gl3->sclass); + + vec_add1 (ips, gl3->ip); + + gbp_endpoint_update_and_lock (GBP_ENDPOINT_SRC_DP, + gl3->sw_if_index, ips, NULL, + INDEX_INVALID, INDEX_INVALID, gl3->sclass, + (GBP_ENDPOINT_FLAG_REMOTE | + GBP_ENDPOINT_FLAG_LEARNT), + &gl3->outer_dst, &gl3->outer_src, NULL); + vec_free (ips); +} + +static void +gbp_learn_ip4_dp (const ip4_address_t * ip, + u32 fib_index, u32 sw_if_index, sclass_t sclass, + const ip4_address_t * outer_src, + const ip4_address_t * outer_dst) +{ + /* *INDENT-OFF* */ + gbp_learn_l3_t gl3 = { + .ip = { + .ip4 = *ip, + }, + .sw_if_index = sw_if_index, + .fib_index = fib_index, + .sclass = sclass, + .outer_src.ip4 = *outer_src, + .outer_dst.ip4 = *outer_dst, + }; + /* *INDENT-ON* */ + + vl_api_rpc_call_main_thread (gbp_learn_l3_cp, (u8 *) & gl3, sizeof (gl3)); +} + +static void +gbp_learn_ip6_dp (const ip6_address_t * ip, + u32 fib_index, u32 sw_if_index, sclass_t sclass, + const ip4_address_t * outer_src, + const ip4_address_t * outer_dst) +{ + /* *INDENT-OFF* */ + gbp_learn_l3_t gl3 = { + .ip = { + .ip6 = *ip, + }, + .sw_if_index = sw_if_index, + .fib_index = fib_index, + .sclass = sclass, + .outer_src.ip4 = *outer_src, + .outer_dst.ip4 = *outer_dst, + }; + /* *INDENT-ON* */ + + vl_api_rpc_call_main_thread (gbp_learn_l3_cp, (u8 *) & gl3, sizeof (gl3)); +} + +/** + * per-packet trace data + */ +typedef struct gbp_learn_l3_trace_t_ +{ + /* per-pkt trace data */ + ip46_address_t ip; + u32 sw_if_index; + u32 new; + u32 throttled; + u32 sclass; +} gbp_learn_l3_trace_t; + +static uword +gbp_learn_l3 (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame, + fib_protocol_t fproto) +{ + u32 n_left_from, *from, *to_next, next_index, thread_index, seed; + gbp_learn_main_t *glm; + f64 time_now; + + glm = &gbp_learn_main; + next_index = 0; + n_left_from = frame->n_vectors; + from = vlib_frame_vector_args (frame); + time_now = vlib_time_now (vm); + thread_index = vm->thread_index; + + seed = throttle_seed (&glm->gl_l3_throttle, thread_index, time_now); + + 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) + { + CLIB_UNUSED (const ip4_header_t *) ip4_0; + CLIB_UNUSED (const ip6_header_t *) ip6_0; + u32 bi0, sw_if_index0, t0, fib_index0; + ip4_address_t outer_src, outer_dst; + ethernet_header_t *eth0; + gbp_learn_next_t next0; + gbp_endpoint_t *ge0; + vlib_buffer_t *b0; + sclass_t sclass0; + + next0 = GBP_LEARN_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); + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + sclass0 = vnet_buffer2 (b0)->gbp.sclass; + ip6_0 = NULL; + ip4_0 = NULL; + + vnet_feature_next (&next0, b0); + + if (vnet_buffer2 (b0)->gbp.flags & VXLAN_GBP_GPFLAGS_D) + { + t0 = 1; + ge0 = NULL; + goto trace; + } + + fib_index0 = fib_table_get_index_for_sw_if_index (fproto, + sw_if_index0); + + if (FIB_PROTOCOL_IP6 == fproto) + { + ip6_0 = vlib_buffer_get_current (b0); + eth0 = (ethernet_header_t *) (((u8 *) ip6_0) - sizeof (*eth0)); + + gbp_learn_get_outer (eth0, &outer_src, &outer_dst); + + ge0 = gbp_endpoint_find_ip6 (&ip6_0->src_address, fib_index0); + + if ((NULL == ge0) || + gbp_endpoint_update_required (ge0, sw_if_index0, sclass0)) + { + t0 = throttle_check (&glm->gl_l3_throttle, + thread_index, + ip6_address_hash_to_u32 + (&ip6_0->src_address), seed); + + if (!t0) + { + gbp_learn_ip6_dp (&ip6_0->src_address, + fib_index0, sw_if_index0, sclass0, + &outer_src, &outer_dst); + } + } + else + { + /* + * this update could happen simultaneoulsy from multiple + * workers but that's ok we are not interested in being + * very accurate. + */ + t0 = 0; + ge0->ge_last_time = time_now; + } + } + else + { + ip4_0 = vlib_buffer_get_current (b0); + eth0 = (ethernet_header_t *) (((u8 *) ip4_0) - sizeof (*eth0)); + + gbp_learn_get_outer (eth0, &outer_src, &outer_dst); + ge0 = gbp_endpoint_find_ip4 (&ip4_0->src_address, fib_index0); + + if ((NULL == ge0) || + gbp_endpoint_update_required (ge0, sw_if_index0, sclass0)) + { + t0 = throttle_check (&glm->gl_l3_throttle, thread_index, + ip4_0->src_address.as_u32, seed); + + if (!t0) + { + gbp_learn_ip4_dp (&ip4_0->src_address, + fib_index0, sw_if_index0, sclass0, + &outer_src, &outer_dst); + } + } + else + { + /* + * this update could happen simultaneoulsy from multiple + * workers but that's ok we are not interested in being + * very accurate. + */ + t0 = 0; + ge0->ge_last_time = time_now; + } + } + trace: + if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED))) + { + gbp_learn_l3_trace_t *t; + + t = vlib_add_trace (vm, node, b0, sizeof (*t)); + if (FIB_PROTOCOL_IP6 == fproto && ip6_0) + ip46_address_set_ip6 (&t->ip, &ip6_0->src_address); + if (FIB_PROTOCOL_IP4 == fproto && ip4_0) + ip46_address_set_ip4 (&t->ip, &ip4_0->src_address); + t->new = (NULL == ge0); + t->throttled = t0; + t->sw_if_index = sw_if_index0; + t->sclass = sclass0; + } + + 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_learn_l3_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_learn_l3_trace_t *t = va_arg (*args, gbp_learn_l3_trace_t *); + + s = format (s, "new:%d throttled:%d ip:%U itf:%d sclass:%d", + t->new, t->throttled, + format_ip46_address, &t->ip, IP46_TYPE_ANY, t->sw_if_index, + t->sclass); + + return s; +} + +VLIB_NODE_FN (gbp_learn_ip4_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return (gbp_learn_l3 (vm, node, frame, FIB_PROTOCOL_IP4)); +} + +VLIB_NODE_FN (gbp_learn_ip6_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return (gbp_learn_l3 (vm, node, frame, FIB_PROTOCOL_IP6)); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (gbp_learn_ip4_node) = { + .name = "gbp-learn-ip4", + .vector_size = sizeof (u32), + .format_trace = format_gbp_learn_l3_trace, + .type = VLIB_NODE_TYPE_INTERNAL, +}; + +VNET_FEATURE_INIT (gbp_learn_ip4, static) = +{ + .arc_name = "ip4-unicast", + .node_name = "gbp-learn-ip4", +}; + +VLIB_REGISTER_NODE (gbp_learn_ip6_node) = { + .name = "gbp-learn-ip6", + .vector_size = sizeof (u32), + .format_trace = format_gbp_learn_l3_trace, + .type = VLIB_NODE_TYPE_INTERNAL, +}; + +VNET_FEATURE_INIT (gbp_learn_ip6, static) = +{ + .arc_name = "ip6-unicast", + .node_name = "gbp-learn-ip6", +}; + +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_policy.c b/extras/deprecated/plugins/gbp/gbp_policy.c new file mode 100644 index 00000000000..127c6d3f059 --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_policy.c @@ -0,0 +1,79 @@ +/* + * 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.h> +#include <vnet/vxlan-gbp/vxlan_gbp_packet.h> + +gbp_policy_main_t gbp_policy_main; + +/* packet trace format function */ +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, + "scope:%d sclass:%d, dclass:%d, action:%U flags:%U acl: %d rule: %d", + t->scope, t->sclass, t->dclass, format_gbp_rule_action, t->action, + format_vxlan_gbp_header_gpflags, t->flags, t->acl_match, + t->rule_match); + + return s; +} + +static clib_error_t * +gbp_policy_init (vlib_main_t * vm) +{ + gbp_policy_main_t *gpm = &gbp_policy_main; + clib_error_t *error = 0; + + /* 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[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[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; +} + +VLIB_INIT_FUNCTION (gbp_policy_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_policy.h b/extras/deprecated/plugins/gbp/gbp_policy.h new file mode 100644 index 00000000000..6f87f2ec7c4 --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_policy.h @@ -0,0 +1,57 @@ +/* + * 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_H__ +#define __GBP_POLICY_H__ + +#include <plugins/gbp/gbp_contract.h> + +/** + * per-packet trace data + */ +typedef struct gbp_policy_trace_t_ +{ + /* per-pkt trace data */ + gbp_scope_t scope; + sclass_t sclass; + sclass_t dclass; + gbp_rule_action_t action; + u32 flags; + u32 acl_match; + u32 rule_match; +} gbp_policy_trace_t; + +/* packet trace format function */ +u8 * format_gbp_policy_trace (u8 * s, va_list * args); + +static_always_inline void +gbp_policy_trace(vlib_main_t * vm, vlib_node_runtime_t * node, vlib_buffer_t *b, const gbp_contract_key_t *key, gbp_rule_action_t action, u32 acl_match, u32 rule_match) +{ + gbp_policy_trace_t *t; + + if (PREDICT_TRUE (!(b->flags & VLIB_BUFFER_IS_TRACED))) + return; + + t = vlib_add_trace (vm, node, b, sizeof (*t)); + t->sclass = key->gck_src; + t->dclass = key->gck_dst; + t->scope = key->gck_scope; + t->action = action; + t->flags = vnet_buffer2 (b)->gbp.flags; + t->acl_match = acl_match; + t->rule_match = rule_match; +} + +#endif /* __GBP_POLICY_H__ */ diff --git a/extras/deprecated/plugins/gbp/gbp_policy_dpo.c b/extras/deprecated/plugins/gbp/gbp_policy_dpo.c new file mode 100644 index 00000000000..9f26b9c67ab --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_policy_dpo.c @@ -0,0 +1,420 @@ +/* + * 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 <vnet/dpo/dvr_dpo.h> +#include <vnet/dpo/drop_dpo.h> +#include <vnet/vxlan-gbp/vxlan_gbp_packet.h> +#include <vnet/vxlan-gbp/vxlan_gbp.h> + +#include <plugins/gbp/gbp.h> +#include <plugins/gbp/gbp_policy.h> +#include <plugins/gbp/gbp_policy_dpo.h> +#include <plugins/gbp/gbp_recirc.h> +#include <plugins/gbp/gbp_contract.h> + +#ifndef CLIB_MARCH_VARIANT +/** + * DPO pool + */ +gbp_policy_dpo_t *gbp_policy_dpo_pool; + +/** + * DPO type registered for these GBP FWD + */ +dpo_type_t gbp_policy_dpo_type; + +static gbp_policy_dpo_t * +gbp_policy_dpo_alloc (void) +{ + gbp_policy_dpo_t *gpd; + + pool_get_aligned_zero (gbp_policy_dpo_pool, gpd, CLIB_CACHE_LINE_BYTES); + + 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 (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); + } +} + +static u32 +gbp_policy_dpo_get_urpf (const dpo_id_t * dpo) +{ + gbp_policy_dpo_t *gpd; + + gpd = gbp_policy_dpo_get_from_dpo (dpo); + + return (gpd->gpd_sw_if_index); +} + +void +gbp_policy_dpo_add_or_lock (dpo_proto_t dproto, + gbp_scope_t scope, + sclass_t sclass, u32 sw_if_index, dpo_id_t * dpo) +{ + gbp_policy_dpo_t *gpd; + dpo_id_t parent = DPO_INVALID; + + gpd = gbp_policy_dpo_alloc (); + + gpd->gpd_proto = dproto; + gpd->gpd_sw_if_index = sw_if_index; + gpd->gpd_sclass = sclass; + gpd->gpd_scope = scope; + + if (~0 != sw_if_index) + { + /* + * stack on the DVR DPO for the output interface + */ + dvr_dpo_add_or_lock (sw_if_index, dproto, &parent); + } + else + { + dpo_copy (&parent, drop_dpo_get (dproto)); + } + + 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 (index); + vnet_main_t *vnm = vnet_get_main (); + + s = format (s, "gbp-policy-dpo: %U, scope:%d sclass:%d out:%U", + format_dpo_proto, gpd->gpd_proto, + gpd->gpd_scope, (int) gpd->gpd_sclass, + 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); +} + +/** + * Interpose a policy DPO + */ +static void +gbp_policy_dpo_interpose (const dpo_id_t * original, + const dpo_id_t * parent, dpo_id_t * clone) +{ + gbp_policy_dpo_t *gpd, *gpd_clone; + + gpd_clone = gbp_policy_dpo_alloc (); + gpd = gbp_policy_dpo_get (original->dpoi_index); + + gpd_clone->gpd_proto = gpd->gpd_proto; + gpd_clone->gpd_scope = gpd->gpd_scope; + gpd_clone->gpd_sclass = gpd->gpd_sclass; + gpd_clone->gpd_sw_if_index = gpd->gpd_sw_if_index; + + /* + * if no interface is provided, grab one from the parent + * on which we stack + */ + if (~0 == gpd_clone->gpd_sw_if_index) + gpd_clone->gpd_sw_if_index = dpo_get_urpf (parent); + + dpo_stack (gbp_policy_dpo_type, + gpd_clone->gpd_proto, &gpd_clone->gpd_dpo, parent); + + dpo_set (clone, + gbp_policy_dpo_type, + gpd_clone->gpd_proto, gbp_policy_dpo_get_index (gpd_clone)); +} + +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, + .dv_get_urpf = gbp_policy_dpo_get_urpf, + .dv_mk_interpose = gbp_policy_dpo_interpose, +}; + +/** + * @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); +#endif /* CLIB_MARCH_VARIANT */ + +typedef enum +{ + GBP_POLICY_DROP, + GBP_POLICY_N_NEXT, +} gbp_policy_next_t; + +always_inline u32 +gbp_rule_l3_redirect (const gbp_rule_t * gu, vlib_buffer_t * b0, int is_ip6) +{ + gbp_policy_node_t pnode; + const dpo_id_t *dpo; + dpo_proto_t dproto; + + pnode = (is_ip6 ? GBP_POLICY_NODE_IP6 : GBP_POLICY_NODE_IP4); + dproto = (is_ip6 ? DPO_PROTO_IP6 : DPO_PROTO_IP4); + dpo = &gu->gu_dpo[pnode][dproto]; + + /* The flow hash is still valid as this is a IP packet being switched */ + vnet_buffer (b0)->ip.adj_index[VLIB_TX] = dpo->dpoi_index; + + return (dpo->dpoi_next_node); +} + +always_inline uword +gbp_policy_dpo_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame, u8 is_ip6) +{ + gbp_main_t *gm = &gbp_main; + u32 n_left_from, next_index, *from, *to_next; + u32 n_allow_intra, n_allow_a_bit, n_allow_sclass_1; + + from = vlib_frame_vector_args (from_frame); + n_left_from = from_frame->n_vectors; + n_allow_intra = n_allow_a_bit = n_allow_sclass_1 = 0; + + 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) + { + gbp_rule_action_t action0 = GBP_RULE_DENY; + u32 acl_match = ~0, rule_match = ~0; + const gbp_policy_dpo_t *gpd0; + gbp_contract_error_t err0; + gbp_contract_key_t key0; + vlib_buffer_t *b0; + gbp_rule_t *rule0; + u32 bi0, next0; + + 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; + + b0 = vlib_get_buffer (vm, bi0); + + gpd0 = gbp_policy_dpo_get (vnet_buffer (b0)->ip.adj_index[VLIB_TX]); + vnet_buffer (b0)->ip.adj_index[VLIB_TX] = gpd0->gpd_dpo.dpoi_index; + + /* + * Reflection check; in and out on an ivxlan tunnel + */ + if ((~0 != vxlan_gbp_tunnel_by_sw_if_index (gpd0->gpd_sw_if_index)) + && (vnet_buffer2 (b0)->gbp.flags & VXLAN_GBP_GPFLAGS_R)) + { + goto trace; + } + + if (vnet_buffer2 (b0)->gbp.flags & VXLAN_GBP_GPFLAGS_A) + { + next0 = gpd0->gpd_dpo.dpoi_next_node; + key0.as_u64 = ~0; + n_allow_a_bit++; + goto trace; + } + + /* zero out the key to ensure the pad space is clear */ + key0.as_u64 = 0; + key0.gck_src = vnet_buffer2 (b0)->gbp.sclass; + + if (SCLASS_INVALID == key0.gck_src) + { + /* + * 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; + goto trace; + } + + key0.gck_scope = gpd0->gpd_scope; + key0.gck_dst = gpd0->gpd_sclass; + + action0 = + gbp_contract_apply (vm, gm, &key0, b0, &rule0, &n_allow_intra, + &n_allow_sclass_1, &acl_match, &rule_match, + &err0, + is_ip6 ? GBP_CONTRACT_APPLY_IP6 : + GBP_CONTRACT_APPLY_IP4); + switch (action0) + { + case GBP_RULE_PERMIT: + next0 = gpd0->gpd_dpo.dpoi_next_node; + vnet_buffer2 (b0)->gbp.flags |= VXLAN_GBP_GPFLAGS_A; + break; + case GBP_RULE_REDIRECT: + next0 = gbp_rule_l3_redirect (rule0, b0, is_ip6); + vnet_buffer2 (b0)->gbp.flags |= VXLAN_GBP_GPFLAGS_A; + break; + case GBP_RULE_DENY: + next0 = GBP_POLICY_DROP; + b0->error = node->errors[err0]; + break; + } + + trace: + gbp_policy_trace (vm, node, b0, &key0, action0, acl_match, + rule_match); + + 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); + } + + vlib_node_increment_counter (vm, node->node_index, + GBP_CONTRACT_ERROR_ALLOW_INTRA, n_allow_intra); + vlib_node_increment_counter (vm, node->node_index, + GBP_CONTRACT_ERROR_ALLOW_A_BIT, n_allow_a_bit); + vlib_node_increment_counter (vm, node->node_index, + GBP_CONTRACT_ERROR_ALLOW_SCLASS_1, + n_allow_sclass_1); + return from_frame->n_vectors; +} + +VLIB_NODE_FN (ip4_gbp_policy_dpo_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + return (gbp_policy_dpo_inline (vm, node, from_frame, 0)); +} + +VLIB_NODE_FN (ip6_gbp_policy_dpo_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + return (gbp_policy_dpo_inline (vm, node, from_frame, 1)); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (ip4_gbp_policy_dpo_node) = { + .name = "ip4-gbp-policy-dpo", + .vector_size = sizeof (u32), + .format_trace = format_gbp_policy_trace, + + .n_errors = ARRAY_LEN(gbp_contract_error_strings), + .error_strings = gbp_contract_error_strings, + + .n_next_nodes = GBP_POLICY_N_NEXT, + .next_nodes = + { + [GBP_POLICY_DROP] = "ip4-drop", + } +}; +VLIB_REGISTER_NODE (ip6_gbp_policy_dpo_node) = { + .name = "ip6-gbp-policy-dpo", + .vector_size = sizeof (u32), + .format_trace = format_gbp_policy_trace, + + .n_errors = ARRAY_LEN(gbp_contract_error_strings), + .error_strings = gbp_contract_error_strings, + + .n_next_nodes = GBP_POLICY_N_NEXT, + .next_nodes = + { + [GBP_POLICY_DROP] = "ip6-drop", + } +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_policy_dpo.h b/extras/deprecated/plugins/gbp/gbp_policy_dpo.h new file mode 100644 index 00000000000..77ca5d93bd0 --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_policy_dpo.h @@ -0,0 +1,121 @@ +/* + * 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> +#include <vnet/dpo/load_balance.h> +#include <vnet/fib/ip4_fib.h> +#include <vnet/fib/ip6_fib.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_ +{ + CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); + + /** + * The protocol of packets using this DPO + */ + dpo_proto_t gpd_proto; + + /** + * SClass + */ + sclass_t gpd_sclass; + + /** + * sclass scope + */ + gbp_scope_t gpd_scope; + + /** + * output sw_if_index + */ + u32 gpd_sw_if_index; + + /** + * number of locks. + */ + u16 gpd_locks; + + /** + * Stacked DPO on DVR/ADJ of output interface + */ + dpo_id_t gpd_dpo; +} gbp_policy_dpo_t; + +extern void gbp_policy_dpo_add_or_lock (dpo_proto_t dproto, + gbp_scope_t scope, + sclass_t sclass, + u32 sw_if_index, dpo_id_t * dpo); + +extern dpo_type_t gbp_policy_dpo_get_type (void); + +extern vlib_node_registration_t ip4_gbp_policy_dpo_node; +extern vlib_node_registration_t ip6_gbp_policy_dpo_node; +extern vlib_node_registration_t gbp_policy_port_node; + +/** + * Types exposed for the Data-plane + */ +extern dpo_type_t gbp_policy_dpo_type; +extern gbp_policy_dpo_t *gbp_policy_dpo_pool; + +always_inline gbp_policy_dpo_t * +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 (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 + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ + +#endif diff --git a/extras/deprecated/plugins/gbp/gbp_policy_node.c b/extras/deprecated/plugins/gbp/gbp_policy_node.c new file mode 100644 index 00000000000..8c6ef5c2b94 --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_policy_node.c @@ -0,0 +1,341 @@ +/* + * 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_classify.h> +#include <plugins/gbp/gbp_policy.h> +#include <plugins/gbp/gbp_policy_dpo.h> +#include <plugins/gbp/gbp_bridge_domain.h> +#include <plugins/gbp/gbp_ext_itf.h> +#include <plugins/gbp/gbp_contract.h> + +#include <vnet/vxlan-gbp/vxlan_gbp_packet.h> +#include <vnet/vxlan-gbp/vxlan_gbp.h> + +typedef enum +{ + GBP_POLICY_NEXT_DROP, + GBP_POLICY_N_NEXT, +} gbp_policy_next_t; + +always_inline dpo_proto_t +ethertype_to_dpo_proto (u16 etype) +{ + etype = clib_net_to_host_u16 (etype); + + switch (etype) + { + case ETHERNET_TYPE_IP4: + return (DPO_PROTO_IP4); + case ETHERNET_TYPE_IP6: + return (DPO_PROTO_IP6); + } + + return (DPO_PROTO_NONE); +} + +always_inline u32 +gbp_rule_l2_redirect (const gbp_rule_t * gu, vlib_buffer_t * b0) +{ + const ethernet_header_t *eth0; + const dpo_id_t *dpo; + dpo_proto_t dproto; + + eth0 = vlib_buffer_get_current (b0); + /* pop the ethernet header to prepare for L3 rewrite */ + vlib_buffer_advance (b0, vnet_buffer (b0)->l2.l2_len); + + dproto = ethertype_to_dpo_proto (eth0->type); + dpo = &gu->gu_dpo[GBP_POLICY_NODE_L2][dproto]; + + /* save the LB index for the next node and reset the IP flow hash + * so it's recalculated */ + vnet_buffer (b0)->ip.adj_index[VLIB_TX] = dpo->dpoi_index; + vnet_buffer (b0)->ip.flow_hash = 0; + + return (dpo->dpoi_next_node); +} + +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, const gbp_policy_type_t type) +{ + gbp_main_t *gm = &gbp_main; + gbp_policy_main_t *gpm = &gbp_policy_main; + u32 n_left_from, *from, *to_next; + u32 next_index; + u32 n_allow_intra, n_allow_a_bit, n_allow_sclass_1; + + next_index = 0; + n_left_from = frame->n_vectors; + from = vlib_frame_vector_args (frame); + n_allow_intra = n_allow_a_bit = n_allow_sclass_1 = 0; + + 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_rule_action_t action0 = GBP_RULE_DENY; + const ethernet_header_t *h0; + const gbp_endpoint_t *ge0; + gbp_contract_error_t err0; + u32 acl_match = ~0, rule_match = ~0; + gbp_policy_next_t next0; + gbp_contract_key_t key0; + u32 bi0, sw_if_index0; + vlib_buffer_t *b0; + gbp_rule_t *rule0; + + next0 = GBP_POLICY_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); + h0 = vlib_buffer_get_current (b0); + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX]; + + /* + * Reflection check; in and out on an ivxlan tunnel + */ + if ((~0 != vxlan_gbp_tunnel_by_sw_if_index (sw_if_index0)) && + (vnet_buffer2 (b0)->gbp.flags & VXLAN_GBP_GPFLAGS_R)) + { + goto trace; + } + + /* + * If the A-bit is set then policy has already been applied + * and we skip enforcement here. + */ + if (vnet_buffer2 (b0)->gbp.flags & VXLAN_GBP_GPFLAGS_A) + { + next0 = gbp_policy_l2_feature_next (gpm, b0, type); + n_allow_a_bit++; + key0.as_u64 = ~0; + goto trace; + } + + /* + * determine the src and dst EPG + */ + + /* zero out the key to ensure the pad space is clear */ + key0.as_u64 = 0; + key0.gck_src = vnet_buffer2 (b0)->gbp.sclass; + key0.gck_dst = SCLASS_INVALID; + + if (GBP_POLICY_LPM == type) + { + 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_CONTRACT_ERROR_DROP_NO_DCLASS]; + goto trace; + } + + key0.gck_src = vnet_buffer2 (b0)->gbp.sclass; + if (SCLASS_INVALID == key0.gck_src) + { + /* + * the src EPG is not set when the packet arrives on an EPG + * uplink interface and we do not need to apply policy + */ + next0 = gbp_policy_l2_feature_next (gpm, b0, type); + goto trace; + } + + key0.gck_scope = + gbp_bridge_domain_get_scope (vnet_buffer (b0)->l2.bd_index); + + action0 = + gbp_contract_apply (vm, gm, &key0, b0, &rule0, &n_allow_intra, + &n_allow_sclass_1, &acl_match, &rule_match, + &err0, GBP_CONTRACT_APPLY_L2); + switch (action0) + { + case GBP_RULE_PERMIT: + next0 = gbp_policy_l2_feature_next (gpm, b0, type); + vnet_buffer2 (b0)->gbp.flags |= VXLAN_GBP_GPFLAGS_A; + break; + case GBP_RULE_REDIRECT: + next0 = gbp_rule_l2_redirect (rule0, b0); + vnet_buffer2 (b0)->gbp.flags |= VXLAN_GBP_GPFLAGS_A; + break; + case GBP_RULE_DENY: + next0 = GBP_POLICY_NEXT_DROP; + b0->error = node->errors[err0]; + break; + } + + trace: + gbp_policy_trace (vm, node, b0, &key0, action0, acl_match, + rule_match); + + /* 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); + } + + vlib_node_increment_counter (vm, node->node_index, + GBP_CONTRACT_ERROR_ALLOW_INTRA, n_allow_intra); + vlib_node_increment_counter (vm, node->node_index, + GBP_CONTRACT_ERROR_ALLOW_A_BIT, n_allow_a_bit); + vlib_node_increment_counter (vm, node->node_index, + GBP_CONTRACT_ERROR_ALLOW_SCLASS_1, + n_allow_sclass_1); + + return frame->n_vectors; +} + +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, 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, 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)); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (gbp_policy_port_node) = { + .name = "gbp-policy-port", + .vector_size = sizeof (u32), + .format_trace = format_gbp_policy_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(gbp_contract_error_strings), + .error_strings = gbp_contract_error_strings, + + .n_next_nodes = GBP_POLICY_N_NEXT, + .next_nodes = { + [GBP_POLICY_NEXT_DROP] = "error-drop", + }, +}; + +VLIB_REGISTER_NODE (gbp_policy_mac_node) = { + .name = "gbp-policy-mac", + .vector_size = sizeof (u32), + .format_trace = format_gbp_policy_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(gbp_contract_error_strings), + .error_strings = gbp_contract_error_strings, + + .n_next_nodes = GBP_POLICY_N_NEXT, + .next_nodes = { + [GBP_POLICY_NEXT_DROP] = "error-drop", + }, +}; + +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_contract_error_strings), + .error_strings = gbp_contract_error_strings, + + .n_next_nodes = GBP_POLICY_N_NEXT, + .next_nodes = { + [GBP_POLICY_NEXT_DROP] = "error-drop", + }, +}; + +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_recirc.c b/extras/deprecated/plugins/gbp/gbp_recirc.c new file mode 100644 index 00000000000..8d56f11b4e3 --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_recirc.c @@ -0,0 +1,292 @@ +/* + * 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 <plugins/gbp/gbp_itf.h> + +#include <vnet/dpo/dvr_dpo.h> +#include <vnet/fib/fib_table.h> + +#include <vlib/unix/plugin.h> + +/** + * Pool of GBP recircs + */ +gbp_recirc_t *gbp_recirc_pool; + +/** + * Recirc configs keyed by sw_if_index + */ +index_t *gbp_recirc_db; + +/** + * logger + */ +vlib_log_class_t gr_logger; + +/** + * L2 Emulation enable/disable symbols + */ +static void (*l2e_enable) (u32 sw_if_index); +static void (*l2e_disable) (u32 sw_if_index); + +#define GBP_RECIRC_DBG(...) \ + vlib_log_debug (gr_logger, __VA_ARGS__); + +u8 * +format_gbp_recirc (u8 * s, va_list * args) +{ + gbp_recirc_t *gr = va_arg (*args, gbp_recirc_t *); + vnet_main_t *vnm = vnet_get_main (); + + return format (s, " %U, sclass:%d, ext:%d", + format_vnet_sw_if_index_name, vnm, + gr->gr_sw_if_index, gr->gr_sclass, gr->gr_is_ext); +} + +int +gbp_recirc_add (u32 sw_if_index, sclass_t sclass, 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 *gg; + fib_protocol_t fproto; + index_t ggi; + + ggi = gbp_endpoint_group_find (sclass); + + if (INDEX_INVALID == ggi) + return (VNET_API_ERROR_NO_SUCH_ENTRY); + + gbp_endpoint_group_lock (ggi); + pool_get_zero (gbp_recirc_pool, gr); + gri = gr - gbp_recirc_pool; + + gr->gr_sclass = sclass; + 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 + */ + gr->gr_epgi = ggi; + + gg = gbp_endpoint_group_get (gr->gr_epgi); + FOR_EACH_FIB_IP_PROTOCOL (fproto) + { + gr->gr_fib_index[fib_proto_to_dpo (fproto)] = + gbp_endpoint_group_get_fib_index (gg, fproto); + } + + /* + * bind to the bridge-domain of the EPG + */ + gr->gr_itf = gbp_itf_l2_add_and_lock (gr->gr_sw_if_index, gg->gg_gbd); + + /* + * set the interface into L2 emulation mode + */ + l2e_enable (gr->gr_sw_if_index); + + /* + * Packets on the recirculation interface are subject 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) + { + mac_address_t mac; + /* + * recirc is for post-NAT translation packets going into + * the external EPG, these are classified to the NAT EPG + * based on its port + */ + mac_address_from_bytes (&mac, + vnet_sw_interface_get_hw_address + (vnet_get_main (), gr->gr_sw_if_index)); + gbp_endpoint_update_and_lock (GBP_ENDPOINT_SRC_CP, + gr->gr_sw_if_index, + NULL, &mac, INDEX_INVALID, + INDEX_INVALID, gr->gr_sclass, + GBP_ENDPOINT_FLAG_NONE, + NULL, NULL, &gr->gr_ep); + 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; + } + else + { + gr = gbp_recirc_get (gri); + } + + GBP_RECIRC_DBG ("add: %U", format_gbp_recirc, gr); + return (0); +} + +int +gbp_recirc_delete (u32 sw_if_index) +{ + gbp_recirc_t *gr; + index_t gri; + + if (vec_len (gbp_recirc_db) <= sw_if_index) + return VNET_API_ERROR_INVALID_SW_IF_INDEX; + gri = gbp_recirc_db[sw_if_index]; + + if (INDEX_INVALID != gri) + { + gr = pool_elt_at_index (gbp_recirc_pool, gri); + + GBP_RECIRC_DBG ("del: %U", format_gbp_recirc, gr); + + if (gr->gr_is_ext) + { + gbp_endpoint_unlock (GBP_ENDPOINT_SRC_CP, gr->gr_ep); + 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); + l2e_disable (gr->gr_sw_if_index); + + gbp_itf_unlock (&gr->gr_itf); + + gbp_endpoint_group_unlock (gr->gr_epgi); + gbp_recirc_db[sw_if_index] = INDEX_INVALID; + pool_put (gbp_recirc_pool, gr); + return (0); + } + return VNET_API_ERROR_NO_SUCH_ENTRY; +} + +void +gbp_recirc_walk (gbp_recirc_cb_t cb, void *ctx) +{ + gbp_recirc_t *ge; + + /* *INDENT-OFF* */ + pool_foreach (ge, gbp_recirc_pool) + { + if (!cb(ge, ctx)) + break; + } + /* *INDENT-ON* */ +} + +static walk_rc_t +gbp_recirc_show_one (gbp_recirc_t * gr, void *ctx) +{ + vlib_cli_output (ctx, " %U", format_gbp_recirc, gr); + + return (WALK_CONTINUE); +} + +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* */ + +static clib_error_t * +gbp_recirc_init (vlib_main_t * vm) +{ + gr_logger = vlib_log_register_class ("gbp", "recirc"); + + l2e_enable = + vlib_get_plugin_symbol ("l2e_plugin.so", "l2_emulation_enable"); + l2e_disable = + vlib_get_plugin_symbol ("l2e_plugin.so", "l2_emulation_disable"); + + return (NULL); +} + +VLIB_INIT_FUNCTION (gbp_recirc_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_recirc.h b/extras/deprecated/plugins/gbp/gbp_recirc.h new file mode 100644 index 00000000000..2f3354b794e --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_recirc.h @@ -0,0 +1,88 @@ +/* + * 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 <plugins/gbp/gbp_itf.h> +#include <vnet/fib/fib_types.h> + +/** + * A GBP recirculation interface representation + * Thes interfaces join Bridge domains that are internal to those that are + * NAT external, so the packets can be NAT translated and then undergo the + * whole policy process again. + */ +typedef struct gpb_recirc_t_ +{ + /** + * EPG ID that packets will classify to when they arrive on this recirc + */ + sclass_t gr_sclass; + + /** + * The index of the EPG + */ + index_t gr_epgi; + + /** + * FIB indices the EPG is mapped to + */ + u32 gr_fib_index[DPO_PROTO_NUM]; + + /** + * Is the interface for packets post-NAT translation (i.e. ext) + * or pre-NAT translation (i.e. internal) + */ + u8 gr_is_ext; + + /** + */ + u32 gr_sw_if_index; + gbp_itf_hdl_t gr_itf; + + /** + * The endpoint created to represent the reric interface + */ + index_t gr_ep; +} gbp_recirc_t; + +extern int gbp_recirc_add (u32 sw_if_index, sclass_t sclass, u8 is_ext); +extern int gbp_recirc_delete (u32 sw_if_index); + +typedef walk_rc_t (*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 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/extras/deprecated/plugins/gbp/gbp_route_domain.c b/extras/deprecated/plugins/gbp/gbp_route_domain.c new file mode 100644 index 00000000000..6cc595d0fa9 --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_route_domain.c @@ -0,0 +1,447 @@ +/* + * 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_route_domain.h> +#include <plugins/gbp/gbp_endpoint.h> + +#include <vnet/dpo/dvr_dpo.h> +#include <vnet/fib/fib_table.h> + +/** + * A fixed MAC address to use as the source MAC for packets L3 switched + * onto the routed uu-fwd interfaces. + * Magic values - origin lost to the mists of time... + */ +/* *INDENT-OFF* */ +const static mac_address_t GBP_ROUTED_SRC_MAC = { + .bytes = { + 0x0, 0x22, 0xBD, 0xF8, 0x19, 0xFF, + } +}; + +const static mac_address_t GBP_ROUTED_DST_MAC = { + .bytes = { + 00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + } +}; +/* *INDENT-ON* */ + +/** + * Pool of GBP route_domains + */ +gbp_route_domain_t *gbp_route_domain_pool; + +/** + * DB of route_domains + */ +typedef struct gbp_route_domain_db_t +{ + uword *gbd_by_rd_id; +} gbp_route_domain_db_t; + +static gbp_route_domain_db_t gbp_route_domain_db; +static fib_source_t gbp_fib_source; + +/** + * logger + */ +vlib_log_class_t grd_logger; + +#define GBP_BD_DBG(...) \ + vlib_log_debug (grd_logger, __VA_ARGS__); + +index_t +gbp_route_domain_index (const gbp_route_domain_t * grd) +{ + return (grd - gbp_route_domain_pool); +} + +gbp_route_domain_t * +gbp_route_domain_get (index_t i) +{ + return (pool_elt_at_index (gbp_route_domain_pool, i)); +} + +static void +gbp_route_domain_lock (index_t i) +{ + gbp_route_domain_t *grd; + + grd = gbp_route_domain_get (i); + grd->grd_locks++; +} + +index_t +gbp_route_domain_find (u32 rd_id) +{ + uword *p; + + p = hash_get (gbp_route_domain_db.gbd_by_rd_id, rd_id); + + if (NULL != p) + return p[0]; + + return (INDEX_INVALID); +} + +index_t +gbp_route_domain_find_and_lock (u32 rd_id) +{ + index_t grdi; + + grdi = gbp_route_domain_find (rd_id); + + if (INDEX_INVALID != grdi) + { + gbp_route_domain_lock (grdi); + } + return (grdi); +} + +static void +gbp_route_domain_db_add (gbp_route_domain_t * grd) +{ + index_t grdi = grd - gbp_route_domain_pool; + + hash_set (gbp_route_domain_db.gbd_by_rd_id, grd->grd_id, grdi); +} + +static void +gbp_route_domain_db_remove (gbp_route_domain_t * grd) +{ + hash_unset (gbp_route_domain_db.gbd_by_rd_id, grd->grd_id); +} + +int +gbp_route_domain_add_and_lock (u32 rd_id, + gbp_scope_t scope, + u32 ip4_table_id, + u32 ip6_table_id, + u32 ip4_uu_sw_if_index, u32 ip6_uu_sw_if_index) +{ + gbp_route_domain_t *grd; + index_t grdi; + + grdi = gbp_route_domain_find (rd_id); + + if (INDEX_INVALID == grdi) + { + fib_protocol_t fproto; + + pool_get_zero (gbp_route_domain_pool, grd); + + grd->grd_id = rd_id; + grd->grd_scope = scope; + grd->grd_table_id[FIB_PROTOCOL_IP4] = ip4_table_id; + grd->grd_table_id[FIB_PROTOCOL_IP6] = ip6_table_id; + grd->grd_uu_sw_if_index[FIB_PROTOCOL_IP4] = ip4_uu_sw_if_index; + grd->grd_uu_sw_if_index[FIB_PROTOCOL_IP6] = ip6_uu_sw_if_index; + + FOR_EACH_FIB_IP_PROTOCOL (fproto) + { + grd->grd_fib_index[fproto] = + fib_table_find_or_create_and_lock (fproto, + grd->grd_table_id[fproto], + gbp_fib_source); + + if (~0 != grd->grd_uu_sw_if_index[fproto]) + { + ethernet_header_t *eth; + u8 *rewrite; + + rewrite = NULL; + vec_validate (rewrite, sizeof (*eth) - 1); + eth = (ethernet_header_t *) rewrite; + + eth->type = clib_host_to_net_u16 ((fproto == FIB_PROTOCOL_IP4 ? + ETHERNET_TYPE_IP4 : + ETHERNET_TYPE_IP6)); + + mac_address_to_bytes (gbp_route_domain_get_local_mac (), + eth->src_address); + mac_address_to_bytes (gbp_route_domain_get_remote_mac (), + eth->dst_address); + + /* + * create an adjacency out of the uu-fwd interfaces that will + * be used when adding subnet routes. + */ + grd->grd_adj[fproto] = + adj_nbr_add_or_lock_w_rewrite (fproto, + fib_proto_to_link (fproto), + &ADJ_BCAST_ADDR, + grd->grd_uu_sw_if_index[fproto], + rewrite); + } + else + { + grd->grd_adj[fproto] = INDEX_INVALID; + } + } + + gbp_route_domain_db_add (grd); + } + else + { + grd = gbp_route_domain_get (grdi); + } + + grd->grd_locks++; + GBP_BD_DBG ("add: %U", format_gbp_route_domain, grd); + + return (0); +} + +void +gbp_route_domain_unlock (index_t index) +{ + gbp_route_domain_t *grd; + + grd = gbp_route_domain_get (index); + + grd->grd_locks--; + + if (0 == grd->grd_locks) + { + fib_protocol_t fproto; + + GBP_BD_DBG ("destroy: %U", format_gbp_route_domain, grd); + + FOR_EACH_FIB_IP_PROTOCOL (fproto) + { + fib_table_unlock (grd->grd_fib_index[fproto], fproto, gbp_fib_source); + if (INDEX_INVALID != grd->grd_adj[fproto]) + adj_unlock (grd->grd_adj[fproto]); + } + + gbp_route_domain_db_remove (grd); + + pool_put (gbp_route_domain_pool, grd); + } +} + +u32 +gbp_route_domain_get_rd_id (index_t grdi) +{ + gbp_route_domain_t *grd; + + grd = gbp_route_domain_get (grdi); + + return (grd->grd_id); +} + +gbp_scope_t +gbp_route_domain_get_scope (index_t grdi) +{ + gbp_route_domain_t *grd; + + grd = gbp_route_domain_get (grdi); + + return (grd->grd_scope); +} + +int +gbp_route_domain_delete (u32 rd_id) +{ + index_t grdi; + + GBP_BD_DBG ("del: %d", rd_id); + grdi = gbp_route_domain_find (rd_id); + + if (INDEX_INVALID != grdi) + { + GBP_BD_DBG ("del: %U", format_gbp_route_domain, + gbp_route_domain_get (grdi)); + gbp_route_domain_unlock (grdi); + + return (0); + } + + return (VNET_API_ERROR_NO_SUCH_ENTRY); +} + +const mac_address_t * +gbp_route_domain_get_local_mac (void) +{ + return (&GBP_ROUTED_SRC_MAC); +} + +const mac_address_t * +gbp_route_domain_get_remote_mac (void) +{ + return (&GBP_ROUTED_DST_MAC); +} + +void +gbp_route_domain_walk (gbp_route_domain_cb_t cb, void *ctx) +{ + gbp_route_domain_t *gbpe; + + /* *INDENT-OFF* */ + pool_foreach (gbpe, gbp_route_domain_pool) + { + if (!cb(gbpe, ctx)) + break; + } + /* *INDENT-ON* */ +} + +static clib_error_t * +gbp_route_domain_cli (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + vnet_main_t *vnm = vnet_get_main (); + u32 ip4_uu_sw_if_index = ~0; + u32 ip6_uu_sw_if_index = ~0; + u32 ip4_table_id = ~0; + u32 ip6_table_id = ~0; + u32 scope = ~0; + u32 rd_id = ~0; + u8 add = 1; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "ip4-uu %U", unformat_vnet_sw_interface, + vnm, &ip4_uu_sw_if_index)) + ; + else if (unformat (input, "ip6-uu %U", unformat_vnet_sw_interface, + vnm, &ip6_uu_sw_if_index)) + ; + else if (unformat (input, "ip4-table-id %d", &ip4_table_id)) + ; + else if (unformat (input, "ip6-table-id %d", &ip6_table_id)) + ; + else if (unformat (input, "add")) + add = 1; + else if (unformat (input, "del")) + add = 0; + else if (unformat (input, "rd %d", &rd_id)) + ; + else if (unformat (input, "scope %d", &scope)) + ; + else + break; + } + + if (~0 == rd_id) + return clib_error_return (0, "RD-ID must be specified"); + + if (add) + { + if (~0 == ip4_table_id) + return clib_error_return (0, "IP4 table-ID must be specified"); + if (~0 == ip6_table_id) + return clib_error_return (0, "IP6 table-ID must be specified"); + + gbp_route_domain_add_and_lock (rd_id, scope, + ip4_table_id, + ip6_table_id, + ip4_uu_sw_if_index, ip6_uu_sw_if_index); + } + else + gbp_route_domain_delete (rd_id); + + return (NULL); +} + +/*? + * Configure a GBP route-domain + * + * @cliexpar + * @cliexstart{gbp route-domain [del] rd <ID> ip4-table-id <ID> ip6-table-id <ID> [ip4-uu <interface>] [ip6-uu <interface>]} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_route_domain_cli_node, static) = { + .path = "gbp route-domain", + .short_help = "gbp route-domain [del] rd <ID> ip4-table-id <ID> ip6-table-id <ID> [ip4-uu <interface>] [ip6-uu <interface>]", + .function = gbp_route_domain_cli, +}; + +u8 * +format_gbp_route_domain (u8 * s, va_list * args) +{ + gbp_route_domain_t *grd = va_arg (*args, gbp_route_domain_t*); + vnet_main_t *vnm = vnet_get_main (); + + if (NULL != grd) + s = format (s, "[%d] rd:%d ip4-uu:%U ip6-uu:%U locks:%d", + grd - gbp_route_domain_pool, + grd->grd_id, + format_vnet_sw_if_index_name, vnm, grd->grd_uu_sw_if_index[FIB_PROTOCOL_IP4], + format_vnet_sw_if_index_name, vnm, grd->grd_uu_sw_if_index[FIB_PROTOCOL_IP6], + grd->grd_locks); + else + s = format (s, "NULL"); + + return (s); +} + +static int +gbp_route_domain_show_one (gbp_route_domain_t *gb, void *ctx) +{ + vlib_main_t *vm; + + vm = ctx; + vlib_cli_output (vm, " %U",format_gbp_route_domain, gb); + + return (1); +} + +static clib_error_t * +gbp_route_domain_show (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + vlib_cli_output (vm, "Route-Domains:"); + gbp_route_domain_walk (gbp_route_domain_show_one, vm); + + return (NULL); +} + +/*? + * Show Group Based Policy Route_Domains and derived information + * + * @cliexpar + * @cliexstart{show gbp route_domain} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_route_domain_show_node, static) = { + .path = "show gbp route-domain", + .short_help = "show gbp route-domain\n", + .function = gbp_route_domain_show, +}; +/* *INDENT-ON* */ + +static clib_error_t * +gbp_route_domain_init (vlib_main_t * vm) +{ + grd_logger = vlib_log_register_class ("gbp", "rd"); + gbp_fib_source = fib_source_allocate ("gbp-rd", + FIB_SOURCE_PRIORITY_HI, + FIB_SOURCE_BH_DROP); + + return (NULL); +} + +VLIB_INIT_FUNCTION (gbp_route_domain_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_route_domain.h b/extras/deprecated/plugins/gbp/gbp_route_domain.h new file mode 100644 index 00000000000..897c1bdd7ac --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_route_domain.h @@ -0,0 +1,84 @@ +/* + * 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_ROUTE_DOMAIN_H__ +#define __GBP_ROUTE_DOMAIN_H__ + +#include <plugins/gbp/gbp_types.h> + +#include <vnet/fib/fib_types.h> +#include <vnet/ethernet/mac_address.h> + +/** + * A route Domain Representation. + * This is a standard route-domain plus all the attributes it must + * have to supprt the GBP model. + */ +typedef struct gpb_route_domain_t_ +{ + /** + * Route-domain ID + */ + u32 grd_id; + gbp_scope_t grd_scope; + u32 grd_fib_index[FIB_PROTOCOL_IP_MAX]; + u32 grd_table_id[FIB_PROTOCOL_IP_MAX]; + + /** + * The interfaces on which to send packets to unnknown EPs + */ + u32 grd_uu_sw_if_index[FIB_PROTOCOL_IP_MAX]; + + /** + * adjacencies on the UU interfaces. + */ + u32 grd_adj[FIB_PROTOCOL_IP_MAX]; + + u32 grd_locks; +} gbp_route_domain_t; + +extern int gbp_route_domain_add_and_lock (u32 rd_id, + gbp_scope_t scope, + u32 ip4_table_id, + u32 ip6_table_id, + u32 ip4_uu_sw_if_index, + u32 ip6_uu_sw_if_index); +extern void gbp_route_domain_unlock (index_t grdi); +extern index_t gbp_route_domain_find_and_lock (u32 rd_id); +extern index_t gbp_route_domain_find (u32 rd_id); +extern index_t gbp_route_domain_index (const gbp_route_domain_t *); + +extern int gbp_route_domain_delete (u32 rd_id); +extern gbp_route_domain_t *gbp_route_domain_get (index_t i); +extern u32 gbp_route_domain_get_rd_id (index_t i); +extern gbp_scope_t gbp_route_domain_get_scope (index_t i); + +typedef int (*gbp_route_domain_cb_t) (gbp_route_domain_t * gb, void *ctx); +extern void gbp_route_domain_walk (gbp_route_domain_cb_t bgpe, void *ctx); + +extern const mac_address_t *gbp_route_domain_get_local_mac (void); +extern const mac_address_t *gbp_route_domain_get_remote_mac (void); + +extern u8 *format_gbp_route_domain (u8 * s, va_list * args); + +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_scanner.c b/extras/deprecated/plugins/gbp/gbp_scanner.c new file mode 100644 index 00000000000..9ae962b7449 --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_scanner.c @@ -0,0 +1,136 @@ +/* + * 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_scanner.h> +#include <plugins/gbp/gbp_endpoint.h> +#include <plugins/gbp/gbp_vxlan.h> + +/** + * Scanner logger + */ +vlib_log_class_t gs_logger; + +/** + * Scanner state + */ +static bool gs_enabled; + +#define GBP_SCANNER_DBG(...) \ + vlib_log_debug (gs_logger, __VA_ARGS__); + +static uword +gbp_scanner (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f) +{ + uword event_type, *event_data = 0; + bool do_scan = 0; + + while (1) + { + do_scan = 0; + + if (gs_enabled) + { + /* scan every 'inactive threshold' seconds */ + vlib_process_wait_for_event_or_clock (vm, 2); + } + else + vlib_process_wait_for_event (vm); + + event_type = vlib_process_get_events (vm, &event_data); + vec_reset_length (event_data); + + switch (event_type) + { + case ~0: + /* timer expired */ + do_scan = 1; + break; + + case GBP_ENDPOINT_SCAN_START: + gs_enabled = 1; + break; + + case GBP_ENDPOINT_SCAN_STOP: + gs_enabled = 0; + break; + + case GBP_ENDPOINT_SCAN_SET_TIME: + break; + + default: + ASSERT (0); + } + + if (do_scan) + { + GBP_SCANNER_DBG ("start"); + gbp_endpoint_scan (vm); + GBP_SCANNER_DBG ("stop"); + } + } + return 0; +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (gbp_scanner_node) = { + .function = gbp_scanner, + .type = VLIB_NODE_TYPE_PROCESS, + .name = "gbp-scanner", +}; +/* *INDENT-ON* */ + +static clib_error_t * +gbp_scanner_cli (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + vlib_cli_output (vm, "GBP-scanner: enabled:%d interval:2", gs_enabled); + + return (NULL); +} + +/*? + * Show GBP scanner + * + * @cliexpar + * @cliexstart{show gbp scanner} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_scanner_cli_node, static) = { + .path = "show gbp scanner", + .short_help = "show gbp scanner", + .function = gbp_scanner_cli, +}; +/* *INDENT-ON* */ + +static clib_error_t * +gbp_scanner_init (vlib_main_t * vm) +{ + gs_logger = vlib_log_register_class ("gbp", "scan"); + + return (NULL); +} + +VLIB_INIT_FUNCTION (gbp_scanner_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_scanner.h b/extras/deprecated/plugins/gbp/gbp_scanner.h new file mode 100644 index 00000000000..1133167d927 --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_scanner.h @@ -0,0 +1,30 @@ +/* + * 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_SCANNER_H__ +#define __GBP_SCANNER_H__ + +#include <vlib/vlib.h> + +typedef enum gbp_scan_event_t_ +{ + GBP_ENDPOINT_SCAN_START, + GBP_ENDPOINT_SCAN_STOP, + GBP_ENDPOINT_SCAN_SET_TIME, +} gbp_scan_event_t; + +extern vlib_node_registration_t gbp_scanner_node; + +#endif diff --git a/extras/deprecated/plugins/gbp/gbp_subnet.c b/extras/deprecated/plugins/gbp/gbp_subnet.c new file mode 100644 index 00000000000..8d3b571657c --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_subnet.c @@ -0,0 +1,598 @@ +/* + * 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 <plugins/gbp/gbp_route_domain.h> + +#include <vnet/fib/fib_table.h> +#include <vnet/dpo/load_balance.h> + +/** + * 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_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); + + if (~0 == grd->grd_uu_sw_if_index[fproto]) + return (VNET_API_ERROR_INVALID_SW_IF_INDEX); + + 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); + + return (0); +} + +static int +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; + + 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); + + 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); + + return (0); +} + +static int +gbp_subnet_l3_out_add (gbp_subnet_t * gs, sclass_t sclass, int is_anon) +{ + 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_del (u32 rd_id, const fib_prefix_t * pfx) +{ + gbp_route_domain_t *grd; + index_t gsi, grdi; + u32 fib_index; + + grdi = gbp_route_domain_find (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); + + if (INDEX_INVALID == gsi) + return (VNET_API_ERROR_NO_SUCH_ENTRY); + + gbp_subnet_del_i (gsi); + + return (0); +} + +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_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); + } + + 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) +{ + 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; + + if (is_add) + rv = gbp_subnet_add (rd_id, &pfx, type, sw_if_index, sclass); + else + rv = gbp_subnet_del (rd_id, &pfx); + + switch (rv) + { + case 0: + return 0; + case VNET_API_ERROR_NO_SUCH_FIB: + return clib_error_return (0, "no such FIB"); + } + + return clib_error_return (0, "unknown error %d", rv); +} + +/*? + * Add Group Based Policy Subnets + * + * @cliexpar + * @cliexstart{gbp subnet [del] rd <ID> prefix <prefix> type <type> [<interface>] [sclass <sclass>]} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_subnet_add_del, static) = { + .path = "gbp subnet", + .short_help = "gbp subnet [del] rd <ID> prefix <prefix> type <type> [<interface>] [sclass <sclass>]\n", + .function = gbp_subnet_add_del_cli, +}; +/* *INDENT-ON* */ + + + +void +gbp_subnet_walk (gbp_subnet_cb_t cb, void *ctx) +{ + gbp_route_domain_t *grd; + gbp_subnet_t *gs; + u32 sw_if_index; + sclass_t sclass; + + sclass = SCLASS_INVALID; + sw_if_index = ~0; + + /* *INDENT-OFF* */ + pool_foreach (gs, gbp_subnet_pool) + { + 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 + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_subnet.h b/extras/deprecated/plugins/gbp/gbp_subnet.h new file mode 100644 index 00000000000..6fbef01ceba --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_subnet.h @@ -0,0 +1,53 @@ +/* + * 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> + +typedef enum gbp_subnet_type_t_ +{ + GBP_SUBNET_TRANSPORT, + GBP_SUBNET_STITCHED_INTERNAL, + GBP_SUBNET_STITCHED_EXTERNAL, + GBP_SUBNET_L3_OUT, + GBP_SUBNET_ANON_L3_OUT, +} gbp_subnet_type_t; + +extern int gbp_subnet_add (u32 rd_id, + const fib_prefix_t * pfx, + gbp_subnet_type_t type, + u32 sw_if_index, sclass_t sclass); + +extern int gbp_subnet_del (u32 rd_id, const fib_prefix_t * pfx); + +typedef walk_rc_t (*gbp_subnet_cb_t) (u32 rd_id, + const fib_prefix_t * pfx, + gbp_subnet_type_t type, + u32 sw_if_index, + sclass_t sclass, 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/extras/deprecated/plugins/gbp/gbp_types.h b/extras/deprecated/plugins/gbp/gbp_types.h new file mode 100644 index 00000000000..ac983b1cdd2 --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_types.h @@ -0,0 +1,36 @@ +/* + * 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 vnid_t; +#define VNID_INVALID ((u16)~0) + +typedef u16 gbp_scope_t; +typedef u16 sclass_t; +#define SCLASS_INVALID ((u16)~0) + +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_vxlan.c b/extras/deprecated/plugins/gbp/gbp_vxlan.c new file mode 100644 index 00000000000..77e4d7ac11b --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_vxlan.c @@ -0,0 +1,654 @@ +/* + * 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_vxlan.h> +#include <plugins/gbp/gbp_learn.h> +#include <plugins/gbp/gbp_bridge_domain.h> +#include <plugins/gbp/gbp_route_domain.h> + +#include <vnet/vxlan-gbp/vxlan_gbp.h> +#include <vlibmemory/api.h> +#include <vnet/fib/fib_table.h> +#include <vlib/punt.h> + +/** + * A reference to a VXLAN-GBP tunnel created as a child/dependent tunnel + * of the template GBP-VXLAN tunnel + */ +typedef struct vxlan_tunnel_ref_t_ +{ + gbp_itf_hdl_t vxr_itf; + u32 vxr_sw_if_index; + index_t vxr_parent; + gbp_vxlan_tunnel_layer_t vxr_layer; +} vxlan_tunnel_ref_t; + +/** + * DB of added tunnels + */ +uword *gv_db; + +/** + * Logger + */ +static vlib_log_class_t gt_logger; + +/** + * Pool of template tunnels + */ +static gbp_vxlan_tunnel_t *gbp_vxlan_tunnel_pool; + +/** + * Pool of child tunnels + */ +static vxlan_tunnel_ref_t *vxlan_tunnel_ref_pool; + +/** + * DB of template interfaces by SW interface index + */ +static index_t *gbp_vxlan_tunnel_db; + +/** + * DB of child interfaces by SW interface index + */ +static index_t *vxlan_tunnel_ref_db; + +/** + * handle registered with the ;unt infra + */ +static vlib_punt_hdl_t punt_hdl; + +static char *gbp_vxlan_tunnel_layer_strings[] = { +#define _(n,s) [GBP_VXLAN_TUN_##n] = s, + foreach_gbp_vxlan_tunnel_layer +#undef _ +}; + +#define GBP_VXLAN_TUN_DBG(...) \ + vlib_log_debug (gt_logger, __VA_ARGS__); + + +gbp_vxlan_tunnel_t * +gbp_vxlan_tunnel_get (index_t gti) +{ + return (pool_elt_at_index (gbp_vxlan_tunnel_pool, gti)); +} + +static vxlan_tunnel_ref_t * +vxlan_tunnel_ref_get (index_t vxri) +{ + return (pool_elt_at_index (vxlan_tunnel_ref_pool, vxri)); +} + +static u8 * +format_vxlan_tunnel_ref (u8 * s, va_list * args) +{ + index_t vxri = va_arg (*args, u32); + vxlan_tunnel_ref_t *vxr; + + vxr = vxlan_tunnel_ref_get (vxri); + + s = format (s, "[%U]", format_gbp_itf_hdl, vxr->vxr_itf); + + return (s); +} + +static void +gdb_vxlan_dep_del (u32 sw_if_index) +{ + vxlan_tunnel_ref_t *vxr; + gbp_vxlan_tunnel_t *gt; + index_t vxri; + u32 pos; + + vxr = vxlan_tunnel_ref_get (vxlan_tunnel_ref_db[sw_if_index]); + vxri = vxr - vxlan_tunnel_ref_pool; + gt = gbp_vxlan_tunnel_get (vxr->vxr_parent); + + GBP_VXLAN_TUN_DBG ("del-dep:%U", format_vxlan_tunnel_ref, vxri); + + vxlan_tunnel_ref_db[vxr->vxr_sw_if_index] = INDEX_INVALID; + pos = vec_search (gt->gt_tuns, vxri); + + ASSERT (~0 != pos); + vec_del1 (gt->gt_tuns, pos); + + vnet_vxlan_gbp_tunnel_del (vxr->vxr_sw_if_index); + + pool_put (vxlan_tunnel_ref_pool, vxr); +} + +static gbp_itf_hdl_t +gdb_vxlan_dep_add (gbp_vxlan_tunnel_t * gt, + const ip46_address_t * src, const ip46_address_t * dst) +{ + vnet_vxlan_gbp_tunnel_add_del_args_t args = { + .is_add = 1, + .is_ip6 = !ip46_address_is_ip4 (src), + .vni = gt->gt_vni, + .src = *src, + .dst = *dst, + .instance = ~0, + .mode = (GBP_VXLAN_TUN_L2 == gt->gt_layer ? + VXLAN_GBP_TUNNEL_MODE_L2 : VXLAN_GBP_TUNNEL_MODE_L3), + }; + vxlan_tunnel_ref_t *vxr; + u32 sw_if_index; + index_t vxri; + int rv; + + sw_if_index = ~0; + rv = vnet_vxlan_gbp_tunnel_add_del (&args, &sw_if_index); + + if (VNET_API_ERROR_TUNNEL_EXIST == rv) + { + vxri = vxlan_tunnel_ref_db[sw_if_index]; + + vxr = vxlan_tunnel_ref_get (vxri); + gbp_itf_lock (vxr->vxr_itf); + } + else if (0 == rv) + { + ASSERT (~0 != sw_if_index); + GBP_VXLAN_TUN_DBG ("add-dep:%U %U %U %d", format_vnet_sw_if_index_name, + vnet_get_main (), sw_if_index, + format_ip46_address, src, IP46_TYPE_ANY, + format_ip46_address, dst, IP46_TYPE_ANY, gt->gt_vni); + + pool_get_zero (vxlan_tunnel_ref_pool, vxr); + + vxri = (vxr - vxlan_tunnel_ref_pool); + vxr->vxr_parent = gt - gbp_vxlan_tunnel_pool; + vxr->vxr_sw_if_index = sw_if_index; + vxr->vxr_layer = gt->gt_layer; + + /* + * store the child both on the parent's list and the global DB + */ + vec_add1 (gt->gt_tuns, vxri); + + vec_validate_init_empty (vxlan_tunnel_ref_db, + vxr->vxr_sw_if_index, INDEX_INVALID); + vxlan_tunnel_ref_db[vxr->vxr_sw_if_index] = vxri; + + if (GBP_VXLAN_TUN_L2 == vxr->vxr_layer) + { + l2output_feat_masks_t ofeat; + l2input_feat_masks_t ifeat; + gbp_bridge_domain_t *gbd; + + gbd = gbp_bridge_domain_get (gt->gt_gbd); + vxr->vxr_itf = gbp_itf_l2_add_and_lock_w_free + (vxr->vxr_sw_if_index, gt->gt_gbd, gdb_vxlan_dep_del); + + ofeat = L2OUTPUT_FEAT_GBP_POLICY_MAC; + ifeat = L2INPUT_FEAT_NONE; + + if (!(gbd->gb_flags & GBP_BD_FLAG_DO_NOT_LEARN)) + ifeat |= L2INPUT_FEAT_GBP_LEARN; + + gbp_itf_l2_set_output_feature (vxr->vxr_itf, ofeat); + gbp_itf_l2_set_input_feature (vxr->vxr_itf, ifeat); + } + else + { + vxr->vxr_itf = gbp_itf_l3_add_and_lock_w_free + (vxr->vxr_sw_if_index, gt->gt_grd, gdb_vxlan_dep_del); + + gbp_itf_l3_set_input_feature (vxr->vxr_itf, GBP_ITF_L3_FEAT_LEARN); + } + } + else + { + return (GBP_ITF_HDL_INVALID); + } + + return (vxr->vxr_itf); +} + +u32 +vxlan_gbp_tunnel_get_parent (u32 sw_if_index) +{ + ASSERT ((sw_if_index < vec_len (vxlan_tunnel_ref_db)) && + (INDEX_INVALID != vxlan_tunnel_ref_db[sw_if_index])); + + gbp_vxlan_tunnel_t *gt; + vxlan_tunnel_ref_t *vxr; + + vxr = vxlan_tunnel_ref_get (vxlan_tunnel_ref_db[sw_if_index]); + gt = gbp_vxlan_tunnel_get (vxr->vxr_parent); + + return (gt->gt_sw_if_index); +} + +gbp_itf_hdl_t +vxlan_gbp_tunnel_lock_itf (u32 sw_if_index) +{ + ASSERT ((sw_if_index < vec_len (vxlan_tunnel_ref_db)) && + (INDEX_INVALID != vxlan_tunnel_ref_db[sw_if_index])); + + vxlan_tunnel_ref_t *vxr; + + vxr = vxlan_tunnel_ref_get (vxlan_tunnel_ref_db[sw_if_index]); + + gbp_itf_lock (vxr->vxr_itf); + + return (vxr->vxr_itf); +} + + +gbp_vxlan_tunnel_type_t +gbp_vxlan_tunnel_get_type (u32 sw_if_index) +{ + if (sw_if_index < vec_len (vxlan_tunnel_ref_db) && + INDEX_INVALID != vxlan_tunnel_ref_db[sw_if_index]) + { + return (VXLAN_GBP_TUNNEL); + } + else if (sw_if_index < vec_len (gbp_vxlan_tunnel_db) && + INDEX_INVALID != gbp_vxlan_tunnel_db[sw_if_index]) + { + return (GBP_VXLAN_TEMPLATE_TUNNEL); + } + + ASSERT (0); + return (GBP_VXLAN_TEMPLATE_TUNNEL); +} + +gbp_itf_hdl_t +gbp_vxlan_tunnel_clone_and_lock (u32 sw_if_index, + const ip46_address_t * src, + const ip46_address_t * dst) +{ + gbp_vxlan_tunnel_t *gt; + index_t gti; + + gti = gbp_vxlan_tunnel_db[sw_if_index]; + + if (INDEX_INVALID == gti) + return (GBP_ITF_HDL_INVALID); + + gt = pool_elt_at_index (gbp_vxlan_tunnel_pool, gti); + + return (gdb_vxlan_dep_add (gt, src, dst)); +} + +void +vxlan_gbp_tunnel_unlock (u32 sw_if_index) +{ + /* vxlan_tunnel_ref_t *vxr; */ + /* index_t vxri; */ + + /* vxri = vxlan_tunnel_ref_db[sw_if_index]; */ + + /* ASSERT (vxri != INDEX_INVALID); */ + + /* vxr = vxlan_tunnel_ref_get (vxri); */ + + /* gdb_vxlan_dep_del (vxri); */ +} + +void +gbp_vxlan_walk (gbp_vxlan_cb_t cb, void *ctx) +{ + gbp_vxlan_tunnel_t *gt; + + /* *INDENT-OFF* */ + pool_foreach (gt, gbp_vxlan_tunnel_pool) + { + if (WALK_CONTINUE != cb(gt, ctx)) + break; + } + /* *INDENT-ON* */ +} + +static walk_rc_t +gbp_vxlan_tunnel_show_one (gbp_vxlan_tunnel_t * gt, void *ctx) +{ + vlib_cli_output (ctx, "%U", format_gbp_vxlan_tunnel, + gt - gbp_vxlan_tunnel_pool); + + return (WALK_CONTINUE); +} + +static u8 * +format_gbp_vxlan_tunnel_name (u8 * s, va_list * args) +{ + u32 dev_instance = va_arg (*args, u32); + + return format (s, "gbp-vxlan-%d", dev_instance); +} + +u8 * +format_gbp_vxlan_tunnel_layer (u8 * s, va_list * args) +{ + gbp_vxlan_tunnel_layer_t gl = va_arg (*args, gbp_vxlan_tunnel_layer_t); + s = format (s, "%s", gbp_vxlan_tunnel_layer_strings[gl]); + + return (s); +} + +u8 * +format_gbp_vxlan_tunnel (u8 * s, va_list * args) +{ + u32 dev_instance = va_arg (*args, u32); + CLIB_UNUSED (int verbose) = va_arg (*args, int); + gbp_vxlan_tunnel_t *gt = gbp_vxlan_tunnel_get (dev_instance); + index_t *vxri; + + s = format (s, " [%d] gbp-vxlan-tunnel: hw:%d sw:%d vni:%d %U", + dev_instance, gt->gt_hw_if_index, + gt->gt_sw_if_index, gt->gt_vni, + format_gbp_vxlan_tunnel_layer, gt->gt_layer); + if (GBP_VXLAN_TUN_L2 == gt->gt_layer) + s = format (s, " BD:%d gbd-index:%d", gt->gt_bd_rd_id, gt->gt_gbd); + else + s = format (s, " RD:%d grd-index:%d", gt->gt_bd_rd_id, gt->gt_grd); + + s = format (s, " dependents:"); + vec_foreach (vxri, gt->gt_tuns) + { + s = format (s, "\n %U, ", format_vxlan_tunnel_ref, *vxri); + } + + return s; +} + +typedef struct gbp_vxlan_tx_trace_t_ +{ + u32 vni; +} gbp_vxlan_tx_trace_t; + +u8 * +format_gbp_vxlan_tx_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_vxlan_tx_trace_t *t = va_arg (*args, gbp_vxlan_tx_trace_t *); + + s = format (s, "GBP-VXLAN: vni:%d", t->vni); + + return (s); +} + +clib_error_t * +gbp_vxlan_interface_admin_up_down (vnet_main_t * vnm, + u32 hw_if_index, u32 flags) +{ + vnet_hw_interface_t *hi; + u32 ti; + + hi = vnet_get_hw_interface (vnm, hw_if_index); + + if (NULL == gbp_vxlan_tunnel_db || + hi->sw_if_index >= vec_len (gbp_vxlan_tunnel_db)) + return (NULL); + + ti = gbp_vxlan_tunnel_db[hi->sw_if_index]; + + if (~0 == ti) + /* not one of ours */ + return (NULL); + + if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) + vnet_hw_interface_set_flags (vnm, hw_if_index, + VNET_HW_INTERFACE_FLAG_LINK_UP); + else + vnet_hw_interface_set_flags (vnm, hw_if_index, 0); + + return (NULL); +} + +static uword +gbp_vxlan_interface_tx (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + clib_warning ("you shouldn't be here, leaking buffers..."); + return frame->n_vectors; +} + +/* *INDENT-OFF* */ +VNET_DEVICE_CLASS (gbp_vxlan_device_class) = { + .name = "GBP VXLAN tunnel-template", + .format_device_name = format_gbp_vxlan_tunnel_name, + .format_device = format_gbp_vxlan_tunnel, + .format_tx_trace = format_gbp_vxlan_tx_trace, + .admin_up_down_function = gbp_vxlan_interface_admin_up_down, + .tx_function = gbp_vxlan_interface_tx, +}; + +VNET_HW_INTERFACE_CLASS (gbp_vxlan_hw_interface_class) = { + .name = "GBP-VXLAN", + .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P, +}; +/* *INDENT-ON* */ + +int +gbp_vxlan_tunnel_add (u32 vni, gbp_vxlan_tunnel_layer_t layer, + u32 bd_rd_id, + const ip4_address_t * src, u32 * sw_if_indexp) +{ + gbp_vxlan_tunnel_t *gt; + index_t gti; + uword *p; + int rv; + + rv = 0; + p = hash_get (gv_db, vni); + + GBP_VXLAN_TUN_DBG ("add: %d %d %d", vni, layer, bd_rd_id); + + if (NULL == p) + { + vnet_sw_interface_t *si; + vnet_hw_interface_t *hi; + index_t gbi, grdi; + vnet_main_t *vnm; + + gbi = grdi = INDEX_INVALID; + + if (layer == GBP_VXLAN_TUN_L2) + { + gbi = gbp_bridge_domain_find_and_lock (bd_rd_id); + + if (INDEX_INVALID == gbi) + { + return (VNET_API_ERROR_BD_NOT_MODIFIABLE); + } + } + else + { + grdi = gbp_route_domain_find_and_lock (bd_rd_id); + + if (INDEX_INVALID == grdi) + { + return (VNET_API_ERROR_NO_SUCH_FIB); + } + } + + vnm = vnet_get_main (); + pool_get (gbp_vxlan_tunnel_pool, gt); + gti = gt - gbp_vxlan_tunnel_pool; + + gt->gt_vni = vni; + gt->gt_layer = layer; + gt->gt_bd_rd_id = bd_rd_id; + gt->gt_src.ip4.as_u32 = src->as_u32; + gt->gt_hw_if_index = vnet_register_interface (vnm, + gbp_vxlan_device_class.index, + gti, + gbp_vxlan_hw_interface_class.index, + gti); + + hi = vnet_get_hw_interface (vnm, gt->gt_hw_if_index); + + gt->gt_sw_if_index = hi->sw_if_index; + + /* don't flood packets in a BD to these interfaces */ + si = vnet_get_sw_interface (vnm, gt->gt_sw_if_index); + si->flood_class = VNET_FLOOD_CLASS_NO_FLOOD; + + if (layer == GBP_VXLAN_TUN_L2) + { + gbp_bridge_domain_t *gb; + + gb = gbp_bridge_domain_get (gbi); + + gt->gt_gbd = gbi; + gb->gb_vni = gti; + /* set it up as a GBP interface */ + gt->gt_itf = gbp_itf_l2_add_and_lock (gt->gt_sw_if_index, + gt->gt_gbd); + gbp_itf_l2_set_input_feature (gt->gt_itf, L2INPUT_FEAT_GBP_LEARN); + } + else + { + gt->gt_grd = grdi; + gt->gt_itf = gbp_itf_l3_add_and_lock (gt->gt_sw_if_index, + gt->gt_grd); + gbp_itf_l3_set_input_feature (gt->gt_itf, GBP_ITF_L3_FEAT_LEARN); + } + + /* + * save the tunnel by VNI and by sw_if_index + */ + hash_set (gv_db, vni, gti); + + vec_validate_init_empty (gbp_vxlan_tunnel_db, + gt->gt_sw_if_index, INDEX_INVALID); + gbp_vxlan_tunnel_db[gt->gt_sw_if_index] = gti; + + if (sw_if_indexp) + *sw_if_indexp = gt->gt_sw_if_index; + + vxlan_gbp_register_udp_ports (); + } + else + { + gti = p[0]; + rv = VNET_API_ERROR_IF_ALREADY_EXISTS; + } + + GBP_VXLAN_TUN_DBG ("add: %U", format_gbp_vxlan_tunnel, gti); + + return (rv); +} + +int +gbp_vxlan_tunnel_del (u32 vni) +{ + gbp_vxlan_tunnel_t *gt; + uword *p; + + p = hash_get (gv_db, vni); + + if (NULL != p) + { + vnet_main_t *vnm; + + vnm = vnet_get_main (); + gt = gbp_vxlan_tunnel_get (p[0]); + + vxlan_gbp_unregister_udp_ports (); + + GBP_VXLAN_TUN_DBG ("del: %U", format_gbp_vxlan_tunnel, + gt - gbp_vxlan_tunnel_pool); + + gbp_endpoint_flush (GBP_ENDPOINT_SRC_DP, gt->gt_sw_if_index); + ASSERT (0 == vec_len (gt->gt_tuns)); + vec_free (gt->gt_tuns); + + gbp_itf_unlock (>->gt_itf); + + if (GBP_VXLAN_TUN_L2 == gt->gt_layer) + { + gbp_bridge_domain_unlock (gt->gt_gbd); + } + else + { + gbp_route_domain_unlock (gt->gt_grd); + } + + vnet_sw_interface_set_flags (vnm, gt->gt_sw_if_index, 0); + vnet_delete_hw_interface (vnm, gt->gt_hw_if_index); + + hash_unset (gv_db, vni); + gbp_vxlan_tunnel_db[gt->gt_sw_if_index] = INDEX_INVALID; + + pool_put (gbp_vxlan_tunnel_pool, gt); + } + else + return VNET_API_ERROR_NO_SUCH_ENTRY; + + return (0); +} + +static clib_error_t * +gbp_vxlan_show (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + + vlib_cli_output (vm, "GBP-VXLAN Interfaces:"); + + gbp_vxlan_walk (gbp_vxlan_tunnel_show_one, vm); + + return (NULL); +} + +/*? + * Show Group Based Policy VXLAN tunnels + * + * @cliexpar + * @cliexstart{show gbp vxlan} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_vxlan_show_node, static) = { + .path = "show gbp vxlan", + .short_help = "show gbp vxlan\n", + .function = gbp_vxlan_show, +}; +/* *INDENT-ON* */ + +static clib_error_t * +gbp_vxlan_init (vlib_main_t * vm) +{ + vxlan_gbp_main_t *vxm = &vxlan_gbp_main; + + gt_logger = vlib_log_register_class ("gbp", "tun"); + + punt_hdl = vlib_punt_client_register ("gbp-vxlan"); + + vlib_punt_register (punt_hdl, + vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP4], + "gbp-vxlan4"); + + return (0); +} + +/* *INDENT-OFF* */ +VLIB_INIT_FUNCTION (gbp_vxlan_init) = +{ + .runs_after = VLIB_INITS("punt_init", "vxlan_gbp_init"), +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_vxlan.h b/extras/deprecated/plugins/gbp/gbp_vxlan.h new file mode 100644 index 00000000000..706fe2a0e85 --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_vxlan.h @@ -0,0 +1,135 @@ +/* + * 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_VXLAN_H__ +#define __GBP_VXLAN_H__ + +#include <vnet/fib/fib_types.h> +#include <plugins/gbp/gbp_itf.h> + +#define foreach_gbp_vxlan_tunnel_layer \ + _ (L2, "l2") \ + _ (L3, "l3") + +typedef enum gbp_vxlan_tunnel_layer_t_ +{ +#define _(s,n) GBP_VXLAN_TUN_##s, + foreach_gbp_vxlan_tunnel_layer +#undef _ +} gbp_vxlan_tunnel_layer_t; + +/** + * GBP VXLAN (template) tunnel. + * A template tunnel has only a VNI, it does not have src,dst address. + * As such it cannot be used to send traffic. It is used in the RX path + * to RX vxlan-gbp packets that do not match an existing tunnel; + */ +typedef struct gbp_vxlan_tunnel_t_ +{ + u32 gt_hw_if_index; + u32 gt_sw_if_index; + u32 gt_vni; + + /** + * The BD or RD value (depending on the layer) that the tunnel is bound to + */ + u32 gt_bd_rd_id; + gbp_vxlan_tunnel_layer_t gt_layer; + + union + { + struct + { + /** + * Reference to the GPB-BD + */ + index_t gt_gbd; + }; + struct + { + /** + * References to the GBP-RD + */ + index_t gt_grd; + }; + }; + + /** + * gbp-itf config for this interface + */ + gbp_itf_hdl_t gt_itf; + + /** + * list of child vxlan-gbp tunnels built from this template + */ + index_t *gt_tuns; + + /** + * The source address to use for child tunnels + */ + ip46_address_t gt_src; +} gbp_vxlan_tunnel_t; + +/** + * The different types of interfaces that endpoints are learned on + */ +typedef enum gbp_vxlan_tunnel_type_t_ +{ + /** + * This is the object type defined above. + * A template representation of a vxlan-gbp tunnel. from this tunnel + * type, real vxlan-gbp tunnels are created (by cloning the VNI) + */ + GBP_VXLAN_TEMPLATE_TUNNEL, + + /** + * A real VXLAN-GBP tunnel (from vnet/vxlan-gbp/...) + */ + VXLAN_GBP_TUNNEL, +} gbp_vxlan_tunnel_type_t; + +extern int gbp_vxlan_tunnel_add (u32 vni, gbp_vxlan_tunnel_layer_t layer, + u32 bd_rd_id, + const ip4_address_t * src, + u32 * sw_if_indexp); +extern int gbp_vxlan_tunnel_del (u32 vni); + +extern gbp_vxlan_tunnel_type_t gbp_vxlan_tunnel_get_type (u32 sw_if_index); + +extern gbp_itf_hdl_t gbp_vxlan_tunnel_clone_and_lock (u32 parent_tunnel, + const ip46_address_t * + src, + const ip46_address_t * + dst); + +extern u32 vxlan_gbp_tunnel_get_parent (u32 sw_if_index); +extern gbp_itf_hdl_t vxlan_gbp_tunnel_lock_itf (u32 sw_if_index); + +typedef walk_rc_t (*gbp_vxlan_cb_t) (gbp_vxlan_tunnel_t * gt, void *ctx); +extern void gbp_vxlan_walk (gbp_vxlan_cb_t cb, void *ctx); + +extern u8 *format_gbp_vxlan_tunnel (u8 * s, va_list * args); +extern u8 *format_gbp_vxlan_tunnel_layer (u8 * s, va_list * args); + +extern gbp_vxlan_tunnel_t *gbp_vxlan_tunnel_get (index_t gti); +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/gbp_vxlan_node.c b/extras/deprecated/plugins/gbp/gbp_vxlan_node.c new file mode 100644 index 00000000000..413a9f47e1b --- /dev/null +++ b/extras/deprecated/plugins/gbp/gbp_vxlan_node.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2019 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_vxlan.h> +#include <plugins/gbp/gbp_itf.h> +#include <plugins/gbp/gbp_learn.h> +#include <plugins/gbp/gbp_bridge_domain.h> +#include <plugins/gbp/gbp_route_domain.h> + +#include <vnet/vxlan-gbp/vxlan_gbp.h> +#include <vlibmemory/api.h> +#include <vnet/fib/fib_table.h> + +extern uword *gv_db; + +typedef struct gbp_vxlan_trace_t_ +{ + u8 dropped; + u32 vni; + u32 sw_if_index; + u16 sclass; + u8 flags; +} gbp_vxlan_trace_t; + +#define foreach_gbp_vxlan_input_next \ + _(DROP, "error-drop") \ + _(L2_INPUT, "l2-input") \ + _(IP4_INPUT, "ip4-input") \ + _(IP6_INPUT, "ip6-input") + +typedef enum +{ +#define _(s,n) GBP_VXLAN_INPUT_NEXT_##s, + foreach_gbp_vxlan_input_next +#undef _ + GBP_VXLAN_INPUT_N_NEXT, +} gbp_vxlan_input_next_t; + + +#define foreach_gbp_vxlan_error \ + _(DECAPPED, "decapped") \ + _(LEARNED, "learned") + +typedef enum +{ +#define _(s,n) GBP_VXLAN_ERROR_##s, + foreach_gbp_vxlan_error +#undef _ + GBP_VXLAN_N_ERROR, +} gbp_vxlan_input_error_t; + +static char *gbp_vxlan_error_strings[] = { +#define _(n,s) s, + foreach_gbp_vxlan_error +#undef _ +}; + +static uword +gbp_vxlan_decap (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame, u8 is_ip4) +{ + u32 n_left_to_next, n_left_from, next_index, *to_next, *from; + + next_index = 0; + from = vlib_frame_vector_args (from_frame); + n_left_from = from_frame->n_vectors; + + while (n_left_from > 0) + { + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + vxlan_gbp_header_t *vxlan_gbp0; + gbp_vxlan_input_next_t next0; + gbp_vxlan_tunnel_t *gt0; + vlib_buffer_t *b0; + u32 bi0, vni0; + uword *p; + + bi0 = to_next[0] = from[0]; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + next0 = GBP_VXLAN_INPUT_NEXT_DROP; + + b0 = vlib_get_buffer (vm, bi0); + vxlan_gbp0 = + vlib_buffer_get_current (b0) - sizeof (vxlan_gbp_header_t); + + vni0 = vxlan_gbp_get_vni (vxlan_gbp0); + p = hash_get (gv_db, vni0); + + if (PREDICT_FALSE (NULL == p)) + { + gt0 = NULL; + next0 = GBP_VXLAN_INPUT_NEXT_DROP; + } + else + { + gt0 = gbp_vxlan_tunnel_get (p[0]); + + vnet_buffer (b0)->sw_if_index[VLIB_RX] = gt0->gt_sw_if_index; + + if (GBP_VXLAN_TUN_L2 == gt0->gt_layer) + /* + * An L2 layer tunnel goes into the BD + */ + next0 = GBP_VXLAN_INPUT_NEXT_L2_INPUT; + else + { + /* + * An L3 layer tunnel needs to strip the L2 header + * an inject into the RD + */ + ethernet_header_t *e0; + u16 type0; + + e0 = vlib_buffer_get_current (b0); + type0 = clib_net_to_host_u16 (e0->type); + switch (type0) + { + case ETHERNET_TYPE_IP4: + next0 = GBP_VXLAN_INPUT_NEXT_IP4_INPUT; + break; + case ETHERNET_TYPE_IP6: + next0 = GBP_VXLAN_INPUT_NEXT_IP6_INPUT; + break; + default: + goto trace; + } + vlib_buffer_advance (b0, sizeof (*e0)); + } + } + + trace: + if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) + { + gbp_vxlan_trace_t *tr; + + tr = vlib_add_trace (vm, node, b0, sizeof (*tr)); + tr->dropped = (next0 == GBP_VXLAN_INPUT_NEXT_DROP); + tr->vni = vni0; + tr->sw_if_index = (gt0 ? gt0->gt_sw_if_index : ~0); + tr->flags = vxlan_gbp_get_gpflags (vxlan_gbp0); + tr->sclass = vxlan_gbp_get_sclass (vxlan_gbp0); + } + + 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; +} + +VLIB_NODE_FN (gbp_vxlan4_input_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + return gbp_vxlan_decap (vm, node, from_frame, 1); +} + +static u8 * +format_gbp_vxlan_rx_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_vxlan_trace_t *t = va_arg (*args, gbp_vxlan_trace_t *); + + s = format (s, "vni:%d dropped:%d rx:%d sclass:%d flags:%U", + t->vni, t->dropped, t->sw_if_index, + t->sclass, format_vxlan_gbp_header_gpflags, t->flags); + + return (s); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (gbp_vxlan4_input_node) = +{ + .name = "gbp-vxlan4", + .vector_size = sizeof (u32), + .n_errors = GBP_VXLAN_N_ERROR, + .error_strings = gbp_vxlan_error_strings, + .n_next_nodes = GBP_VXLAN_INPUT_N_NEXT, + .format_trace = format_gbp_vxlan_rx_trace, + .next_nodes = { +#define _(s,n) [GBP_VXLAN_INPUT_NEXT_##s] = n, + foreach_gbp_vxlan_input_next +#undef _ + }, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/gbp/test_gbp.py b/extras/deprecated/plugins/gbp/test_gbp.py new file mode 100644 index 00000000000..c30a729519d --- /dev/null +++ b/extras/deprecated/plugins/gbp/test_gbp.py @@ -0,0 +1,5926 @@ +#!/usr/bin/env python3 +import typing +from socket import AF_INET6, inet_pton, inet_ntop +import unittest +from ipaddress import ip_address, IPv4Network, IPv6Network + +from scapy.packet import Raw +from scapy.layers.l2 import Ether, ARP, Dot1Q +from scapy.layers.inet import IP, UDP, ICMP +from scapy.layers.inet6 import ( + IPv6, + ICMPv6ND_NS, + ICMPv6NDOptSrcLLAddr, + ICMPv6ND_NA, + ICMPv6EchoRequest, +) +from scapy.utils6 import in6_getnsma, in6_getnsmac +from scapy.layers.vxlan import VXLAN +from scapy.data import ETH_P_IP, ETH_P_IPV6 + +from framework import tag_fixme_vpp_workers +from framework import VppTestCase, VppTestRunner +from vpp_object import VppObject +from vpp_interface import VppInterface +from vpp_ip_route import ( + VppIpRoute, + VppRoutePath, + VppIpTable, + VppIpInterfaceAddress, + VppIpInterfaceBind, + find_route, + FibPathProto, + FibPathType, +) +from vpp_l2 import ( + VppBridgeDomain, + VppBridgeDomainPort, + VppBridgeDomainArpEntry, + VppL2FibEntry, + find_bridge_domain_port, + VppL2Vtr, +) +from vpp_sub_interface import L2_VTR_OP, VppDot1QSubint +from vpp_ip import DpoProto, get_dpo_proto +from vpp_papi import VppEnum, MACAddress +from vpp_vxlan_gbp_tunnel import find_vxlan_gbp_tunnel, INDEX_INVALID, \ + VppVxlanGbpTunnel +from vpp_neighbor import VppNeighbor +from vpp_acl import AclRule, VppAcl + +NUM_PKTS = 67 + + +def find_gbp_endpoint(test, sw_if_index=None, ip=None, mac=None, + tep=None, sclass=None, flags=None): + if ip: + vip = ip + if mac: + vmac = MACAddress(mac) + + eps = test.vapi.gbp_endpoint_dump() + + for ep in eps: + if tep: + src = tep[0] + dst = tep[1] + if src != str(ep.endpoint.tun.src) or \ + dst != str(ep.endpoint.tun.dst): + continue + if sw_if_index: + if ep.endpoint.sw_if_index != sw_if_index: + continue + if sclass: + if ep.endpoint.sclass != sclass: + continue + if flags: + if flags != (flags & ep.endpoint.flags): + continue + if ip: + for eip in ep.endpoint.ips: + if vip == str(eip): + return True + if mac: + if vmac == ep.endpoint.mac: + return True + + return False + + +def find_gbp_vxlan(test: VppTestCase, vni): + ts = test.vapi.gbp_vxlan_tunnel_dump() + for t in ts: + if t.tunnel.vni == vni: + return True + return False + + +class VppGbpEndpoint(VppObject): + """ + GBP Endpoint + """ + + @property + def mac(self): + return str(self.vmac) + + @property + def ip4(self): + return self._ip4 + + @property + def fip4(self): + return self._fip4 + + @property + def ip6(self): + return self._ip6 + + @property + def fip6(self): + return self._fip6 + + @property + def ips(self): + return [self.ip4, self.ip6] + + @property + def fips(self): + return [self.fip4, self.fip6] + + def __init__(self, test, itf, epg, recirc, ip4, fip4, ip6, fip6, + flags=0, + tun_src="0.0.0.0", + tun_dst="0.0.0.0", + mac=True): + self._test = test + self.itf = itf + self.handle = None + self.epg = epg + self.recirc = recirc + + self._ip4 = ip4 + self._fip4 = fip4 + self._ip6 = ip6 + self._fip6 = fip6 + + if mac: + self.vmac = MACAddress(self.itf.remote_mac) + else: + self.vmac = MACAddress("00:00:00:00:00:00") + + self.flags = flags + self.tun_src = tun_src + self.tun_dst = tun_dst + + def encode(self): + ips = [self.ip4, self.ip6] + return { + "sw_if_index": self.itf.sw_if_index, + "ips": ips, + "n_ips": len(ips), + "mac": self.vmac.packed, + "sclass": self.epg.sclass, + "flags": self.flags, + "tun": { + "src": self.tun_src, + "dst": self.tun_dst, + }, + } + + def add_vpp_config(self): + res = self._test.vapi.gbp_endpoint_add( + endpoint=self.encode(), + ) + self.handle = res.handle + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.gbp_endpoint_del(handle=self.handle) + + def object_id(self): + return "gbp-endpoint:[%d==%d:%s:%d]" % (self.handle, + self.itf.sw_if_index, + self.ip4, + self.epg.sclass) + + def query_vpp_config(self): + return find_gbp_endpoint(self._test, + self.itf.sw_if_index, + self.ip4) + + +class VppGbpRecirc(VppObject): + """ + GBP Recirculation Interface + """ + + def __init__(self, test, epg, recirc, is_ext=False): + self._test = test + self.recirc = recirc + self.epg = epg + self.is_ext = is_ext + + def encode(self): + return { + "is_ext": self.is_ext, + "sw_if_index": self.recirc.sw_if_index, + "sclass": self.epg.sclass, + } + + def add_vpp_config(self): + self._test.vapi.gbp_recirc_add_del( + 1, + recirc=self.encode(), + ) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.gbp_recirc_add_del( + 0, + recirc=self.encode(), + ) + + def object_id(self): + return "gbp-recirc:[%d]" % (self.recirc.sw_if_index) + + def query_vpp_config(self): + rs = self._test.vapi.gbp_recirc_dump() + for r in rs: + if r.recirc.sw_if_index == self.recirc.sw_if_index: + return True + return False + + +class VppGbpExtItf(VppObject): + """ + GBP ExtItfulation Interface + """ + + def __init__(self, test, itf, bd, rd, anon=False): + self._test = test + self.itf = itf + self.bd = bd + self.rd = rd + self.flags = 1 if anon else 0 + + def encode(self): + return { + "sw_if_index": self.itf.sw_if_index, + "bd_id": self.bd.bd_id, + "rd_id": self.rd.rd_id, + "flags": self.flags, + } + + def add_vpp_config(self): + self._test.vapi.gbp_ext_itf_add_del( + 1, + ext_itf=self.encode(), + ) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.gbp_ext_itf_add_del( + 0, + ext_itf=self.encode(), + ) + + def object_id(self): + return "gbp-ext-itf:[%d]%s" % (self.itf.sw_if_index, + " [anon]" if self.flags else "") + + def query_vpp_config(self): + rs = self._test.vapi.gbp_ext_itf_dump() + for r in rs: + if r.ext_itf.sw_if_index == self.itf.sw_if_index: + return True + return False + + +class VppGbpSubnet(VppObject): + """ + GBP Subnet + """ + + def __init__(self, test, rd, address, address_len, + type, sw_if_index=0xffffffff, sclass=0xffff): + # TODO: replace hardcoded defaults when vpp_papi supports + # defaults in typedefs + self._test = test + self.rd_id = rd.rd_id + a = ip_address(address) + if 4 == a.version: + self.prefix = IPv4Network("%s/%d" % (address, address_len), + strict=False) + else: + self.prefix = IPv6Network("%s/%d" % (address, address_len), + strict=False) + self.type = type + self.sw_if_index = sw_if_index + self.sclass = sclass + + def encode(self): + return { + "type": self.type, + "sw_if_index": self.sw_if_index, + "sclass": self.sclass, + "prefix": self.prefix, + "rd_id": self.rd_id, + } + + def add_vpp_config(self): + self._test.vapi.gbp_subnet_add_del( + is_add=1, + subnet=self.encode(), + ) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.gbp_subnet_add_del( + is_add=0, + subnet=self.encode() + ) + + def object_id(self): + return "gbp-subnet:[%d-%s]" % (self.rd_id, self.prefix) + + def query_vpp_config(self): + ss = self._test.vapi.gbp_subnet_dump() + for s in ss: + if s.subnet.rd_id == self.rd_id and \ + s.subnet.type == self.type and \ + s.subnet.prefix == self.prefix: + return True + return False + + +class VppGbpEndpointRetention(object): + def __init__(self, remote_ep_timeout=0xffffffff): + self.remote_ep_timeout = remote_ep_timeout + + def encode(self): + return {'remote_ep_timeout': self.remote_ep_timeout} + + +class VppGbpEndpointGroup(VppObject): + """ + GBP Endpoint Group + """ + + def __init__(self, test, vnid, sclass, rd, bd, uplink, + bvi, bvi_ip4, bvi_ip6=None, + retention=VppGbpEndpointRetention()): + self._test = test + self.uplink = uplink + self.bvi = bvi + self.bvi_ip4 = bvi_ip4 + self.bvi_ip6 = bvi_ip6 + self.vnid = vnid + self.bd = bd # VppGbpBridgeDomain + self.rd = rd + self.sclass = sclass + if 0 == self.sclass: + self.sclass = 0xffff + self.retention = retention + + def encode(self) -> dict: + return { + "uplink_sw_if_index": self.uplink.sw_if_index + if self.uplink else INDEX_INVALID, + "bd_id": self.bd.bd.bd_id, + "rd_id": self.rd.rd_id, + "vnid": self.vnid, + "sclass": self.sclass, + "retention": self.retention.encode(), + } + + def add_vpp_config(self): + self._test.vapi.gbp_endpoint_group_add(epg=self.encode()) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.gbp_endpoint_group_del(sclass=self.sclass) + + def object_id(self) -> str: + return "gbp-endpoint-group:[%d]" % (self.vnid) + + def query_vpp_config(self) -> bool: + epgs = self._test.vapi.gbp_endpoint_group_dump() + for epg in epgs: + if epg.epg.vnid == self.vnid: + return True + return False + + +class VppGbpBridgeDomain(VppObject): + """ + GBP Bridge Domain + """ + + def __init__(self, test, bd, rd, bvi, + uu_fwd: typing.Optional[VppVxlanGbpTunnel] = None, + bm_flood=None, learn=True, + uu_drop=False, bm_drop=False, + ucast_arp=False): + self._test = test + self.bvi = bvi + self.uu_fwd = uu_fwd + self.bm_flood = bm_flood + self.bd = bd + self.rd = rd + + e = VppEnum.vl_api_gbp_bridge_domain_flags_t + + self.flags = e.GBP_BD_API_FLAG_NONE + if not learn: + self.flags |= e.GBP_BD_API_FLAG_DO_NOT_LEARN + if uu_drop: + self.flags |= e.GBP_BD_API_FLAG_UU_FWD_DROP + if bm_drop: + self.flags |= e.GBP_BD_API_FLAG_MCAST_DROP + if ucast_arp: + self.flags |= e.GBP_BD_API_FLAG_UCAST_ARP + + def encode(self) -> dict: + return { + "flags": self.flags, + "bvi_sw_if_index": self.bvi.sw_if_index, + "uu_fwd_sw_if_index": self.uu_fwd.sw_if_index + if self.uu_fwd else INDEX_INVALID, + "bm_flood_sw_if_index": self.bm_flood.sw_if_index + if self.bm_flood else INDEX_INVALID, + "bd_id": self.bd.bd_id, + "rd_id": self.rd.rd_id, + } + + def add_vpp_config(self): + self._test.vapi.gbp_bridge_domain_add( + bd=self.encode(), + ) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.gbp_bridge_domain_del(bd_id=self.bd.bd_id) + + def object_id(self) -> str: + return "gbp-bridge-domain:[%d]" % (self.bd.bd_id) + + def query_vpp_config(self) -> bool: + bds = self._test.vapi.gbp_bridge_domain_dump() + for bd in bds: + if bd.bd.bd_id == self.bd.bd_id: + return True + return False + + +class VppGbpRouteDomain(VppObject): + """ + GBP Route Domain + """ + + def __init__(self, test, rd_id, scope, t4, t6, ip4_uu=None, ip6_uu=None): + self._test = test + self.rd_id = rd_id + self.scope = scope + self.t4 = t4 + self.t6 = t6 + self.ip4_uu = ip4_uu + self.ip6_uu = ip6_uu + + def encode(self) -> dict: + return { + "rd_id": self.rd_id, + "scope": self.scope, + "ip4_table_id": self.t4.table_id, + "ip6_table_id": self.t6.table_id, + "ip4_uu_sw_if_index": self.ip4_uu.sw_if_index + if self.ip4_uu else INDEX_INVALID, + "ip6_uu_sw_if_index": self.ip6_uu.sw_if_index + if self.ip6_uu else INDEX_INVALID, + + } + + def add_vpp_config(self): + self._test.vapi.gbp_route_domain_add( + rd=self.encode(), + ) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.gbp_route_domain_del(rd_id=self.rd_id) + + def object_id(self): + return "gbp-route-domain:[%d]" % (self.rd_id) + + def query_vpp_config(self): + rds = self._test.vapi.gbp_route_domain_dump() + for rd in rds: + if rd.rd.rd_id == self.rd_id: + return True + return False + + +class VppGbpContractNextHop: + def __init__(self, mac, bd, ip, rd): + self.mac = mac + self.ip = ip + self.bd = bd + self.rd = rd + + def encode(self) -> dict: + return { + "ip": self.ip, + "mac": self.mac.packed, + "bd_id": self.bd.bd.bd_id, + "rd_id": self.rd.rd_id, + } + + +class VppGbpContractRule: + def __init__(self, action, hash_mode, nhs=None): + self.action = action + self.hash_mode = hash_mode + self.nhs = [] if nhs is None else nhs + + def encode(self) -> dict: + nhs = [] + for nh in self.nhs: + nhs.append(nh.encode()) + while len(nhs) < 8: + nhs.append({}) + return {'action': self.action, + 'nh_set': { + 'hash_mode': self.hash_mode, + 'n_nhs': len(self.nhs), + 'nhs': nhs}} + + def __repr__(self): + return '<VppGbpContractRule action=%s, hash_mode=%s>' % ( + self.action, self.hash_mode) + + +class VppGbpContract(VppObject): + """ + GBP Contract + """ + + def __init__(self, test, scope, sclass, dclass, acl_index, + rules: list, allowed_ethertypes: list): + self._test = test + self.scope = scope + self.acl_index = acl_index + self.sclass = sclass + self.dclass = dclass + self.rules = rules + self.allowed_ethertypes = allowed_ethertypes + while (len(self.allowed_ethertypes) < 16): + self.allowed_ethertypes.append(0) + + def encode(self) -> dict: + rules = [] + for r in self.rules: + rules.append(r.encode()) + return { + 'acl_index': self.acl_index, + 'scope': self.scope, + 'sclass': self.sclass, + 'dclass': self.dclass, + 'n_rules': len(rules), + 'rules': rules, + 'n_ether_types': len(self.allowed_ethertypes), + 'allowed_ethertypes': self.allowed_ethertypes, + } + + def add_vpp_config(self): + r = self._test.vapi.gbp_contract_add_del( + is_add=1, + contract=self.encode() + ) + + self.stats_index = r.stats_index + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.gbp_contract_add_del( + is_add=0, + contract=self.encode(), + ) + + def object_id(self): + return "gbp-contract:[%d:%d:%d:%d]" % (self.scope, + self.sclass, + self.dclass, + self.acl_index) + + def query_vpp_config(self): + cs = self._test.vapi.gbp_contract_dump() + for c in cs: + if c.contract.scope == self.scope \ + and c.contract.sclass == self.sclass \ + and c.contract.dclass == self.dclass: + return True + return False + + def get_drop_stats(self): + c = self._test.statistics.get_counter("/net/gbp/contract/drop") + return c[0][self.stats_index] + + def get_permit_stats(self): + c = self._test.statistics.get_counter("/net/gbp/contract/permit") + return c[0][self.stats_index] + + +class VppGbpVxlanTunnel(VppInterface): + """ + GBP VXLAN tunnel + """ + + def __init__(self, test, vni, bd_rd_id, mode, src): + super(VppGbpVxlanTunnel, self).__init__(test) + self._test = test + self.vni = vni + self.bd_rd_id = bd_rd_id + self.mode = mode + self.src = src + + def encode(self) -> dict: + return { + "vni": self.vni, + "mode": self.mode, + "bd_rd_id": self.bd_rd_id, + "src": self.src, + } + + def add_vpp_config(self): + r = self._test.vapi.gbp_vxlan_tunnel_add( + tunnel=self.encode(), + ) + self.set_sw_if_index(r.sw_if_index) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.gbp_vxlan_tunnel_del(vni=self.vni) + + def object_id(self): + return "gbp-vxlan:%d" % (self.sw_if_index) + + def query_vpp_config(self): + return find_gbp_vxlan(self._test, self.vni) + + +@tag_fixme_vpp_workers +class TestGBP(VppTestCase): + """ GBP Test Case """ + + @property + def nat_config_flags(self): + return VppEnum.vl_api_nat_config_flags_t + + @property + def nat44_config_flags(self): + return VppEnum.vl_api_nat44_config_flags_t + + @classmethod + def setUpClass(cls): + super(TestGBP, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(TestGBP, cls).tearDownClass() + + def setUp(self): + super(TestGBP, self).setUp() + + self.create_pg_interfaces(range(9)) + self.create_loopback_interfaces(8) + + self.router_mac = MACAddress("00:11:22:33:44:55") + + for i in self.pg_interfaces: + i.admin_up() + for i in self.lo_interfaces: + i.admin_up() + + self.vlan_100 = VppDot1QSubint(self, self.pg0, 100) + self.vlan_100.admin_up() + self.vlan_101 = VppDot1QSubint(self, self.pg0, 101) + self.vlan_101.admin_up() + self.vlan_102 = VppDot1QSubint(self, self.pg0, 102) + self.vlan_102.admin_up() + + def tearDown(self): + for i in self.pg_interfaces: + i.admin_down() + super(TestGBP, self).tearDown() + for i in self.lo_interfaces: + i.remove_vpp_config() + self.lo_interfaces = [] + self.vlan_102.remove_vpp_config() + self.vlan_101.remove_vpp_config() + self.vlan_100.remove_vpp_config() + + def send_and_expect_bridged(self, src, tx, dst): + rx = self.send_and_expect(src, tx, dst) + + for r in rx: + self.assertEqual(r[Ether].src, tx[0][Ether].src) + self.assertEqual(r[Ether].dst, tx[0][Ether].dst) + self.assertEqual(r[IP].src, tx[0][IP].src) + self.assertEqual(r[IP].dst, tx[0][IP].dst) + return rx + + def send_and_expect_bridged6(self, src, tx, dst): + rx = self.send_and_expect(src, tx, dst) + + for r in rx: + self.assertEqual(r[Ether].src, tx[0][Ether].src) + self.assertEqual(r[Ether].dst, tx[0][Ether].dst) + self.assertEqual(r[IPv6].src, tx[0][IPv6].src) + self.assertEqual(r[IPv6].dst, tx[0][IPv6].dst) + return rx + + def send_and_expect_routed(self, src, tx, dst, src_mac): + rx = self.send_and_expect(src, tx, dst) + + for r in rx: + self.assertEqual(r[Ether].src, src_mac) + self.assertEqual(r[Ether].dst, dst.remote_mac) + self.assertEqual(r[IP].src, tx[0][IP].src) + self.assertEqual(r[IP].dst, tx[0][IP].dst) + return rx + + def send_and_expect_routed6(self, src, tx, dst, src_mac): + rx = self.send_and_expect(src, tx, dst) + + for r in rx: + self.assertEqual(r[Ether].src, src_mac) + self.assertEqual(r[Ether].dst, dst.remote_mac) + self.assertEqual(r[IPv6].src, tx[0][IPv6].src) + self.assertEqual(r[IPv6].dst, tx[0][IPv6].dst) + return rx + + def send_and_expect_natted(self, src, tx, dst, src_ip): + rx = self.send_and_expect(src, tx, dst) + + for r in rx: + self.assertEqual(r[Ether].src, tx[0][Ether].src) + self.assertEqual(r[Ether].dst, tx[0][Ether].dst) + self.assertEqual(r[IP].src, src_ip) + self.assertEqual(r[IP].dst, tx[0][IP].dst) + return rx + + def send_and_expect_natted6(self, src, tx, dst, src_ip): + rx = self.send_and_expect(src, tx, dst) + + for r in rx: + self.assertEqual(r[Ether].src, tx[0][Ether].src) + self.assertEqual(r[Ether].dst, tx[0][Ether].dst) + self.assertEqual(r[IPv6].src, src_ip) + self.assertEqual(r[IPv6].dst, tx[0][IPv6].dst) + return rx + + def send_and_expect_unnatted(self, src, tx, dst, dst_ip): + rx = self.send_and_expect(src, tx, dst) + + for r in rx: + self.assertEqual(r[Ether].src, tx[0][Ether].src) + self.assertEqual(r[Ether].dst, tx[0][Ether].dst) + self.assertEqual(r[IP].dst, dst_ip) + self.assertEqual(r[IP].src, tx[0][IP].src) + return rx + + def send_and_expect_unnatted6(self, src, tx, dst, dst_ip): + rx = self.send_and_expect(src, tx, dst) + + for r in rx: + self.assertEqual(r[Ether].src, tx[0][Ether].src) + self.assertEqual(r[Ether].dst, tx[0][Ether].dst) + self.assertEqual(r[IPv6].dst, dst_ip) + self.assertEqual(r[IPv6].src, tx[0][IPv6].src) + return rx + + def send_and_expect_double_natted(self, src, tx, dst, src_ip, dst_ip): + rx = self.send_and_expect(src, tx, dst) + + for r in rx: + self.assertEqual(r[Ether].src, str(self.router_mac)) + self.assertEqual(r[Ether].dst, dst.remote_mac) + self.assertEqual(r[IP].dst, dst_ip) + self.assertEqual(r[IP].src, src_ip) + return rx + + def send_and_expect_double_natted6(self, src, tx, dst, src_ip, dst_ip): + rx = self.send_and_expect(src, tx, dst) + + for r in rx: + self.assertEqual(r[Ether].src, str(self.router_mac)) + self.assertEqual(r[Ether].dst, dst.remote_mac) + self.assertEqual(r[IPv6].dst, dst_ip) + self.assertEqual(r[IPv6].src, src_ip) + return rx + + def send_and_expect_no_arp(self, src, tx, dst): + self.pg_send(src, tx) + dst.get_capture(0, timeout=1) + dst.assert_nothing_captured(remark="") + + def send_and_expect_arp(self, src, tx, dst): + rx = self.send_and_expect(src, tx, dst) + + for r in rx: + self.assertEqual(r[Ether].src, tx[0][Ether].src) + self.assertEqual(r[Ether].dst, tx[0][Ether].dst) + self.assertEqual(r[ARP].psrc, tx[0][ARP].psrc) + self.assertEqual(r[ARP].pdst, tx[0][ARP].pdst) + self.assertEqual(r[ARP].hwsrc, tx[0][ARP].hwsrc) + self.assertEqual(r[ARP].hwdst, tx[0][ARP].hwdst) + return rx + + def test_gbp(self): + """ Group Based Policy """ + + ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t + + # + # Route Domains + # + gt4 = VppIpTable(self, 0) + gt4.add_vpp_config() + gt6 = VppIpTable(self, 0, is_ip6=True) + gt6.add_vpp_config() + nt4 = VppIpTable(self, 20) + nt4.add_vpp_config() + nt6 = VppIpTable(self, 20, is_ip6=True) + nt6.add_vpp_config() + + rd0 = VppGbpRouteDomain(self, 0, 400, gt4, gt6, None, None) + rd20 = VppGbpRouteDomain(self, 20, 420, nt4, nt6, None, None) + + rd0.add_vpp_config() + rd20.add_vpp_config() + + # + # Bridge Domains + # + bd1 = VppBridgeDomain(self, 1) + bd2 = VppBridgeDomain(self, 2) + bd20 = VppBridgeDomain(self, 20) + + bd1.add_vpp_config() + bd2.add_vpp_config() + bd20.add_vpp_config() + + gbd1 = VppGbpBridgeDomain(self, bd1, rd0, self.loop0) + gbd2 = VppGbpBridgeDomain(self, bd2, rd0, self.loop1) + gbd20 = VppGbpBridgeDomain(self, bd20, rd20, self.loop2) + + gbd1.add_vpp_config() + gbd2.add_vpp_config() + gbd20.add_vpp_config() + + # + # 3 EPGs, 2 of which share a BD. + # 2 NAT EPGs, one for floating-IP subnets, the other for internet + # + epgs = [VppGbpEndpointGroup(self, 220, 1220, rd0, gbd1, + self.pg4, self.loop0, + "10.0.0.128", "2001:10::128"), + VppGbpEndpointGroup(self, 221, 1221, rd0, gbd1, + self.pg5, self.loop0, + "10.0.1.128", "2001:10:1::128"), + VppGbpEndpointGroup(self, 222, 1222, rd0, gbd2, + self.pg6, self.loop1, + "10.0.2.128", "2001:10:2::128"), + VppGbpEndpointGroup(self, 333, 1333, rd20, gbd20, + self.pg7, self.loop2, + "11.0.0.128", "3001::128"), + VppGbpEndpointGroup(self, 444, 1444, rd20, gbd20, + self.pg8, self.loop2, + "11.0.0.129", "3001::129")] + recircs = [VppGbpRecirc(self, epgs[0], self.loop3), + VppGbpRecirc(self, epgs[1], self.loop4), + VppGbpRecirc(self, epgs[2], self.loop5), + VppGbpRecirc(self, epgs[3], self.loop6, is_ext=True), + VppGbpRecirc(self, epgs[4], self.loop7, is_ext=True)] + + epg_nat = epgs[3] + recirc_nat = recircs[3] + + # + # 4 end-points, 2 in the same subnet, 3 in the same BD + # + eps = [VppGbpEndpoint(self, self.pg0, + epgs[0], recircs[0], + "10.0.0.1", "11.0.0.1", + "2001:10::1", "3001::1"), + VppGbpEndpoint(self, self.pg1, + epgs[0], recircs[0], + "10.0.0.2", "11.0.0.2", + "2001:10::2", "3001::2"), + VppGbpEndpoint(self, self.pg2, + epgs[1], recircs[1], + "10.0.1.1", "11.0.0.3", + "2001:10:1::1", "3001::3"), + VppGbpEndpoint(self, self.pg3, + epgs[2], recircs[2], + "10.0.2.1", "11.0.0.4", + "2001:10:2::1", "3001::4")] + + self.vapi.nat44_ed_plugin_enable_disable(enable=1) + self.vapi.nat66_plugin_enable_disable(enable=1) + + # + # Config related to each of the EPGs + # + for epg in epgs: + # IP config on the BVI interfaces + if epg != epgs[1] and epg != epgs[4]: + b4 = VppIpInterfaceBind(self, epg.bvi, + epg.rd.t4).add_vpp_config() + b6 = VppIpInterfaceBind(self, epg.bvi, + epg.rd.t6).add_vpp_config() + epg.bvi.set_mac(self.router_mac) + + # The BVIs are NAT inside interfaces + flags = self.nat_config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=epg.bvi.sw_if_index, + flags=flags, is_add=1) + self.vapi.nat66_add_del_interface( + sw_if_index=epg.bvi.sw_if_index, + flags=flags, is_add=1) + + if_ip4 = VppIpInterfaceAddress(self, epg.bvi, + epg.bvi_ip4, 32, + bind=b4).add_vpp_config() + if_ip6 = VppIpInterfaceAddress(self, epg.bvi, + epg.bvi_ip6, 128, + bind=b6).add_vpp_config() + + # EPG uplink interfaces in the RD + VppIpInterfaceBind(self, epg.uplink, epg.rd.t4).add_vpp_config() + VppIpInterfaceBind(self, epg.uplink, epg.rd.t6).add_vpp_config() + + # add the BD ARP termination entry for BVI IP + epg.bd_arp_ip4 = VppBridgeDomainArpEntry(self, epg.bd.bd, + str(self.router_mac), + epg.bvi_ip4) + epg.bd_arp_ip6 = VppBridgeDomainArpEntry(self, epg.bd.bd, + str(self.router_mac), + epg.bvi_ip6) + epg.bd_arp_ip4.add_vpp_config() + epg.bd_arp_ip6.add_vpp_config() + + # EPG in VPP + epg.add_vpp_config() + + for recirc in recircs: + # EPG's ingress recirculation interface maps to its RD + VppIpInterfaceBind(self, recirc.recirc, + recirc.epg.rd.t4).add_vpp_config() + VppIpInterfaceBind(self, recirc.recirc, + recirc.epg.rd.t6).add_vpp_config() + + self.vapi.nat44_interface_add_del_feature( + sw_if_index=recirc.recirc.sw_if_index, is_add=1) + self.vapi.nat66_add_del_interface( + sw_if_index=recirc.recirc.sw_if_index, is_add=1) + + recirc.add_vpp_config() + + for recirc in recircs: + self.assertTrue(find_bridge_domain_port(self, + recirc.epg.bd.bd.bd_id, + recirc.recirc.sw_if_index)) + + for ep in eps: + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + # + # routes to the endpoints. We need these since there are no + # adj-fibs due to the fact the the BVI address has /32 and + # the subnet is not attached. + # + for (ip, fip) in zip(ep.ips, ep.fips): + # Add static mappings for each EP from the 10/8 to 11/8 network + if ip_address(ip).version == 4: + flags = self.nat_config_flags.NAT_IS_ADDR_ONLY + self.vapi.nat44_add_del_static_mapping( + is_add=1, + local_ip_address=ip, + external_ip_address=fip, + external_sw_if_index=0xFFFFFFFF, + vrf_id=0, + flags=flags) + else: + self.vapi.nat66_add_del_static_mapping( + local_ip_address=ip, + external_ip_address=fip, + vrf_id=0, is_add=1) + + # VPP EP create ... + ep.add_vpp_config() + + self.logger.info(self.vapi.cli("sh gbp endpoint")) + + # ... results in a Gratuitous ARP/ND on the EPG's uplink + rx = ep.epg.uplink.get_capture(len(ep.ips) + 1, timeout=0.2) + + for ii, ip in enumerate(ep.ips): + p = rx[ii] + + if ip_address(ip).version == 6: + self.assertTrue(p.haslayer(ICMPv6ND_NA)) + self.assertEqual(p[ICMPv6ND_NA].tgt, ip) + else: + self.assertTrue(p.haslayer(ARP)) + self.assertEqual(p[ARP].psrc, ip) + self.assertEqual(p[ARP].pdst, ip) + + # add the BD ARP termination entry for floating IP + for fip in ep.fips: + ba = VppBridgeDomainArpEntry(self, epg_nat.bd.bd, ep.mac, + fip) + ba.add_vpp_config() + + # floating IPs route via EPG recirc + r = VppIpRoute( + self, fip, ip_address(fip).max_prefixlen, + [VppRoutePath(fip, + ep.recirc.recirc.sw_if_index, + type=FibPathType.FIB_PATH_TYPE_DVR, + proto=get_dpo_proto(fip))], + table_id=20) + r.add_vpp_config() + + # L2 FIB entries in the NAT EPG BD to bridge the packets from + # the outside direct to the internal EPG + lf = VppL2FibEntry(self, epg_nat.bd.bd, ep.mac, + ep.recirc.recirc, bvi_mac=0) + lf.add_vpp_config() + + # + # ARP packets for unknown IP are sent to the EPG uplink + # + pkt_arp = (Ether(dst="ff:ff:ff:ff:ff:ff", + src=self.pg0.remote_mac) / + ARP(op="who-has", + hwdst="ff:ff:ff:ff:ff:ff", + hwsrc=self.pg0.remote_mac, + pdst="10.0.0.88", + psrc="10.0.0.99")) + + self.vapi.cli("clear trace") + self.pg0.add_stream(pkt_arp) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rxd = epgs[0].uplink.get_capture(1) + + # + # ARP/ND packets get a response + # + pkt_arp = (Ether(dst="ff:ff:ff:ff:ff:ff", + src=self.pg0.remote_mac) / + ARP(op="who-has", + hwdst="ff:ff:ff:ff:ff:ff", + hwsrc=self.pg0.remote_mac, + pdst=epgs[0].bvi_ip4, + psrc=eps[0].ip4)) + + self.send_and_expect(self.pg0, [pkt_arp], self.pg0) + + nsma = in6_getnsma(inet_pton(AF_INET6, eps[0].ip6)) + d = inet_ntop(AF_INET6, nsma) + pkt_nd = (Ether(dst=in6_getnsmac(nsma), + src=self.pg0.remote_mac) / + IPv6(dst=d, src=eps[0].ip6) / + ICMPv6ND_NS(tgt=epgs[0].bvi_ip6) / + ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac)) + self.send_and_expect(self.pg0, [pkt_nd], self.pg0) + + # + # broadcast packets are flooded + # + pkt_bcast = (Ether(dst="ff:ff:ff:ff:ff:ff", + src=self.pg0.remote_mac) / + IP(src=eps[0].ip4, dst="232.1.1.1") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + self.vapi.cli("clear trace") + self.pg0.add_stream(pkt_bcast) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rxd = eps[1].itf.get_capture(1) + self.assertEqual(rxd[0][Ether].dst, pkt_bcast[Ether].dst) + rxd = epgs[0].uplink.get_capture(1) + self.assertEqual(rxd[0][Ether].dst, pkt_bcast[Ether].dst) + + # + # packets to non-local L3 destinations dropped + # + pkt_intra_epg_220_ip4 = (Ether(src=self.pg0.remote_mac, + dst=str(self.router_mac)) / + IP(src=eps[0].ip4, + dst="10.0.0.99") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + pkt_inter_epg_222_ip4 = (Ether(src=self.pg0.remote_mac, + dst=str(self.router_mac)) / + IP(src=eps[0].ip4, + dst="10.0.1.99") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + self.send_and_assert_no_replies(self.pg0, + pkt_intra_epg_220_ip4 * NUM_PKTS) + + pkt_inter_epg_222_ip6 = (Ether(src=self.pg0.remote_mac, + dst=str(self.router_mac)) / + IPv6(src=eps[0].ip6, + dst="2001:10::99") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + self.send_and_assert_no_replies(self.pg0, + pkt_inter_epg_222_ip6 * NUM_PKTS) + + # + # Add the subnet routes + # + s41 = VppGbpSubnet( + self, rd0, "10.0.0.0", 24, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL) + s42 = VppGbpSubnet( + self, rd0, "10.0.1.0", 24, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL) + s43 = VppGbpSubnet( + self, rd0, "10.0.2.0", 24, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL) + s61 = VppGbpSubnet( + self, rd0, "2001:10::1", 64, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL) + s62 = VppGbpSubnet( + self, rd0, "2001:10:1::1", 64, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL) + s63 = VppGbpSubnet( + self, rd0, "2001:10:2::1", 64, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL) + s41.add_vpp_config() + s42.add_vpp_config() + s43.add_vpp_config() + s61.add_vpp_config() + s62.add_vpp_config() + s63.add_vpp_config() + + self.send_and_expect_bridged(eps[0].itf, + pkt_intra_epg_220_ip4 * NUM_PKTS, + eps[0].epg.uplink) + self.send_and_expect_bridged(eps[0].itf, + pkt_inter_epg_222_ip4 * NUM_PKTS, + eps[0].epg.uplink) + self.send_and_expect_bridged6(eps[0].itf, + pkt_inter_epg_222_ip6 * NUM_PKTS, + eps[0].epg.uplink) + + self.logger.info(self.vapi.cli("sh ip fib 11.0.0.2")) + self.logger.info(self.vapi.cli("sh gbp endpoint-group")) + self.logger.info(self.vapi.cli("sh gbp endpoint")) + self.logger.info(self.vapi.cli("sh gbp recirc")) + self.logger.info(self.vapi.cli("sh int")) + self.logger.info(self.vapi.cli("sh int addr")) + self.logger.info(self.vapi.cli("sh int feat loop6")) + self.logger.info(self.vapi.cli("sh vlib graph ip4-gbp-src-classify")) + self.logger.info(self.vapi.cli("sh int feat loop3")) + self.logger.info(self.vapi.cli("sh int feat pg0")) + + # + # Packet destined to unknown unicast is sent on the epg uplink ... + # + pkt_intra_epg_220_to_uplink = (Ether(src=self.pg0.remote_mac, + dst="00:00:00:33:44:55") / + IP(src=eps[0].ip4, + dst="10.0.0.99") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + self.send_and_expect_bridged(eps[0].itf, + pkt_intra_epg_220_to_uplink * NUM_PKTS, + eps[0].epg.uplink) + # ... and nowhere else + self.pg1.get_capture(0, timeout=0.1) + self.pg1.assert_nothing_captured(remark="Flood onto other VMS") + + pkt_intra_epg_221_to_uplink = (Ether(src=self.pg2.remote_mac, + dst="00:00:00:33:44:66") / + IP(src=eps[0].ip4, + dst="10.0.0.99") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + self.send_and_expect_bridged(eps[2].itf, + pkt_intra_epg_221_to_uplink * NUM_PKTS, + eps[2].epg.uplink) + + # + # Packets from the uplink are forwarded in the absence of a contract + # + pkt_intra_epg_220_from_uplink = (Ether(src="00:00:00:33:44:55", + dst=self.pg0.remote_mac) / + IP(src=eps[0].ip4, + dst="10.0.0.99") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + self.send_and_expect_bridged(self.pg4, + pkt_intra_epg_220_from_uplink * NUM_PKTS, + self.pg0) + + # + # in the absence of policy, endpoints in the same EPG + # can communicate + # + pkt_intra_epg = (Ether(src=self.pg0.remote_mac, + dst=self.pg1.remote_mac) / + IP(src=eps[0].ip4, + dst=eps[1].ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + self.send_and_expect_bridged(self.pg0, + pkt_intra_epg * NUM_PKTS, + self.pg1) + + # + # in the absence of policy, endpoints in the different EPG + # cannot communicate + # + pkt_inter_epg_220_to_221 = (Ether(src=self.pg0.remote_mac, + dst=self.pg2.remote_mac) / + IP(src=eps[0].ip4, + dst=eps[2].ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + pkt_inter_epg_221_to_220 = (Ether(src=self.pg2.remote_mac, + dst=self.pg0.remote_mac) / + IP(src=eps[2].ip4, + dst=eps[0].ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + pkt_inter_epg_220_to_222 = (Ether(src=self.pg0.remote_mac, + dst=str(self.router_mac)) / + IP(src=eps[0].ip4, + dst=eps[3].ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + self.send_and_assert_no_replies(eps[0].itf, + pkt_inter_epg_220_to_221 * NUM_PKTS) + self.send_and_assert_no_replies(eps[0].itf, + pkt_inter_epg_220_to_222 * NUM_PKTS) + + # + # A uni-directional contract from EPG 220 -> 221 + # + rule = AclRule(is_permit=1, proto=17) + rule2 = AclRule(src_prefix=IPv6Network((0, 0)), + dst_prefix=IPv6Network((0, 0)), is_permit=1, proto=17) + acl = VppAcl(self, rules=[rule, rule2]) + acl.add_vpp_config() + + c1 = VppGbpContract( + self, 400, epgs[0].sclass, epgs[1].sclass, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + [])], + [ETH_P_IP, ETH_P_IPV6]) + c1.add_vpp_config() + + self.send_and_expect_bridged(eps[0].itf, + pkt_inter_epg_220_to_221 * NUM_PKTS, + eps[2].itf) + self.send_and_assert_no_replies(eps[0].itf, + pkt_inter_epg_220_to_222 * NUM_PKTS) + + # + # contract for the return direction + # + c2 = VppGbpContract( + self, 400, epgs[1].sclass, epgs[0].sclass, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + [])], + [ETH_P_IP, ETH_P_IPV6]) + c2.add_vpp_config() + + self.send_and_expect_bridged(eps[0].itf, + pkt_inter_epg_220_to_221 * NUM_PKTS, + eps[2].itf) + self.send_and_expect_bridged(eps[2].itf, + pkt_inter_epg_221_to_220 * NUM_PKTS, + eps[0].itf) + + ds = c2.get_drop_stats() + self.assertEqual(ds['packets'], 0) + ps = c2.get_permit_stats() + self.assertEqual(ps['packets'], NUM_PKTS) + + # + # the contract does not allow non-IP + # + pkt_non_ip_inter_epg_220_to_221 = (Ether(src=self.pg0.remote_mac, + dst=self.pg2.remote_mac) / + ARP()) + self.send_and_assert_no_replies(eps[0].itf, + pkt_non_ip_inter_epg_220_to_221 * 17) + + # + # check that inter group is still disabled for the groups + # not in the contract. + # + self.send_and_assert_no_replies(eps[0].itf, + pkt_inter_epg_220_to_222 * NUM_PKTS) + + # + # A uni-directional contract from EPG 220 -> 222 'L3 routed' + # + c3 = VppGbpContract( + self, 400, epgs[0].sclass, epgs[2].sclass, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + [])], + [ETH_P_IP, ETH_P_IPV6]) + c3.add_vpp_config() + + self.logger.info(self.vapi.cli("sh gbp contract")) + + self.send_and_expect_routed(eps[0].itf, + pkt_inter_epg_220_to_222 * NUM_PKTS, + eps[3].itf, + str(self.router_mac)) + # + # remove both contracts, traffic stops in both directions + # + c2.remove_vpp_config() + c1.remove_vpp_config() + c3.remove_vpp_config() + acl.remove_vpp_config() + + self.send_and_assert_no_replies(eps[2].itf, + pkt_inter_epg_221_to_220 * NUM_PKTS) + self.send_and_assert_no_replies(eps[0].itf, + pkt_inter_epg_220_to_221 * NUM_PKTS) + self.send_and_expect_bridged(eps[0].itf, + pkt_intra_epg * NUM_PKTS, + eps[1].itf) + + # + # EPs to the outside world + # + + # in the EP's RD an external subnet via the NAT EPG's recirc + se1 = VppGbpSubnet( + self, rd0, "0.0.0.0", 0, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL, + sw_if_index=recirc_nat.recirc.sw_if_index, + sclass=epg_nat.sclass) + se2 = VppGbpSubnet( + self, rd0, "11.0.0.0", 8, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL, + sw_if_index=recirc_nat.recirc.sw_if_index, + sclass=epg_nat.sclass) + se16 = VppGbpSubnet( + self, rd0, "::", 0, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL, + sw_if_index=recirc_nat.recirc.sw_if_index, + sclass=epg_nat.sclass) + # in the NAT RD an external subnet via the NAT EPG's uplink + se3 = VppGbpSubnet( + self, rd20, "0.0.0.0", 0, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL, + sw_if_index=epg_nat.uplink.sw_if_index, + sclass=epg_nat.sclass) + se36 = VppGbpSubnet( + self, rd20, "::", 0, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL, + sw_if_index=epg_nat.uplink.sw_if_index, + sclass=epg_nat.sclass) + se4 = VppGbpSubnet( + self, rd20, "11.0.0.0", 8, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL, + sw_if_index=epg_nat.uplink.sw_if_index, + sclass=epg_nat.sclass) + se1.add_vpp_config() + se2.add_vpp_config() + se16.add_vpp_config() + se3.add_vpp_config() + se36.add_vpp_config() + se4.add_vpp_config() + + self.logger.info(self.vapi.cli("sh ip fib 0.0.0.0/0")) + self.logger.info(self.vapi.cli("sh ip fib 11.0.0.1")) + self.logger.info(self.vapi.cli("sh ip6 fib ::/0")) + self.logger.info(self.vapi.cli("sh ip6 fib %s" % + eps[0].fip6)) + + # + # From an EP to an outside address: IN2OUT + # + pkt_inter_epg_220_to_global = (Ether(src=self.pg0.remote_mac, + dst=str(self.router_mac)) / + IP(src=eps[0].ip4, + dst="1.1.1.1") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + # no policy yet + self.send_and_assert_no_replies(eps[0].itf, + pkt_inter_epg_220_to_global * NUM_PKTS) + rule = AclRule(is_permit=1, proto=17, ports=1234) + rule2 = AclRule(is_permit=1, proto=17, ports=1234, + src_prefix=IPv6Network((0, 0)), + dst_prefix=IPv6Network((0, 0))) + acl2 = VppAcl(self, rules=[rule, rule2]) + acl2.add_vpp_config() + + c4 = VppGbpContract( + self, 400, epgs[0].sclass, epgs[3].sclass, acl2.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + [])], + [ETH_P_IP, ETH_P_IPV6]) + c4.add_vpp_config() + + self.send_and_expect_natted(eps[0].itf, + pkt_inter_epg_220_to_global * NUM_PKTS, + self.pg7, + eps[0].fip4) + + pkt_inter_epg_220_to_global = (Ether(src=self.pg0.remote_mac, + dst=str(self.router_mac)) / + IPv6(src=eps[0].ip6, + dst="6001::1") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + self.send_and_expect_natted6(self.pg0, + pkt_inter_epg_220_to_global * NUM_PKTS, + self.pg7, + eps[0].fip6) + # + # From a global address to an EP: OUT2IN + # + pkt_inter_epg_220_from_global = (Ether(src=str(self.router_mac), + dst=self.pg0.remote_mac) / + IP(dst=eps[0].fip4, + src="1.1.1.1") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + self.send_and_assert_no_replies( + self.pg7, pkt_inter_epg_220_from_global * NUM_PKTS) + + c5 = VppGbpContract( + self, 400, epgs[3].sclass, epgs[0].sclass, acl2.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + [])], + [ETH_P_IP, ETH_P_IPV6]) + c5.add_vpp_config() + + self.send_and_expect_unnatted(self.pg7, + pkt_inter_epg_220_from_global * NUM_PKTS, + eps[0].itf, + eps[0].ip4) + + pkt_inter_epg_220_from_global = (Ether(src=str(self.router_mac), + dst=self.pg0.remote_mac) / + IPv6(dst=eps[0].fip6, + src="6001::1") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + self.send_and_expect_unnatted6( + self.pg7, + pkt_inter_epg_220_from_global * NUM_PKTS, + eps[0].itf, + eps[0].ip6) + + # + # From a local VM to another local VM using resp. public addresses: + # IN2OUT2IN + # + pkt_intra_epg_220_global = (Ether(src=self.pg0.remote_mac, + dst=str(self.router_mac)) / + IP(src=eps[0].ip4, + dst=eps[1].fip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + self.send_and_expect_double_natted(eps[0].itf, + pkt_intra_epg_220_global * NUM_PKTS, + eps[1].itf, + eps[0].fip4, + eps[1].ip4) + + pkt_intra_epg_220_global = (Ether(src=self.pg0.remote_mac, + dst=str(self.router_mac)) / + IPv6(src=eps[0].ip6, + dst=eps[1].fip6) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + self.send_and_expect_double_natted6( + eps[0].itf, + pkt_intra_epg_220_global * NUM_PKTS, + eps[1].itf, + eps[0].fip6, + eps[1].ip6) + + # + # cleanup + # + self.vapi.nat44_ed_plugin_enable_disable(enable=0) + self.vapi.nat66_plugin_enable_disable(enable=0) + + def wait_for_ep_timeout(self, sw_if_index=None, ip=None, mac=None, + tep=None, n_tries=100, s_time=1): + # only learnt EP can timeout + ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t + flags = ep_flags.GBP_API_ENDPOINT_FLAG_LEARNT + while (n_tries): + if not find_gbp_endpoint(self, sw_if_index, ip, mac, tep=tep, + flags=flags): + return True + n_tries = n_tries - 1 + self.sleep(s_time) + self.assertFalse(find_gbp_endpoint(self, sw_if_index, ip, mac, tep=tep, + flags=flags)) + return False + + def test_gbp_learn_l2(self): + """ GBP L2 Endpoint Learning """ + + drop_no_contract = self.statistics.get_err_counter( + '/err/gbp-policy-port/drop-no-contract') + allow_intra_class = self.statistics.get_err_counter( + '/err/gbp-policy-port/allow-intra-sclass') + + ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t + learnt = [{'mac': '00:00:11:11:11:01', + 'ip': '10.0.0.1', + 'ip6': '2001:10::2'}, + {'mac': '00:00:11:11:11:02', + 'ip': '10.0.0.2', + 'ip6': '2001:10::3'}] + + # + # IP tables + # + gt4 = VppIpTable(self, 1) + gt4.add_vpp_config() + gt6 = VppIpTable(self, 1, is_ip6=True) + gt6.add_vpp_config() + + rd1 = VppGbpRouteDomain(self, 1, 401, gt4, gt6) + rd1.add_vpp_config() + + # + # Pg2 hosts the vxlan tunnel, hosts on pg2 to act as TEPs + # Pg3 hosts the IP4 UU-flood VXLAN tunnel + # Pg4 hosts the IP6 UU-flood VXLAN tunnel + # + self.pg2.config_ip4() + self.pg2.resolve_arp() + self.pg2.generate_remote_hosts(4) + self.pg2.configure_ipv4_neighbors() + self.pg3.config_ip4() + self.pg3.resolve_arp() + self.pg4.config_ip4() + self.pg4.resolve_arp() + + # + # Add a mcast destination VXLAN-GBP tunnel for B&M traffic + # + tun_bm = VppVxlanGbpTunnel(self, self.pg4.local_ip4, + "239.1.1.1", 88, + mcast_itf=self.pg4) + tun_bm.add_vpp_config() + + # + # a GBP bridge domain with a BVI and a UU-flood interface + # + bd1 = VppBridgeDomain(self, 1) + bd1.add_vpp_config() + gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0, + self.pg3, tun_bm) + gbd1.add_vpp_config() + + self.logger.info(self.vapi.cli("sh bridge 1 detail")) + self.logger.info(self.vapi.cli("sh gbp bridge")) + + # ... and has a /32 applied + ip_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", 32) + ip_addr.add_vpp_config() + + # + # The Endpoint-group in which we are learning endpoints + # + epg_220 = VppGbpEndpointGroup(self, 220, 112, rd1, gbd1, + None, self.loop0, + "10.0.0.128", + "2001:10::128", + VppGbpEndpointRetention(4)) + epg_220.add_vpp_config() + epg_330 = VppGbpEndpointGroup(self, 330, 113, rd1, gbd1, + None, self.loop1, + "10.0.1.128", + "2001:11::128", + VppGbpEndpointRetention(4)) + epg_330.add_vpp_config() + + # + # The VXLAN GBP tunnel is a bridge-port and has L2 endpoint + # learning enabled + # + vx_tun_l2_1 = VppGbpVxlanTunnel( + self, 99, bd1.bd_id, + VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L2, + self.pg2.local_ip4) + vx_tun_l2_1.add_vpp_config() + + # + # A static endpoint that the learnt endpoints are trying to + # talk to + # + ep = VppGbpEndpoint(self, self.pg0, + epg_220, None, + "10.0.0.127", "11.0.0.127", + "2001:10::1", "3001::1") + ep.add_vpp_config() + + self.assertTrue(find_route(self, ep.ip4, 32, table_id=1)) + + # a packet with an sclass from an unknown EPG + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[0].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=99, gpid=88, flags=0x88) / + Ether(src=learnt[0]["mac"], dst=ep.mac) / + IP(src=learnt[0]["ip"], dst=ep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + self.send_and_assert_no_replies(self.pg2, p) + + self.logger.info(self.vapi.cli("sh error")) + self.assert_error_counter_equal( + '/err/gbp-policy-port/drop-no-contract', + drop_no_contract + 1) + + # + # we should not have learnt a new tunnel endpoint, since + # the EPG was not learnt. + # + self.assertEqual(INDEX_INVALID, + find_vxlan_gbp_tunnel(self, + self.pg2.local_ip4, + self.pg2.remote_hosts[0].ip4, + 99)) + + # ep is not learnt, because the EPG is unknown + self.assertEqual(len(self.vapi.gbp_endpoint_dump()), 1) + + # + # Learn new EPs from IP packets + # + for ii, l in enumerate(learnt): + # a packet with an sclass from a known EPG + # arriving on an unknown TEP + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=99, gpid=112, flags=0x88) / + Ether(src=l['mac'], dst=ep.mac) / + IP(src=l['ip'], dst=ep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, [p], self.pg0) + + # the new TEP + tep1_sw_if_index = find_vxlan_gbp_tunnel( + self, + self.pg2.local_ip4, + self.pg2.remote_hosts[1].ip4, + 99) + self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index) + + # + # the EP is learnt via the learnt TEP + # both from its MAC and its IP + # + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l2_1.sw_if_index, + mac=l['mac'])) + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l2_1.sw_if_index, + ip=l['ip'])) + + self.assert_error_counter_equal( + '/err/gbp-policy-port/allow-intra-sclass', + allow_intra_class + 2) + + self.logger.info(self.vapi.cli("show gbp endpoint")) + self.logger.info(self.vapi.cli("show gbp vxlan")) + self.logger.info(self.vapi.cli("show ip mfib")) + + # + # If we sleep for the threshold time, the learnt endpoints should + # age out + # + for l in learnt: + self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index, + mac=l['mac']) + + # + # Learn new EPs from GARP packets received on the BD's mcast tunnel + # + for ii, l in enumerate(learnt): + # add some junk in the reserved field of the vxlan-header + # next to the VNI. we should accept since reserved bits are + # ignored on rx. + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst="239.1.1.1") / + UDP(sport=1234, dport=48879) / + VXLAN(vni=88, reserved2=0x80, gpid=112, flags=0x88) / + Ether(src=l['mac'], dst="ff:ff:ff:ff:ff:ff") / + ARP(op="who-has", + psrc=l['ip'], pdst=l['ip'], + hwsrc=l['mac'], hwdst="ff:ff:ff:ff:ff:ff")) + + rx = self.send_and_expect(self.pg4, [p], self.pg0) + + # the new TEP + tep1_sw_if_index = find_vxlan_gbp_tunnel( + self, + self.pg2.local_ip4, + self.pg2.remote_hosts[1].ip4, + 99) + self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index) + + # + # the EP is learnt via the learnt TEP + # both from its MAC and its IP + # + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l2_1.sw_if_index, + mac=l['mac'])) + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l2_1.sw_if_index, + ip=l['ip'])) + + # + # wait for the learnt endpoints to age out + # + for l in learnt: + self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index, + mac=l['mac']) + + # + # Learn new EPs from L2 packets + # + for ii, l in enumerate(learnt): + # a packet with an sclass from a known EPG + # arriving on an unknown TEP + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=99, gpid=112, flags=0x88) / + Ether(src=l['mac'], dst=ep.mac) / + Raw(b'\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, [p], self.pg0) + + # the new TEP + tep1_sw_if_index = find_vxlan_gbp_tunnel( + self, + self.pg2.local_ip4, + self.pg2.remote_hosts[1].ip4, + 99) + self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index) + + # + # the EP is learnt via the learnt TEP + # both from its MAC and its IP + # + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l2_1.sw_if_index, + mac=l['mac'])) + + self.logger.info(self.vapi.cli("show gbp endpoint")) + self.logger.info(self.vapi.cli("show gbp vxlan")) + self.logger.info(self.vapi.cli("show vxlan-gbp tunnel")) + + # + # wait for the learnt endpoints to age out + # + for l in learnt: + self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index, + mac=l['mac']) + + # + # repeat. the do not learn bit is set so the EPs are not learnt + # + for l in learnt: + # a packet with an sclass from a known EPG + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=99, gpid=112, flags=0x88, gpflags="D") / + Ether(src=l['mac'], dst=ep.mac) / + IP(src=l['ip'], dst=ep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0) + + for l in learnt: + self.assertFalse(find_gbp_endpoint(self, + vx_tun_l2_1.sw_if_index, + mac=l['mac'])) + + # + # repeat + # + for l in learnt: + # a packet with an sclass from a known EPG + # set a reserved bit in addition to the G and I + # reserved bits should not be checked on rx. + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=99, gpid=112, flags=0xc8) / + Ether(src=l['mac'], dst=ep.mac) / + IP(src=l['ip'], dst=ep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0) + + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l2_1.sw_if_index, + mac=l['mac'])) + + # + # Static EP replies to dynamics + # + self.logger.info(self.vapi.cli("sh l2fib bd_id 1")) + for l in learnt: + p = (Ether(src=ep.mac, dst=l['mac']) / + IP(dst=l['ip'], src=ep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, p * 17, self.pg2) + + for rx in rxs: + self.assertEqual(rx[IP].src, self.pg2.local_ip4) + self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4) + self.assertEqual(rx[UDP].dport, 48879) + # the UDP source port is a random value for hashing + self.assertEqual(rx[VXLAN].gpid, 112) + self.assertEqual(rx[VXLAN].vni, 99) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + self.assertTrue(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + + for l in learnt: + self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index, + mac=l['mac']) + + # + # repeat in the other EPG + # there's no contract between 220 and 330, but the A-bit is set + # so the packet is cleared for delivery + # + for l in learnt: + # a packet with an sclass from a known EPG + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=99, gpid=113, flags=0x88, gpflags='A') / + Ether(src=l['mac'], dst=ep.mac) / + IP(src=l['ip'], dst=ep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0) + + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l2_1.sw_if_index, + mac=l['mac'])) + + # + # static EP cannot reach the learnt EPs since there is no contract + # only test 1 EP as the others could timeout + # + p = (Ether(src=ep.mac, dst=l['mac']) / + IP(dst=learnt[0]['ip'], src=ep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + self.send_and_assert_no_replies(self.pg0, [p]) + + # + # refresh the entries after the check for no replies above + # + for l in learnt: + # a packet with an sclass from a known EPG + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=99, gpid=113, flags=0x88, gpflags='A') / + Ether(src=l['mac'], dst=ep.mac) / + IP(src=l['ip'], dst=ep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0) + + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l2_1.sw_if_index, + mac=l['mac'])) + + # + # Add the contract so they can talk + # + rule = AclRule(is_permit=1, proto=17) + rule2 = AclRule(src_prefix=IPv6Network((0, 0)), + dst_prefix=IPv6Network((0, 0)), is_permit=1, proto=17) + acl = VppAcl(self, rules=[rule, rule2]) + acl.add_vpp_config() + + c1 = VppGbpContract( + self, 401, epg_220.sclass, epg_330.sclass, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + [])], + [ETH_P_IP, ETH_P_IPV6]) + c1.add_vpp_config() + + for l in learnt: + p = (Ether(src=ep.mac, dst=l['mac']) / + IP(dst=l['ip'], src=ep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + self.send_and_expect(self.pg0, [p], self.pg2) + + # + # send UU packets from the local EP + # + self.logger.info(self.vapi.cli("sh gbp bridge")) + self.logger.info(self.vapi.cli("sh bridge-domain 1 detail")) + p_uu = (Ether(src=ep.mac, dst="00:11:11:11:11:11") / + IP(dst="10.0.0.133", src=ep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + rxs = self.send_and_expect(ep.itf, [p_uu], gbd1.uu_fwd) + + self.logger.info(self.vapi.cli("sh bridge 1 detail")) + + p_bm = (Ether(src=ep.mac, dst="ff:ff:ff:ff:ff:ff") / + IP(dst="10.0.0.133", src=ep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + rxs = self.send_and_expect_only(ep.itf, [p_bm], tun_bm.mcast_itf) + + for rx in rxs: + self.assertEqual(rx[IP].src, self.pg4.local_ip4) + self.assertEqual(rx[IP].dst, "239.1.1.1") + self.assertEqual(rx[UDP].dport, 48879) + # the UDP source port is a random value for hashing + self.assertEqual(rx[VXLAN].gpid, 112) + self.assertEqual(rx[VXLAN].vni, 88) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + self.assertFalse(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + + rule = AclRule(is_permit=1, proto=17) + rule2 = AclRule(src_prefix=IPv6Network((0, 0)), + dst_prefix=IPv6Network((0, 0)), is_permit=1, proto=17) + acl = VppAcl(self, rules=[rule, rule2]) + acl.add_vpp_config() + + c2 = VppGbpContract( + self, 401, epg_330.sclass, epg_220.sclass, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + [])], + [ETH_P_IP, ETH_P_IPV6]) + c2.add_vpp_config() + + for l in learnt: + self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index, + mac=l['mac']) + # + # Check v6 Endpoints learning + # + for l in learnt: + # a packet with an sclass from a known EPG + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=99, gpid=113, flags=0x88) / + Ether(src=l['mac'], dst=ep.mac) / + IPv6(src=l['ip6'], dst=ep.ip6) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0) + rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0) + + self.assertTrue(find_gbp_endpoint( + self, + vx_tun_l2_1.sw_if_index, + ip=l['ip6'], + tep=[self.pg2.local_ip4, + self.pg2.remote_hosts[1].ip4])) + + self.logger.info(self.vapi.cli("sh int")) + self.logger.info(self.vapi.cli("sh vxlan-gbp tunnel")) + self.logger.info(self.vapi.cli("sh gbp vxlan")) + self.logger.info(self.vapi.cli("sh gbp endpoint")) + self.logger.info(self.vapi.cli("sh gbp interface")) + + # + # EP moves to a different TEP + # + for l in learnt: + # a packet with an sclass from a known EPG + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[2].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=99, gpid=113, flags=0x88) / + Ether(src=l['mac'], dst=ep.mac) / + IPv6(src=l['ip6'], dst=ep.ip6) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, p * 1, self.pg0) + rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0) + + self.assertTrue(find_gbp_endpoint( + self, + vx_tun_l2_1.sw_if_index, + sclass=113, + mac=l['mac'], + tep=[self.pg2.local_ip4, + self.pg2.remote_hosts[2].ip4])) + + # + # v6 remote EP reachability + # + for l in learnt: + p = (Ether(src=ep.mac, dst=l['mac']) / + IPv6(dst=l['ip6'], src=ep.ip6) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg2) + + for rx in rxs: + self.assertEqual(rx[IP].src, self.pg2.local_ip4) + self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[2].ip4) + self.assertEqual(rx[UDP].dport, 48879) + # the UDP source port is a random value for hashing + self.assertEqual(rx[VXLAN].gpid, 112) + self.assertEqual(rx[VXLAN].vni, 99) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + self.assertTrue(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + self.assertEqual(rx[IPv6].dst, l['ip6']) + + # + # EP changes sclass + # + for l in learnt: + # a packet with an sclass from a known EPG + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[2].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=99, gpid=112, flags=0x88) / + Ether(src=l['mac'], dst=ep.mac) / + IPv6(src=l['ip6'], dst=ep.ip6) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, p * 1, self.pg0) + rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0) + + self.assertTrue(find_gbp_endpoint( + self, + vx_tun_l2_1.sw_if_index, + mac=l['mac'], + sclass=112, + tep=[self.pg2.local_ip4, + self.pg2.remote_hosts[2].ip4])) + + # + # check reachability and contract intra-epg + # + allow_intra_class = self.statistics.get_err_counter( + '/err/gbp-policy-mac/allow-intra-sclass') + + for l in learnt: + p = (Ether(src=ep.mac, dst=l['mac']) / + IPv6(dst=l['ip6'], src=ep.ip6) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg2) + + for rx in rxs: + self.assertEqual(rx[IP].src, self.pg2.local_ip4) + self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[2].ip4) + self.assertEqual(rx[UDP].dport, 48879) + self.assertEqual(rx[VXLAN].gpid, 112) + self.assertEqual(rx[VXLAN].vni, 99) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + self.assertTrue(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + self.assertEqual(rx[IPv6].dst, l['ip6']) + + allow_intra_class += NUM_PKTS + + self.assert_error_counter_equal( + '/err/gbp-policy-mac/allow-intra-sclass', + allow_intra_class) + + # + # clean up + # + for l in learnt: + self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index, + mac=l['mac']) + self.pg2.unconfig_ip4() + self.pg3.unconfig_ip4() + self.pg4.unconfig_ip4() + + def test_gbp_contract(self): + """ GBP Contracts """ + + # + # Route Domains + # + gt4 = VppIpTable(self, 0) + gt4.add_vpp_config() + gt6 = VppIpTable(self, 0, is_ip6=True) + gt6.add_vpp_config() + + rd0 = VppGbpRouteDomain(self, 0, 400, gt4, gt6, None, None) + + rd0.add_vpp_config() + + # + # Bridge Domains + # + bd1 = VppBridgeDomain(self, 1, arp_term=0) + bd2 = VppBridgeDomain(self, 2, arp_term=0) + + bd1.add_vpp_config() + bd2.add_vpp_config() + + gbd1 = VppGbpBridgeDomain(self, bd1, rd0, self.loop0) + gbd2 = VppGbpBridgeDomain(self, bd2, rd0, self.loop1) + + gbd1.add_vpp_config() + gbd2.add_vpp_config() + + # + # 3 EPGs, 2 of which share a BD. + # + epgs = [VppGbpEndpointGroup(self, 220, 1220, rd0, gbd1, + None, self.loop0, + "10.0.0.128", "2001:10::128"), + VppGbpEndpointGroup(self, 221, 1221, rd0, gbd1, + None, self.loop0, + "10.0.1.128", "2001:10:1::128"), + VppGbpEndpointGroup(self, 222, 1222, rd0, gbd2, + None, self.loop1, + "10.0.2.128", "2001:10:2::128")] + # + # 4 end-points, 2 in the same subnet, 3 in the same BD + # + eps = [VppGbpEndpoint(self, self.pg0, + epgs[0], None, + "10.0.0.1", "11.0.0.1", + "2001:10::1", "3001::1"), + VppGbpEndpoint(self, self.pg1, + epgs[0], None, + "10.0.0.2", "11.0.0.2", + "2001:10::2", "3001::2"), + VppGbpEndpoint(self, self.pg2, + epgs[1], None, + "10.0.1.1", "11.0.0.3", + "2001:10:1::1", "3001::3"), + VppGbpEndpoint(self, self.pg3, + epgs[2], None, + "10.0.2.1", "11.0.0.4", + "2001:10:2::1", "3001::4")] + + # + # Config related to each of the EPGs + # + for epg in epgs: + # IP config on the BVI interfaces + if epg != epgs[1]: + b4 = VppIpInterfaceBind(self, epg.bvi, + epg.rd.t4).add_vpp_config() + b6 = VppIpInterfaceBind(self, epg.bvi, + epg.rd.t6).add_vpp_config() + epg.bvi.set_mac(self.router_mac) + + if_ip4 = VppIpInterfaceAddress(self, epg.bvi, + epg.bvi_ip4, 32, + bind=b4).add_vpp_config() + if_ip6 = VppIpInterfaceAddress(self, epg.bvi, + epg.bvi_ip6, 128, + bind=b6).add_vpp_config() + + # add the BD ARP termination entry for BVI IP + epg.bd_arp_ip4 = VppBridgeDomainArpEntry(self, epg.bd.bd, + str(self.router_mac), + epg.bvi_ip4) + epg.bd_arp_ip4.add_vpp_config() + + # EPG in VPP + epg.add_vpp_config() + + # + # config ep + # + for ep in eps: + ep.add_vpp_config() + + self.logger.info(self.vapi.cli("show gbp endpoint")) + self.logger.info(self.vapi.cli("show interface")) + self.logger.info(self.vapi.cli("show br")) + + # + # Intra epg allowed without contract + # + pkt_intra_epg_220_to_220 = (Ether(src=self.pg0.remote_mac, + dst=self.pg1.remote_mac) / + IP(src=eps[0].ip4, + dst=eps[1].ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + self.send_and_expect_bridged(self.pg0, + pkt_intra_epg_220_to_220 * 65, + self.pg1) + + pkt_intra_epg_220_to_220 = (Ether(src=self.pg0.remote_mac, + dst=self.pg1.remote_mac) / + IPv6(src=eps[0].ip6, + dst=eps[1].ip6) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + self.send_and_expect_bridged6(self.pg0, + pkt_intra_epg_220_to_220 * 65, + self.pg1) + + # + # Inter epg denied without contract + # + pkt_inter_epg_220_to_221 = (Ether(src=self.pg0.remote_mac, + dst=self.pg2.remote_mac) / + IP(src=eps[0].ip4, + dst=eps[2].ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + self.send_and_assert_no_replies(self.pg0, pkt_inter_epg_220_to_221) + + # + # A uni-directional contract from EPG 220 -> 221 + # + rule = AclRule(is_permit=1, proto=17) + rule2 = AclRule(src_prefix=IPv6Network((0, 0)), + dst_prefix=IPv6Network((0, 0)), is_permit=1, proto=17) + rule3 = AclRule(is_permit=1, proto=1) + acl = VppAcl(self, rules=[rule, rule2, rule3]) + acl.add_vpp_config() + + c1 = VppGbpContract( + self, 400, epgs[0].sclass, epgs[1].sclass, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + [])], + [ETH_P_IP, ETH_P_IPV6]) + c1.add_vpp_config() + + self.send_and_expect_bridged(eps[0].itf, + pkt_inter_epg_220_to_221 * 65, + eps[2].itf) + + pkt_inter_epg_220_to_222 = (Ether(src=self.pg0.remote_mac, + dst=str(self.router_mac)) / + IP(src=eps[0].ip4, + dst=eps[3].ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + self.send_and_assert_no_replies(eps[0].itf, + pkt_inter_epg_220_to_222 * 65) + + # + # ping router IP in different BD + # + pkt_router_ping_220_to_221 = (Ether(src=self.pg0.remote_mac, + dst=str(self.router_mac)) / + IP(src=eps[0].ip4, + dst=epgs[1].bvi_ip4) / + ICMP(type='echo-request')) + + self.send_and_expect(self.pg0, [pkt_router_ping_220_to_221], self.pg0) + + pkt_router_ping_220_to_221 = (Ether(src=self.pg0.remote_mac, + dst=str(self.router_mac)) / + IPv6(src=eps[0].ip6, + dst=epgs[1].bvi_ip6) / + ICMPv6EchoRequest()) + + self.send_and_expect(self.pg0, [pkt_router_ping_220_to_221], self.pg0) + + # + # contract for the return direction + # + c2 = VppGbpContract( + self, 400, epgs[1].sclass, epgs[0].sclass, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + [])], + [ETH_P_IP, ETH_P_IPV6]) + c2.add_vpp_config() + + self.send_and_expect_bridged(eps[0].itf, + pkt_inter_epg_220_to_221 * 65, + eps[2].itf) + pkt_inter_epg_221_to_220 = (Ether(src=self.pg2.remote_mac, + dst=self.pg0.remote_mac) / + IP(src=eps[2].ip4, + dst=eps[0].ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + self.send_and_expect_bridged(eps[2].itf, + pkt_inter_epg_221_to_220 * 65, + eps[0].itf) + pkt_inter_epg_221_to_220 = (Ether(src=self.pg2.remote_mac, + dst=str(self.router_mac)) / + IP(src=eps[2].ip4, + dst=eps[0].ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + self.send_and_expect_routed(eps[2].itf, + pkt_inter_epg_221_to_220 * 65, + eps[0].itf, + str(self.router_mac)) + pkt_inter_epg_221_to_220 = (Ether(src=self.pg2.remote_mac, + dst=str(self.router_mac)) / + IPv6(src=eps[2].ip6, + dst=eps[0].ip6) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + self.send_and_expect_routed6(eps[2].itf, + pkt_inter_epg_221_to_220 * 65, + eps[0].itf, + str(self.router_mac)) + + # + # contract between 220 and 222 uni-direction + # + c3 = VppGbpContract( + self, 400, epgs[0].sclass, epgs[2].sclass, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + [])], + [ETH_P_IP, ETH_P_IPV6]) + c3.add_vpp_config() + + self.send_and_expect(eps[0].itf, + pkt_inter_epg_220_to_222 * 65, + eps[3].itf) + + c3.remove_vpp_config() + c1.remove_vpp_config() + c2.remove_vpp_config() + acl.remove_vpp_config() + + def test_gbp_bd_drop_flags(self): + """ GBP BD drop flags """ + + # + # IP tables + # + gt4 = VppIpTable(self, 1) + gt4.add_vpp_config() + gt6 = VppIpTable(self, 1, is_ip6=True) + gt6.add_vpp_config() + + rd1 = VppGbpRouteDomain(self, 1, 401, gt4, gt6) + rd1.add_vpp_config() + + # + # a GBP bridge domain with a BVI only + # + bd1 = VppBridgeDomain(self, 1) + bd1.add_vpp_config() + + gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0, + None, None, + uu_drop=True, bm_drop=True) + gbd1.add_vpp_config() + + self.logger.info(self.vapi.cli("sh bridge 1 detail")) + self.logger.info(self.vapi.cli("sh gbp bridge")) + + # ... and has a /32 applied + ip_addr = VppIpInterfaceAddress(self, gbd1.bvi, + "10.0.0.128", 32).add_vpp_config() + + # + # The Endpoint-group + # + epg_220 = VppGbpEndpointGroup(self, 220, 112, rd1, gbd1, + None, self.loop0, + "10.0.0.128", + "2001:10::128", + VppGbpEndpointRetention(3)) + epg_220.add_vpp_config() + + ep = VppGbpEndpoint(self, self.pg0, + epg_220, None, + "10.0.0.127", "11.0.0.127", + "2001:10::1", "3001::1") + ep.add_vpp_config() + + # + # send UU/BM packet from the local EP with UU drop and BM drop enabled + # in bd + # + self.logger.info(self.vapi.cli("sh bridge 1 detail")) + self.logger.info(self.vapi.cli("sh gbp bridge")) + p_uu = (Ether(src=ep.mac, dst="00:11:11:11:11:11") / + IP(dst="10.0.0.133", src=ep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + self.send_and_assert_no_replies(ep.itf, [p_uu]) + + p_bm = (Ether(src=ep.mac, dst="ff:ff:ff:ff:ff:ff") / + IP(dst="10.0.0.133", src=ep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + self.send_and_assert_no_replies(ep.itf, [p_bm]) + + self.pg3.unconfig_ip4() + + self.logger.info(self.vapi.cli("sh int")) + + def test_gbp_bd_arp_flags(self): + """ GBP BD arp flags """ + + # + # IP tables + # + gt4 = VppIpTable(self, 1) + gt4.add_vpp_config() + gt6 = VppIpTable(self, 1, is_ip6=True) + gt6.add_vpp_config() + + rd1 = VppGbpRouteDomain(self, 1, 401, gt4, gt6) + rd1.add_vpp_config() + + # + # Pg4 hosts the IP6 UU-flood VXLAN tunnel + # + self.pg4.config_ip4() + self.pg4.resolve_arp() + + # + # Add a mcast destination VXLAN-GBP tunnel for B&M traffic + # + tun_uu = VppVxlanGbpTunnel(self, self.pg4.local_ip4, + "239.1.1.1", 88, + mcast_itf=self.pg4) + tun_uu.add_vpp_config() + + # + # a GBP bridge domain with a BVI and a UU-flood interface + # + bd1 = VppBridgeDomain(self, 1) + bd1.add_vpp_config() + + gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0, + tun_uu, None, + ucast_arp=True) + gbd1.add_vpp_config() + + # ... and has a /32 applied + ip_addr = VppIpInterfaceAddress(self, gbd1.bvi, + "10.0.0.128", 32).add_vpp_config() + + # + # The Endpoint-group + # + epg_220 = VppGbpEndpointGroup(self, 220, 112, rd1, gbd1, + None, self.loop0, + "10.0.0.128", + "2001:10::128", + VppGbpEndpointRetention(2)) + epg_220.add_vpp_config() + + ep = VppGbpEndpoint(self, self.pg0, + epg_220, None, + "10.0.0.127", "11.0.0.127", + "2001:10::1", "3001::1") + ep.add_vpp_config() + + # + # send ARP packet from the local EP expect it on the uu interface + # + self.logger.info(self.vapi.cli("sh bridge 1 detail")) + self.logger.info(self.vapi.cli("sh gbp bridge")) + p_arp = (Ether(src=ep.mac, dst="ff:ff:ff:ff:ff:ff") / + ARP(op="who-has", + psrc=ep.ip4, pdst="10.0.0.99", + hwsrc=ep.mac, + hwdst="ff:ff:ff:ff:ff:ff")) + self.send_and_expect(ep.itf, [p_arp], self.pg4) + + self.pg4.unconfig_ip4() + + def test_gbp_learn_vlan_l2(self): + """ GBP L2 Endpoint w/ VLANs""" + + ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t + learnt = [{'mac': '00:00:11:11:11:01', + 'ip': '10.0.0.1', + 'ip6': '2001:10::2'}, + {'mac': '00:00:11:11:11:02', + 'ip': '10.0.0.2', + 'ip6': '2001:10::3'}] + + # + # IP tables + # + gt4 = VppIpTable(self, 1) + gt4.add_vpp_config() + gt6 = VppIpTable(self, 1, is_ip6=True) + gt6.add_vpp_config() + + rd1 = VppGbpRouteDomain(self, 1, 401, gt4, gt6) + rd1.add_vpp_config() + + # + # Pg2 hosts the vxlan tunnel, hosts on pg2 to act as TEPs + # + self.pg2.config_ip4() + self.pg2.resolve_arp() + self.pg2.generate_remote_hosts(4) + self.pg2.configure_ipv4_neighbors() + self.pg3.config_ip4() + self.pg3.resolve_arp() + + # + # The EP will be on a vlan sub-interface + # + vlan_11 = VppDot1QSubint(self, self.pg0, 11) + vlan_11.admin_up() + self.vapi.l2_interface_vlan_tag_rewrite( + sw_if_index=vlan_11.sw_if_index, vtr_op=L2_VTR_OP.L2_POP_1, + push_dot1q=11) + + bd_uu_fwd = VppVxlanGbpTunnel(self, self.pg3.local_ip4, + self.pg3.remote_ip4, 116) + bd_uu_fwd.add_vpp_config() + + # + # a GBP bridge domain with a BVI and a UU-flood interface + # The BD is marked as do not learn, so no endpoints are ever + # learnt in this BD. + # + bd1 = VppBridgeDomain(self, 1) + bd1.add_vpp_config() + gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0, bd_uu_fwd, + learn=False) + gbd1.add_vpp_config() + + self.logger.info(self.vapi.cli("sh bridge 1 detail")) + self.logger.info(self.vapi.cli("sh gbp bridge")) + + # ... and has a /32 applied + ip_addr = VppIpInterfaceAddress(self, gbd1.bvi, + "10.0.0.128", 32).add_vpp_config() + + # + # The Endpoint-group in which we are learning endpoints + # + epg_220 = VppGbpEndpointGroup(self, 220, 441, rd1, gbd1, + None, self.loop0, + "10.0.0.128", + "2001:10::128", + VppGbpEndpointRetention(4)) + epg_220.add_vpp_config() + + # + # The VXLAN GBP tunnel is a bridge-port and has L2 endpoint + # learning enabled + # + vx_tun_l2_1 = VppGbpVxlanTunnel( + self, 99, bd1.bd_id, + VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L2, + self.pg2.local_ip4) + vx_tun_l2_1.add_vpp_config() + + # + # A static endpoint that the learnt endpoints are trying to + # talk to + # + ep = VppGbpEndpoint(self, vlan_11, + epg_220, None, + "10.0.0.127", "11.0.0.127", + "2001:10::1", "3001::1") + ep.add_vpp_config() + + self.assertTrue(find_route(self, ep.ip4, 32, table_id=1)) + + # + # Send to the static EP + # + for ii, l in enumerate(learnt): + # a packet with an sclass from a known EPG + # arriving on an unknown TEP + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=99, gpid=441, flags=0x88) / + Ether(src=l['mac'], dst=ep.mac) / + IP(src=l['ip'], dst=ep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_expect(self.pg2, [p], self.pg0) + + # + # packet to EP has the EP's vlan tag + # + for rx in rxs: + self.assertEqual(rx[Dot1Q].vlan, 11) + + # + # the EP is not learnt since the BD setting prevents it + # also no TEP too + # + self.assertFalse(find_gbp_endpoint(self, + vx_tun_l2_1.sw_if_index, + mac=l['mac'])) + self.assertEqual(INDEX_INVALID, + find_vxlan_gbp_tunnel( + self, + self.pg2.local_ip4, + self.pg2.remote_hosts[1].ip4, + 99)) + + self.assertEqual(len(self.vapi.gbp_endpoint_dump()), 1) + + # + # static to remotes + # we didn't learn the remotes so they are sent to the UU-fwd + # + for l in learnt: + p = (Ether(src=ep.mac, dst=l['mac']) / + Dot1Q(vlan=11) / + IP(dst=l['ip'], src=ep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, p * 17, self.pg3) + + for rx in rxs: + self.assertEqual(rx[IP].src, self.pg3.local_ip4) + self.assertEqual(rx[IP].dst, self.pg3.remote_ip4) + self.assertEqual(rx[UDP].dport, 48879) + # the UDP source port is a random value for hashing + self.assertEqual(rx[VXLAN].gpid, 441) + self.assertEqual(rx[VXLAN].vni, 116) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + self.assertFalse(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + + self.pg2.unconfig_ip4() + self.pg3.unconfig_ip4() + + def test_gbp_learn_l3(self): + """ GBP L3 Endpoint Learning """ + + self.vapi.cli("set logging class gbp level debug") + + ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t + routed_dst_mac = "00:0c:0c:0c:0c:0c" + routed_src_mac = "00:22:bd:f8:19:ff" + + learnt = [{'mac': '00:00:11:11:11:02', + 'ip': '10.0.1.2', + 'ip6': '2001:10::2'}, + {'mac': '00:00:11:11:11:03', + 'ip': '10.0.1.3', + 'ip6': '2001:10::3'}] + + # + # IP tables + # + t4 = VppIpTable(self, 1) + t4.add_vpp_config() + t6 = VppIpTable(self, 1, True) + t6.add_vpp_config() + + tun_ip4_uu = VppVxlanGbpTunnel(self, self.pg4.local_ip4, + self.pg4.remote_ip4, 114) + tun_ip6_uu = VppVxlanGbpTunnel(self, self.pg4.local_ip4, + self.pg4.remote_ip4, 116) + tun_ip4_uu.add_vpp_config() + tun_ip6_uu.add_vpp_config() + + rd1 = VppGbpRouteDomain(self, 2, 401, t4, t6, tun_ip4_uu, tun_ip6_uu) + rd1.add_vpp_config() + + self.loop0.set_mac(self.router_mac) + + # + # Bind the BVI to the RD + # + b4 = VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config() + b6 = VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config() + + # + # Pg2 hosts the vxlan tunnel + # hosts on pg2 to act as TEPs + # pg3 is BD uu-fwd + # pg4 is RD uu-fwd + # + self.pg2.config_ip4() + self.pg2.resolve_arp() + self.pg2.generate_remote_hosts(4) + self.pg2.configure_ipv4_neighbors() + self.pg3.config_ip4() + self.pg3.resolve_arp() + self.pg4.config_ip4() + self.pg4.resolve_arp() + + # + # a GBP bridge domain with a BVI and a UU-flood interface + # + bd1 = VppBridgeDomain(self, 1) + bd1.add_vpp_config() + gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0, self.pg3) + gbd1.add_vpp_config() + + self.logger.info(self.vapi.cli("sh bridge 1 detail")) + self.logger.info(self.vapi.cli("sh gbp bridge")) + self.logger.info(self.vapi.cli("sh gbp route")) + + # ... and has a /32 and /128 applied + ip4_addr = VppIpInterfaceAddress(self, gbd1.bvi, + "10.0.0.128", 32, + bind=b4).add_vpp_config() + ip6_addr = VppIpInterfaceAddress(self, gbd1.bvi, + "2001:10::128", 128, + bind=b6).add_vpp_config() + + # + # The Endpoint-group in which we are learning endpoints + # + epg_220 = VppGbpEndpointGroup(self, 220, 441, rd1, gbd1, + None, self.loop0, + "10.0.0.128", + "2001:10::128", + VppGbpEndpointRetention(4)) + epg_220.add_vpp_config() + + # + # The VXLAN GBP tunnel is in L3 mode with learning enabled + # + vx_tun_l3 = VppGbpVxlanTunnel( + self, 101, rd1.rd_id, + VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3, + self.pg2.local_ip4) + vx_tun_l3.add_vpp_config() + + # + # A static endpoint that the learnt endpoints are trying to + # talk to + # + ep = VppGbpEndpoint(self, self.pg0, + epg_220, None, + "10.0.0.127", "11.0.0.127", + "2001:10::1", "3001::1") + ep.add_vpp_config() + + # + # learn some remote IPv4 EPs + # + for ii, l in enumerate(learnt): + # a packet with an sclass from a known EPG + # arriving on an unknown TEP + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=101, gpid=441, flags=0x88) / + Ether(src=l['mac'], dst="00:00:00:11:11:11") / + IP(src=l['ip'], dst=ep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, [p], self.pg0) + + # the new TEP + tep1_sw_if_index = find_vxlan_gbp_tunnel( + self, + self.pg2.local_ip4, + self.pg2.remote_hosts[1].ip4, + vx_tun_l3.vni) + self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index) + + # endpoint learnt via the parent GBP-vxlan interface + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l3._sw_if_index, + ip=l['ip'])) + + # + # Static IPv4 EP replies to learnt + # + for l in learnt: + p = (Ether(src=ep.mac, dst=self.loop0.local_mac) / + IP(dst=l['ip'], src=ep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, p * 1, self.pg2) + + for rx in rxs: + self.assertEqual(rx[IP].src, self.pg2.local_ip4) + self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4) + self.assertEqual(rx[UDP].dport, 48879) + # the UDP source port is a random value for hashing + self.assertEqual(rx[VXLAN].gpid, 441) + self.assertEqual(rx[VXLAN].vni, 101) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + self.assertTrue(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + + inner = rx[VXLAN].payload + + self.assertEqual(inner[Ether].src, routed_src_mac) + self.assertEqual(inner[Ether].dst, routed_dst_mac) + self.assertEqual(inner[IP].src, ep.ip4) + self.assertEqual(inner[IP].dst, l['ip']) + + for l in learnt: + self.assertFalse(find_gbp_endpoint(self, + tep1_sw_if_index, + ip=l['ip'])) + + # + # learn some remote IPv6 EPs + # + for ii, l in enumerate(learnt): + # a packet with an sclass from a known EPG + # arriving on an unknown TEP + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=101, gpid=441, flags=0x88) / + Ether(src=l['mac'], dst="00:00:00:11:11:11") / + IPv6(src=l['ip6'], dst=ep.ip6) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, [p], self.pg0) + + # the new TEP + tep1_sw_if_index = find_vxlan_gbp_tunnel( + self, + self.pg2.local_ip4, + self.pg2.remote_hosts[1].ip4, + vx_tun_l3.vni) + self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index) + + self.logger.info(self.vapi.cli("show gbp bridge")) + self.logger.info(self.vapi.cli("show vxlan-gbp tunnel")) + self.logger.info(self.vapi.cli("show gbp vxlan")) + self.logger.info(self.vapi.cli("show int addr")) + + # endpoint learnt via the TEP + self.assertTrue(find_gbp_endpoint(self, ip=l['ip6'])) + + self.logger.info(self.vapi.cli("show gbp endpoint")) + self.logger.info(self.vapi.cli("show ip fib index 1 %s" % l['ip'])) + + # + # Static EP replies to learnt + # + for l in learnt: + p = (Ether(src=ep.mac, dst=self.loop0.local_mac) / + IPv6(dst=l['ip6'], src=ep.ip6) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg2) + + for rx in rxs: + self.assertEqual(rx[IP].src, self.pg2.local_ip4) + self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4) + self.assertEqual(rx[UDP].dport, 48879) + # the UDP source port is a random value for hashing + self.assertEqual(rx[VXLAN].gpid, 441) + self.assertEqual(rx[VXLAN].vni, 101) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + self.assertTrue(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + + inner = rx[VXLAN].payload + + self.assertEqual(inner[Ether].src, routed_src_mac) + self.assertEqual(inner[Ether].dst, routed_dst_mac) + self.assertEqual(inner[IPv6].src, ep.ip6) + self.assertEqual(inner[IPv6].dst, l['ip6']) + + self.logger.info(self.vapi.cli("sh gbp endpoint")) + for l in learnt: + self.wait_for_ep_timeout(ip=l['ip']) + + # + # Static sends to unknown EP with no route + # + p = (Ether(src=ep.mac, dst=self.loop0.local_mac) / + IP(dst="10.0.0.99", src=ep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + self.send_and_assert_no_replies(self.pg0, [p]) + + # + # Add a route to static EP's v4 and v6 subnet + # + se_10_24 = VppGbpSubnet( + self, rd1, "10.0.0.0", 24, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_TRANSPORT) + se_10_24.add_vpp_config() + + # + # static pings router + # + p = (Ether(src=ep.mac, dst=self.loop0.local_mac) / + IP(dst=epg_220.bvi_ip4, src=ep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg0) + + p = (Ether(src=ep.mac, dst=self.loop0.local_mac) / + IPv6(dst=epg_220.bvi_ip6, src=ep.ip6) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg0) + + # + # packets to address in the subnet are sent on the uu-fwd + # + p = (Ether(src=ep.mac, dst=self.loop0.local_mac) / + IP(dst="10.0.0.99", src=ep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, [p], self.pg4) + for rx in rxs: + self.assertEqual(rx[IP].src, self.pg4.local_ip4) + self.assertEqual(rx[IP].dst, self.pg4.remote_ip4) + self.assertEqual(rx[UDP].dport, 48879) + # the UDP source port is a random value for hashing + self.assertEqual(rx[VXLAN].gpid, 441) + self.assertEqual(rx[VXLAN].vni, 114) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + # policy is not applied to packets sent to the uu-fwd interfaces + self.assertFalse(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + + # + # learn some remote IPv4 EPs + # + for ii, l in enumerate(learnt): + # a packet with an sclass from a known EPG + # arriving on an unknown TEP + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[2].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=101, gpid=441, flags=0x88) / + Ether(src=l['mac'], dst="00:00:00:11:11:11") / + IP(src=l['ip'], dst=ep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, [p], self.pg0) + + # the new TEP + tep1_sw_if_index = find_vxlan_gbp_tunnel( + self, + self.pg2.local_ip4, + self.pg2.remote_hosts[2].ip4, + vx_tun_l3.vni) + self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index) + + # endpoint learnt via the parent GBP-vxlan interface + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l3._sw_if_index, + ip=l['ip'])) + + # + # Add a remote endpoint from the API + # + rep_88 = VppGbpEndpoint(self, vx_tun_l3, + epg_220, None, + "10.0.0.88", "11.0.0.88", + "2001:10::88", "3001::88", + ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE, + self.pg2.local_ip4, + self.pg2.remote_hosts[2].ip4, + mac=None) + rep_88.add_vpp_config() + + # + # Add a remote endpoint from the API that matches an existing one + # this is a lower priority, hence the packet is sent to the DP leanrt + # TEP + # + rep_2 = VppGbpEndpoint(self, vx_tun_l3, + epg_220, None, + learnt[0]['ip'], "11.0.0.101", + learnt[0]['ip6'], "3001::101", + ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE, + self.pg2.local_ip4, + self.pg2.remote_hosts[1].ip4, + mac=None) + rep_2.add_vpp_config() + + # + # Add a route to the learned EP's v4 subnet + # packets should be send on the v4/v6 uu=fwd interface resp. + # + se_10_1_24 = VppGbpSubnet( + self, rd1, "10.0.1.0", 24, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_TRANSPORT) + se_10_1_24.add_vpp_config() + + self.logger.info(self.vapi.cli("show gbp endpoint")) + + ips = ["10.0.0.88", learnt[0]['ip']] + for ip in ips: + p = (Ether(src=ep.mac, dst=self.loop0.local_mac) / + IP(dst=ip, src=ep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg2) + + for rx in rxs: + self.assertEqual(rx[IP].src, self.pg2.local_ip4) + self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[2].ip4) + self.assertEqual(rx[UDP].dport, 48879) + # the UDP source port is a random value for hashing + self.assertEqual(rx[VXLAN].gpid, 441) + self.assertEqual(rx[VXLAN].vni, 101) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + self.assertTrue(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + + inner = rx[VXLAN].payload + + self.assertEqual(inner[Ether].src, routed_src_mac) + self.assertEqual(inner[Ether].dst, routed_dst_mac) + self.assertEqual(inner[IP].src, ep.ip4) + self.assertEqual(inner[IP].dst, ip) + + # + # remove the API remote EPs, only API sourced is gone, the DP + # learnt one remains + # + rep_88.remove_vpp_config() + rep_2.remove_vpp_config() + + self.assertTrue(find_gbp_endpoint(self, ip=rep_2.ip4)) + + p = (Ether(src=ep.mac, dst=self.loop0.local_mac) / + IP(src=ep.ip4, dst=rep_2.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + rxs = self.send_and_expect(self.pg0, [p], self.pg2) + + self.assertFalse(find_gbp_endpoint(self, ip=rep_88.ip4)) + + p = (Ether(src=ep.mac, dst=self.loop0.local_mac) / + IP(src=ep.ip4, dst=rep_88.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + rxs = self.send_and_expect(self.pg0, [p], self.pg4) + + # + # to appease the testcase we cannot have the registered EP still + # present (because it's DP learnt) when the TC ends so wait until + # it is removed + # + self.wait_for_ep_timeout(ip=rep_88.ip4) + self.wait_for_ep_timeout(ip=rep_2.ip4) + + # + # Same as above, learn a remote EP via CP and DP + # this time remove the DP one first. expect the CP data to remain + # + rep_3 = VppGbpEndpoint(self, vx_tun_l3, + epg_220, None, + "10.0.1.4", "11.0.0.103", + "2001::10:3", "3001::103", + ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE, + self.pg2.local_ip4, + self.pg2.remote_hosts[1].ip4, + mac=None) + rep_3.add_vpp_config() + + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[2].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=101, gpid=441, flags=0x88) / + Ether(src=l['mac'], dst="00:00:00:11:11:11") / + IP(src="10.0.1.4", dst=ep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + rxs = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0) + + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l3._sw_if_index, + ip=rep_3.ip4, + tep=[self.pg2.local_ip4, + self.pg2.remote_hosts[2].ip4])) + + p = (Ether(src=ep.mac, dst=self.loop0.local_mac) / + IP(dst="10.0.1.4", src=ep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + rxs = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg2) + + # host 2 is the DP learned TEP + for rx in rxs: + self.assertEqual(rx[IP].src, self.pg2.local_ip4) + self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[2].ip4) + + self.wait_for_ep_timeout(ip=rep_3.ip4, + tep=[self.pg2.local_ip4, + self.pg2.remote_hosts[2].ip4]) + + rxs = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg2) + + # host 1 is the CP learned TEP + for rx in rxs: + self.assertEqual(rx[IP].src, self.pg2.local_ip4) + self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4) + + # + # shutdown with learnt endpoint present + # + p = (Ether(src=self.pg2.remote_mac, + dst=self.pg2.local_mac) / + IP(src=self.pg2.remote_hosts[1].ip4, + dst=self.pg2.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=101, gpid=441, flags=0x88) / + Ether(src=l['mac'], dst="00:00:00:11:11:11") / + IP(src=learnt[1]['ip'], dst=ep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rx = self.send_and_expect(self.pg2, [p], self.pg0) + + # endpoint learnt via the parent GBP-vxlan interface + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l3._sw_if_index, + ip=l['ip'])) + + # + # TODO + # remote endpoint becomes local + # + self.pg2.unconfig_ip4() + self.pg3.unconfig_ip4() + self.pg4.unconfig_ip4() + + def test_gbp_redirect(self): + """ GBP Endpoint Redirect """ + + self.vapi.cli("set logging class gbp level debug") + + ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t + routed_dst_mac = "00:0c:0c:0c:0c:0c" + routed_src_mac = "00:22:bd:f8:19:ff" + + learnt = [{'mac': '00:00:11:11:11:02', + 'ip': '10.0.1.2', + 'ip6': '2001:10::2'}, + {'mac': '00:00:11:11:11:03', + 'ip': '10.0.1.3', + 'ip6': '2001:10::3'}] + + # + # IP tables + # + t4 = VppIpTable(self, 1) + t4.add_vpp_config() + t6 = VppIpTable(self, 1, True) + t6.add_vpp_config() + + rd1 = VppGbpRouteDomain(self, 2, 402, t4, t6) + rd1.add_vpp_config() + + self.loop0.set_mac(self.router_mac) + + # + # Bind the BVI to the RD + # + b_ip4 = VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config() + b_ip6 = VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config() + + # + # Pg7 hosts a BD's UU-fwd + # + self.pg7.config_ip4() + self.pg7.resolve_arp() + + # + # a GBP bridge domains for the EPs + # + bd1 = VppBridgeDomain(self, 1) + bd1.add_vpp_config() + gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0) + gbd1.add_vpp_config() + + bd2 = VppBridgeDomain(self, 2) + bd2.add_vpp_config() + gbd2 = VppGbpBridgeDomain(self, bd2, rd1, self.loop1) + gbd2.add_vpp_config() + + # ... and has a /32 and /128 applied + ip4_addr = VppIpInterfaceAddress(self, gbd1.bvi, + "10.0.0.128", 32, + bind=b_ip4).add_vpp_config() + ip6_addr = VppIpInterfaceAddress(self, gbd1.bvi, + "2001:10::128", 128, + bind=b_ip6).add_vpp_config() + ip4_addr = VppIpInterfaceAddress(self, gbd2.bvi, + "10.0.1.128", 32).add_vpp_config() + ip6_addr = VppIpInterfaceAddress(self, gbd2.bvi, + "2001:11::128", 128).add_vpp_config() + + # + # The Endpoint-groups in which we are learning endpoints + # + epg_220 = VppGbpEndpointGroup(self, 220, 440, rd1, gbd1, + None, gbd1.bvi, + "10.0.0.128", + "2001:10::128", + VppGbpEndpointRetention(60)) + epg_220.add_vpp_config() + epg_221 = VppGbpEndpointGroup(self, 221, 441, rd1, gbd2, + None, gbd2.bvi, + "10.0.1.128", + "2001:11::128", + VppGbpEndpointRetention(60)) + epg_221.add_vpp_config() + epg_222 = VppGbpEndpointGroup(self, 222, 442, rd1, gbd1, + None, gbd1.bvi, + "10.0.2.128", + "2001:12::128", + VppGbpEndpointRetention(60)) + epg_222.add_vpp_config() + + # + # a GBP bridge domains for the SEPs + # + bd_uu1 = VppVxlanGbpTunnel(self, self.pg7.local_ip4, + self.pg7.remote_ip4, 116) + bd_uu1.add_vpp_config() + bd_uu2 = VppVxlanGbpTunnel(self, self.pg7.local_ip4, + self.pg7.remote_ip4, 117) + bd_uu2.add_vpp_config() + + bd3 = VppBridgeDomain(self, 3) + bd3.add_vpp_config() + gbd3 = VppGbpBridgeDomain(self, bd3, rd1, self.loop2, + bd_uu1, learn=False) + gbd3.add_vpp_config() + bd4 = VppBridgeDomain(self, 4) + bd4.add_vpp_config() + gbd4 = VppGbpBridgeDomain(self, bd4, rd1, self.loop3, + bd_uu2, learn=False) + gbd4.add_vpp_config() + + # + # EPGs in which the service endpoints exist + # + epg_320 = VppGbpEndpointGroup(self, 320, 550, rd1, gbd3, + None, gbd1.bvi, + "12.0.0.128", + "4001:10::128", + VppGbpEndpointRetention(60)) + epg_320.add_vpp_config() + epg_321 = VppGbpEndpointGroup(self, 321, 551, rd1, gbd4, + None, gbd2.bvi, + "12.0.1.128", + "4001:11::128", + VppGbpEndpointRetention(60)) + epg_321.add_vpp_config() + + # + # three local endpoints + # + ep1 = VppGbpEndpoint(self, self.pg0, + epg_220, None, + "10.0.0.1", "11.0.0.1", + "2001:10::1", "3001:10::1") + ep1.add_vpp_config() + ep2 = VppGbpEndpoint(self, self.pg1, + epg_221, None, + "10.0.1.1", "11.0.1.1", + "2001:11::1", "3001:11::1") + ep2.add_vpp_config() + ep3 = VppGbpEndpoint(self, self.pg2, + epg_222, None, + "10.0.2.2", "11.0.2.2", + "2001:12::1", "3001:12::1") + ep3.add_vpp_config() + + # + # service endpoints + # + sep1 = VppGbpEndpoint(self, self.pg3, + epg_320, None, + "12.0.0.1", "13.0.0.1", + "4001:10::1", "5001:10::1") + sep1.add_vpp_config() + sep2 = VppGbpEndpoint(self, self.pg4, + epg_320, None, + "12.0.0.2", "13.0.0.2", + "4001:10::2", "5001:10::2") + sep2.add_vpp_config() + sep3 = VppGbpEndpoint(self, self.pg5, + epg_321, None, + "12.0.1.1", "13.0.1.1", + "4001:11::1", "5001:11::1") + sep3.add_vpp_config() + # this EP is not installed immediately + sep4 = VppGbpEndpoint(self, self.pg6, + epg_321, None, + "12.0.1.2", "13.0.1.2", + "4001:11::2", "5001:11::2") + + # + # an L2 switch packet between local EPs in different EPGs + # different dest ports on each so the are LB hashed differently + # + p4 = [(Ether(src=ep1.mac, dst=ep3.mac) / + IP(src=ep1.ip4, dst=ep3.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)), + (Ether(src=ep3.mac, dst=ep1.mac) / + IP(src=ep3.ip4, dst=ep1.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100))] + p6 = [(Ether(src=ep1.mac, dst=ep3.mac) / + IPv6(src=ep1.ip6, dst=ep3.ip6) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)), + (Ether(src=ep3.mac, dst=ep1.mac) / + IPv6(src=ep3.ip6, dst=ep1.ip6) / + UDP(sport=1234, dport=1230) / + Raw(b'\xa5' * 100))] + + # should be dropped since no contract yet + self.send_and_assert_no_replies(self.pg0, [p4[0]]) + self.send_and_assert_no_replies(self.pg0, [p6[0]]) + + # + # Add a contract with a rule to load-balance redirect via SEP1 and SEP2 + # one of the next-hops is via an EP that is not known + # + rule4 = AclRule(is_permit=1, proto=17) + rule6 = AclRule(src_prefix=IPv6Network((0, 0)), + dst_prefix=IPv6Network((0, 0)), is_permit=1, proto=17) + acl = VppAcl(self, rules=[rule4, rule6]) + acl.add_vpp_config() + + # + # test the src-ip hash mode + # + c1 = VppGbpContract( + self, 402, epg_220.sclass, epg_222.sclass, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd, + sep1.ip4, sep1.epg.rd), + VppGbpContractNextHop(sep2.vmac, sep2.epg.bd, + sep2.ip4, sep2.epg.rd)]), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd, + sep3.ip6, sep3.epg.rd), + VppGbpContractNextHop(sep4.vmac, sep4.epg.bd, + sep4.ip6, sep4.epg.rd)])], + [ETH_P_IP, ETH_P_IPV6]) + c1.add_vpp_config() + + c2 = VppGbpContract( + self, 402, epg_222.sclass, epg_220.sclass, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd, + sep1.ip4, sep1.epg.rd), + VppGbpContractNextHop(sep2.vmac, sep2.epg.bd, + sep2.ip4, sep2.epg.rd)]), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd, + sep3.ip6, sep3.epg.rd), + VppGbpContractNextHop(sep4.vmac, sep4.epg.bd, + sep4.ip6, sep4.epg.rd)])], + [ETH_P_IP, ETH_P_IPV6]) + c2.add_vpp_config() + + # + # send again with the contract preset, now packets arrive + # at SEP1 or SEP2 depending on the hashing + # + rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf) + + for rx in rxs: + self.assertEqual(rx[Ether].src, routed_src_mac) + self.assertEqual(rx[Ether].dst, sep1.mac) + self.assertEqual(rx[IP].src, ep1.ip4) + self.assertEqual(rx[IP].dst, ep3.ip4) + + rxs = self.send_and_expect(self.pg2, p4[1] * 17, sep2.itf) + + for rx in rxs: + self.assertEqual(rx[Ether].src, routed_src_mac) + self.assertEqual(rx[Ether].dst, sep2.mac) + self.assertEqual(rx[IP].src, ep3.ip4) + self.assertEqual(rx[IP].dst, ep1.ip4) + + rxs = self.send_and_expect(self.pg0, p6[0] * 17, self.pg7) + + for rx in rxs: + self.assertEqual(rx[Ether].src, self.pg7.local_mac) + self.assertEqual(rx[Ether].dst, self.pg7.remote_mac) + self.assertEqual(rx[IP].src, self.pg7.local_ip4) + self.assertEqual(rx[IP].dst, self.pg7.remote_ip4) + self.assertEqual(rx[VXLAN].vni, 117) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + # redirect policy has been applied + self.assertTrue(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + + inner = rx[VXLAN].payload + + self.assertEqual(inner[Ether].src, routed_src_mac) + self.assertEqual(inner[Ether].dst, sep4.mac) + self.assertEqual(inner[IPv6].src, ep1.ip6) + self.assertEqual(inner[IPv6].dst, ep3.ip6) + + rxs = self.send_and_expect(self.pg2, p6[1] * 17, sep3.itf) + + for rx in rxs: + self.assertEqual(rx[Ether].src, routed_src_mac) + self.assertEqual(rx[Ether].dst, sep3.mac) + self.assertEqual(rx[IPv6].src, ep3.ip6) + self.assertEqual(rx[IPv6].dst, ep1.ip6) + + # + # programme the unknown EP + # + sep4.add_vpp_config() + + rxs = self.send_and_expect(self.pg0, p6[0] * 17, sep4.itf) + + for rx in rxs: + self.assertEqual(rx[Ether].src, routed_src_mac) + self.assertEqual(rx[Ether].dst, sep4.mac) + self.assertEqual(rx[IPv6].src, ep1.ip6) + self.assertEqual(rx[IPv6].dst, ep3.ip6) + + # + # and revert back to unprogrammed + # + sep4.remove_vpp_config() + + rxs = self.send_and_expect(self.pg0, p6[0] * 17, self.pg7) + + for rx in rxs: + self.assertEqual(rx[Ether].src, self.pg7.local_mac) + self.assertEqual(rx[Ether].dst, self.pg7.remote_mac) + self.assertEqual(rx[IP].src, self.pg7.local_ip4) + self.assertEqual(rx[IP].dst, self.pg7.remote_ip4) + self.assertEqual(rx[VXLAN].vni, 117) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + # redirect policy has been applied + self.assertTrue(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + + inner = rx[VXLAN].payload + + self.assertEqual(inner[Ether].src, routed_src_mac) + self.assertEqual(inner[Ether].dst, sep4.mac) + self.assertEqual(inner[IPv6].src, ep1.ip6) + self.assertEqual(inner[IPv6].dst, ep3.ip6) + + c1.remove_vpp_config() + c2.remove_vpp_config() + + # + # test the symmetric hash mode + # + c1 = VppGbpContract( + self, 402, epg_220.sclass, epg_222.sclass, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC, + [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd, + sep1.ip4, sep1.epg.rd), + VppGbpContractNextHop(sep2.vmac, sep2.epg.bd, + sep2.ip4, sep2.epg.rd)]), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC, + [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd, + sep3.ip6, sep3.epg.rd), + VppGbpContractNextHop(sep4.vmac, sep4.epg.bd, + sep4.ip6, sep4.epg.rd)])], + [ETH_P_IP, ETH_P_IPV6]) + c1.add_vpp_config() + + c2 = VppGbpContract( + self, 402, epg_222.sclass, epg_220.sclass, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC, + [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd, + sep1.ip4, sep1.epg.rd), + VppGbpContractNextHop(sep2.vmac, sep2.epg.bd, + sep2.ip4, sep2.epg.rd)]), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC, + [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd, + sep3.ip6, sep3.epg.rd), + VppGbpContractNextHop(sep4.vmac, sep4.epg.bd, + sep4.ip6, sep4.epg.rd)])], + [ETH_P_IP, ETH_P_IPV6]) + c2.add_vpp_config() + + # + # send again with the contract preset, now packets arrive + # at SEP1 for both directions + # + rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf) + + for rx in rxs: + self.assertEqual(rx[Ether].src, routed_src_mac) + self.assertEqual(rx[Ether].dst, sep1.mac) + self.assertEqual(rx[IP].src, ep1.ip4) + self.assertEqual(rx[IP].dst, ep3.ip4) + + rxs = self.send_and_expect(self.pg2, p4[1] * 17, sep1.itf) + + for rx in rxs: + self.assertEqual(rx[Ether].src, routed_src_mac) + self.assertEqual(rx[Ether].dst, sep1.mac) + self.assertEqual(rx[IP].src, ep3.ip4) + self.assertEqual(rx[IP].dst, ep1.ip4) + + # + # programme the unknown EP for the L3 tests + # + sep4.add_vpp_config() + + # + # an L3 switch packet between local EPs in different EPGs + # different dest ports on each so the are LB hashed differently + # + p4 = [(Ether(src=ep1.mac, dst=str(self.router_mac)) / + IP(src=ep1.ip4, dst=ep2.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)), + (Ether(src=ep2.mac, dst=str(self.router_mac)) / + IP(src=ep2.ip4, dst=ep1.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100))] + p6 = [(Ether(src=ep1.mac, dst=str(self.router_mac)) / + IPv6(src=ep1.ip6, dst=ep2.ip6) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)), + (Ether(src=ep2.mac, dst=str(self.router_mac)) / + IPv6(src=ep2.ip6, dst=ep1.ip6) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100))] + + c3 = VppGbpContract( + self, 402, epg_220.sclass, epg_221.sclass, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC, + [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd, + sep1.ip4, sep1.epg.rd), + VppGbpContractNextHop(sep2.vmac, sep2.epg.bd, + sep2.ip4, sep2.epg.rd)]), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC, + [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd, + sep3.ip6, sep3.epg.rd), + VppGbpContractNextHop(sep4.vmac, sep4.epg.bd, + sep4.ip6, sep4.epg.rd)])], + [ETH_P_IP, ETH_P_IPV6]) + c3.add_vpp_config() + + rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf) + + for rx in rxs: + self.assertEqual(rx[Ether].src, routed_src_mac) + self.assertEqual(rx[Ether].dst, sep1.mac) + self.assertEqual(rx[IP].src, ep1.ip4) + self.assertEqual(rx[IP].dst, ep2.ip4) + + # + # learn a remote EP in EPG 221 + # packets coming from unknown remote EPs will be leant & redirected + # + vx_tun_l3 = VppGbpVxlanTunnel( + self, 444, rd1.rd_id, + VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3, + self.pg2.local_ip4) + vx_tun_l3.add_vpp_config() + + c4 = VppGbpContract( + self, 402, epg_221.sclass, epg_220.sclass, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd, + sep1.ip4, sep1.epg.rd), + VppGbpContractNextHop(sep2.vmac, sep2.epg.bd, + sep2.ip4, sep2.epg.rd)]), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd, + sep3.ip6, sep3.epg.rd), + VppGbpContractNextHop(sep4.vmac, sep4.epg.bd, + sep4.ip6, sep4.epg.rd)])], + [ETH_P_IP, ETH_P_IPV6]) + c4.add_vpp_config() + + p = (Ether(src=self.pg7.remote_mac, + dst=self.pg7.local_mac) / + IP(src=self.pg7.remote_ip4, + dst=self.pg7.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=444, gpid=441, flags=0x88) / + Ether(src="00:22:22:22:22:33", dst=str(self.router_mac)) / + IP(src="10.0.0.88", dst=ep1.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + # unknown remote EP to local EP redirected + rxs = self.send_and_expect(self.pg7, [p], sep1.itf) + + for rx in rxs: + self.assertEqual(rx[Ether].src, routed_src_mac) + self.assertEqual(rx[Ether].dst, sep1.mac) + self.assertEqual(rx[IP].src, "10.0.0.88") + self.assertEqual(rx[IP].dst, ep1.ip4) + + # endpoint learnt via the parent GBP-vxlan interface + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l3._sw_if_index, + ip="10.0.0.88")) + + p = (Ether(src=self.pg7.remote_mac, + dst=self.pg7.local_mac) / + IP(src=self.pg7.remote_ip4, + dst=self.pg7.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=444, gpid=441, flags=0x88) / + Ether(src="00:22:22:22:22:33", dst=str(self.router_mac)) / + IPv6(src="2001:10::88", dst=ep1.ip6) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + # unknown remote EP to local EP redirected (ipv6) + rxs = self.send_and_expect(self.pg7, [p], sep3.itf) + + for rx in rxs: + self.assertEqual(rx[Ether].src, routed_src_mac) + self.assertEqual(rx[Ether].dst, sep3.mac) + self.assertEqual(rx[IPv6].src, "2001:10::88") + self.assertEqual(rx[IPv6].dst, ep1.ip6) + + # endpoint learnt via the parent GBP-vxlan interface + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l3._sw_if_index, + ip="2001:10::88")) + + # + # L3 switch from local to remote EP + # + p4 = [(Ether(src=ep1.mac, dst=str(self.router_mac)) / + IP(src=ep1.ip4, dst="10.0.0.88") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100))] + p6 = [(Ether(src=ep1.mac, dst=str(self.router_mac)) / + IPv6(src=ep1.ip6, dst="2001:10::88") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100))] + + rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf) + + for rx in rxs: + self.assertEqual(rx[Ether].src, routed_src_mac) + self.assertEqual(rx[Ether].dst, sep1.mac) + self.assertEqual(rx[IP].src, ep1.ip4) + self.assertEqual(rx[IP].dst, "10.0.0.88") + + rxs = self.send_and_expect(self.pg0, p6[0] * 17, sep4.itf) + + for rx in rxs: + self.assertEqual(rx[Ether].src, routed_src_mac) + self.assertEqual(rx[Ether].dst, sep4.mac) + self.assertEqual(rx[IPv6].src, ep1.ip6) + self.assertEqual(rx[IPv6].dst, "2001:10::88") + + # + # test the dst-ip hash mode + # + c5 = VppGbpContract( + self, 402, epg_220.sclass, epg_221.sclass, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP, + [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd, + sep1.ip4, sep1.epg.rd), + VppGbpContractNextHop(sep2.vmac, sep2.epg.bd, + sep2.ip4, sep2.epg.rd)]), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP, + [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd, + sep3.ip6, sep3.epg.rd), + VppGbpContractNextHop(sep4.vmac, sep4.epg.bd, + sep4.ip6, sep4.epg.rd)])], + [ETH_P_IP, ETH_P_IPV6]) + c5.add_vpp_config() + + rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf) + + for rx in rxs: + self.assertEqual(rx[Ether].src, routed_src_mac) + self.assertEqual(rx[Ether].dst, sep1.mac) + self.assertEqual(rx[IP].src, ep1.ip4) + self.assertEqual(rx[IP].dst, "10.0.0.88") + + rxs = self.send_and_expect(self.pg0, p6[0] * 17, sep3.itf) + + for rx in rxs: + self.assertEqual(rx[Ether].src, routed_src_mac) + self.assertEqual(rx[Ether].dst, sep3.mac) + self.assertEqual(rx[IPv6].src, ep1.ip6) + self.assertEqual(rx[IPv6].dst, "2001:10::88") + + # + # a programmed remote SEP in EPG 320 + # + + # gbp vxlan tunnel for the remote SEP + vx_tun_l3_sep = VppGbpVxlanTunnel( + self, 555, rd1.rd_id, + VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3, + self.pg2.local_ip4) + vx_tun_l3_sep.add_vpp_config() + + # remote SEP + sep5 = VppGbpEndpoint(self, vx_tun_l3_sep, + epg_320, None, + "12.0.0.10", "13.0.0.10", + "4001:10::10", "5001:10::10", + ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE, + self.pg7.local_ip4, + self.pg7.remote_ip4, + mac=None) + sep5.add_vpp_config() + + # + # local l3out redirect tests + # + + # add local l3out + # the external bd + self.loop4.set_mac(self.router_mac) + b_lo4_ip4 = VppIpInterfaceBind(self, self.loop4, t4).add_vpp_config() + b_lo4_ip6 = VppIpInterfaceBind(self, self.loop4, t6).add_vpp_config() + ebd = VppBridgeDomain(self, 100) + ebd.add_vpp_config() + gebd = VppGbpBridgeDomain(self, ebd, rd1, self.loop4, None, None) + gebd.add_vpp_config() + # the external epg + eepg = VppGbpEndpointGroup(self, 888, 765, rd1, gebd, + None, gebd.bvi, + "10.1.0.128", + "2001:10:1::128", + VppGbpEndpointRetention(60)) + eepg.add_vpp_config() + # add subnets to BVI + VppIpInterfaceAddress( + self, + gebd.bvi, + "10.1.0.128", + 24, bind=b_lo4_ip4).add_vpp_config() + VppIpInterfaceAddress( + self, + gebd.bvi, + "2001:10:1::128", + 64, bind=b_lo4_ip6).add_vpp_config() + # ... which are L3-out subnets + VppGbpSubnet(self, rd1, "10.1.0.0", 24, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT, + sclass=765).add_vpp_config() + VppGbpSubnet(self, rd1, "2001:10:1::128", 64, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT, + sclass=765).add_vpp_config() + # external endpoints + VppL2Vtr(self, self.vlan_100, L2_VTR_OP.L2_POP_1).add_vpp_config() + eep1 = VppGbpEndpoint(self, self.vlan_100, eepg, None, "10.1.0.1", + "11.1.0.1", "2001:10:1::1", "3001:10:1::1", + ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL) + eep1.add_vpp_config() + VppL2Vtr(self, self.vlan_101, L2_VTR_OP.L2_POP_1).add_vpp_config() + eep2 = VppGbpEndpoint(self, self.vlan_101, eepg, None, "10.1.0.2", + "11.1.0.2", "2001:10:1::2", "3001:10:1::2", + ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL) + eep2.add_vpp_config() + + # external subnets reachable though eep1 and eep2 respectively + VppIpRoute(self, "10.220.0.0", 24, + [VppRoutePath(eep1.ip4, eep1.epg.bvi.sw_if_index)], + table_id=t4.table_id).add_vpp_config() + VppGbpSubnet(self, rd1, "10.220.0.0", 24, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT, + sclass=4220).add_vpp_config() + VppIpRoute(self, "10:220::", 64, + [VppRoutePath(eep1.ip6, eep1.epg.bvi.sw_if_index)], + table_id=t6.table_id).add_vpp_config() + VppGbpSubnet(self, rd1, "10:220::", 64, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT, + sclass=4220).add_vpp_config() + VppIpRoute(self, "10.221.0.0", 24, + [VppRoutePath(eep2.ip4, eep2.epg.bvi.sw_if_index)], + table_id=t4.table_id).add_vpp_config() + VppGbpSubnet(self, rd1, "10.221.0.0", 24, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT, + sclass=4221).add_vpp_config() + VppIpRoute(self, "10:221::", 64, + [VppRoutePath(eep2.ip6, eep2.epg.bvi.sw_if_index)], + table_id=t6.table_id).add_vpp_config() + VppGbpSubnet(self, rd1, "10:221::", 64, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT, + sclass=4221).add_vpp_config() + + # + # l3out redirect to remote (known, then unknown) SEP + # + + # packets from 1 external subnet to the other + p = [(Ether(src=eep1.mac, dst=self.router_mac) / + Dot1Q(vlan=100) / + IP(src="10.220.0.17", dst="10.221.0.65") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)), + (Ether(src=eep1.mac, dst=self.router_mac) / + Dot1Q(vlan=100) / + IPv6(src="10:220::17", dst="10:221::65") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100))] + + # packets should be dropped in absence of contract + self.send_and_assert_no_replies(self.pg0, p) + + # contract redirecting to sep5 + VppGbpContract( + self, 402, 4220, 4221, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP, + [VppGbpContractNextHop(sep5.vmac, sep5.epg.bd, + sep5.ip4, sep5.epg.rd)]), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP, + [VppGbpContractNextHop(sep5.vmac, sep5.epg.bd, + sep5.ip6, sep5.epg.rd)])], + [ETH_P_IP, ETH_P_IPV6]).add_vpp_config() + + rxs = self.send_and_expect(self.pg0, p, self.pg7) + + for rx, tx in zip(rxs, p): + self.assertEqual(rx[Ether].src, self.pg7.local_mac) + self.assertEqual(rx[Ether].dst, self.pg7.remote_mac) + self.assertEqual(rx[IP].src, self.pg7.local_ip4) + self.assertEqual(rx[IP].dst, self.pg7.remote_ip4) + # this should use the programmed remote leaf TEP + self.assertEqual(rx[VXLAN].vni, 555) + self.assertEqual(rx[VXLAN].gpid, 4220) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + # redirect policy has been applied + self.assertTrue(rx[VXLAN].gpflags.A) + self.assertTrue(rx[VXLAN].gpflags.D) + rxip = rx[VXLAN][Ether].payload + txip = tx[Dot1Q].payload + self.assertEqual(rxip.src, txip.src) + self.assertEqual(rxip.dst, txip.dst) + + # remote SEP: it is now an unknown remote SEP and should go + # to spine proxy + sep5.remove_vpp_config() + + rxs = self.send_and_expect(self.pg0, p, self.pg7) + + for rx, tx in zip(rxs, p): + self.assertEqual(rx[Ether].src, self.pg7.local_mac) + self.assertEqual(rx[Ether].dst, self.pg7.remote_mac) + self.assertEqual(rx[IP].src, self.pg7.local_ip4) + self.assertEqual(rx[IP].dst, self.pg7.remote_ip4) + # this should use the spine proxy TEP + self.assertEqual(rx[VXLAN].vni, epg_320.bd.uu_fwd.vni) + self.assertEqual(rx[VXLAN].gpid, 4220) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + # redirect policy has been applied + self.assertTrue(rx[VXLAN].gpflags.A) + self.assertTrue(rx[VXLAN].gpflags.D) + rxip = rx[VXLAN][Ether].payload + txip = tx[Dot1Q].payload + self.assertEqual(rxip.src, txip.src) + self.assertEqual(rxip.dst, txip.dst) + + # + # l3out redirect to local SEP + # + + # change the contract between l3out to redirect to local SEPs + # instead of remote SEP + VppGbpContract( + self, 402, 4220, 4221, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP, + [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd, + sep1.ip4, sep1.epg.rd)]), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP, + [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd, + sep1.ip6, sep1.epg.rd)])], + [ETH_P_IP, ETH_P_IPV6]).add_vpp_config() + + rxs = self.send_and_expect(self.pg0, p, sep1.itf) + for rx, tx in zip(rxs, p): + self.assertEqual(rx[Ether].src, routed_src_mac) + self.assertEqual(rx[Ether].dst, sep1.mac) + rxip = rx[Ether].payload + txip = tx[Ether].payload + self.assertEqual(rxip.src, txip.src) + self.assertEqual(rxip.dst, txip.dst) + + # + # redirect remote EP to remote (known then unknown) SEP + # + + # remote SEP known again + sep5.add_vpp_config() + + # contract to redirect to learnt SEP + VppGbpContract( + self, 402, epg_221.sclass, epg_222.sclass, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP, + [VppGbpContractNextHop(sep5.vmac, sep5.epg.bd, + sep5.ip4, sep5.epg.rd)]), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP, + [VppGbpContractNextHop(sep5.vmac, sep5.epg.bd, + sep5.ip6, sep5.epg.rd)])], + [ETH_P_IP, ETH_P_IPV6]).add_vpp_config() + + # packets from unknown EP 221 to known EP in EPG 222 + # should be redirected to known remote SEP + base = (Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac) / + IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=444, gpid=441, flags=0x88) / + Ether(src="00:22:22:22:22:44", dst=str(self.router_mac))) + p = [(base / + IP(src="10.0.1.100", dst=ep3.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)), + (base / + IPv6(src="2001:10::100", dst=ep3.ip6) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100))] + + # unknown remote EP to local EP redirected to known remote SEP + rxs = self.send_and_expect(self.pg7, p, self.pg7) + + for rx, tx in zip(rxs, p): + self.assertEqual(rx[Ether].src, self.pg7.local_mac) + self.assertEqual(rx[Ether].dst, self.pg7.remote_mac) + self.assertEqual(rx[IP].src, self.pg7.local_ip4) + self.assertEqual(rx[IP].dst, self.pg7.remote_ip4) + # this should use the programmed remote leaf TEP + self.assertEqual(rx[VXLAN].vni, 555) + self.assertEqual(rx[VXLAN].gpid, epg_221.sclass) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + # redirect policy has been applied + self.assertTrue(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + rxip = rx[VXLAN][Ether].payload + txip = tx[VXLAN][Ether].payload + self.assertEqual(rxip.src, txip.src) + self.assertEqual(rxip.dst, txip.dst) + + # endpoint learnt via the parent GBP-vxlan interface + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l3._sw_if_index, + ip="10.0.1.100")) + self.assertTrue(find_gbp_endpoint(self, + vx_tun_l3._sw_if_index, + ip="2001:10::100")) + + # remote SEP: it is now an unknown remote SEP and should go + # to spine proxy + sep5.remove_vpp_config() + + # remote EP (coming from spine proxy) to local EP redirected to + # known remote SEP + rxs = self.send_and_expect(self.pg7, p, self.pg7) + + for rx, tx in zip(rxs, p): + self.assertEqual(rx[Ether].src, self.pg7.local_mac) + self.assertEqual(rx[Ether].dst, self.pg7.remote_mac) + self.assertEqual(rx[IP].src, self.pg7.local_ip4) + self.assertEqual(rx[IP].dst, self.pg7.remote_ip4) + # this should use the spine proxy TEP + self.assertEqual(rx[VXLAN].vni, epg_320.bd.uu_fwd.vni) + self.assertEqual(rx[VXLAN].gpid, epg_221.sclass) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + # redirect policy has been applied + self.assertTrue(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + rxip = rx[VXLAN][Ether].payload + txip = tx[VXLAN][Ether].payload + self.assertEqual(rxip.src, txip.src) + self.assertEqual(rxip.dst, txip.dst) + + # + # cleanup + # + self.pg7.unconfig_ip4() + + def test_gbp_redirect_extended(self): + """ GBP Endpoint Redirect Extended """ + + self.vapi.cli("set logging class gbp level debug") + + ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t + routed_dst_mac = "00:0c:0c:0c:0c:0c" + routed_src_mac = "00:22:bd:f8:19:ff" + + learnt = [{'mac': '00:00:11:11:11:02', + 'ip': '10.0.1.2', + 'ip6': '2001:10::2'}, + {'mac': '00:00:11:11:11:03', + 'ip': '10.0.1.3', + 'ip6': '2001:10::3'}] + + # + # IP tables + # + t4 = VppIpTable(self, 1) + t4.add_vpp_config() + t6 = VppIpTable(self, 1, True) + t6.add_vpp_config() + + # create IPv4 and IPv6 RD UU VxLAN-GBP TEP and bind them to the right + # VRF + rd_uu4 = VppVxlanGbpTunnel( + self, + self.pg7.local_ip4, + self.pg7.remote_ip4, + 114, + mode=(VppEnum.vl_api_vxlan_gbp_api_tunnel_mode_t. + VXLAN_GBP_API_TUNNEL_MODE_L3)) + rd_uu4.add_vpp_config() + VppIpInterfaceBind(self, rd_uu4, t4).add_vpp_config() + + rd_uu6 = VppVxlanGbpTunnel( + self, + self.pg7.local_ip4, + self.pg7.remote_ip4, + 115, + mode=(VppEnum.vl_api_vxlan_gbp_api_tunnel_mode_t. + VXLAN_GBP_API_TUNNEL_MODE_L3)) + rd_uu6.add_vpp_config() + VppIpInterfaceBind(self, rd_uu6, t4).add_vpp_config() + + rd1 = VppGbpRouteDomain(self, 2, 402, t4, t6, rd_uu4, rd_uu6) + rd1.add_vpp_config() + + self.loop0.set_mac(self.router_mac) + self.loop1.set_mac(self.router_mac) + self.loop2.set_mac(self.router_mac) + + # + # Bind the BVI to the RD + # + b_lo0_ip4 = VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config() + b_lo0_ip6 = VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config() + b_lo1_ip4 = VppIpInterfaceBind(self, self.loop1, t4).add_vpp_config() + b_lo1_ip6 = VppIpInterfaceBind(self, self.loop1, t6).add_vpp_config() + b_lo2_ip4 = VppIpInterfaceBind(self, self.loop2, t4).add_vpp_config() + b_lo2_ip6 = VppIpInterfaceBind(self, self.loop2, t6).add_vpp_config() + + # + # Pg7 hosts a BD's UU-fwd + # + self.pg7.config_ip4() + self.pg7.resolve_arp() + + # + # a GBP bridge domains for the EPs + # + bd1 = VppBridgeDomain(self, 1) + bd1.add_vpp_config() + gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0) + gbd1.add_vpp_config() + + bd2 = VppBridgeDomain(self, 2) + bd2.add_vpp_config() + gbd2 = VppGbpBridgeDomain(self, bd2, rd1, self.loop1) + gbd2.add_vpp_config() + + # ... and has a /32 and /128 applied + ip4_addr1 = VppIpInterfaceAddress(self, gbd1.bvi, + "10.0.0.128", 32, + bind=b_lo0_ip4).add_vpp_config() + ip6_addr1 = VppIpInterfaceAddress(self, gbd1.bvi, + "2001:10::128", 128, + bind=b_lo0_ip6).add_vpp_config() + ip4_addr2 = VppIpInterfaceAddress(self, gbd2.bvi, + "10.0.1.128", 32, + bind=b_lo1_ip4).add_vpp_config() + ip6_addr2 = VppIpInterfaceAddress(self, gbd2.bvi, + "2001:11::128", 128, + bind=b_lo1_ip6).add_vpp_config() + + # + # The Endpoint-groups + # + epg_220 = VppGbpEndpointGroup(self, 220, 440, rd1, gbd1, + None, gbd1.bvi, + "10.0.0.128", + "2001:10::128", + VppGbpEndpointRetention(60)) + epg_220.add_vpp_config() + epg_221 = VppGbpEndpointGroup(self, 221, 441, rd1, gbd2, + None, gbd2.bvi, + "10.0.1.128", + "2001:11::128", + VppGbpEndpointRetention(60)) + epg_221.add_vpp_config() + + # + # a GBP bridge domains for the SEPs + # + bd_uu3 = VppVxlanGbpTunnel(self, self.pg7.local_ip4, + self.pg7.remote_ip4, 116) + bd_uu3.add_vpp_config() + + bd3 = VppBridgeDomain(self, 3) + bd3.add_vpp_config() + gbd3 = VppGbpBridgeDomain(self, bd3, rd1, self.loop2, + bd_uu3, learn=False) + gbd3.add_vpp_config() + + ip4_addr3 = VppIpInterfaceAddress(self, gbd3.bvi, + "12.0.0.128", 32, + bind=b_lo2_ip4).add_vpp_config() + ip6_addr3 = VppIpInterfaceAddress(self, gbd3.bvi, + "4001:10::128", 128, + bind=b_lo2_ip6).add_vpp_config() + + # + # self.logger.info(self.vapi.cli("show gbp bridge")) + # self.logger.info(self.vapi.cli("show vxlan-gbp tunnel")) + # self.logger.info(self.vapi.cli("show gbp vxlan")) + # self.logger.info(self.vapi.cli("show int addr")) + # + + # + # EPGs in which the service endpoints exist + # + epg_320 = VppGbpEndpointGroup(self, 320, 550, rd1, gbd3, + None, gbd3.bvi, + "12.0.0.128", + "4001:10::128", + VppGbpEndpointRetention(60)) + epg_320.add_vpp_config() + + # + # endpoints + # + ep1 = VppGbpEndpoint(self, self.pg0, + epg_220, None, + "10.0.0.1", "11.0.0.1", + "2001:10::1", "3001:10::1") + ep1.add_vpp_config() + ep2 = VppGbpEndpoint(self, self.pg1, + epg_221, None, + "10.0.1.1", "11.0.1.1", + "2001:11::1", "3001:11::1") + ep2.add_vpp_config() + + # + # service endpoints + # + sep1 = VppGbpEndpoint(self, self.pg3, + epg_320, None, + "12.0.0.1", "13.0.0.1", + "4001:10::1", "5001:10::1") + sep2 = VppGbpEndpoint(self, self.pg4, + epg_320, None, + "12.0.0.2", "13.0.0.2", + "4001:10::2", "5001:10::2") + + # sep1 and sep2 are not added to config yet + # they are unknown for now + + # + # add routes to EPG subnets + # + VppGbpSubnet(self, rd1, "10.0.0.0", 24, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_TRANSPORT + ).add_vpp_config() + VppGbpSubnet(self, rd1, "10.0.1.0", 24, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_TRANSPORT + ).add_vpp_config() + + # + # Local host to known local host in different BD + # with SFC contract (source and destination are in + # one node and service endpoint in another node) + # + p4 = [(Ether(src=ep1.mac, dst=str(self.router_mac)) / + IP(src=ep1.ip4, dst=ep2.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)), + (Ether(src=ep2.mac, dst=str(self.router_mac)) / + IP(src=ep2.ip4, dst=ep1.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100))] + p6 = [(Ether(src=ep1.mac, dst=str(self.router_mac)) / + IPv6(src=ep1.ip6, dst=ep2.ip6) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)), + (Ether(src=ep2.mac, dst=str(self.router_mac)) / + IPv6(src=ep2.ip6, dst=ep1.ip6) / + UDP(sport=1234, dport=1230) / + Raw(b'\xa5' * 100))] + + # should be dropped since no contract yet + self.send_and_assert_no_replies(self.pg0, [p4[0]]) + self.send_and_assert_no_replies(self.pg0, [p6[0]]) + + # + # Add a contract with a rule to load-balance redirect via SEP1 and SEP2 + # one of the next-hops is via an EP that is not known + # + rule4 = AclRule(is_permit=1, proto=17) + rule6 = AclRule(src_prefix=IPv6Network((0, 0)), + dst_prefix=IPv6Network((0, 0)), is_permit=1, proto=17) + acl = VppAcl(self, rules=[rule4, rule6]) + acl.add_vpp_config() + + # + # test the src-ip hash mode + # + c1 = VppGbpContract( + self, 402, epg_220.sclass, epg_221.sclass, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC, + [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd, + sep1.ip4, sep1.epg.rd)]), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC, + [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd, + sep1.ip6, sep1.epg.rd)])], + [ETH_P_IP, ETH_P_IPV6]) + c1.add_vpp_config() + + c2 = VppGbpContract( + self, 402, epg_221.sclass, epg_220.sclass, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC, + [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd, + sep1.ip4, sep1.epg.rd)]), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC, + [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd, + sep1.ip6, sep1.epg.rd)])], + [ETH_P_IP, ETH_P_IPV6]) + c2.add_vpp_config() + + # ep1 <--> ep2 redirected through sep1 + # sep1 is unknown + # packet is redirected to sep bd and then go through sep bd UU + + rxs = self.send_and_expect(self.pg0, p4[0] * 17, self.pg7) + + for rx in rxs: + self.assertEqual(rx[Ether].src, self.pg7.local_mac) + self.assertEqual(rx[Ether].dst, self.pg7.remote_mac) + self.assertEqual(rx[IP].src, self.pg7.local_ip4) + self.assertEqual(rx[IP].dst, self.pg7.remote_ip4) + self.assertEqual(rx[VXLAN].vni, 116) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + # redirect policy has been applied + self.assertTrue(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + + inner = rx[VXLAN].payload + + self.assertEqual(inner[Ether].src, routed_src_mac) + self.assertEqual(inner[Ether].dst, sep1.mac) + self.assertEqual(inner[IP].src, ep1.ip4) + self.assertEqual(inner[IP].dst, ep2.ip4) + + rxs = self.send_and_expect(self.pg1, p4[1] * 17, self.pg7) + + for rx in rxs: + self.assertEqual(rx[Ether].src, self.pg7.local_mac) + self.assertEqual(rx[Ether].dst, self.pg7.remote_mac) + self.assertEqual(rx[IP].src, self.pg7.local_ip4) + self.assertEqual(rx[IP].dst, self.pg7.remote_ip4) + self.assertEqual(rx[VXLAN].vni, 116) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + # redirect policy has been applied + self.assertTrue(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + + inner = rx[VXLAN].payload + + self.assertEqual(inner[Ether].src, routed_src_mac) + self.assertEqual(inner[Ether].dst, sep1.mac) + self.assertEqual(inner[IP].src, ep2.ip4) + self.assertEqual(inner[IP].dst, ep1.ip4) + + rxs = self.send_and_expect(self.pg0, p6[0] * 17, self.pg7) + + for rx in rxs: + self.assertEqual(rx[Ether].src, self.pg7.local_mac) + self.assertEqual(rx[Ether].dst, self.pg7.remote_mac) + self.assertEqual(rx[IP].src, self.pg7.local_ip4) + self.assertEqual(rx[IP].dst, self.pg7.remote_ip4) + self.assertEqual(rx[VXLAN].vni, 116) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + # redirect policy has been applied + inner = rx[VXLAN].payload + + self.assertEqual(inner[Ether].src, routed_src_mac) + self.assertEqual(inner[Ether].dst, sep1.mac) + self.assertEqual(inner[IPv6].src, ep1.ip6) + self.assertEqual(inner[IPv6].dst, ep2.ip6) + + rxs = self.send_and_expect(self.pg1, p6[1] * 17, self.pg7) + + for rx in rxs: + self.assertEqual(rx[Ether].src, self.pg7.local_mac) + self.assertEqual(rx[Ether].dst, self.pg7.remote_mac) + self.assertEqual(rx[IP].src, self.pg7.local_ip4) + self.assertEqual(rx[IP].dst, self.pg7.remote_ip4) + self.assertEqual(rx[VXLAN].vni, 116) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + # redirect policy has been applied + self.assertTrue(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + + inner = rx[VXLAN].payload + + self.assertEqual(inner[Ether].src, routed_src_mac) + self.assertEqual(inner[Ether].dst, sep1.mac) + self.assertEqual(inner[IPv6].src, ep2.ip6) + self.assertEqual(inner[IPv6].dst, ep1.ip6) + + # configure sep1: it is now local + # packets between ep1 and ep2 are redirected locally + sep1.add_vpp_config() + + rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf) + + for rx in rxs: + self.assertEqual(rx[Ether].src, routed_src_mac) + self.assertEqual(rx[Ether].dst, sep1.mac) + self.assertEqual(rx[IP].src, ep1.ip4) + self.assertEqual(rx[IP].dst, ep2.ip4) + + rxs = self.send_and_expect(self.pg1, p6[1] * 17, sep1.itf) + + for rx in rxs: + self.assertEqual(rx[Ether].src, routed_src_mac) + self.assertEqual(rx[Ether].dst, sep1.mac) + self.assertEqual(rx[IPv6].src, ep2.ip6) + self.assertEqual(rx[IPv6].dst, ep1.ip6) + + # packet coming from the l2 spine-proxy to sep1 + p = (Ether(src=self.pg7.remote_mac, + dst=self.pg7.local_mac) / + IP(src=self.pg7.remote_ip4, + dst=self.pg7.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=116, gpid=440, gpflags=0x08, flags=0x88) / + Ether(src=str(self.router_mac), dst=sep1.mac) / + IP(src=ep1.ip4, dst=ep2.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_expect(self.pg7, [p] * 17, sep1.itf) + + for rx in rxs: + self.assertEqual(rx[Ether].src, str(self.router_mac)) + self.assertEqual(rx[Ether].dst, sep1.mac) + self.assertEqual(rx[IP].src, ep1.ip4) + self.assertEqual(rx[IP].dst, ep2.ip4) + + # contract for SEP to communicate with dst EP + c3 = VppGbpContract( + self, 402, epg_320.sclass, epg_221.sclass, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC)], + [ETH_P_IP, ETH_P_IPV6]) + c3.add_vpp_config() + + # temporarily remove ep2, so that ep2 is remote & unknown + ep2.remove_vpp_config() + + # packet going back from sep1 to its original dest (ep2) + # as ep2 is now unknown (see above), it must go through + # the rd UU (packet is routed) + + p1 = (Ether(src=sep1.mac, dst=self.router_mac) / + IP(src=ep1.ip4, dst=ep2.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_expect(self.pg3, [p1] * 17, self.pg7) + + for rx in rxs: + self.assertEqual(rx[Ether].src, self.pg7.local_mac) + self.assertEqual(rx[Ether].dst, self.pg7.remote_mac) + self.assertEqual(rx[IP].src, self.pg7.local_ip4) + self.assertEqual(rx[IP].dst, self.pg7.remote_ip4) + self.assertEqual(rx[VXLAN].vni, 114) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + # redirect policy has been applied + inner = rx[VXLAN].payload + self.assertEqual(inner[Ether].src, routed_src_mac) + self.assertEqual(inner[Ether].dst, routed_dst_mac) + self.assertEqual(inner[IP].src, ep1.ip4) + self.assertEqual(inner[IP].dst, ep2.ip4) + + self.logger.info(self.vapi.cli("show bridge 3 detail")) + sep1.remove_vpp_config() + + self.logger.info(self.vapi.cli("show bridge 1 detail")) + self.logger.info(self.vapi.cli("show bridge 2 detail")) + + # re-add ep2: it is local again :) + ep2.add_vpp_config() + + # packet coming back from the remote sep through rd UU + p2 = (Ether(src=self.pg7.remote_mac, + dst=self.pg7.local_mac) / + IP(src=self.pg7.remote_ip4, + dst=self.pg7.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=114, gpid=441, gpflags=0x09, flags=0x88) / + Ether(src=str(self.router_mac), dst=self.router_mac) / + IP(src=ep1.ip4, dst=ep2.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_expect(self.pg7, [p2], self.pg1) + + for rx in rxs: + self.assertEqual(rx[Ether].src, str(self.router_mac)) + self.assertEqual(rx[Ether].dst, self.pg1.remote_mac) + self.assertEqual(rx[IP].src, ep1.ip4) + self.assertEqual(rx[IP].dst, ep2.ip4) + + # + # bd_uu2.add_vpp_config() + # + + # + # cleanup + # + c1.remove_vpp_config() + c2.remove_vpp_config() + c3.remove_vpp_config() + self.pg7.unconfig_ip4() + + def test_gbp_l3_out(self): + """ GBP L3 Out """ + + ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t + self.vapi.cli("set logging class gbp level debug") + + routed_dst_mac = "00:0c:0c:0c:0c:0c" + routed_src_mac = "00:22:bd:f8:19:ff" + + # + # IP tables + # + t4 = VppIpTable(self, 1) + t4.add_vpp_config() + t6 = VppIpTable(self, 1, True) + t6.add_vpp_config() + + rd1 = VppGbpRouteDomain(self, 2, 55, t4, t6) + rd1.add_vpp_config() + + self.loop0.set_mac(self.router_mac) + + # + # Bind the BVI to the RD + # + b_ip4 = VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config() + b_ip6 = VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config() + + # + # Pg7 hosts a BD's BUM + # Pg1 some other l3 interface + # + self.pg7.config_ip4() + self.pg7.resolve_arp() + + # + # a multicast vxlan-gbp tunnel for broadcast in the BD + # + tun_bm = VppVxlanGbpTunnel(self, self.pg7.local_ip4, + "239.1.1.1", 88, + mcast_itf=self.pg7) + tun_bm.add_vpp_config() + + # + # a GBP external bridge domains for the EPs + # + bd1 = VppBridgeDomain(self, 1) + bd1.add_vpp_config() + gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0, None, tun_bm) + gbd1.add_vpp_config() + + # + # The Endpoint-groups in which the external endpoints exist + # + epg_220 = VppGbpEndpointGroup(self, 220, 113, rd1, gbd1, + None, gbd1.bvi, + "10.0.0.128", + "2001:10::128", + VppGbpEndpointRetention(4)) + epg_220.add_vpp_config() + + # the BVIs have the subnets applied ... + ip4_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", + 24, bind=b_ip4).add_vpp_config() + ip6_addr = VppIpInterfaceAddress(self, gbd1.bvi, "2001:10::128", + 64, bind=b_ip6).add_vpp_config() + + # ... which are L3-out subnets + l3o_1 = VppGbpSubnet( + self, rd1, "10.0.0.0", 24, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT, + sclass=113) + l3o_1.add_vpp_config() + + # + # an external interface attached to the outside world and the + # external BD + # + VppL2Vtr(self, self.vlan_100, L2_VTR_OP.L2_POP_1).add_vpp_config() + VppL2Vtr(self, self.vlan_101, L2_VTR_OP.L2_POP_1).add_vpp_config() + vlan_144 = VppDot1QSubint(self, self.pg0, 144) + vlan_144.admin_up() + # vlan_102 is not poped + + # + # an unicast vxlan-gbp for inter-RD traffic + # + vx_tun_l3 = VppGbpVxlanTunnel( + self, 444, rd1.rd_id, + VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3, + self.pg2.local_ip4) + vx_tun_l3.add_vpp_config() + + # + # External Endpoints + # + eep1 = VppGbpEndpoint(self, self.vlan_100, + epg_220, None, + "10.0.0.1", "11.0.0.1", + "2001:10::1", "3001::1", + ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL) + eep1.add_vpp_config() + eep2 = VppGbpEndpoint(self, self.vlan_101, + epg_220, None, + "10.0.0.2", "11.0.0.2", + "2001:10::2", "3001::2", + ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL) + eep2.add_vpp_config() + eep3 = VppGbpEndpoint(self, self.vlan_102, + epg_220, None, + "10.0.0.3", "11.0.0.3", + "2001:10::3", "3001::3", + ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL) + eep3.add_vpp_config() + + # + # A remote external endpoint + # + rep = VppGbpEndpoint(self, vx_tun_l3, + epg_220, None, + "10.0.0.101", "11.0.0.101", + "2001:10::101", "3001::101", + ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE, + self.pg7.local_ip4, + self.pg7.remote_ip4, + mac=None) + rep.add_vpp_config() + + # + # EP1 impersonating EP3 is dropped + # + p = (Ether(src=eep1.mac, dst="ff:ff:ff:ff:ff:ff") / + Dot1Q(vlan=100) / + ARP(op="who-has", + psrc="10.0.0.3", pdst="10.0.0.128", + hwsrc=eep1.mac, hwdst="ff:ff:ff:ff:ff:ff")) + self.send_and_assert_no_replies(self.pg0, p) + + # + # ARP packet from External EPs are accepted and replied to + # + p_arp = (Ether(src=eep1.mac, dst="ff:ff:ff:ff:ff:ff") / + Dot1Q(vlan=100) / + ARP(op="who-has", + psrc=eep1.ip4, pdst="10.0.0.128", + hwsrc=eep1.mac, hwdst="ff:ff:ff:ff:ff:ff")) + rxs = self.send_and_expect(self.pg0, p_arp * 1, self.pg0) + + # + # ARP packet from host in remote subnet are accepted and replied to + # + p_arp = (Ether(src=eep3.mac, dst="ff:ff:ff:ff:ff:ff") / + Dot1Q(vlan=102) / + ARP(op="who-has", + psrc=eep3.ip4, pdst="10.0.0.128", + hwsrc=eep3.mac, hwdst="ff:ff:ff:ff:ff:ff")) + rxs = self.send_and_expect(self.pg0, p_arp * 1, self.pg0) + + # + # packets destined to unknown addresses in the BVI's subnet + # are ARP'd for + # + p4 = (Ether(src=eep1.mac, dst=str(self.router_mac)) / + Dot1Q(vlan=100) / + IP(src="10.0.0.1", dst="10.0.0.88") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + p6 = (Ether(src=eep1.mac, dst=str(self.router_mac)) / + Dot1Q(vlan=100) / + IPv6(src="2001:10::1", dst="2001:10::88") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, p4 * 1, self.pg7) + + for rx in rxs: + self.assertEqual(rx[Ether].src, self.pg7.local_mac) + # self.assertEqual(rx[Ether].dst, self.pg7.remote_mac) + self.assertEqual(rx[IP].src, self.pg7.local_ip4) + self.assertEqual(rx[IP].dst, "239.1.1.1") + self.assertEqual(rx[VXLAN].vni, 88) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + # policy was applied to the original IP packet + self.assertEqual(rx[VXLAN].gpid, 113) + self.assertTrue(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + + inner = rx[VXLAN].payload + + self.assertTrue(inner.haslayer(ARP)) + + # + # remote to external + # + p = (Ether(src=self.pg7.remote_mac, + dst=self.pg7.local_mac) / + IP(src=self.pg7.remote_ip4, + dst=self.pg7.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=444, gpid=113, flags=0x88) / + Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) / + IP(src="10.0.0.101", dst="10.0.0.1") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_expect(self.pg7, p * 1, self.pg0) + + # + # local EP pings router + # + p = (Ether(src=eep1.mac, dst=str(self.router_mac)) / + Dot1Q(vlan=100) / + IP(src=eep1.ip4, dst="10.0.0.128") / + ICMP(type='echo-request')) + + rxs = self.send_and_expect(self.pg0, p * 1, self.pg0) + + for rx in rxs: + self.assertEqual(rx[Ether].src, str(self.router_mac)) + self.assertEqual(rx[Ether].dst, eep1.mac) + self.assertEqual(rx[Dot1Q].vlan, 100) + + # + # local EP pings other local EP + # + p = (Ether(src=eep1.mac, dst=eep2.mac) / + Dot1Q(vlan=100) / + IP(src=eep1.ip4, dst=eep2.ip4) / + ICMP(type='echo-request')) + + rxs = self.send_and_expect(self.pg0, p * 1, self.pg0) + + for rx in rxs: + self.assertEqual(rx[Ether].src, eep1.mac) + self.assertEqual(rx[Ether].dst, eep2.mac) + self.assertEqual(rx[Dot1Q].vlan, 101) + + # + # local EP pings router w/o vlan tag poped + # + p = (Ether(src=eep3.mac, dst=str(self.router_mac)) / + Dot1Q(vlan=102) / + IP(src=eep3.ip4, dst="10.0.0.128") / + ICMP(type='echo-request')) + + rxs = self.send_and_expect(self.pg0, p * 1, self.pg0) + + for rx in rxs: + self.assertEqual(rx[Ether].src, str(self.router_mac)) + self.assertEqual(rx[Ether].dst, self.vlan_102.remote_mac) + + # + # A ip4 subnet reachable through the external EP1 + # + ip_220 = VppIpRoute(self, "10.220.0.0", 24, + [VppRoutePath(eep1.ip4, + eep1.epg.bvi.sw_if_index)], + table_id=t4.table_id) + ip_220.add_vpp_config() + + l3o_220 = VppGbpSubnet( + self, rd1, "10.220.0.0", 24, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT, + sclass=4220) + l3o_220.add_vpp_config() + + # + # An ip6 subnet reachable through the external EP1 + # + ip6_220 = VppIpRoute(self, "10:220::", 64, + [VppRoutePath(eep1.ip6, + eep1.epg.bvi.sw_if_index)], + table_id=t6.table_id) + ip6_220.add_vpp_config() + + l3o6_220 = VppGbpSubnet( + self, rd1, "10:220::", 64, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT, + sclass=4220) + l3o6_220.add_vpp_config() + + # + # A subnet reachable through the external EP2 + # + ip_221 = VppIpRoute(self, "10.221.0.0", 24, + [VppRoutePath(eep2.ip4, + eep2.epg.bvi.sw_if_index)], + table_id=t4.table_id) + ip_221.add_vpp_config() + + l3o_221 = VppGbpSubnet( + self, rd1, "10.221.0.0", 24, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT, + sclass=4221) + l3o_221.add_vpp_config() + + # + # ping between hosts in remote subnets + # dropped without a contract + # + p = (Ether(src=eep1.mac, dst=str(self.router_mac)) / + Dot1Q(vlan=100) / + IP(src="10.220.0.1", dst="10.221.0.1") / + ICMP(type='echo-request')) + + self.send_and_assert_no_replies(self.pg0, p * 1) + + # + # contract for the external nets to communicate + # + rule4 = AclRule(is_permit=1, proto=17) + rule6 = AclRule(src_prefix=IPv6Network((0, 0)), + dst_prefix=IPv6Network((0, 0)), is_permit=1, proto=17) + acl = VppAcl(self, rules=[rule4, rule6]) + acl.add_vpp_config() + + # + # A contract with the wrong scope is not matched + # + c_44 = VppGbpContract( + self, 44, 4220, 4221, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + [])], + [ETH_P_IP, ETH_P_IPV6]) + c_44.add_vpp_config() + self.send_and_assert_no_replies(self.pg0, p * 1) + + c1 = VppGbpContract( + self, 55, 4220, 4221, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + [])], + [ETH_P_IP, ETH_P_IPV6]) + c1.add_vpp_config() + + # + # Contracts allowing ext-net 200 to talk with external EPs + # + c2 = VppGbpContract( + self, 55, 4220, 113, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + [])], + [ETH_P_IP, ETH_P_IPV6]) + c2.add_vpp_config() + c3 = VppGbpContract( + self, 55, 113, 4220, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + [])], + [ETH_P_IP, ETH_P_IPV6]) + c3.add_vpp_config() + + # + # ping between hosts in remote subnets + # + p = (Ether(src=eep1.mac, dst=str(self.router_mac)) / + Dot1Q(vlan=100) / + IP(src="10.220.0.1", dst="10.221.0.1") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, p * 1, self.pg0) + + for rx in rxs: + self.assertEqual(rx[Ether].src, str(self.router_mac)) + self.assertEqual(rx[Ether].dst, eep2.mac) + self.assertEqual(rx[Dot1Q].vlan, 101) + + # we did not learn these external hosts + self.assertFalse(find_gbp_endpoint(self, ip="10.220.0.1")) + self.assertFalse(find_gbp_endpoint(self, ip="10.221.0.1")) + + # + # from remote external EP to local external EP + # + p = (Ether(src=self.pg7.remote_mac, + dst=self.pg7.local_mac) / + IP(src=self.pg7.remote_ip4, + dst=self.pg7.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=444, gpid=113, flags=0x88) / + Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) / + IP(src="10.0.0.101", dst="10.220.0.1") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_expect(self.pg7, p * 1, self.pg0) + + # + # ping from an external host to the remote external EP + # + p = (Ether(src=eep1.mac, dst=str(self.router_mac)) / + Dot1Q(vlan=100) / + IP(src="10.220.0.1", dst=rep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, p * 1, self.pg7) + + for rx in rxs: + self.assertEqual(rx[Ether].src, self.pg7.local_mac) + # self.assertEqual(rx[Ether].dst, self.pg7.remote_mac) + self.assertEqual(rx[IP].src, self.pg7.local_ip4) + self.assertEqual(rx[IP].dst, self.pg7.remote_ip4) + self.assertEqual(rx[VXLAN].vni, 444) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + # the sclass of the ext-net the packet came from + self.assertEqual(rx[VXLAN].gpid, 4220) + # policy was applied to the original IP packet + self.assertTrue(rx[VXLAN].gpflags.A) + # since it's an external host the reciever should not learn it + self.assertTrue(rx[VXLAN].gpflags.D) + inner = rx[VXLAN].payload + self.assertEqual(inner[IP].src, "10.220.0.1") + self.assertEqual(inner[IP].dst, rep.ip4) + + # + # An external subnet reachable via the remote external EP + # + + # + # first the VXLAN-GBP tunnel over which it is reached + # + vx_tun_r1 = VppVxlanGbpTunnel( + self, self.pg7.local_ip4, + self.pg7.remote_ip4, 445, + mode=(VppEnum.vl_api_vxlan_gbp_api_tunnel_mode_t. + VXLAN_GBP_API_TUNNEL_MODE_L3)) + vx_tun_r1.add_vpp_config() + VppIpInterfaceBind(self, vx_tun_r1, t4).add_vpp_config() + + self.logger.info(self.vapi.cli("sh vxlan-gbp tunnel")) + + # + # then the special adj to resolve through on that tunnel + # + n1 = VppNeighbor(self, + vx_tun_r1.sw_if_index, + "00:0c:0c:0c:0c:0c", + self.pg7.remote_ip4) + n1.add_vpp_config() + + # + # the route via the adj above + # + ip_222 = VppIpRoute(self, "10.222.0.0", 24, + [VppRoutePath(self.pg7.remote_ip4, + vx_tun_r1.sw_if_index)], + table_id=t4.table_id) + ip_222.add_vpp_config() + + l3o_222 = VppGbpSubnet( + self, rd1, "10.222.0.0", 24, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT, + sclass=4222) + l3o_222.add_vpp_config() + + # + # ping between hosts in local and remote external subnets + # dropped without a contract + # + p = (Ether(src=eep1.mac, dst=str(self.router_mac)) / + Dot1Q(vlan=100) / + IP(src="10.220.0.1", dst="10.222.0.1") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_assert_no_replies(self.pg0, p * 1) + + # + # Add contracts ext-nets for 220 -> 222 + # + c4 = VppGbpContract( + self, 55, 4220, 4222, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + [])], + [ETH_P_IP, ETH_P_IPV6]) + c4.add_vpp_config() + + # + # ping from host in local to remote external subnets + # + p = (Ether(src=eep1.mac, dst=str(self.router_mac)) / + Dot1Q(vlan=100) / + IP(src="10.220.0.1", dst="10.222.0.1") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, p * 3, self.pg7) + + for rx in rxs: + self.assertEqual(rx[Ether].src, self.pg7.local_mac) + self.assertEqual(rx[Ether].dst, self.pg7.remote_mac) + self.assertEqual(rx[IP].src, self.pg7.local_ip4) + self.assertEqual(rx[IP].dst, self.pg7.remote_ip4) + self.assertEqual(rx[VXLAN].vni, 445) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + # the sclass of the ext-net the packet came from + self.assertEqual(rx[VXLAN].gpid, 4220) + # policy was applied to the original IP packet + self.assertTrue(rx[VXLAN].gpflags.A) + # since it's an external host the reciever should not learn it + self.assertTrue(rx[VXLAN].gpflags.D) + inner = rx[VXLAN].payload + self.assertEqual(inner[Ether].dst, "00:0c:0c:0c:0c:0c") + self.assertEqual(inner[IP].src, "10.220.0.1") + self.assertEqual(inner[IP].dst, "10.222.0.1") + + # + # make the external subnet ECMP + # + vx_tun_r2 = VppVxlanGbpTunnel( + self, self.pg7.local_ip4, + self.pg7.remote_ip4, 446, + mode=(VppEnum.vl_api_vxlan_gbp_api_tunnel_mode_t. + VXLAN_GBP_API_TUNNEL_MODE_L3)) + vx_tun_r2.add_vpp_config() + VppIpInterfaceBind(self, vx_tun_r2, t4).add_vpp_config() + + self.logger.info(self.vapi.cli("sh vxlan-gbp tunnel")) + + n2 = VppNeighbor(self, + vx_tun_r2.sw_if_index, + "00:0c:0c:0c:0c:0c", + self.pg7.remote_ip4) + n2.add_vpp_config() + + ip_222.modify([VppRoutePath(self.pg7.remote_ip4, + vx_tun_r1.sw_if_index), + VppRoutePath(self.pg7.remote_ip4, + vx_tun_r2.sw_if_index)]) + + # + # now expect load-balance + # + p = [(Ether(src=eep1.mac, dst=str(self.router_mac)) / + Dot1Q(vlan=100) / + IP(src="10.220.0.1", dst="10.222.0.1") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)), + (Ether(src=eep1.mac, dst=str(self.router_mac)) / + Dot1Q(vlan=100) / + IP(src="10.220.0.1", dst="10.222.0.1") / + UDP(sport=1222, dport=1235) / + Raw(b'\xa5' * 100))] + + rxs = self.send_and_expect(self.pg0, p, self.pg7) + + self.assertEqual(rxs[0][VXLAN].vni, 445) + self.assertEqual(rxs[1][VXLAN].vni, 446) + + # + # Same LB test for v6 + # + n3 = VppNeighbor(self, + vx_tun_r1.sw_if_index, + "00:0c:0c:0c:0c:0c", + self.pg7.remote_ip6) + n3.add_vpp_config() + n4 = VppNeighbor(self, + vx_tun_r2.sw_if_index, + "00:0c:0c:0c:0c:0c", + self.pg7.remote_ip6) + n4.add_vpp_config() + + ip_222_6 = VppIpRoute(self, "10:222::", 64, + [VppRoutePath(self.pg7.remote_ip6, + vx_tun_r1.sw_if_index), + VppRoutePath(self.pg7.remote_ip6, + vx_tun_r2.sw_if_index)], + table_id=t6.table_id) + ip_222_6.add_vpp_config() + + l3o_222_6 = VppGbpSubnet( + self, rd1, "10:222::", 64, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT, + sclass=4222) + l3o_222_6.add_vpp_config() + + p = [(Ether(src=eep1.mac, dst=str(self.router_mac)) / + Dot1Q(vlan=100) / + IPv6(src="10:220::1", dst="10:222::1") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)), + (Ether(src=eep1.mac, dst=str(self.router_mac)) / + Dot1Q(vlan=100) / + IPv6(src="10:220::1", dst="10:222::1") / + UDP(sport=7777, dport=8881) / + Raw(b'\xa5' * 100))] + + self.logger.info(self.vapi.cli("sh ip6 fib 10:222::1")) + rxs = self.send_and_expect(self.pg0, p, self.pg7) + + self.assertEqual(rxs[0][VXLAN].vni, 445) + self.assertEqual(rxs[1][VXLAN].vni, 446) + + # + # ping from host in remote to local external subnets + # there's no contract for this, but the A bit is set. + # + p = (Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac) / + IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=445, gpid=4222, flags=0x88, gpflags='A') / + Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) / + IP(src="10.222.0.1", dst="10.220.0.1") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_expect(self.pg7, p * 3, self.pg0) + self.assertFalse(find_gbp_endpoint(self, ip="10.222.0.1")) + + # + # ping from host in remote to remote external subnets + # this is dropped by reflection check. + # + p = (Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac) / + IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=445, gpid=4222, flags=0x88, gpflags='A') / + Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) / + IP(src="10.222.0.1", dst="10.222.0.2") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_assert_no_replies(self.pg7, p * 3) + + p = (Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac) / + IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=445, gpid=4222, flags=0x88, gpflags='A') / + Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) / + IPv6(src="10:222::1", dst="10:222::2") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_assert_no_replies(self.pg7, p * 3) + + # + # local EP + # + lep1 = VppGbpEndpoint(self, vlan_144, + epg_220, None, + "10.0.0.44", "11.0.0.44", + "2001:10::44", "3001::44") + lep1.add_vpp_config() + + # + # local EP to local ip4 external subnet + # + p = (Ether(src=lep1.mac, dst=str(self.router_mac)) / + Dot1Q(vlan=144) / + IP(src=lep1.ip4, dst="10.220.0.1") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, p * 1, self.pg0) + + for rx in rxs: + self.assertEqual(rx[Ether].src, str(self.router_mac)) + self.assertEqual(rx[Ether].dst, eep1.mac) + self.assertEqual(rx[Dot1Q].vlan, 100) + + # + # local EP to local ip6 external subnet + # + p = (Ether(src=lep1.mac, dst=str(self.router_mac)) / + Dot1Q(vlan=144) / + IPv6(src=lep1.ip6, dst="10:220::1") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, p * 1, self.pg0) + + for rx in rxs: + self.assertEqual(rx[Ether].src, str(self.router_mac)) + self.assertEqual(rx[Ether].dst, eep1.mac) + self.assertEqual(rx[Dot1Q].vlan, 100) + + # + # ip4 and ip6 subnets that load-balance + # + ip_20 = VppIpRoute(self, "10.20.0.0", 24, + [VppRoutePath(eep1.ip4, + eep1.epg.bvi.sw_if_index), + VppRoutePath(eep2.ip4, + eep2.epg.bvi.sw_if_index)], + table_id=t4.table_id) + ip_20.add_vpp_config() + + l3o_20 = VppGbpSubnet( + self, rd1, "10.20.0.0", 24, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT, + sclass=4220) + l3o_20.add_vpp_config() + + ip6_20 = VppIpRoute(self, "10:20::", 64, + [VppRoutePath(eep1.ip6, + eep1.epg.bvi.sw_if_index), + VppRoutePath(eep2.ip6, + eep2.epg.bvi.sw_if_index)], + table_id=t6.table_id) + ip6_20.add_vpp_config() + + l3o6_20 = VppGbpSubnet( + self, rd1, "10:20::", 64, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT, + sclass=4220) + l3o6_20.add_vpp_config() + + self.logger.info(self.vapi.cli("sh ip fib 10.20.0.1")) + self.logger.info(self.vapi.cli("sh ip6 fib 10:20::1")) + + # two ip6 packets whose port are chosen so they load-balance + p = [(Ether(src=lep1.mac, dst=str(self.router_mac)) / + Dot1Q(vlan=144) / + IPv6(src=lep1.ip6, dst="10:20::1") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)), + (Ether(src=lep1.mac, dst=str(self.router_mac)) / + Dot1Q(vlan=144) / + IPv6(src=lep1.ip6, dst="10:20::1") / + UDP(sport=124, dport=1230) / + Raw(b'\xa5' * 100))] + + rxs = self.send_and_expect(self.pg0, p, self.pg0, 2) + + self.assertEqual(rxs[0][Dot1Q].vlan, 101) + self.assertEqual(rxs[1][Dot1Q].vlan, 100) + + # two ip4 packets whose port are chosen so they load-balance + p = [(Ether(src=lep1.mac, dst=str(self.router_mac)) / + Dot1Q(vlan=144) / + IP(src=lep1.ip4, dst="10.20.0.1") / + UDP(sport=1235, dport=1235) / + Raw(b'\xa5' * 100)), + (Ether(src=lep1.mac, dst=str(self.router_mac)) / + Dot1Q(vlan=144) / + IP(src=lep1.ip4, dst="10.20.0.1") / + UDP(sport=124, dport=1230) / + Raw(b'\xa5' * 100))] + + rxs = self.send_and_expect(self.pg0, p, self.pg0, 2) + + self.assertEqual(rxs[0][Dot1Q].vlan, 101) + self.assertEqual(rxs[1][Dot1Q].vlan, 100) + + # + # cleanup + # + ip_222.remove_vpp_config() + self.pg7.unconfig_ip4() + self.vlan_101.set_vtr(L2_VTR_OP.L2_DISABLED) + self.vlan_100.set_vtr(L2_VTR_OP.L2_DISABLED) + + def test_gbp_anon_l3_out(self): + """ GBP Anonymous L3 Out """ + + ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t + self.vapi.cli("set logging class gbp level debug") + + routed_dst_mac = "00:0c:0c:0c:0c:0c" + routed_src_mac = "00:22:bd:f8:19:ff" + + # + # IP tables + # + t4 = VppIpTable(self, 1) + t4.add_vpp_config() + t6 = VppIpTable(self, 1, True) + t6.add_vpp_config() + + rd1 = VppGbpRouteDomain(self, 2, 55, t4, t6) + rd1.add_vpp_config() + + self.loop0.set_mac(self.router_mac) + + # + # Bind the BVI to the RD + # + bind_l0_ip4 = VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config() + bind_l0_ip6 = VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config() + + # + # Pg7 hosts a BD's BUM + # Pg1 some other l3 interface + # + self.pg7.config_ip4() + self.pg7.resolve_arp() + + # + # a GBP external bridge domains for the EPs + # + bd1 = VppBridgeDomain(self, 1) + bd1.add_vpp_config() + gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0, None, None) + gbd1.add_vpp_config() + + # + # The Endpoint-groups in which the external endpoints exist + # + epg_220 = VppGbpEndpointGroup(self, 220, 113, rd1, gbd1, + None, gbd1.bvi, + "10.0.0.128", + "2001:10::128", + VppGbpEndpointRetention(4)) + epg_220.add_vpp_config() + + # the BVIs have the subnet applied ... + ip4_addr = VppIpInterfaceAddress(self, gbd1.bvi, + "10.0.0.128", 24, + bind=bind_l0_ip4).add_vpp_config() + + # ... which is an Anonymous L3-out subnets + l3o_1 = VppGbpSubnet( + self, rd1, "10.0.0.0", 24, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_ANON_L3_OUT, + sclass=113) + l3o_1.add_vpp_config() + + # + # an external interface attached to the outside world and the + # external BD + # + VppL2Vtr(self, self.vlan_100, L2_VTR_OP.L2_POP_1).add_vpp_config() + VppL2Vtr(self, self.vlan_101, L2_VTR_OP.L2_POP_1).add_vpp_config() + + # + # vlan_100 and vlan_101 are anonymous l3-out interfaces + # + ext_itf = VppGbpExtItf(self, self.vlan_100, bd1, rd1, anon=True) + ext_itf.add_vpp_config() + ext_itf = VppGbpExtItf(self, self.vlan_101, bd1, rd1, anon=True) + ext_itf.add_vpp_config() + + # + # an unicast vxlan-gbp for inter-RD traffic + # + vx_tun_l3 = VppGbpVxlanTunnel( + self, 444, rd1.rd_id, + VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3, + self.pg2.local_ip4) + vx_tun_l3.add_vpp_config() + + # + # A remote external endpoint + # + rep = VppGbpEndpoint(self, vx_tun_l3, + epg_220, None, + "10.0.0.201", "11.0.0.201", + "2001:10::201", "3001::101", + ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE, + self.pg7.local_ip4, + self.pg7.remote_ip4, + mac=None) + rep.add_vpp_config() + + # + # ARP packet from host in external subnet are accepted, flooded and + # replied to. We expect 2 packets: + # - APR request flooded over the other vlan subif + # - ARP reply from BVI + # + p_arp = (Ether(src=self.vlan_100.remote_mac, + dst="ff:ff:ff:ff:ff:ff") / + Dot1Q(vlan=100) / + ARP(op="who-has", + psrc="10.0.0.100", + pdst="10.0.0.128", + hwsrc=self.vlan_100.remote_mac, + hwdst="ff:ff:ff:ff:ff:ff")) + rxs = self.send_and_expect(self.pg0, p_arp * 1, self.pg0, n_rx=2) + + p_arp = (Ether(src=self.vlan_101.remote_mac, + dst="ff:ff:ff:ff:ff:ff") / + Dot1Q(vlan=101) / + ARP(op="who-has", + psrc='10.0.0.101', + pdst="10.0.0.128", + hwsrc=self.vlan_101.remote_mac, + hwdst="ff:ff:ff:ff:ff:ff")) + rxs = self.send_and_expect(self.pg0, p_arp * 1, self.pg0, n_rx=2) + + # + # remote to external + # + p = (Ether(src=self.pg7.remote_mac, + dst=self.pg7.local_mac) / + IP(src=self.pg7.remote_ip4, + dst=self.pg7.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=vx_tun_l3.vni, gpid=epg_220.sclass, flags=0x88) / + Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) / + IP(src=str(rep.ip4), dst="10.0.0.100") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + rxs = self.send_and_expect(self.pg7, p * 1, self.pg0) + + # + # local EP pings router + # + p = (Ether(src=self.vlan_100.remote_mac, dst=str(self.router_mac)) / + Dot1Q(vlan=100) / + IP(src="10.0.0.100", dst="10.0.0.128") / + ICMP(type='echo-request')) + rxs = self.send_and_expect(self.pg0, p * 1, self.pg0) + + for rx in rxs: + self.assertEqual(rx[Ether].src, str(self.router_mac)) + self.assertEqual(rx[Ether].dst, self.vlan_100.remote_mac) + self.assertEqual(rx[Dot1Q].vlan, 100) + + # + # local EP pings other local EP + # + p = (Ether(src=self.vlan_100.remote_mac, + dst=self.vlan_101.remote_mac) / + Dot1Q(vlan=100) / + IP(src="10.0.0.100", dst="10.0.0.101") / + ICMP(type='echo-request')) + rxs = self.send_and_expect(self.pg0, p * 1, self.pg0) + + for rx in rxs: + self.assertEqual(rx[Ether].src, self.vlan_100.remote_mac) + self.assertEqual(rx[Ether].dst, self.vlan_101.remote_mac) + self.assertEqual(rx[Dot1Q].vlan, 101) + + # + # A subnet reachable through an external router on vlan 100 + # + ip_220 = VppIpRoute(self, "10.220.0.0", 24, + [VppRoutePath("10.0.0.100", + epg_220.bvi.sw_if_index)], + table_id=t4.table_id) + ip_220.add_vpp_config() + + l3o_220 = VppGbpSubnet( + self, rd1, "10.220.0.0", 24, + # note: this a "regular" L3 out subnet (not connected) + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT, + sclass=4220) + l3o_220.add_vpp_config() + + # + # A subnet reachable through an external router on vlan 101 + # + ip_221 = VppIpRoute(self, "10.221.0.0", 24, + [VppRoutePath("10.0.0.101", + epg_220.bvi.sw_if_index)], + table_id=t4.table_id) + ip_221.add_vpp_config() + + l3o_221 = VppGbpSubnet( + self, rd1, "10.221.0.0", 24, + # note: this a "regular" L3 out subnet (not connected) + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT, + sclass=4221) + l3o_221.add_vpp_config() + + # + # ping between hosts in remote subnets + # dropped without a contract + # + p = (Ether(src=self.vlan_100.remote_mac, dst=str(self.router_mac)) / + Dot1Q(vlan=100) / + IP(src="10.220.0.1", dst="10.221.0.1") / + ICMP(type='echo-request')) + + rxs = self.send_and_assert_no_replies(self.pg0, p * 1) + + # + # contract for the external nets to communicate + # + rule4 = AclRule(is_permit=1, proto=17) + rule6 = AclRule(src_prefix=IPv6Network((0, 0)), + dst_prefix=IPv6Network((0, 0)), is_permit=1, proto=17) + acl = VppAcl(self, rules=[rule4, rule6]) + acl.add_vpp_config() + + c1 = VppGbpContract( + self, 55, 4220, 4221, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + [])], + [ETH_P_IP, ETH_P_IPV6]) + c1.add_vpp_config() + + # + # Contracts allowing ext-net 200 to talk with external EPs + # + c2 = VppGbpContract( + self, 55, 4220, 113, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + [])], + [ETH_P_IP, ETH_P_IPV6]) + c2.add_vpp_config() + c3 = VppGbpContract( + self, 55, 113, 4220, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + [])], + [ETH_P_IP, ETH_P_IPV6]) + c3.add_vpp_config() + + # + # ping between hosts in remote subnets + # + p = (Ether(src=self.vlan_100.remote_mac, dst=str(self.router_mac)) / + Dot1Q(vlan=100) / + IP(src="10.220.0.1", dst="10.221.0.1") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, p * 1, self.pg0) + + for rx in rxs: + self.assertEqual(rx[Ether].src, str(self.router_mac)) + self.assertEqual(rx[Ether].dst, self.vlan_101.remote_mac) + self.assertEqual(rx[Dot1Q].vlan, 101) + + # we did not learn these external hosts + self.assertFalse(find_gbp_endpoint(self, ip="10.220.0.1")) + self.assertFalse(find_gbp_endpoint(self, ip="10.221.0.1")) + + # + # from remote external EP to local external EP + # + p = (Ether(src=self.pg7.remote_mac, + dst=self.pg7.local_mac) / + IP(src=self.pg7.remote_ip4, + dst=self.pg7.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=444, gpid=113, flags=0x88) / + Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) / + IP(src=rep.ip4, dst="10.220.0.1") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_expect(self.pg7, p * 1, self.pg0) + + # + # ping from an external host to the remote external EP + # + p = (Ether(src=self.vlan_100.remote_mac, dst=str(self.router_mac)) / + Dot1Q(vlan=100) / + IP(src="10.220.0.1", dst=rep.ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, p * 1, self.pg7) + + for rx in rxs: + self.assertEqual(rx[Ether].src, self.pg7.local_mac) + # self.assertEqual(rx[Ether].dst, self.pg7.remote_mac) + self.assertEqual(rx[IP].src, self.pg7.local_ip4) + self.assertEqual(rx[IP].dst, self.pg7.remote_ip4) + self.assertEqual(rx[VXLAN].vni, 444) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + # the sclass of the ext-net the packet came from + self.assertEqual(rx[VXLAN].gpid, 4220) + # policy was applied to the original IP packet + self.assertTrue(rx[VXLAN].gpflags.A) + # since it's an external host the reciever should not learn it + self.assertTrue(rx[VXLAN].gpflags.D) + inner = rx[VXLAN].payload + self.assertEqual(inner[IP].src, "10.220.0.1") + self.assertEqual(inner[IP].dst, rep.ip4) + + # + # An external subnet reachable via the remote external EP + # + + # + # first the VXLAN-GBP tunnel over which it is reached + # + vx_tun_r = VppVxlanGbpTunnel( + self, self.pg7.local_ip4, + self.pg7.remote_ip4, 445, + mode=(VppEnum.vl_api_vxlan_gbp_api_tunnel_mode_t. + VXLAN_GBP_API_TUNNEL_MODE_L3)) + vx_tun_r.add_vpp_config() + VppIpInterfaceBind(self, vx_tun_r, t4).add_vpp_config() + + self.logger.info(self.vapi.cli("sh vxlan-gbp tunnel")) + + # + # then the special adj to resolve through on that tunnel + # + n1 = VppNeighbor(self, + vx_tun_r.sw_if_index, + "00:0c:0c:0c:0c:0c", + self.pg7.remote_ip4) + n1.add_vpp_config() + + # + # the route via the adj above + # + ip_222 = VppIpRoute(self, "10.222.0.0", 24, + [VppRoutePath(self.pg7.remote_ip4, + vx_tun_r.sw_if_index)], + table_id=t4.table_id) + ip_222.add_vpp_config() + + l3o_222 = VppGbpSubnet( + self, rd1, "10.222.0.0", 24, + # note: this a "regular" l3out subnet (not connected) + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT, + sclass=4222) + l3o_222.add_vpp_config() + + # + # ping between hosts in local and remote external subnets + # dropped without a contract + # + p = (Ether(src=self.vlan_100.remote_mac, dst=str(self.router_mac)) / + Dot1Q(vlan=100) / + IP(src="10.220.0.1", dst="10.222.0.1") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_assert_no_replies(self.pg0, p * 1) + + # + # Add contracts ext-nets for 220 -> 222 + # + c4 = VppGbpContract( + self, 55, 4220, 4222, acl.acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP, + [])], + [ETH_P_IP, ETH_P_IPV6]) + c4.add_vpp_config() + + # + # ping from host in local to remote external subnets + # + p = (Ether(src=self.vlan_100.remote_mac, dst=str(self.router_mac)) / + Dot1Q(vlan=100) / + IP(src="10.220.0.1", dst="10.222.0.1") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_expect(self.pg0, p * 3, self.pg7) + + for rx in rxs: + self.assertEqual(rx[Ether].src, self.pg7.local_mac) + self.assertEqual(rx[Ether].dst, self.pg7.remote_mac) + self.assertEqual(rx[IP].src, self.pg7.local_ip4) + self.assertEqual(rx[IP].dst, self.pg7.remote_ip4) + self.assertEqual(rx[VXLAN].vni, 445) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + # the sclass of the ext-net the packet came from + self.assertEqual(rx[VXLAN].gpid, 4220) + # policy was applied to the original IP packet + self.assertTrue(rx[VXLAN].gpflags.A) + # since it's an external host the reciever should not learn it + self.assertTrue(rx[VXLAN].gpflags.D) + inner = rx[VXLAN].payload + self.assertEqual(inner[Ether].dst, "00:0c:0c:0c:0c:0c") + self.assertEqual(inner[IP].src, "10.220.0.1") + self.assertEqual(inner[IP].dst, "10.222.0.1") + + # + # ping from host in remote to local external subnets + # there's no contract for this, but the A bit is set. + # + p = (Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac) / + IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=445, gpid=4222, flags=0x88, gpflags='A') / + Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) / + IP(src="10.222.0.1", dst="10.220.0.1") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_expect(self.pg7, p * 3, self.pg0) + self.assertFalse(find_gbp_endpoint(self, ip="10.222.0.1")) + + # + # ping from host in remote to remote external subnets + # this is dropped by reflection check. + # + p = (Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac) / + IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4) / + UDP(sport=1234, dport=48879) / + VXLAN(vni=445, gpid=4222, flags=0x88, gpflags='A') / + Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) / + IP(src="10.222.0.1", dst="10.222.0.2") / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100)) + + rxs = self.send_and_assert_no_replies(self.pg7, p * 3) + + # + # cleanup + # + self.vlan_101.set_vtr(L2_VTR_OP.L2_DISABLED) + self.vlan_100.set_vtr(L2_VTR_OP.L2_DISABLED) + self.pg7.unconfig_ip4() + # make sure the programmed EP is no longer learnt from DP + self.wait_for_ep_timeout(sw_if_index=rep.itf.sw_if_index, ip=rep.ip4) + + +if __name__ == '__main__': + unittest.main(testRunner=VppTestRunner) diff --git a/extras/deprecated/plugins/l2e/CMakeLists.txt b/extras/deprecated/plugins/l2e/CMakeLists.txt new file mode 100644 index 00000000000..2bfb05a43e6 --- /dev/null +++ b/extras/deprecated/plugins/l2e/CMakeLists.txt @@ -0,0 +1,28 @@ +# 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. + +add_vpp_plugin(l2e + SOURCES + l2e_node.c + l2e_api.c + l2e.c + + MULTIARCH_SOURCES + l2e_node.c + + API_FILES + l2e.api + + INSTALL_HEADERS + l2e.h +) diff --git a/extras/deprecated/plugins/l2e/l2e.api b/extras/deprecated/plugins/l2e/l2e.api new file mode 100644 index 00000000000..586e2bae5ca --- /dev/null +++ b/extras/deprecated/plugins/l2e/l2e.api @@ -0,0 +1,39 @@ +/* Hey Emacs use -*- mode: C -*- */ +/* + * Copyright (c) 2016 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. + */ + +option version = "1.0.0"; +import "vnet/interface_types.api"; + +/** \brief L2 emulation at L3 + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param sw_if_index - interface the operation is applied to + @param enable - Turn the service on or off +*/ +autoreply define l2_emulation +{ + option status="in_progress"; + u32 client_index; + u32 context; + vl_api_interface_index_t sw_if_index; + bool enable; +}; + +/* + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/l2e/l2e.c b/extras/deprecated/plugins/l2e/l2e.c new file mode 100644 index 00000000000..4c6eac50446 --- /dev/null +++ b/extras/deprecated/plugins/l2e/l2e.c @@ -0,0 +1,198 @@ +/* + * l2e.c : Extract L3 packets from the L2 input and feed + * them into the L3 path. + * + * 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/l2e/l2e.h> +#include <vnet/l2/l2_input.h> +#include <vnet/l2/feat_bitmap.h> +#include <vnet/ip/ip.h> + +l2_emulation_main_t l2_emulation_main; + +/** + * A zero'd out struct we can use in the vec_validate + */ +static const l2_emulation_t ezero = { }; + +__clib_export void +l2_emulation_enable (u32 sw_if_index) +{ + l2_emulation_main_t *em = &l2_emulation_main; + vec_validate_init_empty (em->l2_emulations, sw_if_index, ezero); + + l2_emulation_t *l23e = &em->l2_emulations[sw_if_index]; + + l23e->enabled = 1; + + /* + * L3 enable the interface - using IP unnumbered from the control + * plane may not be possible since there may be no BVI interface + * to which to unnumber + */ + ip4_sw_interface_enable_disable (sw_if_index, 1); + ip6_sw_interface_enable_disable (sw_if_index, 1); + + l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_L2_EMULATION, 1); +} + + +__clib_export void +l2_emulation_disable (u32 sw_if_index) +{ + l2_emulation_main_t *em = &l2_emulation_main; + if (vec_len (em->l2_emulations) >= sw_if_index) + { + l2_emulation_t *l23e = &em->l2_emulations[sw_if_index]; + clib_memset (l23e, 0, sizeof (*l23e)); + + l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_L2_EMULATION, 0); + ip4_sw_interface_enable_disable (sw_if_index, 0); + ip6_sw_interface_enable_disable (sw_if_index, 0); + } +} + +static clib_error_t * +l2_emulation_interface_add_del (vnet_main_t * vnm, + u32 sw_if_index, u32 is_add) +{ + l2_emulation_main_t *em = &l2_emulation_main; + if (is_add) + { + vec_validate_init_empty (em->l2_emulations, sw_if_index, ezero); + } + + return (NULL); +} + +VNET_SW_INTERFACE_ADD_DEL_FUNCTION (l2_emulation_interface_add_del); + +static clib_error_t * +l2_emulation_cli (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + vnet_main_t *vnm = vnet_get_main (); + u32 sw_if_index = ~0; + u8 enable = 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, "enable")) + enable = 1; + else if (unformat (input, "disable")) + enable = 0; + else + break; + } + + if (~0 == sw_if_index) + return clib_error_return (0, "interface must be specified"); + + if (enable) + l2_emulation_enable (sw_if_index); + else + l2_emulation_disable (sw_if_index); + + return (NULL); +} + +/*? + * Configure l2 emulation. + * When the interface is in L2 mode, configure the extraction of L3 + * packets out of the L2 path and into the L3 path. + * + * @cliexpar + * @cliexstart{set interface l2 input l2-emulation <interface-name> [disable]} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (l2_emulation_cli_node, static) = { + .path = "set interface l2 l2-emulation", + .short_help = + "set interface l2 l2-emulation <interface-name> [disable|enable]\n", + .function = l2_emulation_cli, +}; +/* *INDENT-ON* */ + +static clib_error_t * +l2_emulation_show (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + l2_emulation_main_t *em = &l2_emulation_main; + vnet_main_t *vnm = vnet_get_main (); + l2_emulation_t *l23e; + u32 sw_if_index; + + vec_foreach_index (sw_if_index, em->l2_emulations) + { + l23e = &em->l2_emulations[sw_if_index]; + if (l23e->enabled) + { + vlib_cli_output (vm, "%U\n", + format_vnet_sw_if_index_name, vnm, sw_if_index); + } + } + return (NULL); +} + +/*? + * Show l2 emulation. + * When the interface is in L2 mode, configure the extraction of L3 + * packets out of the L2 path and into the L3 path. + * + * @cliexpar + * @cliexstart{show interface l2 l2-emulation} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (l2_emulation_show_node, static) = { + .path = "show interface l2 l2-emulation", + .short_help = "show interface l2 l2-emulation\n", + .function = l2_emulation_show, +}; +/* *INDENT-ON* */ + +static clib_error_t * +l2_emulation_init (vlib_main_t * vm) +{ + l2_emulation_main_t *em = &l2_emulation_main; + vlib_node_t *node; + + node = vlib_get_node_by_name (vm, (u8 *) "l2-emulation"); + em->l2_emulation_node_index = node->index; + + /* Initialize the feature next-node indexes */ + feat_bitmap_init_next_nodes (vm, + em->l2_emulation_node_index, + L2INPUT_N_FEAT, + l2input_get_feat_names (), + em->l2_input_feat_next); + + return 0; +} + +VLIB_INIT_FUNCTION (l2_emulation_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/l2e/l2e.h b/extras/deprecated/plugins/l2e/l2e.h new file mode 100644 index 00000000000..e548d333f9d --- /dev/null +++ b/extras/deprecated/plugins/l2e/l2e.h @@ -0,0 +1,84 @@ +/* + * 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. + */ + +#ifndef included_vnet_l2_emulation_h +#define included_vnet_l2_emulation_h + +#include <vlib/vlib.h> +#include <vnet/vnet.h> + +/** + * Per-interface L2 configuration + */ +typedef struct l2_emulation_t_ +{ + /** + * Enabled or Disabled. + * this is required since one L3 protocl can be enabled, but others not + */ + u8 enabled; +} l2_emulation_t; + +/** + * per-packet trace data + */ +typedef struct l2_emulation_trace_t_ +{ + /* per-pkt trace data */ + u8 extracted; +} l2_emulation_trace_t; + +/** + * Grouping of global data for the L2 emulation feature + */ +typedef struct l2_emulation_main_t_ +{ + u16 msg_id_base; + + u32 l2_emulation_node_index; + + /** + * Per-interface vector of emulation configs + */ + l2_emulation_t *l2_emulations; + + /** + * Next nodes for L2 output features + */ + u32 l2_input_feat_next[32]; +} l2_emulation_main_t; + +/** + * L2 Emulation is a feautre that is applied to L2 ports to 'extract' + * IP packets from the L2 path and inject them into the L3 path (i.e. + * into the appropriate ip[4|6]_input node). + * L3 routes in the table_id for that interface should then be configured + * as DVR routes, therefore the forwarded packet has the L2 header + * preserved and togehter the L3 routed system behaves like an L2 bridge. + */ +extern void l2_emulation_enable (u32 sw_if_index); +extern void l2_emulation_disable (u32 sw_if_index); + +extern l2_emulation_main_t l2_emulation_main; + +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/l2e/l2e_api.c b/extras/deprecated/plugins/l2e/l2e_api.c new file mode 100644 index 00000000000..fe2fb7ee06e --- /dev/null +++ b/extras/deprecated/plugins/l2e/l2e_api.c @@ -0,0 +1,89 @@ +/* + *------------------------------------------------------------------ + * l2e_api.c - layer 2 emulation api + * + * Copyright (c) 2016 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 <vnet/vnet.h> +#include <vnet/plugin/plugin.h> + +#include <vnet/interface.h> +#include <vnet/api_errno.h> +#include <vpp/app/version.h> + +#include <l2e/l2e.h> + +#include <vlibapi/api.h> +#include <vlibmemory/api.h> + +/* define message IDs */ +#include <l2e/l2e.api_enum.h> +#include <l2e/l2e.api_types.h> + +#include <vlibapi/api_helper_macros.h> + +#define L2E_MSG_BASE l2em->msg_id_base + +static void +vl_api_l2_emulation_t_handler (vl_api_l2_emulation_t * mp) +{ + l2_emulation_main_t *l2em = &l2_emulation_main; + vl_api_l2_emulation_reply_t *rmp; + int rv = 0; + + VALIDATE_SW_IF_INDEX (mp); + + u32 sw_if_index = ntohl (mp->sw_if_index); + + if (mp->enable) + l2_emulation_enable (sw_if_index); + else + l2_emulation_disable (sw_if_index); + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_L2_EMULATION_REPLY + L2E_MSG_BASE); +} + +#include <l2e/l2e.api.c> +static clib_error_t * +l2e_init (vlib_main_t * vm) +{ + l2_emulation_main_t *l2em = &l2_emulation_main; + + /* Ask for a correctly-sized block of API message decode slots */ + l2em->msg_id_base = setup_message_id_table (); + + return (NULL); +} + +VLIB_API_INIT_FUNCTION (l2e_init); + +/* *INDENT-OFF* */ +VLIB_PLUGIN_REGISTER () = { + .version = VPP_BUILD_VER, + .description = "Layer 2 (L2) Emulation", +}; +/* *INDENT-ON* */ + + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/plugins/l2e/l2e_node.c b/extras/deprecated/plugins/l2e/l2e_node.c new file mode 100644 index 00000000000..71c9b4bc6af --- /dev/null +++ b/extras/deprecated/plugins/l2e/l2e_node.c @@ -0,0 +1,283 @@ +/* + * l2e_node.c : l2 emulation node + * + * Copyright (c) 2019 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/l2e/l2e.h> +#include <vnet/l2/l2_input.h> +#include <vnet/l2/feat_bitmap.h> + +#define foreach_l2_emulation \ + _(IP4, "Extract IPv4") \ + _(IP6, "Extract IPv6") + +typedef enum +{ +#define _(sym,str) L2_EMULATION_ERROR_##sym, + foreach_l2_emulation +#undef _ + L2_EMULATION_N_ERROR, +} l2_emulation_error_t; + +static char *l2_emulation_error_strings[] = { +#define _(sym,string) string, + foreach_l2_emulation +#undef _ +}; + +typedef enum +{ +#define _(sym,str) L2_EMULATION_NEXT_##sym, + foreach_l2_emulation +#undef _ + L2_EMULATION_N_NEXT, +} l2_emulation_next_t; + +/* packet trace format function */ +static u8 * +format_l2_emulation_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 *); + l2_emulation_trace_t *t = va_arg (*args, l2_emulation_trace_t *); + + s = format (s, "l2-emulation: %s", (t->extracted ? "yes" : "no")); + + return s; +} + +VLIB_NODE_FN (l2_emulation_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + l2_emulation_main_t *em = &l2_emulation_main; + u32 n_left_from, *from, *to_next; + l2_emulation_next_t next_index; + u32 ip4_hits = 0; + u32 ip6_hits = 0; + + 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 >= 4 && n_left_to_next >= 2) + { + vlib_buffer_t *b0, *b1; + u32 sw_if_index0, sw_if_index1; + u16 ether_type0, ether_type1; + u32 next0 = ~0, next1 = ~0; + u8 l2_len0, l2_len1; + u32 bi0, bi1; + u8 *h0, *h1; + + bi0 = to_next[0] = from[0]; + bi1 = to_next[1] = from[1]; + + from += 2; + n_left_from -= 2; + to_next += 2; + n_left_to_next -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + l2_len0 = vnet_buffer (b0)->l2.l2_len; + l2_len1 = vnet_buffer (b1)->l2.l2_len; + + h0 = vlib_buffer_get_current (b0); + h1 = vlib_buffer_get_current (b1); + + ether_type0 = clib_net_to_host_u16 (*(u16 *) (h0 + l2_len0 - 2)); + ether_type1 = clib_net_to_host_u16 (*(u16 *) (h1 + l2_len1 - 2)); + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX]; + + /* + * only extract unicast + */ + if (PREDICT_TRUE (!(h0[0] & 0x1))) + { + switch (ether_type0) + { + case ETHERNET_TYPE_IP4: + ASSERT (em->l2_emulations[sw_if_index0].enabled); + ++ip4_hits; + next0 = L2_EMULATION_NEXT_IP4; + vlib_buffer_advance (b0, l2_len0); + break; + case ETHERNET_TYPE_IP6: + ASSERT (em->l2_emulations[sw_if_index0].enabled); + ++ip6_hits; + next0 = L2_EMULATION_NEXT_IP6; + vlib_buffer_advance (b0, l2_len0); + default: + break; + } + } + if (PREDICT_TRUE (!(h1[0] & 0x1))) + { + switch (ether_type1) + { + case ETHERNET_TYPE_IP4: + ASSERT (em->l2_emulations[sw_if_index1].enabled); + ++ip4_hits; + next1 = L2_EMULATION_NEXT_IP4; + vlib_buffer_advance (b1, l2_len1); + break; + case ETHERNET_TYPE_IP6: + ASSERT (em->l2_emulations[sw_if_index1].enabled); + ++ip6_hits; + next1 = L2_EMULATION_NEXT_IP6; + vlib_buffer_advance (b1, l2_len1); + default: + break; + } + } + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + l2_emulation_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->extracted = (next0 != ~0); + } + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b1->flags & VLIB_BUFFER_IS_TRACED))) + { + l2_emulation_trace_t *t = + vlib_add_trace (vm, node, b1, sizeof (*t)); + t->extracted = (next1 != ~0); + } + + /* Determine the next node and remove ourself from bitmap */ + if (PREDICT_TRUE (next0 == ~0)) + next0 = vnet_l2_feature_next (b0, em->l2_input_feat_next, + L2INPUT_FEAT_L2_EMULATION); + + /* Determine the next node and remove ourself from bitmap */ + if (PREDICT_TRUE (next1 == ~0)) + next1 = vnet_l2_feature_next (b1, em->l2_input_feat_next, + L2INPUT_FEAT_L2_EMULATION); + + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, next0, next1); + } + while (n_left_from > 0 && n_left_to_next > 0) + { + vlib_buffer_t *b0; + u32 sw_if_index0; + u16 ether_type0; + u32 next0 = ~0; + u8 l2_len0; + u32 bi0; + u8 *h0; + + 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); + l2_len0 = vnet_buffer (b0)->l2.l2_len; + + h0 = vlib_buffer_get_current (b0); + ether_type0 = clib_net_to_host_u16 (*(u16 *) (h0 + l2_len0 - 2)); + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + + /* + * only extract unicast + */ + if (PREDICT_TRUE (!(h0[0] & 0x1))) + { + switch (ether_type0) + { + case ETHERNET_TYPE_IP4: + ASSERT (em->l2_emulations[sw_if_index0].enabled); + ++ip4_hits; + next0 = L2_EMULATION_NEXT_IP4; + vlib_buffer_advance (b0, l2_len0); + break; + case ETHERNET_TYPE_IP6: + ASSERT (em->l2_emulations[sw_if_index0].enabled); + ++ip6_hits; + next0 = L2_EMULATION_NEXT_IP6; + vlib_buffer_advance (b0, l2_len0); + default: + break; + } + } + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + l2_emulation_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->extracted = (next0 != ~0); + } + + /* Determine the next node and remove ourself from bitmap */ + if (PREDICT_TRUE (next0 == ~0)) + next0 = vnet_l2_feature_next (b0, em->l2_input_feat_next, + L2INPUT_FEAT_L2_EMULATION); + + /* 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); + } + + vlib_node_increment_counter (vm, node->node_index, + L2_EMULATION_ERROR_IP4, ip4_hits); + vlib_node_increment_counter (vm, node->node_index, + L2_EMULATION_ERROR_IP6, ip6_hits); + + return frame->n_vectors; +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (l2_emulation_node) = { + .name = "l2-emulation", + .vector_size = sizeof (u32), + .format_trace = format_l2_emulation_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(l2_emulation_error_strings), + .error_strings = l2_emulation_error_strings, + + .n_next_nodes = L2_EMULATION_N_NEXT, + + /* edit / add dispositions here */ + .next_nodes = { + [L2_EMULATION_NEXT_IP4] = "ip4-input", + [L2_EMULATION_NEXT_IP6] = "ip6-input", + }, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/vnet/vxlan-gbp/decap.c b/extras/deprecated/vnet/vxlan-gbp/decap.c new file mode 100644 index 00000000000..927c778b211 --- /dev/null +++ b/extras/deprecated/vnet/vxlan-gbp/decap.c @@ -0,0 +1,1050 @@ +/* + * decap.c: vxlan gbp tunnel decap packet processing + * + * 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 <vlib/vlib.h> + +#include <vnet/vxlan-gbp/vxlan_gbp.h> + +typedef struct +{ + u32 next_index; + u32 tunnel_index; + u32 error; + u32 vni; + u16 sclass; + u8 flags; +} vxlan_gbp_rx_trace_t; + +static u8 * +format_vxlan_gbp_rx_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 *); + vxlan_gbp_rx_trace_t *t = va_arg (*args, vxlan_gbp_rx_trace_t *); + + if (t->tunnel_index == ~0) + return format (s, + "VXLAN_GBP decap error - tunnel for vni %d does not exist", + t->vni); + return format (s, + "VXLAN_GBP decap from vxlan_gbp_tunnel%d vni %d sclass %d" + " flags %U next %d error %d", + t->tunnel_index, t->vni, t->sclass, + format_vxlan_gbp_header_gpflags, t->flags, + t->next_index, t->error); +} + +always_inline u32 +buf_fib_index (vlib_buffer_t * b, u32 is_ip4) +{ + u32 sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_TX]; + if (sw_if_index != (u32) ~ 0) + return sw_if_index; + + u32 *fib_index_by_sw_if_index = is_ip4 ? + ip4_main.fib_index_by_sw_if_index : ip6_main.fib_index_by_sw_if_index; + sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX]; + + return vec_elt (fib_index_by_sw_if_index, sw_if_index); +} + +typedef vxlan4_gbp_tunnel_key_t last_tunnel_cache4; + +always_inline vxlan_gbp_tunnel_t * +vxlan4_gbp_find_tunnel (vxlan_gbp_main_t * vxm, last_tunnel_cache4 * cache, + u32 fib_index, ip4_header_t * ip4_0, + vxlan_gbp_header_t * vxlan_gbp0) +{ + /* + * Check unicast first since that's where most of the traffic comes from + * Make sure VXLAN_GBP tunnel exist according to packet SIP, DIP and VNI + */ + vxlan4_gbp_tunnel_key_t key4; + int rv; + + key4.key[1] = (((u64) fib_index << 32) | + (vxlan_gbp0->vni_reserved & + clib_host_to_net_u32 (0xffffff00))); + key4.key[0] = + (((u64) ip4_0->dst_address.as_u32 << 32) | ip4_0->src_address.as_u32); + + if (PREDICT_FALSE (key4.key[0] != cache->key[0] || + key4.key[1] != cache->key[1])) + { + rv = clib_bihash_search_inline_16_8 (&vxm->vxlan4_gbp_tunnel_by_key, + &key4); + if (PREDICT_FALSE (rv == 0)) + { + *cache = key4; + return (pool_elt_at_index (vxm->tunnels, cache->value)); + } + } + else + { + return (pool_elt_at_index (vxm->tunnels, cache->value)); + } + + /* No unicast match - try multicast */ + if (PREDICT_TRUE (!ip4_address_is_multicast (&ip4_0->dst_address))) + return (NULL); + + key4.key[0] = ip4_0->dst_address.as_u32; + /* Make sure mcast VXLAN_GBP tunnel exist by packet DIP and VNI */ + rv = clib_bihash_search_inline_16_8 (&vxm->vxlan4_gbp_tunnel_by_key, &key4); + + if (PREDICT_FALSE (rv != 0)) + return (NULL); + + return (pool_elt_at_index (vxm->tunnels, key4.value)); +} + +typedef vxlan6_gbp_tunnel_key_t last_tunnel_cache6; + +always_inline vxlan_gbp_tunnel_t * +vxlan6_gbp_find_tunnel (vxlan_gbp_main_t * vxm, last_tunnel_cache6 * cache, + u32 fib_index, ip6_header_t * ip6_0, + vxlan_gbp_header_t * vxlan_gbp0) +{ + /* Make sure VXLAN_GBP tunnel exist according to packet SIP and VNI */ + vxlan6_gbp_tunnel_key_t key6 = { + .key = { + [0] = ip6_0->src_address.as_u64[0], + [1] = ip6_0->src_address.as_u64[1], + [2] = ((((u64) fib_index) << 32) | + (vxlan_gbp0->vni_reserved & + clib_host_to_net_u32 (0xffffff00))), + } + }; + int rv; + + if (PREDICT_FALSE + (clib_bihash_key_compare_24_8 (key6.key, cache->key) == 0)) + { + rv = clib_bihash_search_inline_24_8 (&vxm->vxlan6_gbp_tunnel_by_key, + &key6); + if (PREDICT_FALSE (rv != 0)) + return NULL; + + *cache = key6; + } + vxlan_gbp_tunnel_t *t0 = pool_elt_at_index (vxm->tunnels, cache->value); + + /* Validate VXLAN_GBP tunnel SIP against packet DIP */ + if (PREDICT_FALSE + (!ip6_address_is_equal (&ip6_0->dst_address, &t0->src.ip6))) + { + /* try multicast */ + if (PREDICT_TRUE (!ip6_address_is_multicast (&ip6_0->dst_address))) + return 0; + + /* Make sure mcast VXLAN_GBP tunnel exist by packet DIP and VNI */ + key6.key[0] = ip6_0->dst_address.as_u64[0]; + key6.key[1] = ip6_0->dst_address.as_u64[1]; + rv = clib_bihash_search_inline_24_8 (&vxm->vxlan6_gbp_tunnel_by_key, + &key6); + if (PREDICT_FALSE (rv != 0)) + return 0; + + } + + return t0; +} + +always_inline vxlan_gbp_input_next_t +vxlan_gbp_tunnel_get_next (const vxlan_gbp_tunnel_t * t, vlib_buffer_t * b0) +{ + if (VXLAN_GBP_TUNNEL_MODE_L2 == t->mode) + return (VXLAN_GBP_INPUT_NEXT_L2_INPUT); + else + { + ethernet_header_t *e0; + u16 type0; + + e0 = vlib_buffer_get_current (b0); + vlib_buffer_advance (b0, sizeof (*e0)); + type0 = clib_net_to_host_u16 (e0->type); + switch (type0) + { + case ETHERNET_TYPE_IP4: + return (VXLAN_GBP_INPUT_NEXT_IP4_INPUT); + case ETHERNET_TYPE_IP6: + return (VXLAN_GBP_INPUT_NEXT_IP6_INPUT); + } + } + return (VXLAN_GBP_INPUT_NEXT_DROP); +} + +always_inline uword +vxlan_gbp_input (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame, u8 is_ip4) +{ + vxlan_gbp_main_t *vxm = &vxlan_gbp_main; + vnet_main_t *vnm = vxm->vnet_main; + vnet_interface_main_t *im = &vnm->interface_main; + vlib_combined_counter_main_t *rx_counter = + im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX; + vlib_combined_counter_main_t *drop_counter = + im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_DROP; + last_tunnel_cache4 last4; + last_tunnel_cache6 last6; + u32 pkts_decapsulated = 0; + u32 thread_index = vlib_get_thread_index (); + + if (is_ip4) + clib_memset (&last4, 0xff, sizeof last4); + else + clib_memset (&last6, 0xff, sizeof last6); + + u32 next_index = node->cached_next_index; + + u32 *from = vlib_frame_vector_args (from_frame); + u32 n_left_from = from_frame->n_vectors; + + while (n_left_from > 0) + { + u32 *to_next, n_left_to_next; + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from >= 4 && n_left_to_next >= 2) + { + /* Prefetch next iteration. */ + { + vlib_buffer_t *p2, *p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header (p2, LOAD); + vlib_prefetch_buffer_header (p3, LOAD); + + CLIB_PREFETCH (p2->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD); + CLIB_PREFETCH (p3->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD); + } + + u32 bi0 = to_next[0] = from[0]; + u32 bi1 = to_next[1] = from[1]; + from += 2; + to_next += 2; + n_left_to_next -= 2; + n_left_from -= 2; + + vlib_buffer_t *b0, *b1; + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + /* udp leaves current_data pointing at the vxlan_gbp header */ + void *cur0 = vlib_buffer_get_current (b0); + void *cur1 = vlib_buffer_get_current (b1); + vxlan_gbp_header_t *vxlan_gbp0 = cur0; + vxlan_gbp_header_t *vxlan_gbp1 = cur1; + + ip4_header_t *ip4_0, *ip4_1; + ip6_header_t *ip6_0, *ip6_1; + if (is_ip4) + { + ip4_0 = cur0 - sizeof (udp_header_t) - sizeof (ip4_header_t); + ip4_1 = cur1 - sizeof (udp_header_t) - sizeof (ip4_header_t); + } + else + { + ip6_0 = cur0 - sizeof (udp_header_t) - sizeof (ip6_header_t); + ip6_1 = cur1 - sizeof (udp_header_t) - sizeof (ip6_header_t); + } + + u32 fi0 = buf_fib_index (b0, is_ip4); + u32 fi1 = buf_fib_index (b1, is_ip4); + + vxlan_gbp_tunnel_t *t0, *t1; + if (is_ip4) + { + t0 = + vxlan4_gbp_find_tunnel (vxm, &last4, fi0, ip4_0, vxlan_gbp0); + t1 = + vxlan4_gbp_find_tunnel (vxm, &last4, fi1, ip4_1, vxlan_gbp1); + } + else + { + t0 = + vxlan6_gbp_find_tunnel (vxm, &last6, fi0, ip6_0, vxlan_gbp0); + t1 = + vxlan6_gbp_find_tunnel (vxm, &last6, fi1, ip6_1, vxlan_gbp1); + } + + u32 len0 = vlib_buffer_length_in_chain (vm, b0); + u32 len1 = vlib_buffer_length_in_chain (vm, b1); + + vxlan_gbp_input_next_t next0, next1; + u8 error0 = 0, error1 = 0; + u8 flags0 = vxlan_gbp_get_flags (vxlan_gbp0); + u8 flags1 = vxlan_gbp_get_flags (vxlan_gbp1); + /* Required to make the l2 tag push / pop code work on l2 subifs */ + /* pop vxlan_gbp */ + vlib_buffer_advance (b0, sizeof *vxlan_gbp0); + vlib_buffer_advance (b1, sizeof *vxlan_gbp1); + + u8 i_and_g0 = ((flags0 & VXLAN_GBP_FLAGS_GI) == VXLAN_GBP_FLAGS_GI); + u8 i_and_g1 = ((flags1 & VXLAN_GBP_FLAGS_GI) == VXLAN_GBP_FLAGS_GI); + + /* Validate VXLAN_GBP tunnel encap-fib index against packet */ + if (PREDICT_FALSE (t0 == NULL || !i_and_g0)) + { + if (t0 != NULL && !i_and_g0) + { + error0 = VXLAN_GBP_ERROR_BAD_FLAGS; + vlib_increment_combined_counter + (drop_counter, thread_index, t0->sw_if_index, 1, len0); + next0 = VXLAN_GBP_INPUT_NEXT_DROP; + } + else + { + error0 = VXLAN_GBP_ERROR_NO_SUCH_TUNNEL; + next0 = VXLAN_GBP_INPUT_NEXT_PUNT; + if (is_ip4) + b0->punt_reason = + vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP4]; + else + b0->punt_reason = + vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP6]; + } + b0->error = node->errors[error0]; + } + else + { + next0 = vxlan_gbp_tunnel_get_next (t0, b0); + + /* Set packet input sw_if_index to unicast VXLAN tunnel for learning */ + vnet_buffer (b0)->sw_if_index[VLIB_RX] = t0->sw_if_index; + vlib_increment_combined_counter + (rx_counter, thread_index, t0->sw_if_index, 1, len0); + pkts_decapsulated++; + } + + vnet_buffer2 (b0)->gbp.flags = (vxlan_gbp_get_gpflags (vxlan_gbp0) | + VXLAN_GBP_GPFLAGS_R); + vnet_buffer2 (b0)->gbp.sclass = vxlan_gbp_get_sclass (vxlan_gbp0); + + + if (PREDICT_FALSE (t1 == NULL || !i_and_g1)) + { + if (t1 != NULL && !i_and_g1) + { + error1 = VXLAN_GBP_ERROR_BAD_FLAGS; + vlib_increment_combined_counter + (drop_counter, thread_index, t1->sw_if_index, 1, len1); + next1 = VXLAN_GBP_INPUT_NEXT_DROP; + } + else + { + error1 = VXLAN_GBP_ERROR_NO_SUCH_TUNNEL; + next1 = VXLAN_GBP_INPUT_NEXT_PUNT; + if (is_ip4) + b1->punt_reason = + vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP4]; + else + b1->punt_reason = + vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP6]; + } + b1->error = node->errors[error1]; + } + else + { + next1 = vxlan_gbp_tunnel_get_next (t1, b1); + + /* Set packet input sw_if_index to unicast VXLAN_GBP tunnel for learning */ + vnet_buffer (b1)->sw_if_index[VLIB_RX] = t1->sw_if_index; + pkts_decapsulated++; + + vlib_increment_combined_counter + (rx_counter, thread_index, t1->sw_if_index, 1, len1); + } + + vnet_buffer2 (b1)->gbp.flags = (vxlan_gbp_get_gpflags (vxlan_gbp1) | + VXLAN_GBP_GPFLAGS_R); + + vnet_buffer2 (b1)->gbp.sclass = vxlan_gbp_get_sclass (vxlan_gbp1); + + vnet_update_l2_len (b0); + vnet_update_l2_len (b1); + + if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) + { + vxlan_gbp_rx_trace_t *tr = + vlib_add_trace (vm, node, b0, sizeof (*tr)); + tr->next_index = next0; + tr->error = error0; + tr->tunnel_index = t0 == 0 ? ~0 : t0 - vxm->tunnels; + tr->vni = vxlan_gbp_get_vni (vxlan_gbp0); + tr->sclass = vxlan_gbp_get_sclass (vxlan_gbp0); + tr->flags = vxlan_gbp_get_gpflags (vxlan_gbp0); + } + if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED)) + { + vxlan_gbp_rx_trace_t *tr = + vlib_add_trace (vm, node, b1, sizeof (*tr)); + tr->next_index = next1; + tr->error = error1; + tr->tunnel_index = t1 == 0 ? ~0 : t1 - vxm->tunnels; + tr->vni = vxlan_gbp_get_vni (vxlan_gbp1); + tr->sclass = vxlan_gbp_get_sclass (vxlan_gbp1); + tr->flags = vxlan_gbp_get_gpflags (vxlan_gbp1); + } + + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, next0, next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0 = to_next[0] = from[0]; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + vlib_buffer_t *b0 = vlib_get_buffer (vm, bi0); + + /* udp leaves current_data pointing at the vxlan_gbp header */ + void *cur0 = vlib_buffer_get_current (b0); + vxlan_gbp_header_t *vxlan_gbp0 = cur0; + ip4_header_t *ip4_0; + ip6_header_t *ip6_0; + if (is_ip4) + ip4_0 = cur0 - sizeof (udp_header_t) - sizeof (ip4_header_t); + else + ip6_0 = cur0 - sizeof (udp_header_t) - sizeof (ip6_header_t); + + u32 fi0 = buf_fib_index (b0, is_ip4); + + vxlan_gbp_tunnel_t *t0; + if (is_ip4) + t0 = vxlan4_gbp_find_tunnel (vxm, &last4, fi0, ip4_0, vxlan_gbp0); + else + t0 = vxlan6_gbp_find_tunnel (vxm, &last6, fi0, ip6_0, vxlan_gbp0); + + uword len0 = vlib_buffer_length_in_chain (vm, b0); + + vxlan_gbp_input_next_t next0; + u8 error0 = 0; + u8 flags0 = vxlan_gbp_get_flags (vxlan_gbp0); + + /* pop (ip, udp, vxlan_gbp) */ + vlib_buffer_advance (b0, sizeof (*vxlan_gbp0)); + + u8 i_and_g0 = ((flags0 & VXLAN_GBP_FLAGS_GI) == VXLAN_GBP_FLAGS_GI); + + /* Validate VXLAN_GBP tunnel encap-fib index against packet */ + if (PREDICT_FALSE (t0 == NULL || !i_and_g0)) + { + if (t0 != NULL && !i_and_g0) + { + error0 = VXLAN_GBP_ERROR_BAD_FLAGS; + vlib_increment_combined_counter + (drop_counter, thread_index, t0->sw_if_index, 1, len0); + next0 = VXLAN_GBP_INPUT_NEXT_DROP; + } + else + { + error0 = VXLAN_GBP_ERROR_NO_SUCH_TUNNEL; + next0 = VXLAN_GBP_INPUT_NEXT_PUNT; + if (is_ip4) + b0->punt_reason = + vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP4]; + else + b0->punt_reason = + vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP6]; + } + b0->error = node->errors[error0]; + } + else + { + next0 = vxlan_gbp_tunnel_get_next (t0, b0); + /* Set packet input sw_if_index to unicast VXLAN_GBP tunnel for learning */ + vnet_buffer (b0)->sw_if_index[VLIB_RX] = t0->sw_if_index; + pkts_decapsulated++; + + vlib_increment_combined_counter + (rx_counter, thread_index, t0->sw_if_index, 1, len0); + } + vnet_buffer2 (b0)->gbp.flags = (vxlan_gbp_get_gpflags (vxlan_gbp0) | + VXLAN_GBP_GPFLAGS_R); + + vnet_buffer2 (b0)->gbp.sclass = vxlan_gbp_get_sclass (vxlan_gbp0); + + /* Required to make the l2 tag push / pop code work on l2 subifs */ + vnet_update_l2_len (b0); + + if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) + { + vxlan_gbp_rx_trace_t *tr + = vlib_add_trace (vm, node, b0, sizeof (*tr)); + tr->next_index = next0; + tr->error = error0; + tr->tunnel_index = t0 == 0 ? ~0 : t0 - vxm->tunnels; + tr->vni = vxlan_gbp_get_vni (vxlan_gbp0); + tr->sclass = vxlan_gbp_get_sclass (vxlan_gbp0); + tr->flags = vxlan_gbp_get_gpflags (vxlan_gbp0); + } + 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); + } + /* Do we still need this now that tunnel tx stats is kept? */ + u32 node_idx = + is_ip4 ? vxlan4_gbp_input_node.index : vxlan6_gbp_input_node.index; + vlib_node_increment_counter (vm, node_idx, VXLAN_GBP_ERROR_DECAPSULATED, + pkts_decapsulated); + + return from_frame->n_vectors; +} + +VLIB_NODE_FN (vxlan4_gbp_input_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + return vxlan_gbp_input (vm, node, from_frame, /* is_ip4 */ 1); +} + +VLIB_NODE_FN (vxlan6_gbp_input_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + return vxlan_gbp_input (vm, node, from_frame, /* is_ip4 */ 0); +} + +static char *vxlan_gbp_error_strings[] = { +#define vxlan_gbp_error(n,s) s, +#include <vnet/vxlan-gbp/vxlan_gbp_error.def> +#undef vxlan_gbp_error +#undef _ +}; + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (vxlan4_gbp_input_node) = +{ + .name = "vxlan4-gbp-input", + .vector_size = sizeof (u32), + .n_errors = VXLAN_GBP_N_ERROR, + .error_strings = vxlan_gbp_error_strings, + .n_next_nodes = VXLAN_GBP_INPUT_N_NEXT, + .format_trace = format_vxlan_gbp_rx_trace, + .next_nodes = { +#define _(s,n) [VXLAN_GBP_INPUT_NEXT_##s] = n, + foreach_vxlan_gbp_input_next +#undef _ + }, +}; + +VLIB_REGISTER_NODE (vxlan6_gbp_input_node) = +{ + .name = "vxlan6-gbp-input", + .vector_size = sizeof (u32), + .n_errors = VXLAN_GBP_N_ERROR, + .error_strings = vxlan_gbp_error_strings, + .n_next_nodes = VXLAN_GBP_INPUT_N_NEXT, + .next_nodes = { +#define _(s,n) [VXLAN_GBP_INPUT_NEXT_##s] = n, + foreach_vxlan_gbp_input_next +#undef _ + }, + .format_trace = format_vxlan_gbp_rx_trace, +}; +/* *INDENT-ON* */ + +typedef enum +{ + IP_VXLAN_GBP_BYPASS_NEXT_DROP, + IP_VXLAN_GBP_BYPASS_NEXT_VXLAN_GBP, + IP_VXLAN_GBP_BYPASS_N_NEXT, +} ip_vxlan_gbp_bypass_next_t; + +always_inline uword +ip_vxlan_gbp_bypass_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, u32 is_ip4) +{ + vxlan_gbp_main_t *vxm = &vxlan_gbp_main; + u32 *from, *to_next, n_left_from, n_left_to_next, next_index; + vlib_node_runtime_t *error_node = + vlib_node_get_runtime (vm, ip4_input_node.index); + ip4_address_t addr4; /* last IPv4 address matching a local VTEP address */ + ip6_address_t addr6; /* last IPv6 address matching a local VTEP address */ + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + if (node->flags & VLIB_NODE_FLAG_TRACE) + ip4_forward_next_trace (vm, node, frame, VLIB_TX); + + if (is_ip4) + addr4.data_u32 = ~0; + else + ip6_address_set_zero (&addr6); + + while (n_left_from > 0) + { + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from >= 4 && n_left_to_next >= 2) + { + vlib_buffer_t *b0, *b1; + ip4_header_t *ip40, *ip41; + ip6_header_t *ip60, *ip61; + udp_header_t *udp0, *udp1; + u32 bi0, ip_len0, udp_len0, flags0, next0; + u32 bi1, ip_len1, udp_len1, flags1, next1; + i32 len_diff0, len_diff1; + u8 error0, good_udp0, proto0; + u8 error1, good_udp1, proto1; + + /* Prefetch next iteration. */ + { + vlib_buffer_t *p2, *p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header (p2, LOAD); + vlib_prefetch_buffer_header (p3, LOAD); + + CLIB_PREFETCH (p2->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD); + CLIB_PREFETCH (p3->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD); + } + + bi0 = to_next[0] = from[0]; + bi1 = to_next[1] = from[1]; + from += 2; + n_left_from -= 2; + to_next += 2; + n_left_to_next -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + if (is_ip4) + { + ip40 = vlib_buffer_get_current (b0); + ip41 = vlib_buffer_get_current (b1); + } + else + { + ip60 = vlib_buffer_get_current (b0); + ip61 = vlib_buffer_get_current (b1); + } + + /* Setup packet for next IP feature */ + vnet_feature_next (&next0, b0); + vnet_feature_next (&next1, b1); + + if (is_ip4) + { + /* Treat IP frag packets as "experimental" protocol for now + until support of IP frag reassembly is implemented */ + proto0 = ip4_is_fragment (ip40) ? 0xfe : ip40->protocol; + proto1 = ip4_is_fragment (ip41) ? 0xfe : ip41->protocol; + } + else + { + proto0 = ip60->protocol; + proto1 = ip61->protocol; + } + + /* Process packet 0 */ + if (proto0 != IP_PROTOCOL_UDP) + goto exit0; /* not UDP packet */ + + if (is_ip4) + udp0 = ip4_next_header (ip40); + else + udp0 = ip6_next_header (ip60); + + if (udp0->dst_port != clib_host_to_net_u16 (UDP_DST_PORT_vxlan_gbp)) + goto exit0; /* not VXLAN_GBP packet */ + + /* Validate DIP against VTEPs */ + if (is_ip4) + { + if (addr4.as_u32 != ip40->dst_address.as_u32) + { + if (!hash_get (vxm->vtep4, ip40->dst_address.as_u32)) + goto exit0; /* no local VTEP for VXLAN_GBP packet */ + addr4 = ip40->dst_address; + } + } + else + { + if (!ip6_address_is_equal (&addr6, &ip60->dst_address)) + { + if (!hash_get_mem (vxm->vtep6, &ip60->dst_address)) + goto exit0; /* no local VTEP for VXLAN_GBP packet */ + addr6 = ip60->dst_address; + } + } + + flags0 = b0->flags; + good_udp0 = (flags0 & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0; + + /* Don't verify UDP checksum for packets with explicit zero checksum. */ + good_udp0 |= udp0->checksum == 0; + + /* Verify UDP length */ + if (is_ip4) + ip_len0 = clib_net_to_host_u16 (ip40->length); + else + ip_len0 = clib_net_to_host_u16 (ip60->payload_length); + udp_len0 = clib_net_to_host_u16 (udp0->length); + len_diff0 = ip_len0 - udp_len0; + + /* Verify UDP checksum */ + if (PREDICT_FALSE (!good_udp0)) + { + if ((flags0 & VNET_BUFFER_F_L4_CHECKSUM_COMPUTED) == 0) + { + if (is_ip4) + flags0 = ip4_tcp_udp_validate_checksum (vm, b0); + else + flags0 = ip6_tcp_udp_icmp_validate_checksum (vm, b0); + good_udp0 = + (flags0 & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0; + } + } + + if (is_ip4) + { + error0 = good_udp0 ? 0 : IP4_ERROR_UDP_CHECKSUM; + error0 = (len_diff0 >= 0) ? error0 : IP4_ERROR_UDP_LENGTH; + } + else + { + error0 = good_udp0 ? 0 : IP6_ERROR_UDP_CHECKSUM; + error0 = (len_diff0 >= 0) ? error0 : IP6_ERROR_UDP_LENGTH; + } + + next0 = error0 ? + IP_VXLAN_GBP_BYPASS_NEXT_DROP : + IP_VXLAN_GBP_BYPASS_NEXT_VXLAN_GBP; + b0->error = error0 ? error_node->errors[error0] : 0; + + /* vxlan-gbp-input node expect current at VXLAN_GBP header */ + if (is_ip4) + vlib_buffer_advance (b0, + sizeof (ip4_header_t) + + sizeof (udp_header_t)); + else + vlib_buffer_advance (b0, + sizeof (ip6_header_t) + + sizeof (udp_header_t)); + + exit0: + /* Process packet 1 */ + if (proto1 != IP_PROTOCOL_UDP) + goto exit1; /* not UDP packet */ + + if (is_ip4) + udp1 = ip4_next_header (ip41); + else + udp1 = ip6_next_header (ip61); + + if (udp1->dst_port != clib_host_to_net_u16 (UDP_DST_PORT_vxlan_gbp)) + goto exit1; /* not VXLAN_GBP packet */ + + /* Validate DIP against VTEPs */ + if (is_ip4) + { + if (addr4.as_u32 != ip41->dst_address.as_u32) + { + if (!hash_get (vxm->vtep4, ip41->dst_address.as_u32)) + goto exit1; /* no local VTEP for VXLAN_GBP packet */ + addr4 = ip41->dst_address; + } + } + else + { + if (!ip6_address_is_equal (&addr6, &ip61->dst_address)) + { + if (!hash_get_mem (vxm->vtep6, &ip61->dst_address)) + goto exit1; /* no local VTEP for VXLAN_GBP packet */ + addr6 = ip61->dst_address; + } + } + + flags1 = b1->flags; + good_udp1 = (flags1 & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0; + + /* Don't verify UDP checksum for packets with explicit zero checksum. */ + good_udp1 |= udp1->checksum == 0; + + /* Verify UDP length */ + if (is_ip4) + ip_len1 = clib_net_to_host_u16 (ip41->length); + else + ip_len1 = clib_net_to_host_u16 (ip61->payload_length); + udp_len1 = clib_net_to_host_u16 (udp1->length); + len_diff1 = ip_len1 - udp_len1; + + /* Verify UDP checksum */ + if (PREDICT_FALSE (!good_udp1)) + { + if ((flags1 & VNET_BUFFER_F_L4_CHECKSUM_COMPUTED) == 0) + { + if (is_ip4) + flags1 = ip4_tcp_udp_validate_checksum (vm, b1); + else + flags1 = ip6_tcp_udp_icmp_validate_checksum (vm, b1); + good_udp1 = + (flags1 & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0; + } + } + + if (is_ip4) + { + error1 = good_udp1 ? 0 : IP4_ERROR_UDP_CHECKSUM; + error1 = (len_diff1 >= 0) ? error1 : IP4_ERROR_UDP_LENGTH; + } + else + { + error1 = good_udp1 ? 0 : IP6_ERROR_UDP_CHECKSUM; + error1 = (len_diff1 >= 0) ? error1 : IP6_ERROR_UDP_LENGTH; + } + + next1 = error1 ? + IP_VXLAN_GBP_BYPASS_NEXT_DROP : + IP_VXLAN_GBP_BYPASS_NEXT_VXLAN_GBP; + b1->error = error1 ? error_node->errors[error1] : 0; + + /* vxlan_gbp-input node expect current at VXLAN_GBP header */ + if (is_ip4) + vlib_buffer_advance (b1, + sizeof (ip4_header_t) + + sizeof (udp_header_t)); + else + vlib_buffer_advance (b1, + sizeof (ip6_header_t) + + sizeof (udp_header_t)); + + exit1: + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, next0, next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + vlib_buffer_t *b0; + ip4_header_t *ip40; + ip6_header_t *ip60; + udp_header_t *udp0; + u32 bi0, ip_len0, udp_len0, flags0, next0; + i32 len_diff0; + u8 error0, good_udp0, proto0; + + bi0 = to_next[0] = from[0]; + from += 1; + n_left_from -= 1; + to_next += 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + if (is_ip4) + ip40 = vlib_buffer_get_current (b0); + else + ip60 = vlib_buffer_get_current (b0); + + /* Setup packet for next IP feature */ + vnet_feature_next (&next0, b0); + + if (is_ip4) + /* Treat IP4 frag packets as "experimental" protocol for now + until support of IP frag reassembly is implemented */ + proto0 = ip4_is_fragment (ip40) ? 0xfe : ip40->protocol; + else + proto0 = ip60->protocol; + + if (proto0 != IP_PROTOCOL_UDP) + goto exit; /* not UDP packet */ + + if (is_ip4) + udp0 = ip4_next_header (ip40); + else + udp0 = ip6_next_header (ip60); + + if (udp0->dst_port != clib_host_to_net_u16 (UDP_DST_PORT_vxlan_gbp)) + goto exit; /* not VXLAN_GBP packet */ + + /* Validate DIP against VTEPs */ + if (is_ip4) + { + if (addr4.as_u32 != ip40->dst_address.as_u32) + { + if (!hash_get (vxm->vtep4, ip40->dst_address.as_u32)) + goto exit; /* no local VTEP for VXLAN_GBP packet */ + addr4 = ip40->dst_address; + } + } + else + { + if (!ip6_address_is_equal (&addr6, &ip60->dst_address)) + { + if (!hash_get_mem (vxm->vtep6, &ip60->dst_address)) + goto exit; /* no local VTEP for VXLAN_GBP packet */ + addr6 = ip60->dst_address; + } + } + + flags0 = b0->flags; + good_udp0 = (flags0 & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0; + + /* Don't verify UDP checksum for packets with explicit zero checksum. */ + good_udp0 |= udp0->checksum == 0; + + /* Verify UDP length */ + if (is_ip4) + ip_len0 = clib_net_to_host_u16 (ip40->length); + else + ip_len0 = clib_net_to_host_u16 (ip60->payload_length); + udp_len0 = clib_net_to_host_u16 (udp0->length); + len_diff0 = ip_len0 - udp_len0; + + /* Verify UDP checksum */ + if (PREDICT_FALSE (!good_udp0)) + { + if ((flags0 & VNET_BUFFER_F_L4_CHECKSUM_COMPUTED) == 0) + { + if (is_ip4) + flags0 = ip4_tcp_udp_validate_checksum (vm, b0); + else + flags0 = ip6_tcp_udp_icmp_validate_checksum (vm, b0); + good_udp0 = + (flags0 & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0; + } + } + + if (is_ip4) + { + error0 = good_udp0 ? 0 : IP4_ERROR_UDP_CHECKSUM; + error0 = (len_diff0 >= 0) ? error0 : IP4_ERROR_UDP_LENGTH; + } + else + { + error0 = good_udp0 ? 0 : IP6_ERROR_UDP_CHECKSUM; + error0 = (len_diff0 >= 0) ? error0 : IP6_ERROR_UDP_LENGTH; + } + + next0 = error0 ? + IP_VXLAN_GBP_BYPASS_NEXT_DROP : + IP_VXLAN_GBP_BYPASS_NEXT_VXLAN_GBP; + b0->error = error0 ? error_node->errors[error0] : 0; + + /* vxlan_gbp-input node expect current at VXLAN_GBP header */ + if (is_ip4) + vlib_buffer_advance (b0, + sizeof (ip4_header_t) + + sizeof (udp_header_t)); + else + vlib_buffer_advance (b0, + sizeof (ip6_header_t) + + sizeof (udp_header_t)); + + exit: + 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; +} + +VLIB_NODE_FN (ip4_vxlan_gbp_bypass_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return ip_vxlan_gbp_bypass_inline (vm, node, frame, /* is_ip4 */ 1); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (ip4_vxlan_gbp_bypass_node) = +{ + .name = "ip4-vxlan-gbp-bypass", + .vector_size = sizeof (u32), + .n_next_nodes = IP_VXLAN_GBP_BYPASS_N_NEXT, + .next_nodes = { + [IP_VXLAN_GBP_BYPASS_NEXT_DROP] = "error-drop", + [IP_VXLAN_GBP_BYPASS_NEXT_VXLAN_GBP] = "vxlan4-gbp-input", + }, + .format_buffer = format_ip4_header, + .format_trace = format_ip4_forward_next_trace, +}; +/* *INDENT-ON* */ + +#ifndef CLIB_MARCH_VARIANT +/* Dummy init function to get us linked in. */ +clib_error_t * +ip4_vxlan_gbp_bypass_init (vlib_main_t * vm) +{ + return 0; +} + +VLIB_INIT_FUNCTION (ip4_vxlan_gbp_bypass_init); +#endif /* CLIB_MARCH_VARIANT */ + +VLIB_NODE_FN (ip6_vxlan_gbp_bypass_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return ip_vxlan_gbp_bypass_inline (vm, node, frame, /* is_ip4 */ 0); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (ip6_vxlan_gbp_bypass_node) = +{ + .name = "ip6-vxlan-gbp-bypass", + .vector_size = sizeof (u32), + .n_next_nodes = IP_VXLAN_GBP_BYPASS_N_NEXT, + .next_nodes = { + [IP_VXLAN_GBP_BYPASS_NEXT_DROP] = "error-drop", + [IP_VXLAN_GBP_BYPASS_NEXT_VXLAN_GBP] = "vxlan6-gbp-input", + }, + .format_buffer = format_ip6_header, + .format_trace = format_ip6_forward_next_trace, +}; +/* *INDENT-ON* */ + +#ifndef CLIB_MARCH_VARIANT +/* Dummy init function to get us linked in. */ +clib_error_t * +ip6_vxlan_gbp_bypass_init (vlib_main_t * vm) +{ + return 0; +} + +VLIB_INIT_FUNCTION (ip6_vxlan_gbp_bypass_init); +#endif /* CLIB_MARCH_VARIANT */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/vnet/vxlan-gbp/dir.dox b/extras/deprecated/vnet/vxlan-gbp/dir.dox new file mode 100644 index 00000000000..6e63c90b17b --- /dev/null +++ b/extras/deprecated/vnet/vxlan-gbp/dir.dox @@ -0,0 +1,24 @@ +/* + * 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. + */ + +/** +@dir +@brief VXLAN-GBP Code. + +This directory contains source code to support VXLAN-GBP. + +*/ +/*? %%clicmd:group_label VXLAN-GBP CLI %% ?*/ diff --git a/extras/deprecated/vnet/vxlan-gbp/encap.c b/extras/deprecated/vnet/vxlan-gbp/encap.c new file mode 100644 index 00000000000..2a4e8a8e312 --- /dev/null +++ b/extras/deprecated/vnet/vxlan-gbp/encap.c @@ -0,0 +1,601 @@ +/* + * 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 <vppinfra/error.h> +#include <vppinfra/hash.h> +#include <vnet/vnet.h> +#include <vnet/ip/ip.h> +#include <vnet/ethernet/ethernet.h> +#include <vnet/interface_output.h> +#include <vnet/vxlan-gbp/vxlan_gbp.h> +#include <vnet/qos/qos_types.h> +#include <vnet/adj/rewrite.h> + +/* Statistics (not all errors) */ +#define foreach_vxlan_gbp_encap_error \ +_(ENCAPSULATED, "good packets encapsulated") + +static char *vxlan_gbp_encap_error_strings[] = { +#define _(sym,string) string, + foreach_vxlan_gbp_encap_error +#undef _ +}; + +typedef enum +{ +#define _(sym,str) VXLAN_GBP_ENCAP_ERROR_##sym, + foreach_vxlan_gbp_encap_error +#undef _ + VXLAN_GBP_ENCAP_N_ERROR, +} vxlan_gbp_encap_error_t; + +typedef enum +{ + VXLAN_GBP_ENCAP_NEXT_DROP, + VXLAN_GBP_ENCAP_N_NEXT, +} vxlan_gbp_encap_next_t; + +typedef struct +{ + u32 tunnel_index; + u32 vni; + u16 sclass; + u8 flags; +} vxlan_gbp_encap_trace_t; + +#ifndef CLIB_MARCH_VARIANT +u8 * +format_vxlan_gbp_encap_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 *); + vxlan_gbp_encap_trace_t *t = va_arg (*args, vxlan_gbp_encap_trace_t *); + + s = + format (s, + "VXLAN_GBP encap to vxlan_gbp_tunnel%d vni %d sclass %d flags %U", + t->tunnel_index, t->vni, t->sclass, + format_vxlan_gbp_header_gpflags, t->flags); + return s; +} +#endif /* CLIB_MARCH_VARIANT */ + +always_inline uword +vxlan_gbp_encap_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame, u8 is_ip4, u8 csum_offload) +{ + u32 n_left_from, next_index, *from, *to_next; + vxlan_gbp_main_t *vxm = &vxlan_gbp_main; + vnet_main_t *vnm = vxm->vnet_main; + vnet_interface_main_t *im = &vnm->interface_main; + vlib_combined_counter_main_t *tx_counter = + im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX; + u32 pkts_encapsulated = 0; + u32 thread_index = vlib_get_thread_index (); + u32 sw_if_index0 = 0, sw_if_index1 = 0; + u32 next0 = 0, next1 = 0; + vxlan_gbp_tunnel_t *t0 = NULL, *t1 = NULL; + index_t dpoi_idx0 = INDEX_INVALID, dpoi_idx1 = INDEX_INVALID; + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs; + + from = vlib_frame_vector_args (from_frame); + n_left_from = from_frame->n_vectors; + vlib_get_buffers (vm, from, bufs, n_left_from); + + next_index = node->cached_next_index; + + STATIC_ASSERT_SIZEOF (ip6_vxlan_gbp_header_t, 56); + STATIC_ASSERT_SIZEOF (ip4_vxlan_gbp_header_t, 36); + + u8 const underlay_hdr_len = is_ip4 ? + sizeof (ip4_vxlan_gbp_header_t) : sizeof (ip6_vxlan_gbp_header_t); + u16 const l3_len = is_ip4 ? sizeof (ip4_header_t) : sizeof (ip6_header_t); + u32 const csum_flags = + is_ip4 ? VNET_BUFFER_F_IS_IP4 | VNET_BUFFER_F_L3_HDR_OFFSET_VALID | + VNET_BUFFER_F_L4_HDR_OFFSET_VALID : + VNET_BUFFER_F_IS_IP6 | VNET_BUFFER_F_L3_HDR_OFFSET_VALID | + VNET_BUFFER_F_L4_HDR_OFFSET_VALID; + u32 const outer_packet_csum_offload_flags = + is_ip4 ? VNET_BUFFER_OFFLOAD_F_IP_CKSUM | VNET_BUFFER_OFFLOAD_F_UDP_CKSUM : + VNET_BUFFER_OFFLOAD_F_UDP_CKSUM; + u32 const inner_packet_removed_flags = + VNET_BUFFER_F_IS_IP4 | VNET_BUFFER_F_IS_IP6 | + VNET_BUFFER_F_L2_HDR_OFFSET_VALID | VNET_BUFFER_F_L3_HDR_OFFSET_VALID | + VNET_BUFFER_F_L4_HDR_OFFSET_VALID; + + 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 >= 4 && n_left_to_next >= 2) + { + /* Prefetch next iteration. */ + { + vlib_buffer_t *p2, *p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header (p2, LOAD); + vlib_prefetch_buffer_header (p3, LOAD); + + CLIB_PREFETCH (b[2]->data - CLIB_CACHE_LINE_BYTES, + 2 * CLIB_CACHE_LINE_BYTES, LOAD); + CLIB_PREFETCH (b[3]->data - CLIB_CACHE_LINE_BYTES, + 2 * CLIB_CACHE_LINE_BYTES, LOAD); + } + + u32 bi0 = to_next[0] = from[0]; + u32 bi1 = to_next[1] = from[1]; + from += 2; + to_next += 2; + n_left_to_next -= 2; + n_left_from -= 2; + + u32 or_flags = b[0]->flags | b[1]->flags; + if (csum_offload && (or_flags & VNET_BUFFER_F_OFFLOAD)) + { + /* Only calculate the non-GSO packet csum offload */ + if ((b[0]->flags & VNET_BUFFER_F_GSO) == 0) + { + vnet_calc_checksums_inline (vm, b[0], + b[0]->flags & + VNET_BUFFER_F_IS_IP4, + b[0]->flags & + VNET_BUFFER_F_IS_IP6); + b[0]->flags &= ~inner_packet_removed_flags; + } + if ((b[1]->flags & VNET_BUFFER_F_GSO) == 0) + { + vnet_calc_checksums_inline (vm, b[1], + b[1]->flags & + VNET_BUFFER_F_IS_IP4, + b[1]->flags & + VNET_BUFFER_F_IS_IP6); + b[1]->flags &= ~inner_packet_removed_flags; + } + } + + u32 flow_hash0 = vnet_l2_compute_flow_hash (b[0]); + u32 flow_hash1 = vnet_l2_compute_flow_hash (b[1]); + + /* Get next node index and adj index from tunnel next_dpo */ + if (sw_if_index0 != vnet_buffer (b[0])->sw_if_index[VLIB_TX]) + { + sw_if_index0 = vnet_buffer (b[0])->sw_if_index[VLIB_TX]; + vnet_hw_interface_t *hi0 = + vnet_get_sup_hw_interface (vnm, sw_if_index0); + t0 = &vxm->tunnels[hi0->dev_instance]; + /* Note: change to always set next0 if it may set to drop */ + next0 = t0->next_dpo.dpoi_next_node; + dpoi_idx0 = t0->next_dpo.dpoi_index; + } + + /* Get next node index and adj index from tunnel next_dpo */ + if (sw_if_index1 != vnet_buffer (b[1])->sw_if_index[VLIB_TX]) + { + if (sw_if_index0 == vnet_buffer (b[1])->sw_if_index[VLIB_TX]) + { + sw_if_index1 = sw_if_index0; + t1 = t0; + next1 = next0; + dpoi_idx1 = dpoi_idx0; + } + else + { + sw_if_index1 = vnet_buffer (b[1])->sw_if_index[VLIB_TX]; + vnet_hw_interface_t *hi1 = + vnet_get_sup_hw_interface (vnm, sw_if_index1); + t1 = &vxm->tunnels[hi1->dev_instance]; + /* Note: change to always set next1 if it may set to drop */ + next1 = t1->next_dpo.dpoi_next_node; + dpoi_idx1 = t1->next_dpo.dpoi_index; + } + } + + vnet_buffer (b[0])->ip.adj_index[VLIB_TX] = dpoi_idx0; + vnet_buffer (b[1])->ip.adj_index[VLIB_TX] = dpoi_idx1; + + ASSERT (t0->rewrite_header.data_bytes == underlay_hdr_len); + ASSERT (t1->rewrite_header.data_bytes == underlay_hdr_len); + vnet_rewrite_two_headers (*t0, *t1, vlib_buffer_get_current (b[0]), + vlib_buffer_get_current (b[1]), + underlay_hdr_len); + + vlib_buffer_advance (b[0], -underlay_hdr_len); + vlib_buffer_advance (b[1], -underlay_hdr_len); + + u32 len0 = vlib_buffer_length_in_chain (vm, b[0]); + u32 len1 = vlib_buffer_length_in_chain (vm, b[1]); + u16 payload_l0 = clib_host_to_net_u16 (len0 - l3_len); + u16 payload_l1 = clib_host_to_net_u16 (len1 - l3_len); + + void *underlay0 = vlib_buffer_get_current (b[0]); + void *underlay1 = vlib_buffer_get_current (b[1]); + + ip4_header_t *ip4_0, *ip4_1; + qos_bits_t ip4_0_tos = 0, ip4_1_tos = 0; + ip6_header_t *ip6_0, *ip6_1; + udp_header_t *udp0, *udp1; + vxlan_gbp_header_t *vxlan_gbp0, *vxlan_gbp1; + u8 *l3_0, *l3_1; + if (is_ip4) + { + ip4_vxlan_gbp_header_t *hdr0 = underlay0; + ip4_vxlan_gbp_header_t *hdr1 = underlay1; + + /* Fix the IP4 checksum and length */ + ip4_0 = &hdr0->ip4; + ip4_1 = &hdr1->ip4; + ip4_0->length = clib_host_to_net_u16 (len0); + ip4_1->length = clib_host_to_net_u16 (len1); + + if (PREDICT_FALSE (b[0]->flags & VNET_BUFFER_F_QOS_DATA_VALID)) + { + ip4_0_tos = vnet_buffer2 (b[0])->qos.bits; + ip4_0->tos = ip4_0_tos; + } + if (PREDICT_FALSE (b[1]->flags & VNET_BUFFER_F_QOS_DATA_VALID)) + { + ip4_1_tos = vnet_buffer2 (b[1])->qos.bits; + ip4_1->tos = ip4_1_tos; + } + + l3_0 = (u8 *) ip4_0; + l3_1 = (u8 *) ip4_1; + udp0 = &hdr0->udp; + udp1 = &hdr1->udp; + vxlan_gbp0 = &hdr0->vxlan_gbp; + vxlan_gbp1 = &hdr1->vxlan_gbp; + } + else /* ipv6 */ + { + ip6_vxlan_gbp_header_t *hdr0 = underlay0; + ip6_vxlan_gbp_header_t *hdr1 = underlay1; + + /* Fix IP6 payload length */ + ip6_0 = &hdr0->ip6; + ip6_1 = &hdr1->ip6; + ip6_0->payload_length = payload_l0; + ip6_1->payload_length = payload_l1; + + l3_0 = (u8 *) ip6_0; + l3_1 = (u8 *) ip6_1; + udp0 = &hdr0->udp; + udp1 = &hdr1->udp; + vxlan_gbp0 = &hdr0->vxlan_gbp; + vxlan_gbp1 = &hdr1->vxlan_gbp; + } + + /* Fix UDP length and set source port */ + udp0->length = payload_l0; + udp0->src_port = flow_hash0; + udp1->length = payload_l1; + udp1->src_port = flow_hash1; + + /* set source class and gpflags */ + vxlan_gbp0->gpflags = vnet_buffer2 (b[0])->gbp.flags; + vxlan_gbp1->gpflags = vnet_buffer2 (b[1])->gbp.flags; + vxlan_gbp0->sclass = + clib_host_to_net_u16 (vnet_buffer2 (b[0])->gbp.sclass); + vxlan_gbp1->sclass = + clib_host_to_net_u16 (vnet_buffer2 (b[1])->gbp.sclass); + + if (csum_offload) + { + b[0]->flags |= csum_flags; + vnet_buffer (b[0])->l3_hdr_offset = l3_0 - b[0]->data; + vnet_buffer (b[0])->l4_hdr_offset = (u8 *) udp0 - b[0]->data; + vnet_buffer_offload_flags_set (b[0], + outer_packet_csum_offload_flags); + b[1]->flags |= csum_flags; + vnet_buffer (b[1])->l3_hdr_offset = l3_1 - b[1]->data; + vnet_buffer (b[1])->l4_hdr_offset = (u8 *) udp1 - b[1]->data; + vnet_buffer_offload_flags_set (b[1], + outer_packet_csum_offload_flags); + } + /* IPv4 UDP checksum only if checksum offload is used */ + else if (is_ip4) + { + ip_csum_t sum0 = ip4_0->checksum; + sum0 = ip_csum_update (sum0, 0, ip4_0->length, ip4_header_t, + length /* changed member */ ); + if (PREDICT_FALSE (ip4_0_tos)) + { + sum0 = ip_csum_update (sum0, 0, ip4_0_tos, ip4_header_t, + tos /* changed member */ ); + } + ip4_0->checksum = ip_csum_fold (sum0); + ip_csum_t sum1 = ip4_1->checksum; + sum1 = ip_csum_update (sum1, 0, ip4_1->length, ip4_header_t, + length /* changed member */ ); + if (PREDICT_FALSE (ip4_1_tos)) + { + sum1 = ip_csum_update (sum1, 0, ip4_1_tos, ip4_header_t, + tos /* changed member */ ); + } + ip4_1->checksum = ip_csum_fold (sum1); + } + /* IPv6 UDP checksum is mandatory */ + else + { + int bogus = 0; + + udp0->checksum = ip6_tcp_udp_icmp_compute_checksum + (vm, b[0], ip6_0, &bogus); + ASSERT (bogus == 0); + if (udp0->checksum == 0) + udp0->checksum = 0xffff; + udp1->checksum = ip6_tcp_udp_icmp_compute_checksum + (vm, b[1], ip6_1, &bogus); + ASSERT (bogus == 0); + if (udp1->checksum == 0) + udp1->checksum = 0xffff; + } + + /* save inner packet flow_hash for load-balance node */ + vnet_buffer (b[0])->ip.flow_hash = flow_hash0; + vnet_buffer (b[1])->ip.flow_hash = flow_hash1; + + vlib_increment_combined_counter (tx_counter, thread_index, + sw_if_index0, 1, len0); + vlib_increment_combined_counter (tx_counter, thread_index, + sw_if_index1, 1, len1); + pkts_encapsulated += 2; + + if (PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED)) + { + vxlan_gbp_encap_trace_t *tr = + vlib_add_trace (vm, node, b[0], sizeof (*tr)); + tr->tunnel_index = t0 - vxm->tunnels; + tr->vni = t0->vni; + tr->sclass = vnet_buffer2 (b[0])->gbp.sclass; + tr->flags = vnet_buffer2 (b[0])->gbp.flags; + } + + if (PREDICT_FALSE (b[1]->flags & VLIB_BUFFER_IS_TRACED)) + { + vxlan_gbp_encap_trace_t *tr = + vlib_add_trace (vm, node, b[1], sizeof (*tr)); + tr->tunnel_index = t1 - vxm->tunnels; + tr->vni = t1->vni; + tr->sclass = vnet_buffer2 (b[1])->gbp.sclass; + tr->flags = vnet_buffer2 (b[1])->gbp.flags; + } + b += 2; + + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, next0, next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0 = to_next[0] = from[0]; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + if (csum_offload && (b[0]->flags & VNET_BUFFER_F_OFFLOAD)) + { + /* Only calculate the non-GSO packet csum offload */ + if ((b[0]->flags & VNET_BUFFER_F_GSO) == 0) + { + vnet_calc_checksums_inline (vm, b[0], + b[0]->flags & + VNET_BUFFER_F_IS_IP4, + b[0]->flags & + VNET_BUFFER_F_IS_IP6); + b[0]->flags &= ~inner_packet_removed_flags; + } + } + + u32 flow_hash0 = vnet_l2_compute_flow_hash (b[0]); + + /* Get next node index and adj index from tunnel next_dpo */ + if (sw_if_index0 != vnet_buffer (b[0])->sw_if_index[VLIB_TX]) + { + sw_if_index0 = vnet_buffer (b[0])->sw_if_index[VLIB_TX]; + vnet_hw_interface_t *hi0 = + vnet_get_sup_hw_interface (vnm, sw_if_index0); + t0 = &vxm->tunnels[hi0->dev_instance]; + /* Note: change to always set next0 if it may be set to drop */ + next0 = t0->next_dpo.dpoi_next_node; + dpoi_idx0 = t0->next_dpo.dpoi_index; + } + vnet_buffer (b[0])->ip.adj_index[VLIB_TX] = dpoi_idx0; + + ASSERT (t0->rewrite_header.data_bytes == underlay_hdr_len); + vnet_rewrite_one_header (*t0, vlib_buffer_get_current (b[0]), + underlay_hdr_len); + + vlib_buffer_advance (b[0], -underlay_hdr_len); + void *underlay0 = vlib_buffer_get_current (b[0]); + + u32 len0 = vlib_buffer_length_in_chain (vm, b[0]); + u16 payload_l0 = clib_host_to_net_u16 (len0 - l3_len); + + vxlan_gbp_header_t *vxlan_gbp0; + udp_header_t *udp0; + ip4_header_t *ip4_0; + qos_bits_t ip4_0_tos = 0; + ip6_header_t *ip6_0; + u8 *l3_0; + if (is_ip4) + { + ip4_vxlan_gbp_header_t *hdr = underlay0; + + /* Fix the IP4 checksum and length */ + ip4_0 = &hdr->ip4; + ip4_0->length = clib_host_to_net_u16 (len0); + + if (PREDICT_FALSE (b[0]->flags & VNET_BUFFER_F_QOS_DATA_VALID)) + { + ip4_0_tos = vnet_buffer2 (b[0])->qos.bits; + ip4_0->tos = ip4_0_tos; + } + + l3_0 = (u8 *) ip4_0; + udp0 = &hdr->udp; + vxlan_gbp0 = &hdr->vxlan_gbp; + } + else /* ip6 path */ + { + ip6_vxlan_gbp_header_t *hdr = underlay0; + + /* Fix IP6 payload length */ + ip6_0 = &hdr->ip6; + ip6_0->payload_length = payload_l0; + + l3_0 = (u8 *) ip6_0; + udp0 = &hdr->udp; + vxlan_gbp0 = &hdr->vxlan_gbp; + } + + /* Fix UDP length and set source port */ + udp0->length = payload_l0; + udp0->src_port = flow_hash0; + + /* set source class and gpflags */ + vxlan_gbp0->gpflags = vnet_buffer2 (b[0])->gbp.flags; + vxlan_gbp0->sclass = + clib_host_to_net_u16 (vnet_buffer2 (b[0])->gbp.sclass); + + if (csum_offload) + { + b[0]->flags |= csum_flags; + vnet_buffer (b[0])->l3_hdr_offset = l3_0 - b[0]->data; + vnet_buffer (b[0])->l4_hdr_offset = (u8 *) udp0 - b[0]->data; + vnet_buffer_offload_flags_set (b[0], + outer_packet_csum_offload_flags); + } + /* IPv4 UDP checksum only if checksum offload is used */ + else if (is_ip4) + { + ip_csum_t sum0 = ip4_0->checksum; + sum0 = ip_csum_update (sum0, 0, ip4_0->length, ip4_header_t, + length /* changed member */ ); + if (PREDICT_FALSE (ip4_0_tos)) + { + sum0 = ip_csum_update (sum0, 0, ip4_0_tos, ip4_header_t, + tos /* changed member */ ); + } + ip4_0->checksum = ip_csum_fold (sum0); + } + /* IPv6 UDP checksum is mandatory */ + else + { + int bogus = 0; + + udp0->checksum = ip6_tcp_udp_icmp_compute_checksum + (vm, b[0], ip6_0, &bogus); + ASSERT (bogus == 0); + if (udp0->checksum == 0) + udp0->checksum = 0xffff; + } + + /* save inner packet flow_hash for load-balance node */ + vnet_buffer (b[0])->ip.flow_hash = flow_hash0; + + vlib_increment_combined_counter (tx_counter, thread_index, + sw_if_index0, 1, len0); + pkts_encapsulated++; + + if (PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED)) + { + vxlan_gbp_encap_trace_t *tr = + vlib_add_trace (vm, node, b[0], sizeof (*tr)); + tr->tunnel_index = t0 - vxm->tunnels; + tr->vni = t0->vni; + tr->sclass = vnet_buffer2 (b[0])->gbp.sclass; + tr->flags = vnet_buffer2 (b[0])->gbp.flags; + } + b += 1; + + 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); + } + + /* Do we still need this now that tunnel tx stats is kept? */ + vlib_node_increment_counter (vm, node->node_index, + VXLAN_GBP_ENCAP_ERROR_ENCAPSULATED, + pkts_encapsulated); + + return from_frame->n_vectors; +} + +VLIB_NODE_FN (vxlan4_gbp_encap_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + /* Disable chksum offload as setup overhead in tx node is not worthwhile + for ip4 header checksum only, unless udp checksum is also required */ + return vxlan_gbp_encap_inline (vm, node, from_frame, /* is_ip4 */ 1, + /* csum_offload */ 0); +} + +VLIB_NODE_FN (vxlan6_gbp_encap_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + /* Enable checksum offload for ip6 as udp checksum is mandatory, */ + return vxlan_gbp_encap_inline (vm, node, from_frame, /* is_ip4 */ 0, + /* csum_offload */ 1); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (vxlan4_gbp_encap_node) = +{ + .name = "vxlan4-gbp-encap", + .vector_size = sizeof (u32), + .format_trace = format_vxlan_gbp_encap_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (vxlan_gbp_encap_error_strings), + .error_strings = vxlan_gbp_encap_error_strings, + .n_next_nodes = VXLAN_GBP_ENCAP_N_NEXT, + .next_nodes = { + [VXLAN_GBP_ENCAP_NEXT_DROP] = "error-drop", + }, +}; + +VLIB_REGISTER_NODE (vxlan6_gbp_encap_node) = +{ + .name = "vxlan6-gbp-encap", + .vector_size = sizeof (u32), + .format_trace = format_vxlan_gbp_encap_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (vxlan_gbp_encap_error_strings), + .error_strings = vxlan_gbp_encap_error_strings, + .n_next_nodes = VXLAN_GBP_ENCAP_N_NEXT, + .next_nodes = { + [VXLAN_GBP_ENCAP_NEXT_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/vnet/vxlan-gbp/test_vxlan_gbp.py b/extras/deprecated/vnet/vxlan-gbp/test_vxlan_gbp.py new file mode 100644 index 00000000000..f332aced7d8 --- /dev/null +++ b/extras/deprecated/vnet/vxlan-gbp/test_vxlan_gbp.py @@ -0,0 +1,293 @@ +#!/usr/bin/env python3 + +import socket +from util import ip4_range, reassemble4_ether +import unittest +from framework import VppTestCase, VppTestRunner +from template_bd import BridgeDomain + +from scapy.layers.l2 import Ether +from scapy.packet import Raw +from scapy.layers.inet import IP, UDP +from scapy.layers.vxlan import VXLAN + +from vpp_ip_route import VppIpRoute, VppRoutePath +from vpp_ip import INVALID_INDEX + + +class TestVxlanGbp(VppTestCase): + """ VXLAN GBP Test Case """ + + @property + def frame_request(self): + """ Ethernet frame modeling a generic request """ + return (Ether(src='00:00:00:00:00:01', dst='00:00:00:00:00:02') / + IP(src='1.2.3.4', dst='4.3.2.1') / + UDP(sport=10000, dport=20000) / + Raw(b'\xa5' * 100)) + + @property + def frame_reply(self): + """ Ethernet frame modeling a generic reply """ + return (Ether(src='00:00:00:00:00:02', dst='00:00:00:00:00:01') / + IP(src='4.3.2.1', dst='1.2.3.4') / + UDP(sport=20000, dport=10000) / + Raw(b'\xa5' * 100)) + + def encapsulate(self, pkt, vni): + """ + Encapsulate the original payload frame by adding VXLAN GBP header with + its UDP, IP and Ethernet fields + """ + return (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) / + UDP(sport=self.dport, dport=self.dport, chksum=0) / + VXLAN(vni=vni, flags=self.flags, gpflags=self.gpflags, + gpid=self.sclass) / pkt) + + def ip_range(self, start, end): + """ range of remote ip's """ + return ip4_range(self.pg0.remote_ip4, start, end) + + def decapsulate(self, pkt): + """ + Decapsulate the original payload frame by removing VXLAN header + """ + # check if is set G and I flag + self.assertEqual(pkt[VXLAN].flags, int('0x88', 16)) + return pkt[VXLAN].payload + + # Method for checking VXLAN GBP encapsulation. + # + def check_encapsulation(self, pkt, vni, local_only=False, mcast_pkt=False): + # TODO: add error messages + # Verify source MAC is VPP_MAC and destination MAC is MY_MAC resolved + # by VPP using ARP. + self.assertEqual(pkt[Ether].src, self.pg0.local_mac) + if not local_only: + if not mcast_pkt: + self.assertEqual(pkt[Ether].dst, self.pg0.remote_mac) + else: + self.assertEqual(pkt[Ether].dst, type(self).mcast_mac) + # Verify VXLAN GBP tunnel source IP is VPP_IP and destination IP is + # MY_IP. + self.assertEqual(pkt[IP].src, self.pg0.local_ip4) + if not local_only: + if not mcast_pkt: + self.assertEqual(pkt[IP].dst, self.pg0.remote_ip4) + else: + self.assertEqual(pkt[IP].dst, type(self).mcast_ip4) + # Verify UDP destination port is VXLAN GBP 48879, source UDP port could + # be arbitrary. + self.assertEqual(pkt[UDP].dport, type(self).dport) + # Verify UDP checksum + self.assert_udp_checksum_valid(pkt) + # Verify VNI + # pkt.show() + self.assertEqual(pkt[VXLAN].vni, vni) + # Verify Source Class + self.assertEqual(pkt[VXLAN].gpid, 0) + + @classmethod + def create_vxlan_gbp_flood_test_bd(cls, vni, n_ucast_tunnels): + # Create 2 ucast vxlan tunnels under bd + ip_range_start = 10 + ip_range_end = ip_range_start + n_ucast_tunnels + next_hop_address = cls.pg0.remote_ip4 + for dest_ip4 in ip4_range(cls.pg0.remote_ip4, + ip_range_start, + ip_range_end): + # add host route so dest_ip4 will not be resolved + rip = VppIpRoute(cls, dest_ip4, 32, + [VppRoutePath(next_hop_address, + INVALID_INDEX)], + register=False) + rip.add_vpp_config() + r = cls.vapi.vxlan_gbp_tunnel_add_del( + tunnel={ + 'src': cls.pg0.local_ip4, + 'dst': dest_ip4, + 'vni': vni, + 'instance': INVALID_INDEX, + 'mcast_sw_if_index': INVALID_INDEX, + 'mode': 1, + }, + is_add=1 + ) + cls.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=r.sw_if_index, + bd_id=vni) + + # Class method to start the VXLAN GBP test case. + # Overrides setUpClass method in VppTestCase class. + # Python try..except statement is used to ensure that the tear down of + # the class will be executed even if exception is raised. + # @param cls The class pointer. + @classmethod + def setUpClass(cls): + super(TestVxlanGbp, cls).setUpClass() + + try: + cls.dport = 48879 + cls.flags = 0x88 + cls.gpflags = 0x0 + cls.sclass = 0 + + # Create 2 pg interfaces. + cls.create_pg_interfaces(range(4)) + for pg in cls.pg_interfaces: + pg.admin_up() + + # Configure IPv4 addresses on VPP pg0. + cls.pg0.config_ip4() + + # Resolve MAC address for VPP's IP address on pg0. + cls.pg0.resolve_arp() + + # Create VXLAN GBP VTEP on VPP pg0, and put vxlan_gbp_tunnel0 and + # pg1 into BD. + cls.single_tunnel_bd = 1 + cls.single_tunnel_vni = 0xabcde + r = cls.vapi.vxlan_gbp_tunnel_add_del( + tunnel={ + 'src': cls.pg0.local_ip4, + 'dst': cls.pg0.remote_ip4, + 'vni': cls.single_tunnel_vni, + 'instance': INVALID_INDEX, + 'mcast_sw_if_index': INVALID_INDEX, + 'mode': 1, + }, + is_add=1 + ) + cls.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=r.sw_if_index, + bd_id=cls.single_tunnel_bd) + cls.vapi.sw_interface_set_l2_bridge( + rx_sw_if_index=cls.pg1.sw_if_index, + bd_id=cls.single_tunnel_bd) + + # Setup vni 2 to test multicast flooding + cls.n_ucast_tunnels = 2 + # Setup vni 3 to test unicast flooding + cls.ucast_flood_bd = 3 + cls.create_vxlan_gbp_flood_test_bd(cls.ucast_flood_bd, + cls.n_ucast_tunnels) + cls.vapi.sw_interface_set_l2_bridge( + rx_sw_if_index=cls.pg3.sw_if_index, + bd_id=cls.ucast_flood_bd) + except Exception: + super(TestVxlanGbp, cls).tearDownClass() + raise + + @classmethod + def tearDownClass(cls): + super(TestVxlanGbp, cls).tearDownClass() + + def assert_eq_pkts(self, pkt1, pkt2): + """ Verify the Ether, IP, UDP, payload are equal in both + packets + """ + self.assertEqual(pkt1[Ether].src, pkt2[Ether].src) + self.assertEqual(pkt1[Ether].dst, pkt2[Ether].dst) + self.assertEqual(pkt1[IP].src, pkt2[IP].src) + self.assertEqual(pkt1[IP].dst, pkt2[IP].dst) + self.assertEqual(pkt1[UDP].sport, pkt2[UDP].sport) + self.assertEqual(pkt1[UDP].dport, pkt2[UDP].dport) + self.assertEqual(pkt1[Raw], pkt2[Raw]) + + def test_decap(self): + """ Decapsulation test + Send encapsulated frames from pg0 + Verify receipt of decapsulated frames on pg1 + """ + encapsulated_pkt = self.encapsulate(self.frame_request, + self.single_tunnel_vni) + + self.pg0.add_stream([encapsulated_pkt, ]) + + self.pg1.enable_capture() + + self.pg_start() + + # Pick first received frame and check if it's the non-encapsulated + # frame + out = self.pg1.get_capture(1) + pkt = out[0] + self.assert_eq_pkts(pkt, self.frame_request) + + def test_encap(self): + """ Encapsulation test + Send frames from pg1 + Verify receipt of encapsulated frames on pg0 + """ + self.pg1.add_stream([self.frame_reply]) + + self.pg0.enable_capture() + + self.pg_start() + + # Pick first received frame and check if it's correctly encapsulated. + out = self.pg0.get_capture(1) + pkt = out[0] + self.check_encapsulation(pkt, self.single_tunnel_vni) + + payload = self.decapsulate(pkt) + self.assert_eq_pkts(payload, self.frame_reply) + + def test_ucast_flood(self): + """ Unicast flood test + Send frames from pg3 + Verify receipt of encapsulated frames on pg0 + """ + self.pg3.add_stream([self.frame_reply]) + + self.pg0.enable_capture() + + self.pg_start() + + # Get packet from each tunnel and assert it's correctly encapsulated. + out = self.pg0.get_capture(self.n_ucast_tunnels) + for pkt in out: + self.check_encapsulation(pkt, self.ucast_flood_bd, True) + payload = self.decapsulate(pkt) + self.assert_eq_pkts(payload, self.frame_reply) + + def test_encap_big_packet(self): + """ Encapsulation test send big frame from pg1 + Verify receipt of encapsulated frames on pg0 + """ + + self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [1500, 0, 0, 0]) + + frame = (Ether(src='00:00:00:00:00:02', dst='00:00:00:00:00:01') / + IP(src='4.3.2.1', dst='1.2.3.4') / + UDP(sport=20000, dport=10000) / + Raw(b'\xa5' * 1450)) + + self.pg1.add_stream([frame]) + + self.pg0.enable_capture() + + self.pg_start() + + # Pick first received frame and check if it's correctly encapsulated. + out = self.pg0.get_capture(2) + pkt = reassemble4_ether(out) + self.check_encapsulation(pkt, self.single_tunnel_vni) + + payload = self.decapsulate(pkt) + self.assert_eq_pkts(payload, frame) + +# Method to define VPP actions before tear down of the test case. +# Overrides tearDown method in VppTestCase class. +# @param self The object pointer. + def tearDown(self): + super(TestVxlanGbp, self).tearDown() + + def show_commands_at_teardown(self): + self.logger.info(self.vapi.cli("show bridge-domain 1 detail")) + self.logger.info(self.vapi.cli("show bridge-domain 3 detail")) + self.logger.info(self.vapi.cli("show vxlan-gbp tunnel")) + self.logger.info(self.vapi.cli("show error")) + + +if __name__ == '__main__': + unittest.main(testRunner=VppTestRunner) diff --git a/extras/deprecated/vnet/vxlan-gbp/vpp_vxlan_gbp_tunnel.py b/extras/deprecated/vnet/vxlan-gbp/vpp_vxlan_gbp_tunnel.py new file mode 100644 index 00000000000..0898bd9f810 --- /dev/null +++ b/extras/deprecated/vnet/vxlan-gbp/vpp_vxlan_gbp_tunnel.py @@ -0,0 +1,75 @@ + +from vpp_interface import VppInterface +from vpp_papi import VppEnum + + +INDEX_INVALID = 0xffffffff + + +def find_vxlan_gbp_tunnel(test, src, dst, vni): + ts = test.vapi.vxlan_gbp_tunnel_dump(INDEX_INVALID) + for t in ts: + if src == str(t.tunnel.src) and \ + dst == str(t.tunnel.dst) and \ + t.tunnel.vni == vni: + return t.tunnel.sw_if_index + return INDEX_INVALID + + +class VppVxlanGbpTunnel(VppInterface): + """ + VPP VXLAN GBP interface + """ + + def __init__(self, test, src, dst, vni, mcast_itf=None, mode=None, + is_ipv6=None, encap_table_id=None, instance=0xffffffff): + """ Create VXLAN-GBP Tunnel interface """ + super(VppVxlanGbpTunnel, self).__init__(test) + self.src = src + self.dst = dst + self.vni = vni + self.mcast_itf = mcast_itf + self.ipv6 = is_ipv6 + self.encap_table_id = encap_table_id + self.instance = instance + if not mode: + self.mode = (VppEnum.vl_api_vxlan_gbp_api_tunnel_mode_t. + VXLAN_GBP_API_TUNNEL_MODE_L2) + else: + self.mode = mode + + def encode(self): + return { + 'src': self.src, + 'dst': self.dst, + 'mode': self.mode, + 'vni': self.vni, + 'mcast_sw_if_index': self.mcast_itf.sw_if_index + if self.mcast_itf else INDEX_INVALID, + 'encap_table_id': self.encap_table_id, + 'instance': self.instance, + } + + def add_vpp_config(self): + reply = self.test.vapi.vxlan_gbp_tunnel_add_del( + is_add=1, + tunnel=self.encode(), + ) + self.set_sw_if_index(reply.sw_if_index) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self.test.vapi.vxlan_gbp_tunnel_add_del( + is_add=0, + tunnel=self.encode(), + ) + + def query_vpp_config(self): + return (INDEX_INVALID != find_vxlan_gbp_tunnel(self._test, + self.src, + self.dst, + self.vni)) + + def object_id(self): + return "vxlan-gbp-%d-%d-%s-%s" % (self.sw_if_index, self.vni, + self.src, self.dst) diff --git a/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.api b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.api new file mode 100644 index 00000000000..68566697000 --- /dev/null +++ b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.api @@ -0,0 +1,100 @@ +/* Hey Emacs use -*- mode: C -*- */ +/* + * 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. + */ + +option version = "1.1.1"; +import "vnet/ip/ip_types.api"; +import "vnet/interface_types.api"; + +enum vxlan_gbp_api_tunnel_mode +{ + VXLAN_GBP_API_TUNNEL_MODE_L2, + VXLAN_GBP_API_TUNNEL_MODE_L3, +}; + +/** \brief Definition of a VXLAN GBP tunnel + @param instance - optional unique custom device instance, else ~0. + @param src - Source IP address + @param dst - Destination IP address, can be multicast + @param mcast_sw_if_index - Interface for multicast destination + @param encap_table_id - Encap route table + @param vni - The VXLAN Network Identifier, uint24 + @param sw_ifindex - Ignored in add message, set in details +*/ +typedef vxlan_gbp_tunnel +{ + u32 instance; + vl_api_address_t src; + vl_api_address_t dst; + vl_api_interface_index_t mcast_sw_if_index; + u32 encap_table_id; + u32 vni; + vl_api_interface_index_t sw_if_index; + vl_api_vxlan_gbp_api_tunnel_mode_t mode; +}; + +/** \brief Create or delete a VXLAN-GBP tunnel + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - Use 1 to create the tunnel, 0 to remove it +*/ +define vxlan_gbp_tunnel_add_del +{ + u32 client_index; + u32 context; + bool is_add [default=true]; + vl_api_vxlan_gbp_tunnel_t tunnel; + option in_progress; +}; + +define vxlan_gbp_tunnel_add_del_reply +{ + u32 context; + i32 retval; + vl_api_interface_index_t sw_if_index; + option in_progress; +}; + +define vxlan_gbp_tunnel_dump +{ + u32 client_index; + u32 context; + vl_api_interface_index_t sw_if_index [default=0xffffffff]; + option in_progress; +}; + +define vxlan_gbp_tunnel_details +{ + u32 context; + vl_api_vxlan_gbp_tunnel_t tunnel; + option in_progress; +}; + +/** \brief Interface set vxlan-bypass request + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param sw_if_index - interface used to reach neighbor + @param is_ipv6 - if non-zero, enable ipv6-vxlan-bypass, else ipv4-vxlan-bypass + @param enable - if non-zero enable, else disable +*/ +autoreply define sw_interface_set_vxlan_gbp_bypass +{ + u32 client_index; + u32 context; + vl_api_interface_index_t sw_if_index; + bool is_ipv6; + bool enable [default=true]; + option in_progress; +}; diff --git a/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.c b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.c new file mode 100644 index 00000000000..eb685b8a40c --- /dev/null +++ b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.c @@ -0,0 +1,1193 @@ +/* + * 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 <vnet/vxlan-gbp/vxlan_gbp.h> +#include <vnet/ip/format.h> +#include <vnet/ip/punt.h> +#include <vnet/fib/fib_entry.h> +#include <vnet/fib/fib_table.h> +#include <vnet/fib/fib_entry_track.h> +#include <vnet/mfib/mfib_table.h> +#include <vnet/adj/adj_mcast.h> +#include <vnet/adj/rewrite.h> +#include <vnet/interface.h> +#include <vlib/vlib.h> + +/** + * @file + * @brief VXLAN GBP. + * + * VXLAN GBP provides the features of vxlan and carry group policy id. + */ +static vlib_punt_hdl_t punt_hdl; + +vxlan_gbp_main_t vxlan_gbp_main; + +u8 * +format_vxlan_gbp_tunnel_mode (u8 * s, va_list * args) +{ + vxlan_gbp_tunnel_mode_t mode = va_arg (*args, vxlan_gbp_tunnel_mode_t); + + switch (mode) + { + case VXLAN_GBP_TUNNEL_MODE_L2: + s = format (s, "L2"); + break; + case VXLAN_GBP_TUNNEL_MODE_L3: + s = format (s, "L3"); + break; + } + return (s); +} + +u8 * +format_vxlan_gbp_tunnel (u8 * s, va_list * args) +{ + vxlan_gbp_tunnel_t *t = va_arg (*args, vxlan_gbp_tunnel_t *); + + s = format (s, + "[%d] instance %d src %U dst %U vni %d fib-idx %d" + " sw-if-idx %d mode %U ", + t->dev_instance, t->user_instance, + format_ip46_address, &t->src, IP46_TYPE_ANY, + format_ip46_address, &t->dst, IP46_TYPE_ANY, + t->vni, t->encap_fib_index, t->sw_if_index, + format_vxlan_gbp_tunnel_mode, t->mode); + + s = format (s, "encap-dpo-idx %d ", t->next_dpo.dpoi_index); + + if (PREDICT_FALSE (ip46_address_is_multicast (&t->dst))) + s = format (s, "mcast-sw-if-idx %d ", t->mcast_sw_if_index); + + return s; +} + +static u8 * +format_vxlan_gbp_name (u8 * s, va_list * args) +{ + u32 dev_instance = va_arg (*args, u32); + vxlan_gbp_main_t *vxm = &vxlan_gbp_main; + vxlan_gbp_tunnel_t *t; + + if (dev_instance == ~0) + return format (s, "<cached-unused>"); + + if (dev_instance >= vec_len (vxm->tunnels)) + return format (s, "<improperly-referenced>"); + + t = pool_elt_at_index (vxm->tunnels, dev_instance); + + return format (s, "vxlan_gbp_tunnel%d", t->user_instance); +} + +static clib_error_t * +vxlan_gbp_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, + u32 flags) +{ + u32 hw_flags = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ? + VNET_HW_INTERFACE_FLAG_LINK_UP : 0; + vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags); + + return /* no error */ 0; +} + +/* *INDENT-OFF* */ +VNET_DEVICE_CLASS (vxlan_gbp_device_class, static) = { + .name = "VXLAN-GBP", + .format_device_name = format_vxlan_gbp_name, + .format_tx_trace = format_vxlan_gbp_encap_trace, + .admin_up_down_function = vxlan_gbp_interface_admin_up_down, +}; +/* *INDENT-ON* */ + +static u8 * +format_vxlan_gbp_header_with_length (u8 * s, va_list * args) +{ + u32 dev_instance = va_arg (*args, u32); + s = format (s, "unimplemented dev %u", dev_instance); + return s; +} + +/* *INDENT-OFF* */ +VNET_HW_INTERFACE_CLASS (vxlan_gbp_hw_class) = { + .name = "VXLAN-GBP", + .format_header = format_vxlan_gbp_header_with_length, + .build_rewrite = default_build_rewrite, +}; +/* *INDENT-ON* */ + +static void +vxlan_gbp_tunnel_restack_dpo (vxlan_gbp_tunnel_t * t) +{ + u8 is_ip4 = ip46_address_is_ip4 (&t->dst); + dpo_id_t dpo = DPO_INVALID; + fib_forward_chain_type_t forw_type = is_ip4 ? + FIB_FORW_CHAIN_TYPE_UNICAST_IP4 : FIB_FORW_CHAIN_TYPE_UNICAST_IP6; + + fib_entry_contribute_forwarding (t->fib_entry_index, forw_type, &dpo); + + /* vxlan_gbp uses the payload hash as the udp source port + * hence the packet's hash is unknown + * skip single bucket load balance dpo's */ + while (DPO_LOAD_BALANCE == dpo.dpoi_type) + { + load_balance_t *lb = load_balance_get (dpo.dpoi_index); + if (lb->lb_n_buckets > 1) + break; + + dpo_copy (&dpo, load_balance_get_bucket_i (lb, 0)); + } + + u32 encap_index = is_ip4 ? + vxlan4_gbp_encap_node.index : vxlan6_gbp_encap_node.index; + dpo_stack_from_node (encap_index, &t->next_dpo, &dpo); + dpo_reset (&dpo); +} + +static vxlan_gbp_tunnel_t * +vxlan_gbp_tunnel_from_fib_node (fib_node_t * node) +{ + ASSERT (FIB_NODE_TYPE_VXLAN_GBP_TUNNEL == node->fn_type); + return ((vxlan_gbp_tunnel_t *) (((char *) node) - + STRUCT_OFFSET_OF (vxlan_gbp_tunnel_t, + node))); +} + +/** + * Function definition to backwalk a FIB node - + * Here we will restack the new dpo of VXLAN DIP to encap node. + */ +static fib_node_back_walk_rc_t +vxlan_gbp_tunnel_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx) +{ + vxlan_gbp_tunnel_restack_dpo (vxlan_gbp_tunnel_from_fib_node (node)); + return (FIB_NODE_BACK_WALK_CONTINUE); +} + +/** + * Function definition to get a FIB node from its index + */ +static fib_node_t * +vxlan_gbp_tunnel_fib_node_get (fib_node_index_t index) +{ + vxlan_gbp_tunnel_t *t; + vxlan_gbp_main_t *vxm = &vxlan_gbp_main; + + t = pool_elt_at_index (vxm->tunnels, index); + + return (&t->node); +} + +/** + * Function definition to inform the FIB node that its last lock has gone. + */ +static void +vxlan_gbp_tunnel_last_lock_gone (fib_node_t * node) +{ + /* + * The VXLAN GBP tunnel is a root of the graph. As such + * it never has children and thus is never locked. + */ + ASSERT (0); +} + +/* + * Virtual function table registered by VXLAN GBP tunnels + * for participation in the FIB object graph. + */ +const static fib_node_vft_t vxlan_gbp_vft = { + .fnv_get = vxlan_gbp_tunnel_fib_node_get, + .fnv_last_lock = vxlan_gbp_tunnel_last_lock_gone, + .fnv_back_walk = vxlan_gbp_tunnel_back_walk, +}; + + +#define foreach_copy_field \ +_(vni) \ +_(mode) \ +_(mcast_sw_if_index) \ +_(encap_fib_index) \ +_(src) \ +_(dst) + +static void +vxlan_gbp_rewrite (vxlan_gbp_tunnel_t * t, bool is_ip6) +{ + union + { + ip4_vxlan_gbp_header_t h4; + ip6_vxlan_gbp_header_t h6; + } h; + int len = is_ip6 ? sizeof h.h6 : sizeof h.h4; + + udp_header_t *udp; + vxlan_gbp_header_t *vxlan_gbp; + /* Fixed portion of the (outer) ip header */ + + clib_memset (&h, 0, sizeof (h)); + if (!is_ip6) + { + ip4_header_t *ip = &h.h4.ip4; + udp = &h.h4.udp, vxlan_gbp = &h.h4.vxlan_gbp; + ip->ip_version_and_header_length = 0x45; + ip->ttl = 254; + ip->protocol = IP_PROTOCOL_UDP; + + ip->src_address = t->src.ip4; + ip->dst_address = t->dst.ip4; + + /* we fix up the ip4 header length and checksum after-the-fact */ + ip->checksum = ip4_header_checksum (ip); + } + else + { + ip6_header_t *ip = &h.h6.ip6; + udp = &h.h6.udp, vxlan_gbp = &h.h6.vxlan_gbp; + ip->ip_version_traffic_class_and_flow_label = + clib_host_to_net_u32 (6 << 28); + ip->hop_limit = 255; + ip->protocol = IP_PROTOCOL_UDP; + + ip->src_address = t->src.ip6; + ip->dst_address = t->dst.ip6; + } + + /* UDP header, randomize src port on something, maybe? */ + udp->src_port = clib_host_to_net_u16 (47789); + udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_vxlan_gbp); + + /* VXLAN header */ + vxlan_gbp_set_header (vxlan_gbp, t->vni); + vnet_rewrite_set_data (*t, &h, len); +} + +static uword +vtep_addr_ref (ip46_address_t * ip) +{ + uword *vtep = ip46_address_is_ip4 (ip) ? + hash_get (vxlan_gbp_main.vtep4, ip->ip4.as_u32) : + hash_get_mem (vxlan_gbp_main.vtep6, &ip->ip6); + if (vtep) + return ++(*vtep); + ip46_address_is_ip4 (ip) ? + hash_set (vxlan_gbp_main.vtep4, ip->ip4.as_u32, 1) : + hash_set_mem_alloc (&vxlan_gbp_main.vtep6, &ip->ip6, 1); + return 1; +} + +static uword +vtep_addr_unref (ip46_address_t * ip) +{ + uword *vtep = ip46_address_is_ip4 (ip) ? + hash_get (vxlan_gbp_main.vtep4, ip->ip4.as_u32) : + hash_get_mem (vxlan_gbp_main.vtep6, &ip->ip6); + ALWAYS_ASSERT (vtep); + if (--(*vtep) != 0) + return *vtep; + ip46_address_is_ip4 (ip) ? + hash_unset (vxlan_gbp_main.vtep4, ip->ip4.as_u32) : + hash_unset_mem_free (&vxlan_gbp_main.vtep6, &ip->ip6); + return 0; +} + +/* *INDENT-OFF* */ +typedef CLIB_PACKED(union +{ + struct + { + fib_node_index_t mfib_entry_index; + adj_index_t mcast_adj_index; + }; + u64 as_u64; +}) mcast_shared_t; +/* *INDENT-ON* */ + +static inline mcast_shared_t +mcast_shared_get (ip46_address_t * ip) +{ + ASSERT (ip46_address_is_multicast (ip)); + uword *p = hash_get_mem (vxlan_gbp_main.mcast_shared, ip); + ALWAYS_ASSERT (p); + mcast_shared_t ret = {.as_u64 = *p }; + return ret; +} + +static inline void +mcast_shared_add (ip46_address_t * dst, fib_node_index_t mfei, adj_index_t ai) +{ + mcast_shared_t new_ep = { + .mcast_adj_index = ai, + .mfib_entry_index = mfei, + }; + + hash_set_mem_alloc (&vxlan_gbp_main.mcast_shared, dst, new_ep.as_u64); +} + +static inline void +mcast_shared_remove (ip46_address_t * dst) +{ + mcast_shared_t ep = mcast_shared_get (dst); + + adj_unlock (ep.mcast_adj_index); + mfib_table_entry_delete_index (ep.mfib_entry_index, MFIB_SOURCE_VXLAN_GBP); + + hash_unset_mem_free (&vxlan_gbp_main.mcast_shared, dst); +} + +inline void +vxlan_gbp_register_udp_ports (void) +{ + vxlan_gbp_main_t *vxm = &vxlan_gbp_main; + + if (vxm->udp_ports_registered == 0) + { + udp_register_dst_port (vxm->vlib_main, UDP_DST_PORT_vxlan_gbp, + vxlan4_gbp_input_node.index, /* is_ip4 */ 1); + udp_register_dst_port (vxm->vlib_main, UDP_DST_PORT_vxlan6_gbp, + vxlan6_gbp_input_node.index, /* is_ip4 */ 0); + } + /* + * Counts the number of vxlan_gbp tunnels + */ + vxm->udp_ports_registered += 1; +} + +inline void +vxlan_gbp_unregister_udp_ports (void) +{ + vxlan_gbp_main_t *vxm = &vxlan_gbp_main; + + ASSERT (vxm->udp_ports_registered != 0); + + if (vxm->udp_ports_registered == 1) + { + udp_unregister_dst_port (vxm->vlib_main, UDP_DST_PORT_vxlan_gbp, + /* is_ip4 */ 1); + udp_unregister_dst_port (vxm->vlib_main, UDP_DST_PORT_vxlan6_gbp, + /* is_ip4 */ 0); + } + + vxm->udp_ports_registered -= 1; +} + +int vnet_vxlan_gbp_tunnel_add_del + (vnet_vxlan_gbp_tunnel_add_del_args_t * a, u32 * sw_if_indexp) +{ + vxlan_gbp_main_t *vxm = &vxlan_gbp_main; + vxlan_gbp_tunnel_t *t = 0; + vnet_main_t *vnm = vxm->vnet_main; + u64 *p; + u32 sw_if_index = ~0; + vxlan4_gbp_tunnel_key_t key4; + vxlan6_gbp_tunnel_key_t key6; + u32 is_ip6 = a->is_ip6; + + int not_found; + if (!is_ip6) + { + key4.key[0] = ip46_address_is_multicast (&a->dst) ? + a->dst.ip4.as_u32 : + a->dst.ip4.as_u32 | (((u64) a->src.ip4.as_u32) << 32); + key4.key[1] = (((u64) a->encap_fib_index) << 32) + | clib_host_to_net_u32 (a->vni << 8); + not_found = + clib_bihash_search_inline_16_8 (&vxm->vxlan4_gbp_tunnel_by_key, + &key4); + p = &key4.value; + } + else + { + key6.key[0] = a->dst.ip6.as_u64[0]; + key6.key[1] = a->dst.ip6.as_u64[1]; + key6.key[2] = (((u64) a->encap_fib_index) << 32) + | clib_host_to_net_u32 (a->vni << 8); + not_found = + clib_bihash_search_inline_24_8 (&vxm->vxlan6_gbp_tunnel_by_key, + &key6); + p = &key6.value; + } + + if (not_found) + p = 0; + + if (a->is_add) + { + l2input_main_t *l2im = &l2input_main; + u32 dev_instance; /* real dev instance tunnel index */ + u32 user_instance; /* request and actual instance number */ + + /* adding a tunnel: tunnel must not already exist */ + if (p) + { + t = pool_elt_at_index (vxm->tunnels, *p); + *sw_if_indexp = t->sw_if_index; + return VNET_API_ERROR_TUNNEL_EXIST; + } + pool_get_aligned (vxm->tunnels, t, CLIB_CACHE_LINE_BYTES); + clib_memset (t, 0, sizeof (*t)); + dev_instance = t - vxm->tunnels; + + /* copy from arg structure */ +#define _(x) t->x = a->x; + foreach_copy_field; +#undef _ + + vxlan_gbp_rewrite (t, is_ip6); + /* + * Reconcile the real dev_instance and a possible requested instance. + */ + user_instance = a->instance; + if (user_instance == ~0) + user_instance = dev_instance; + if (hash_get (vxm->instance_used, user_instance)) + { + pool_put (vxm->tunnels, t); + return VNET_API_ERROR_INSTANCE_IN_USE; + } + hash_set (vxm->instance_used, user_instance, 1); + + t->dev_instance = dev_instance; /* actual */ + t->user_instance = user_instance; /* name */ + + /* copy the key */ + int add_failed; + if (is_ip6) + { + key6.value = (u64) dev_instance; + add_failed = + clib_bihash_add_del_24_8 (&vxm->vxlan6_gbp_tunnel_by_key, &key6, + 1 /*add */ ); + } + else + { + key4.value = (u64) dev_instance; + add_failed = + clib_bihash_add_del_16_8 (&vxm->vxlan4_gbp_tunnel_by_key, &key4, + 1 /*add */ ); + } + + if (add_failed) + { + pool_put (vxm->tunnels, t); + return VNET_API_ERROR_INVALID_REGISTRATION; + } + + vxlan_gbp_register_udp_ports (); + + t->hw_if_index = vnet_register_interface + (vnm, vxlan_gbp_device_class.index, dev_instance, + vxlan_gbp_hw_class.index, dev_instance); + vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, t->hw_if_index); + + /* Set vxlan_gbp tunnel output node */ + u32 encap_index = !is_ip6 ? + vxlan4_gbp_encap_node.index : vxlan6_gbp_encap_node.index; + vnet_set_interface_output_node (vnm, t->hw_if_index, encap_index); + + t->sw_if_index = sw_if_index = hi->sw_if_index; + + if (VXLAN_GBP_TUNNEL_MODE_L3 == t->mode) + { + ip4_sw_interface_enable_disable (t->sw_if_index, 1); + ip6_sw_interface_enable_disable (t->sw_if_index, 1); + } + + vec_validate_init_empty (vxm->tunnel_index_by_sw_if_index, sw_if_index, + ~0); + vxm->tunnel_index_by_sw_if_index[sw_if_index] = dev_instance; + + /* setup l2 input config with l2 feature and bd 0 to drop packet */ + vec_validate (l2im->configs, sw_if_index); + l2im->configs[sw_if_index].feature_bitmap = L2INPUT_FEAT_DROP; + l2im->configs[sw_if_index].bd_index = 0; + + vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index); + si->flags &= ~VNET_SW_INTERFACE_FLAG_HIDDEN; + vnet_sw_interface_set_flags (vnm, sw_if_index, + VNET_SW_INTERFACE_FLAG_ADMIN_UP); + + fib_node_init (&t->node, FIB_NODE_TYPE_VXLAN_GBP_TUNNEL); + fib_prefix_t tun_dst_pfx; + vnet_flood_class_t flood_class = VNET_FLOOD_CLASS_TUNNEL_NORMAL; + + fib_prefix_from_ip46_addr (&t->dst, &tun_dst_pfx); + if (!ip46_address_is_multicast (&t->dst)) + { + /* Unicast tunnel - + * source the FIB entry for the tunnel's destination + * and become a child thereof. The tunnel will then get poked + * when the forwarding for the entry updates, and the tunnel can + * re-stack accordingly + */ + vtep_addr_ref (&t->src); + t->fib_entry_index = fib_entry_track (t->encap_fib_index, + &tun_dst_pfx, + FIB_NODE_TYPE_VXLAN_GBP_TUNNEL, + dev_instance, + &t->sibling_index); + vxlan_gbp_tunnel_restack_dpo (t); + } + else + { + /* Multicast tunnel - + * as the same mcast group can be used for multiple mcast tunnels + * with different VNIs, create the output fib adjacency only if + * it does not already exist + */ + fib_protocol_t fp = fib_ip_proto (is_ip6); + + if (vtep_addr_ref (&t->dst) == 1) + { + fib_node_index_t mfei; + adj_index_t ai; + fib_route_path_t path = { + .frp_proto = fib_proto_to_dpo (fp), + .frp_addr = zero_addr, + .frp_sw_if_index = 0xffffffff, + .frp_fib_index = ~0, + .frp_weight = 0, + .frp_flags = FIB_ROUTE_PATH_LOCAL, + .frp_mitf_flags = MFIB_ITF_FLAG_FORWARD, + }; + const mfib_prefix_t mpfx = { + .fp_proto = fp, + .fp_len = (is_ip6 ? 128 : 32), + .fp_grp_addr = tun_dst_pfx.fp_addr, + }; + + /* + * Setup the (*,G) to receive traffic on the mcast group + * - the forwarding interface is for-us + * - the accepting interface is that from the API + */ + mfib_table_entry_path_update (t->encap_fib_index, &mpfx, + MFIB_SOURCE_VXLAN_GBP, + MFIB_ENTRY_FLAG_NONE, &path); + + path.frp_sw_if_index = a->mcast_sw_if_index; + path.frp_flags = FIB_ROUTE_PATH_FLAG_NONE; + path.frp_mitf_flags = MFIB_ITF_FLAG_ACCEPT; + mfei = mfib_table_entry_path_update ( + t->encap_fib_index, &mpfx, MFIB_SOURCE_VXLAN_GBP, + MFIB_ENTRY_FLAG_NONE, &path); + + /* + * Create the mcast adjacency to send traffic to the group + */ + ai = adj_mcast_add_or_lock (fp, + fib_proto_to_link (fp), + a->mcast_sw_if_index); + + /* + * create a new end-point + */ + mcast_shared_add (&t->dst, mfei, ai); + } + + dpo_id_t dpo = DPO_INVALID; + mcast_shared_t ep = mcast_shared_get (&t->dst); + + /* Stack shared mcast dst mac addr rewrite on encap */ + dpo_set (&dpo, DPO_ADJACENCY_MCAST, + fib_proto_to_dpo (fp), ep.mcast_adj_index); + + dpo_stack_from_node (encap_index, &t->next_dpo, &dpo); + dpo_reset (&dpo); + flood_class = VNET_FLOOD_CLASS_TUNNEL_MASTER; + } + + vnet_get_sw_interface (vnet_get_main (), sw_if_index)->flood_class = + flood_class; + } + else + { + /* deleting a tunnel: tunnel must exist */ + if (!p) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + u32 instance = p[0]; + t = pool_elt_at_index (vxm->tunnels, instance); + + sw_if_index = t->sw_if_index; + vnet_sw_interface_set_flags (vnm, sw_if_index, 0 /* down */ ); + + if (VXLAN_GBP_TUNNEL_MODE_L3 == t->mode) + { + ip4_sw_interface_enable_disable (t->sw_if_index, 0); + ip6_sw_interface_enable_disable (t->sw_if_index, 0); + } + + vxm->tunnel_index_by_sw_if_index[sw_if_index] = ~0; + + if (!is_ip6) + clib_bihash_add_del_16_8 (&vxm->vxlan4_gbp_tunnel_by_key, &key4, + 0 /*del */ ); + else + clib_bihash_add_del_24_8 (&vxm->vxlan6_gbp_tunnel_by_key, &key6, + 0 /*del */ ); + + if (!ip46_address_is_multicast (&t->dst)) + { + vtep_addr_unref (&t->src); + fib_entry_untrack (t->fib_entry_index, t->sibling_index); + } + else if (vtep_addr_unref (&t->dst) == 0) + { + mcast_shared_remove (&t->dst); + } + + vxlan_gbp_unregister_udp_ports (); + vnet_delete_hw_interface (vnm, t->hw_if_index); + hash_unset (vxm->instance_used, t->user_instance); + + fib_node_deinit (&t->node); + pool_put (vxm->tunnels, t); + } + + if (sw_if_indexp) + *sw_if_indexp = sw_if_index; + + return 0; +} + +int +vnet_vxlan_gbp_tunnel_del (u32 sw_if_index) +{ + vxlan_gbp_main_t *vxm = &vxlan_gbp_main; + vxlan_gbp_tunnel_t *t = 0; + u32 ti; + + if (sw_if_index >= vec_len (vxm->tunnel_index_by_sw_if_index)) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + ti = vxm->tunnel_index_by_sw_if_index[sw_if_index]; + if (~0 != ti) + { + t = pool_elt_at_index (vxm->tunnels, ti); + + vnet_vxlan_gbp_tunnel_add_del_args_t args = { + .is_add = 0, + .is_ip6 = !ip46_address_is_ip4 (&t->src), + .vni = t->vni, + .src = t->src, + .dst = t->dst, + .instance = ~0, + }; + + return (vnet_vxlan_gbp_tunnel_add_del (&args, NULL)); + } + + return VNET_API_ERROR_NO_SUCH_ENTRY; +} + +static uword +get_decap_next_for_node (u32 node_index, u32 ipv4_set) +{ + vxlan_gbp_main_t *vxm = &vxlan_gbp_main; + vlib_main_t *vm = vxm->vlib_main; + uword input_node = (ipv4_set) ? vxlan4_gbp_input_node.index : + vxlan6_gbp_input_node.index; + + return vlib_node_add_next (vm, input_node, node_index); +} + +static uword +unformat_decap_next (unformat_input_t * input, va_list * args) +{ + u32 *result = va_arg (*args, u32 *); + u32 ipv4_set = va_arg (*args, int); + vxlan_gbp_main_t *vxm = &vxlan_gbp_main; + vlib_main_t *vm = vxm->vlib_main; + u32 node_index; + u32 tmp; + + if (unformat (input, "l2")) + *result = VXLAN_GBP_INPUT_NEXT_L2_INPUT; + else if (unformat (input, "node %U", unformat_vlib_node, vm, &node_index)) + *result = get_decap_next_for_node (node_index, ipv4_set); + else if (unformat (input, "%d", &tmp)) + *result = tmp; + else + return 0; + return 1; +} + +static clib_error_t * +vxlan_gbp_tunnel_add_del_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + ip46_address_t src = ip46_address_initializer, dst = + ip46_address_initializer; + vxlan_gbp_tunnel_mode_t mode = VXLAN_GBP_TUNNEL_MODE_L2; + u8 is_add = 1; + u8 src_set = 0; + u8 dst_set = 0; + u8 grp_set = 0; + u8 ipv4_set = 0; + u8 ipv6_set = 0; + u32 instance = ~0; + u32 encap_fib_index = 0; + u32 mcast_sw_if_index = ~0; + u32 decap_next_index = VXLAN_GBP_INPUT_NEXT_L2_INPUT; + u32 vni = 0; + u32 table_id; + clib_error_t *parse_error = NULL; + + /* 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, "instance %d", &instance)) + ; + else if (unformat (line_input, "src %U", + unformat_ip46_address, &src, IP46_TYPE_ANY)) + { + src_set = 1; + ip46_address_is_ip4 (&src) ? (ipv4_set = 1) : (ipv6_set = 1); + } + else if (unformat (line_input, "dst %U", + unformat_ip46_address, &dst, IP46_TYPE_ANY)) + { + dst_set = 1; + ip46_address_is_ip4 (&dst) ? (ipv4_set = 1) : (ipv6_set = 1); + } + else if (unformat (line_input, "group %U %U", + unformat_ip46_address, &dst, IP46_TYPE_ANY, + unformat_vnet_sw_interface, + vnet_get_main (), &mcast_sw_if_index)) + { + grp_set = dst_set = 1; + ip46_address_is_ip4 (&dst) ? (ipv4_set = 1) : (ipv6_set = 1); + } + else if (unformat (line_input, "encap-vrf-id %d", &table_id)) + { + encap_fib_index = + fib_table_find (fib_ip_proto (ipv6_set), table_id); + } + else if (unformat (line_input, "decap-next %U", unformat_decap_next, + &decap_next_index, ipv4_set)) + ; + else if (unformat (line_input, "vni %d", &vni)) + ; + else + { + parse_error = clib_error_return (0, "parse error: '%U'", + format_unformat_error, line_input); + break; + } + } + + unformat_free (line_input); + + if (parse_error) + return parse_error; + + if (encap_fib_index == ~0) + return clib_error_return (0, "nonexistent encap-vrf-id %d", table_id); + + if (src_set == 0) + return clib_error_return (0, "tunnel src address not specified"); + + if (dst_set == 0) + return clib_error_return (0, "tunnel dst address not specified"); + + if (grp_set && !ip46_address_is_multicast (&dst)) + return clib_error_return (0, "tunnel group address not multicast"); + + if (grp_set == 0 && ip46_address_is_multicast (&dst)) + return clib_error_return (0, "dst address must be unicast"); + + if (grp_set && mcast_sw_if_index == ~0) + return clib_error_return (0, "tunnel nonexistent multicast device"); + + if (ipv4_set && ipv6_set) + return clib_error_return (0, "both IPv4 and IPv6 addresses specified"); + + if (ip46_address_cmp (&src, &dst) == 0) + return clib_error_return (0, "src and dst addresses are identical"); + + if (decap_next_index == ~0) + return clib_error_return (0, "next node not found"); + + if (vni == 0) + return clib_error_return (0, "vni not specified"); + + if (vni >> 24) + return clib_error_return (0, "vni %d out of range", vni); + + vnet_vxlan_gbp_tunnel_add_del_args_t a = { + .is_add = is_add, + .is_ip6 = ipv6_set, + .instance = instance, +#define _(x) .x = x, + foreach_copy_field +#undef _ + }; + + u32 tunnel_sw_if_index; + int rv = vnet_vxlan_gbp_tunnel_add_del (&a, &tunnel_sw_if_index); + + switch (rv) + { + case 0: + if (is_add) + vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name, + vnet_get_main (), tunnel_sw_if_index); + break; + + case VNET_API_ERROR_TUNNEL_EXIST: + return clib_error_return (0, "tunnel already exists..."); + + case VNET_API_ERROR_NO_SUCH_ENTRY: + return clib_error_return (0, "tunnel does not exist..."); + + case VNET_API_ERROR_INSTANCE_IN_USE: + return clib_error_return (0, "Instance is in use"); + + default: + return clib_error_return + (0, "vnet_vxlan_gbp_tunnel_add_del returned %d", rv); + } + + return 0; +} + +/*? + * Add or delete a VXLAN Tunnel. + * + * VXLAN provides the features needed to allow L2 bridge domains (BDs) + * to span multiple servers. This is done by building an L2 overlay on + * top of an L3 network underlay using VXLAN tunnels. + * + * This makes it possible for servers to be co-located in the same data + * center or be separated geographically as long as they are reachable + * through the underlay L3 network. + * + * You can refer to this kind of L2 overlay bridge domain as a VXLAN + * (Virtual eXtensible VLAN) segment. + * + * @cliexpar + * Example of how to create a VXLAN Tunnel: + * @cliexcmd{create vxlan_gbp tunnel src 10.0.3.1 dst 10.0.3.3 vni 13 encap-vrf-id 7} + * Example of how to create a VXLAN Tunnel with a known name, vxlan_gbp_tunnel42: + * @cliexcmd{create vxlan_gbp tunnel src 10.0.3.1 dst 10.0.3.3 instance 42} + * Example of how to create a multicast VXLAN Tunnel with a known name, vxlan_gbp_tunnel23: + * @cliexcmd{create vxlan_gbp tunnel src 10.0.3.1 group 239.1.1.1 GigabitEthernet0/8/0 instance 23} + * Example of how to delete a VXLAN Tunnel: + * @cliexcmd{create vxlan_gbp tunnel src 10.0.3.1 dst 10.0.3.3 vni 13 del} + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (create_vxlan_gbp_tunnel_command, static) = { + .path = "create vxlan-gbp tunnel", + .short_help = + "create vxlan-gbp tunnel src <local-vtep-addr>" + " {dst <remote-vtep-addr>|group <mcast-vtep-addr> <intf-name>} vni <nn>" + " [instance <id>]" + " [encap-vrf-id <nn>] [decap-next [l2|node <name>]] [del]", + .function = vxlan_gbp_tunnel_add_del_command_fn, +}; +/* *INDENT-ON* */ + +static clib_error_t * +show_vxlan_gbp_tunnel_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + vxlan_gbp_main_t *vxm = &vxlan_gbp_main; + vxlan_gbp_tunnel_t *t; + int raw = 0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "raw")) + raw = 1; + else + return clib_error_return (0, "parse error: '%U'", + format_unformat_error, input); + } + + if (pool_elts (vxm->tunnels) == 0) + vlib_cli_output (vm, "No vxlan-gbp tunnels configured..."); + +/* *INDENT-OFF* */ + pool_foreach (t, vxm->tunnels) + { + vlib_cli_output (vm, "%U", format_vxlan_gbp_tunnel, t); + } +/* *INDENT-ON* */ + + if (raw) + { + vlib_cli_output (vm, "Raw IPv4 Hash Table:\n%U\n", + format_bihash_16_8, &vxm->vxlan4_gbp_tunnel_by_key, + 1 /* verbose */ ); + vlib_cli_output (vm, "Raw IPv6 Hash Table:\n%U\n", + format_bihash_24_8, &vxm->vxlan6_gbp_tunnel_by_key, + 1 /* verbose */ ); + } + + return 0; +} + +/*? + * Display all the VXLAN Tunnel entries. + * + * @cliexpar + * Example of how to display the VXLAN Tunnel entries: + * @cliexstart{show vxlan_gbp tunnel} + * [0] src 10.0.3.1 dst 10.0.3.3 vni 13 encap_fib_index 0 sw_if_index 5 decap_next l2 + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (show_vxlan_gbp_tunnel_command, static) = { + .path = "show vxlan-gbp tunnel", + .short_help = "show vxlan-gbp tunnel [raw]", + .function = show_vxlan_gbp_tunnel_command_fn, +}; +/* *INDENT-ON* */ + + +void +vnet_int_vxlan_gbp_bypass_mode (u32 sw_if_index, u8 is_ip6, u8 is_enable) +{ + if (is_ip6) + vnet_feature_enable_disable ("ip6-unicast", "ip6-vxlan-gbp-bypass", + sw_if_index, is_enable, 0, 0); + else + vnet_feature_enable_disable ("ip4-unicast", "ip4-vxlan-gbp-bypass", + sw_if_index, is_enable, 0, 0); +} + + +static clib_error_t * +set_ip_vxlan_gbp_bypass (u32 is_ip6, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + vnet_main_t *vnm = vnet_get_main (); + clib_error_t *error = 0; + u32 sw_if_index, is_enable; + + sw_if_index = ~0; + is_enable = 1; + + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat_user + (line_input, unformat_vnet_sw_interface, vnm, &sw_if_index)) + ; + else if (unformat (line_input, "del")) + is_enable = 0; + else + { + error = unformat_parse_error (line_input); + goto done; + } + } + + if (~0 == sw_if_index) + { + error = clib_error_return (0, "unknown interface `%U'", + format_unformat_error, line_input); + goto done; + } + + vnet_int_vxlan_gbp_bypass_mode (sw_if_index, is_ip6, is_enable); + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +set_ip4_vxlan_gbp_bypass (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + return set_ip_vxlan_gbp_bypass (0, input, cmd); +} + +/*? + * This command adds the 'ip4-vxlan-gbp-bypass' graph node for a given + * interface. By adding the IPv4 vxlan_gbp-bypass graph node to an interface, + * the node checks for and validate input vxlan_gbp packet and bypass + * ip4-lookup, ip4-local, ip4-udp-lookup nodes to speedup vxlan_gbp packet + * forwarding. This node will cause extra overhead to for non-vxlan_gbp packets + * which is kept at a minimum. + * + * @cliexpar + * @parblock + * Example of graph node before ip4-vxlan_gbp-bypass is enabled: + * @cliexstart{show vlib graph ip4-vxlan_gbp-bypass} + * Name Next Previous + * ip4-vxlan-gbp-bypass error-drop [0] + * vxlan4-gbp-input [1] + * ip4-lookup [2] + * @cliexend + * + * Example of how to enable ip4-vxlan-gbp-bypass on an interface: + * @cliexcmd{set interface ip vxlan-gbp-bypass GigabitEthernet2/0/0} + * + * Example of graph node after ip4-vxlan-gbp-bypass is enabled: + * @cliexstart{show vlib graph ip4-vxlan-gbp-bypass} + * Name Next Previous + * ip4-vxlan-gbp-bypass error-drop [0] ip4-input + * vxlan4-gbp-input [1] ip4-input-no-checksum + * ip4-lookup [2] + * @cliexend + * + * Example of how to display the feature enabled on an interface: + * @cliexstart{show ip interface features GigabitEthernet2/0/0} + * IP feature paths configured on GigabitEthernet2/0/0... + * ... + * ipv4 unicast: + * ip4-vxlan-gbp-bypass + * ip4-lookup + * ... + * @cliexend + * + * Example of how to disable ip4-vxlan-gbp-bypass on an interface: + * @cliexcmd{set interface ip vxlan-gbp-bypass GigabitEthernet2/0/0 del} + * @endparblock +?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (set_interface_ip_vxlan_gbp_bypass_command, static) = { + .path = "set interface ip vxlan-gbp-bypass", + .function = set_ip4_vxlan_gbp_bypass, + .short_help = "set interface ip vxlan-gbp-bypass <interface> [del]", +}; +/* *INDENT-ON* */ + +static clib_error_t * +set_ip6_vxlan_gbp_bypass (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + return set_ip_vxlan_gbp_bypass (1, input, cmd); +} + +/*? + * This command adds the 'ip6-vxlan-gbp-bypass' graph node for a given + * interface. By adding the IPv6 vxlan-gbp-bypass graph node to an interface, + * the node checks for and validate input vxlan_gbp packet and bypass + * ip6-lookup, ip6-local, ip6-udp-lookup nodes to speedup vxlan_gbp packet + * forwarding. This node will cause extra overhead to for non-vxlan packets + * which is kept at a minimum. + * + * @cliexpar + * @parblock + * Example of graph node before ip6-vxlan-gbp-bypass is enabled: + * @cliexstart{show vlib graph ip6-vxlan-gbp-bypass} + * Name Next Previous + * ip6-vxlan-gbp-bypass error-drop [0] + * vxlan6-gbp-input [1] + * ip6-lookup [2] + * @cliexend + * + * Example of how to enable ip6-vxlan-gbp-bypass on an interface: + * @cliexcmd{set interface ip6 vxlan-gbp-bypass GigabitEthernet2/0/0} + * + * Example of graph node after ip6-vxlan-gbp-bypass is enabled: + * @cliexstart{show vlib graph ip6-vxlan-gbp-bypass} + * Name Next Previous + * ip6-vxlan-gbp-bypass error-drop [0] ip6-input + * vxlan6-gbp-input [1] ip4-input-no-checksum + * ip6-lookup [2] + * @cliexend + * + * Example of how to display the feature enabled on an interface: + * @cliexstart{show ip interface features GigabitEthernet2/0/0} + * IP feature paths configured on GigabitEthernet2/0/0... + * ... + * ipv6 unicast: + * ip6-vxlan-gbp-bypass + * ip6-lookup + * ... + * @cliexend + * + * Example of how to disable ip6-vxlan-gbp-bypass on an interface: + * @cliexcmd{set interface ip6 vxlan-gbp-bypass GigabitEthernet2/0/0 del} + * @endparblock +?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (set_interface_ip6_vxlan_gbp_bypass_command, static) = { + .path = "set interface ip6 vxlan-gbp-bypass", + .function = set_ip6_vxlan_gbp_bypass, + .short_help = "set interface ip6 vxlan-gbp-bypass <interface> [del]", +}; +/* *INDENT-ON* */ + +#define VXLAN_GBP_HASH_NUM_BUCKETS (2 * 1024) +#define VXLAN_GBP_HASH_MEMORY_SIZE (1 << 20) + +clib_error_t * +vxlan_gbp_init (vlib_main_t * vm) +{ + vxlan_gbp_main_t *vxm = &vxlan_gbp_main; + + vxm->vnet_main = vnet_get_main (); + vxm->vlib_main = vm; + + /* initialize the ip6 hash */ + clib_bihash_init_16_8 (&vxm->vxlan4_gbp_tunnel_by_key, "vxlan4-gbp", + VXLAN_GBP_HASH_NUM_BUCKETS, + VXLAN_GBP_HASH_MEMORY_SIZE); + clib_bihash_init_24_8 (&vxm->vxlan6_gbp_tunnel_by_key, "vxlan6-gbp", + VXLAN_GBP_HASH_NUM_BUCKETS, + VXLAN_GBP_HASH_MEMORY_SIZE); + vxm->vtep6 = hash_create_mem (0, sizeof (ip6_address_t), sizeof (uword)); + vxm->mcast_shared = hash_create_mem (0, + sizeof (ip46_address_t), + sizeof (mcast_shared_t)); + + fib_node_register_type (FIB_NODE_TYPE_VXLAN_GBP_TUNNEL, &vxlan_gbp_vft); + + punt_hdl = vlib_punt_client_register ("vxlan-gbp"); + + vlib_punt_reason_alloc (punt_hdl, "VXLAN-GBP-no-such-v4-tunnel", NULL, NULL, + &vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP4], + VNET_PUNT_REASON_F_IP4_PACKET, + format_vnet_punt_reason_flags); + vlib_punt_reason_alloc (punt_hdl, "VXLAN-GBP-no-such-v6-tunnel", NULL, NULL, + &vxm->punt_no_such_tunnel[FIB_PROTOCOL_IP6], + VNET_PUNT_REASON_F_IP6_PACKET, + format_vnet_punt_reason_flags); + + return (0); +} + +/* *INDENT-OFF* */ +VLIB_INIT_FUNCTION (vxlan_gbp_init) = +{ + .runs_after = VLIB_INITS("punt_init"), +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.h b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.h new file mode 100644 index 00000000000..fe93587cb00 --- /dev/null +++ b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.h @@ -0,0 +1,250 @@ +/* + * 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 included_vnet_vxlan_gbp_h +#define included_vnet_vxlan_gbp_h + +#include <vppinfra/error.h> +#include <vppinfra/hash.h> +#include <vppinfra/bihash_16_8.h> +#include <vppinfra/bihash_24_8.h> +#include <vnet/vnet.h> +#include <vnet/ip/ip.h> +#include <vnet/l2/l2_input.h> +#include <vnet/l2/l2_output.h> +#include <vnet/l2/l2_bd.h> +#include <vnet/ethernet/ethernet.h> +#include <vnet/vxlan-gbp/vxlan_gbp_packet.h> +#include <vnet/ip/ip4_packet.h> +#include <vnet/ip/ip6_packet.h> +#include <vnet/udp/udp_local.h> +#include <vnet/udp/udp_packet.h> +#include <vnet/dpo/dpo.h> +#include <vnet/adj/adj_types.h> + +/* *INDENT-OFF* */ +typedef CLIB_PACKED (struct { + ip4_header_t ip4; /* 20 bytes */ + udp_header_t udp; /* 8 bytes */ + vxlan_gbp_header_t vxlan_gbp; /* 8 bytes */ +}) ip4_vxlan_gbp_header_t; + +typedef CLIB_PACKED (struct { + ip6_header_t ip6; /* 40 bytes */ + udp_header_t udp; /* 8 bytes */ + vxlan_gbp_header_t vxlan_gbp; /* 8 bytes */ +}) ip6_vxlan_gbp_header_t; +/* *INDENT-ON* */ + +/* +* Key fields: remote ip, vni on incoming VXLAN packet +* all fields in NET byte order +*/ +typedef clib_bihash_kv_16_8_t vxlan4_gbp_tunnel_key_t; + +/* +* Key fields: remote ip, vni and fib index on incoming VXLAN packet +* ip, vni fields in NET byte order +* fib index field in host byte order +*/ +typedef clib_bihash_kv_24_8_t vxlan6_gbp_tunnel_key_t; + +typedef enum vxlan_gbp_tunnel_mode_t_ +{ + VXLAN_GBP_TUNNEL_MODE_L2, + VXLAN_GBP_TUNNEL_MODE_L3, +} vxlan_gbp_tunnel_mode_t; + +extern u8 *format_vxlan_gbp_tunnel_mode (u8 * s, va_list * args); + +typedef struct +{ + /* Required for pool_get_aligned */ + CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); + + /* FIB DPO for IP forwarding of VXLAN encap packet */ + dpo_id_t next_dpo; + + /* flags */ + u16 flags; + + /* vxlan VNI in HOST byte order */ + u32 vni; + + /* tunnel src and dst addresses */ + ip46_address_t src; + ip46_address_t dst; + + /* mcast packet output intfc index (used only if dst is mcast) */ + u32 mcast_sw_if_index; + + /* The FIB index for src/dst addresses */ + u32 encap_fib_index; + + /* vnet intfc index */ + u32 sw_if_index; + u32 hw_if_index; + + /** Next node after VxLAN-GBP encap */ + uword encap_next_node; + + /** + * Tunnel mode. + * L2 tunnels decap to L2 path, L3 tunnels to the L3 path + */ + vxlan_gbp_tunnel_mode_t mode; + + /** + * Linkage into the FIB object graph + */ + fib_node_t node; + + /* + * The FIB entry for (depending on VXLAN-GBP tunnel is unicast or mcast) + * sending unicast VXLAN-GBP encap packets or receiving mcast VXLAN-GBP packets + */ + fib_node_index_t fib_entry_index; + adj_index_t mcast_adj_index; + + /** + * The tunnel is a child of the FIB entry for its destination. This is + * so it receives updates when the forwarding information for that entry + * changes. + * The tunnels sibling index on the FIB entry's dependency list. + */ + u32 sibling_index; + + u32 dev_instance; /* Real device instance in tunnel vector */ + u32 user_instance; /* Instance name being shown to user */ + + + VNET_DECLARE_REWRITE; +} vxlan_gbp_tunnel_t; + +#define foreach_vxlan_gbp_input_next \ + _(DROP, "error-drop") \ + _(PUNT, "punt-dispatch") \ + _(L2_INPUT, "l2-input") \ + _(IP4_INPUT, "ip4-input") \ + _(IP6_INPUT, "ip6-input") + +typedef enum +{ +#define _(s,n) VXLAN_GBP_INPUT_NEXT_##s, + foreach_vxlan_gbp_input_next +#undef _ + VXLAN_GBP_INPUT_N_NEXT, +} vxlan_gbp_input_next_t; + +typedef enum +{ +#define vxlan_gbp_error(n,s) VXLAN_GBP_ERROR_##n, +#include <vnet/vxlan-gbp/vxlan_gbp_error.def> +#undef vxlan_gbp_error + VXLAN_GBP_N_ERROR, +} vxlan_gbp_input_error_t; + +/** + * Call back function packets that do not match a configured tunnel + */ +typedef vxlan_gbp_input_next_t (*vxlan_bgp_no_tunnel_t) (vlib_buffer_t * b, + u32 thread_index, + u8 is_ip6); + +typedef struct +{ + /* vector of encap tunnel instances */ + vxlan_gbp_tunnel_t *tunnels; + + /* lookup tunnel by key */ + clib_bihash_16_8_t vxlan4_gbp_tunnel_by_key; /* keyed on ipv4.dst + fib + vni */ + clib_bihash_24_8_t vxlan6_gbp_tunnel_by_key; /* keyed on ipv6.dst + fib + vni */ + + /* local VTEP IPs ref count used by vxlan-bypass node to check if + received VXLAN packet DIP matches any local VTEP address */ + uword *vtep4; /* local ip4 VTEPs keyed on their ip4 addr */ + uword *vtep6; /* local ip6 VTEPs keyed on their ip6 addr */ + + /* mcast shared info */ + uword *mcast_shared; /* keyed on mcast ip46 addr */ + + /* Mapping from sw_if_index to tunnel index */ + u32 *tunnel_index_by_sw_if_index; + + /* On demand udp port registration */ + u32 udp_ports_registered; + + /* convenience */ + vlib_main_t *vlib_main; + vnet_main_t *vnet_main; + + /* Record used instances */ + uword *instance_used; + + /** + * Punt reasons for no such tunnel + */ + vlib_punt_reason_t punt_no_such_tunnel[FIB_PROTOCOL_IP_MAX]; +} vxlan_gbp_main_t; + +extern vxlan_gbp_main_t vxlan_gbp_main; + +extern vlib_node_registration_t vxlan4_gbp_input_node; +extern vlib_node_registration_t vxlan6_gbp_input_node; +extern vlib_node_registration_t vxlan4_gbp_encap_node; +extern vlib_node_registration_t vxlan6_gbp_encap_node; +extern void vxlan_gbp_register_udp_ports (void); +extern void vxlan_gbp_unregister_udp_ports (void); + +u8 *format_vxlan_gbp_encap_trace (u8 * s, va_list * args); + +typedef struct +{ + u8 is_add; + u8 is_ip6; + u32 instance; + vxlan_gbp_tunnel_mode_t mode; + ip46_address_t src, dst; + u32 mcast_sw_if_index; + u32 encap_fib_index; + u32 vni; +} vnet_vxlan_gbp_tunnel_add_del_args_t; + +int vnet_vxlan_gbp_tunnel_add_del + (vnet_vxlan_gbp_tunnel_add_del_args_t * a, u32 * sw_if_indexp); +int vnet_vxlan_gbp_tunnel_del (u32 sw_if_indexp); + +void vnet_int_vxlan_gbp_bypass_mode (u32 sw_if_index, u8 is_ip6, + u8 is_enable); + +always_inline u32 +vxlan_gbp_tunnel_by_sw_if_index (u32 sw_if_index) +{ + vxlan_gbp_main_t *vxm = &vxlan_gbp_main; + + if (sw_if_index >= vec_len (vxm->tunnel_index_by_sw_if_index)) + return ~0; + + return (vxm->tunnel_index_by_sw_if_index[sw_if_index]); +} + +#endif /* included_vnet_vxlan_gbp_h */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_api.c b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_api.c new file mode 100644 index 00000000000..a3f2246f463 --- /dev/null +++ b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_api.c @@ -0,0 +1,217 @@ +/* + *------------------------------------------------------------------ + * vxlan_gbp_api.c - vxlan gbp api + * + * 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 <vnet/vnet.h> +#include <vlibmemory/api.h> + +#include <vnet/interface.h> +#include <vnet/api_errno.h> +#include <vnet/feature/feature.h> +#include <vnet/vxlan-gbp/vxlan_gbp.h> +#include <vnet/fib/fib_table.h> +#include <vnet/ip/ip_types_api.h> +#include <vnet/format_fns.h> + +#include <vxlan-gbp/vxlan_gbp.api_enum.h> +#include <vxlan-gbp/vxlan_gbp.api_types.h> + +#define REPLY_MSG_ID_BASE msg_id_base +#include <vlibapi/api_helper_macros.h> + +static u16 msg_id_base; + +static void + vl_api_sw_interface_set_vxlan_gbp_bypass_t_handler + (vl_api_sw_interface_set_vxlan_gbp_bypass_t * mp) +{ + vl_api_sw_interface_set_vxlan_gbp_bypass_reply_t *rmp; + int rv = 0; + u32 sw_if_index = ntohl (mp->sw_if_index); + + VALIDATE_SW_IF_INDEX (mp); + + vnet_int_vxlan_gbp_bypass_mode (sw_if_index, mp->is_ipv6, mp->enable); + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_SW_INTERFACE_SET_VXLAN_GBP_BYPASS_REPLY); +} + +static int +vxlan_gbp_tunnel_mode_decode (vl_api_vxlan_gbp_api_tunnel_mode_t in, + vxlan_gbp_tunnel_mode_t * out) +{ + in = clib_net_to_host_u32 (in); + + switch (in) + { + case VXLAN_GBP_API_TUNNEL_MODE_L2: + *out = VXLAN_GBP_TUNNEL_MODE_L2; + return (0); + case VXLAN_GBP_API_TUNNEL_MODE_L3: + *out = VXLAN_GBP_TUNNEL_MODE_L3; + return (0); + } + return (VNET_API_ERROR_INVALID_VALUE); +} + +static void vl_api_vxlan_gbp_tunnel_add_del_t_handler + (vl_api_vxlan_gbp_tunnel_add_del_t * mp) +{ + vl_api_vxlan_gbp_tunnel_add_del_reply_t *rmp; + vxlan_gbp_tunnel_mode_t mode; + ip46_address_t src, dst; + ip46_type_t itype; + int rv = 0; + u32 sw_if_index = ~0; + u32 fib_index; + + itype = ip_address_decode (&mp->tunnel.src, &src); + itype = ip_address_decode (&mp->tunnel.dst, &dst); + + fib_index = fib_table_find (fib_proto_from_ip46 (itype), + ntohl (mp->tunnel.encap_table_id)); + if (fib_index == ~0) + { + rv = VNET_API_ERROR_NO_SUCH_FIB; + goto out; + } + + rv = vxlan_gbp_tunnel_mode_decode (mp->tunnel.mode, &mode); + + if (rv) + goto out; + + vnet_vxlan_gbp_tunnel_add_del_args_t a = { + .is_add = mp->is_add, + .is_ip6 = (itype == IP46_TYPE_IP6), + .instance = ntohl (mp->tunnel.instance), + .mcast_sw_if_index = ntohl (mp->tunnel.mcast_sw_if_index), + .encap_fib_index = fib_index, + .vni = ntohl (mp->tunnel.vni), + .dst = dst, + .src = src, + .mode = mode, + }; + + /* Check src & dst are different */ + if (ip46_address_cmp (&a.dst, &a.src) == 0) + { + rv = VNET_API_ERROR_SAME_SRC_DST; + goto out; + } + if (ip46_address_is_multicast (&a.dst) && + !vnet_sw_if_index_is_api_valid (a.mcast_sw_if_index)) + { + rv = VNET_API_ERROR_INVALID_SW_IF_INDEX; + goto out; + } + + rv = vnet_vxlan_gbp_tunnel_add_del (&a, &sw_if_index); + +out: + /* *INDENT-OFF* */ + REPLY_MACRO2(VL_API_VXLAN_GBP_TUNNEL_ADD_DEL_REPLY, + ({ + rmp->sw_if_index = ntohl (sw_if_index); + })); + /* *INDENT-ON* */ +} + +static void send_vxlan_gbp_tunnel_details + (vxlan_gbp_tunnel_t * t, vl_api_registration_t * reg, u32 context) +{ + vl_api_vxlan_gbp_tunnel_details_t *rmp; + ip46_type_t itype = (ip46_address_is_ip4 (&t->dst) ? + IP46_TYPE_IP4 : IP46_TYPE_IP6); + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + clib_memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_VXLAN_GBP_TUNNEL_DETAILS + REPLY_MSG_ID_BASE); + + ip_address_encode (&t->src, itype, &rmp->tunnel.src); + ip_address_encode (&t->dst, itype, &rmp->tunnel.dst); + rmp->tunnel.encap_table_id = + fib_table_get_table_id (t->encap_fib_index, fib_proto_from_ip46 (itype)); + + rmp->tunnel.instance = htonl (t->user_instance); + rmp->tunnel.mcast_sw_if_index = htonl (t->mcast_sw_if_index); + rmp->tunnel.vni = htonl (t->vni); + rmp->tunnel.sw_if_index = htonl (t->sw_if_index); + rmp->context = context; + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void vl_api_vxlan_gbp_tunnel_dump_t_handler + (vl_api_vxlan_gbp_tunnel_dump_t * mp) +{ + vl_api_registration_t *reg; + vxlan_gbp_main_t *vxm = &vxlan_gbp_main; + vxlan_gbp_tunnel_t *t; + u32 sw_if_index; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + sw_if_index = ntohl (mp->sw_if_index); + + if (~0 == sw_if_index) + { + /* *INDENT-OFF* */ + pool_foreach (t, vxm->tunnels) + { + send_vxlan_gbp_tunnel_details(t, reg, mp->context); + } + /* *INDENT-ON* */ + } + else + { + if ((sw_if_index >= vec_len (vxm->tunnel_index_by_sw_if_index)) || + (~0 == vxm->tunnel_index_by_sw_if_index[sw_if_index])) + { + return; + } + t = &vxm->tunnels[vxm->tunnel_index_by_sw_if_index[sw_if_index]]; + send_vxlan_gbp_tunnel_details (t, reg, mp->context); + } +} + +#include <vxlan-gbp/vxlan_gbp.api.c> +static clib_error_t * +vxlan_gbp_api_hookup (vlib_main_t * vm) +{ + /* + * Set up the (msg_name, crc, message-id) table + */ + msg_id_base = setup_message_id_table (); + + return 0; +} + +VLIB_API_INIT_FUNCTION (vxlan_gbp_api_hookup); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_error.def b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_error.def new file mode 100644 index 00000000000..43ad4dac064 --- /dev/null +++ b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_error.def @@ -0,0 +1,17 @@ +/* + * 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. + */ +vxlan_gbp_error (DECAPSULATED, "good packets decapsulated") +vxlan_gbp_error (NO_SUCH_TUNNEL, "no such tunnel packets") +vxlan_gbp_error (BAD_FLAGS, "packets with bad flags field in vxlan gbp header") diff --git a/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_packet.c b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_packet.c new file mode 100644 index 00000000000..01c7a19bfb9 --- /dev/null +++ b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_packet.c @@ -0,0 +1,60 @@ +/* + * 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 <vnet/vxlan-gbp/vxlan_gbp_packet.h> + +u8 * +format_vxlan_gbp_header_flags (u8 * s, va_list * args) +{ + vxlan_gbp_flags_t flags = va_arg (*args, int); + + if (VXLAN_GBP_FLAGS_NONE == flags) + { + s = format (s, "None"); + } +#define _(n,f) { \ + if (VXLAN_GBP_FLAGS_##f & flags) \ + s = format (s, #f); \ + } + foreach_vxlan_gbp_flags +#undef _ + return (s); +} + +u8 * +format_vxlan_gbp_header_gpflags (u8 * s, va_list * args) +{ + vxlan_gbp_gpflags_t flags = va_arg (*args, int); + + if (VXLAN_GBP_GPFLAGS_NONE == flags) + { + s = format (s, "None"); + } +#define _(n,f) { \ + if (VXLAN_GBP_GPFLAGS_##f & flags) \ + s = format (s, #f); \ + } + foreach_vxlan_gbp_gpflags +#undef _ + return (s); +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_packet.h b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_packet.h new file mode 100644 index 00000000000..e655b333b89 --- /dev/null +++ b/extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_packet.h @@ -0,0 +1,173 @@ +/* + * 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 __included_vxlan_gbp_packet_h__ +#define __included_vxlan_gbp_packet_h__ 1 + +#include <vlib/vlib.h> + +/* + * From draft-smith-vxlan-group-policy-04.txt + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |G|R|R|R|I|R|R|R|R|D|E|S|A|R|R|R| Group Policy ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | VXLAN Network Identifier (VNI) | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * G bit: Bit 0 of the initial word is defined as the G (Group Based + * Policy Extension) bit. + * + * I bit: where the I flag MUST be set to 1 for a valid + * VXLAN Network ID (VNI). + * + * D bit: Bit 9 of the initial word is defined as the Don't Learn bit. + * When set, this bit indicates that the egress VTEP MUST NOT learn the + * source address of the encapsulated frame. + * + * E bit: Bit 10 of the initial word is defined as the bounce packet. + * When set, this bit indicates that packet is bounced and must be + * dropped. + * + * S bit: Bit 11 of the initial word is defined as the source policy + * applied bit. + * + * A bit: Bit 12 of the initial word is defined as the A (Policy + * Applied) bit. This bit is only defined as the A bit when the G bit + * is set to 1. + * + * A = 1 indicates that the group policy has already been applied to + * this packet. Policies MUST NOT be applied by devices when the A + * bit is set. + * + * A = 0 indicates that the group policy has not been applied to this + * packet. Group policies MUST be applied by devices when the A bit + * is set to 0 and the destination Group has been determined. + * Devices that apply the Group policy MUST set the A bit to 1 after + * the policy has been applied. + * + * Group Policy ID: 16 bit identifier that indicates the source TSI + * Group membership being encapsulated by VXLAN. Its value is source + * class id. + * + * FOR INTERNAL USE ONLY + * R bit: Bit 12 of the initial word is defined as the reflection bit + * Set on packet rx checked on tx and dropped if set. this prevents + * packets recieved on an iVXLAN tunnel being reflected back to + * another. + */ + +typedef struct +{ + union + { + struct + { + union + { + struct + { + u8 flag_g_i; + u8 gpflags; + }; + u16 flags; + }; + u16 sclass; + }; + u32 flags_sclass_as_u32; + }; + u32 vni_reserved; +} vxlan_gbp_header_t; + +#define foreach_vxlan_gbp_flags \ + _ (0x80, G) \ + _ (0x08, I) + +typedef enum +{ + VXLAN_GBP_FLAGS_NONE = 0, +#define _(n,f) VXLAN_GBP_FLAGS_##f = n, + foreach_vxlan_gbp_flags +#undef _ +} __attribute__ ((packed)) vxlan_gbp_flags_t; + +#define VXLAN_GBP_FLAGS_GI (VXLAN_GBP_FLAGS_G|VXLAN_GBP_FLAGS_I) + +#define foreach_vxlan_gbp_gpflags \ +_ (0x40, D) \ +_ (0x20, E) \ +_ (0x10, S) \ +_ (0x08, A) \ +_ (0x04, R) + +typedef enum +{ + VXLAN_GBP_GPFLAGS_NONE = 0, +#define _(n,f) VXLAN_GBP_GPFLAGS_##f = n, + foreach_vxlan_gbp_gpflags +#undef _ +} __attribute__ ((packed)) vxlan_gbp_gpflags_t; + +static inline u32 +vxlan_gbp_get_vni (vxlan_gbp_header_t * h) +{ + u32 vni_reserved_host_byte_order; + + vni_reserved_host_byte_order = clib_net_to_host_u32 (h->vni_reserved); + return vni_reserved_host_byte_order >> 8; +} + +static inline u16 +vxlan_gbp_get_sclass (vxlan_gbp_header_t * h) +{ + u16 sclass_host_byte_order; + + sclass_host_byte_order = clib_net_to_host_u16 (h->sclass); + return sclass_host_byte_order; +} + +static inline vxlan_gbp_gpflags_t +vxlan_gbp_get_gpflags (vxlan_gbp_header_t * h) +{ + return h->gpflags; +} + +static inline vxlan_gbp_flags_t +vxlan_gbp_get_flags (vxlan_gbp_header_t * h) +{ + return h->flag_g_i; +} + +static inline void +vxlan_gbp_set_header (vxlan_gbp_header_t * h, u32 vni) +{ + h->vni_reserved = clib_host_to_net_u32 (vni << 8); + h->flags_sclass_as_u32 = 0; + h->flag_g_i = VXLAN_GBP_FLAGS_I | VXLAN_GBP_FLAGS_G; +} + +extern u8 *format_vxlan_gbp_header_flags (u8 * s, va_list * args); +extern u8 *format_vxlan_gbp_header_gpflags (u8 * s, va_list * args); + +#endif /* __included_vxlan_gbp_packet_h__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ |