From 3be9164f80ebb1929488bbe2ee4da1d35f42c1a2 Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Mon, 14 Sep 2020 07:41:48 +0000 Subject: misc: deprecate gbp and its dependents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Type: improvement Signed-off-by: Neale Ranns Signed-off-by: BenoƮt Ganne Change-Id: I2f30a4f04fd9a8635ce2d259b5fd5b0c85cee8c3 --- MAINTAINERS | 16 - extras/deprecated/plugins/gbp/CMakeLists.txt | 54 + extras/deprecated/plugins/gbp/gbp.api | 470 ++ extras/deprecated/plugins/gbp/gbp.h | 80 + extras/deprecated/plugins/gbp/gbp_api.c | 1154 ++++ extras/deprecated/plugins/gbp/gbp_bridge_domain.c | 503 ++ extras/deprecated/plugins/gbp/gbp_bridge_domain.h | 156 + extras/deprecated/plugins/gbp/gbp_classify.c | 71 + extras/deprecated/plugins/gbp/gbp_classify.h | 94 + extras/deprecated/plugins/gbp/gbp_classify_node.c | 628 +++ extras/deprecated/plugins/gbp/gbp_contract.c | 819 +++ extras/deprecated/plugins/gbp/gbp_contract.h | 362 ++ extras/deprecated/plugins/gbp/gbp_endpoint.c | 1597 ++++++ extras/deprecated/plugins/gbp/gbp_endpoint.h | 376 ++ extras/deprecated/plugins/gbp/gbp_endpoint_group.c | 402 ++ extras/deprecated/plugins/gbp/gbp_endpoint_group.h | 166 + extras/deprecated/plugins/gbp/gbp_ext_itf.c | 293 + extras/deprecated/plugins/gbp/gbp_ext_itf.h | 92 + extras/deprecated/plugins/gbp/gbp_fwd.c | 56 + extras/deprecated/plugins/gbp/gbp_fwd_dpo.c | 306 + extras/deprecated/plugins/gbp/gbp_fwd_dpo.h | 62 + extras/deprecated/plugins/gbp/gbp_fwd_node.c | 163 + extras/deprecated/plugins/gbp/gbp_itf.c | 574 ++ extras/deprecated/plugins/gbp/gbp_itf.h | 97 + extras/deprecated/plugins/gbp/gbp_learn.c | 76 + extras/deprecated/plugins/gbp/gbp_learn.h | 63 + extras/deprecated/plugins/gbp/gbp_learn_node.c | 718 +++ extras/deprecated/plugins/gbp/gbp_policy.c | 79 + extras/deprecated/plugins/gbp/gbp_policy.h | 57 + extras/deprecated/plugins/gbp/gbp_policy_dpo.c | 420 ++ extras/deprecated/plugins/gbp/gbp_policy_dpo.h | 121 + extras/deprecated/plugins/gbp/gbp_policy_node.c | 341 ++ extras/deprecated/plugins/gbp/gbp_recirc.c | 292 + extras/deprecated/plugins/gbp/gbp_recirc.h | 88 + extras/deprecated/plugins/gbp/gbp_route_domain.c | 447 ++ extras/deprecated/plugins/gbp/gbp_route_domain.h | 84 + extras/deprecated/plugins/gbp/gbp_scanner.c | 136 + extras/deprecated/plugins/gbp/gbp_scanner.h | 30 + extras/deprecated/plugins/gbp/gbp_subnet.c | 598 ++ extras/deprecated/plugins/gbp/gbp_subnet.h | 53 + extras/deprecated/plugins/gbp/gbp_types.h | 36 + extras/deprecated/plugins/gbp/gbp_vxlan.c | 654 +++ extras/deprecated/plugins/gbp/gbp_vxlan.h | 135 + extras/deprecated/plugins/gbp/gbp_vxlan_node.c | 218 + extras/deprecated/plugins/gbp/test_gbp.py | 5926 ++++++++++++++++++++ extras/deprecated/plugins/l2e/CMakeLists.txt | 28 + extras/deprecated/plugins/l2e/l2e.api | 39 + extras/deprecated/plugins/l2e/l2e.c | 198 + extras/deprecated/plugins/l2e/l2e.h | 84 + extras/deprecated/plugins/l2e/l2e_api.c | 89 + extras/deprecated/plugins/l2e/l2e_node.c | 283 + extras/deprecated/vnet/vxlan-gbp/decap.c | 1050 ++++ extras/deprecated/vnet/vxlan-gbp/dir.dox | 24 + extras/deprecated/vnet/vxlan-gbp/encap.c | 601 ++ extras/deprecated/vnet/vxlan-gbp/test_vxlan_gbp.py | 293 + .../vnet/vxlan-gbp/vpp_vxlan_gbp_tunnel.py | 75 + extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.api | 100 + extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.c | 1193 ++++ extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.h | 250 + extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_api.c | 217 + .../deprecated/vnet/vxlan-gbp/vxlan_gbp_error.def | 17 + .../deprecated/vnet/vxlan-gbp/vxlan_gbp_packet.c | 60 + .../deprecated/vnet/vxlan-gbp/vxlan_gbp_packet.h | 173 + src/plugins/gbp/CMakeLists.txt | 54 - src/plugins/gbp/gbp.api | 470 -- src/plugins/gbp/gbp.h | 80 - src/plugins/gbp/gbp_api.c | 1154 ---- src/plugins/gbp/gbp_bridge_domain.c | 503 -- src/plugins/gbp/gbp_bridge_domain.h | 156 - src/plugins/gbp/gbp_classify.c | 71 - src/plugins/gbp/gbp_classify.h | 94 - src/plugins/gbp/gbp_classify_node.c | 628 --- src/plugins/gbp/gbp_contract.c | 819 --- src/plugins/gbp/gbp_contract.h | 362 -- src/plugins/gbp/gbp_endpoint.c | 1597 ------ src/plugins/gbp/gbp_endpoint.h | 376 -- src/plugins/gbp/gbp_endpoint_group.c | 402 -- src/plugins/gbp/gbp_endpoint_group.h | 166 - src/plugins/gbp/gbp_ext_itf.c | 293 - src/plugins/gbp/gbp_ext_itf.h | 92 - src/plugins/gbp/gbp_fwd.c | 56 - src/plugins/gbp/gbp_fwd_dpo.c | 306 - src/plugins/gbp/gbp_fwd_dpo.h | 62 - src/plugins/gbp/gbp_fwd_node.c | 163 - src/plugins/gbp/gbp_itf.c | 574 -- src/plugins/gbp/gbp_itf.h | 97 - src/plugins/gbp/gbp_learn.c | 76 - src/plugins/gbp/gbp_learn.h | 63 - src/plugins/gbp/gbp_learn_node.c | 718 --- src/plugins/gbp/gbp_policy.c | 79 - src/plugins/gbp/gbp_policy.h | 57 - src/plugins/gbp/gbp_policy_dpo.c | 420 -- src/plugins/gbp/gbp_policy_dpo.h | 121 - src/plugins/gbp/gbp_policy_node.c | 341 -- src/plugins/gbp/gbp_recirc.c | 292 - src/plugins/gbp/gbp_recirc.h | 88 - src/plugins/gbp/gbp_route_domain.c | 447 -- src/plugins/gbp/gbp_route_domain.h | 84 - src/plugins/gbp/gbp_scanner.c | 136 - src/plugins/gbp/gbp_scanner.h | 30 - src/plugins/gbp/gbp_subnet.c | 598 -- src/plugins/gbp/gbp_subnet.h | 53 - src/plugins/gbp/gbp_types.h | 36 - src/plugins/gbp/gbp_vxlan.c | 654 --- src/plugins/gbp/gbp_vxlan.h | 135 - src/plugins/gbp/gbp_vxlan_node.c | 218 - src/plugins/l2e/CMakeLists.txt | 28 - src/plugins/l2e/l2e.api | 39 - src/plugins/l2e/l2e.c | 198 - src/plugins/l2e/l2e.h | 84 - src/plugins/l2e/l2e_api.c | 89 - src/plugins/l2e/l2e_node.c | 283 - src/vnet/CMakeLists.txt | 24 - src/vnet/l2/l2_fwd.c | 3 +- src/vnet/l2/l2_input.h | 7 - src/vnet/l2/l2_input_node.c | 5 +- src/vnet/l2/l2_output.h | 3 - src/vnet/vxlan-gbp/decap.c | 1050 ---- src/vnet/vxlan-gbp/dir.dox | 24 - src/vnet/vxlan-gbp/encap.c | 601 -- src/vnet/vxlan-gbp/vxlan_gbp.api | 100 - src/vnet/vxlan-gbp/vxlan_gbp.c | 1193 ---- src/vnet/vxlan-gbp/vxlan_gbp.h | 250 - src/vnet/vxlan-gbp/vxlan_gbp_api.c | 217 - src/vnet/vxlan-gbp/vxlan_gbp_error.def | 17 - src/vnet/vxlan-gbp/vxlan_gbp_packet.c | 60 - src/vnet/vxlan-gbp/vxlan_gbp_packet.h | 173 - test/patches/scapy-2.3.3/vxlan.patch | 1 - test/patches/scapy-2.4/vxlan.py.patch | 12 - test/test_dvr.py | 153 - test/test_gbp.py | 5926 -------------------- test/test_vxlan_gbp.py | 293 - test/vpp_papi_provider.py | 28 - test/vpp_vxlan_gbp_tunnel.py | 75 - 134 files changed, 23924 insertions(+), 24170 deletions(-) create mode 100644 extras/deprecated/plugins/gbp/CMakeLists.txt create mode 100644 extras/deprecated/plugins/gbp/gbp.api create mode 100644 extras/deprecated/plugins/gbp/gbp.h create mode 100644 extras/deprecated/plugins/gbp/gbp_api.c create mode 100644 extras/deprecated/plugins/gbp/gbp_bridge_domain.c create mode 100644 extras/deprecated/plugins/gbp/gbp_bridge_domain.h create mode 100644 extras/deprecated/plugins/gbp/gbp_classify.c create mode 100644 extras/deprecated/plugins/gbp/gbp_classify.h create mode 100644 extras/deprecated/plugins/gbp/gbp_classify_node.c create mode 100644 extras/deprecated/plugins/gbp/gbp_contract.c create mode 100644 extras/deprecated/plugins/gbp/gbp_contract.h create mode 100644 extras/deprecated/plugins/gbp/gbp_endpoint.c create mode 100644 extras/deprecated/plugins/gbp/gbp_endpoint.h create mode 100644 extras/deprecated/plugins/gbp/gbp_endpoint_group.c create mode 100644 extras/deprecated/plugins/gbp/gbp_endpoint_group.h create mode 100644 extras/deprecated/plugins/gbp/gbp_ext_itf.c create mode 100644 extras/deprecated/plugins/gbp/gbp_ext_itf.h create mode 100644 extras/deprecated/plugins/gbp/gbp_fwd.c create mode 100644 extras/deprecated/plugins/gbp/gbp_fwd_dpo.c create mode 100644 extras/deprecated/plugins/gbp/gbp_fwd_dpo.h create mode 100644 extras/deprecated/plugins/gbp/gbp_fwd_node.c create mode 100644 extras/deprecated/plugins/gbp/gbp_itf.c create mode 100644 extras/deprecated/plugins/gbp/gbp_itf.h create mode 100644 extras/deprecated/plugins/gbp/gbp_learn.c create mode 100644 extras/deprecated/plugins/gbp/gbp_learn.h create mode 100644 extras/deprecated/plugins/gbp/gbp_learn_node.c create mode 100644 extras/deprecated/plugins/gbp/gbp_policy.c create mode 100644 extras/deprecated/plugins/gbp/gbp_policy.h create mode 100644 extras/deprecated/plugins/gbp/gbp_policy_dpo.c create mode 100644 extras/deprecated/plugins/gbp/gbp_policy_dpo.h create mode 100644 extras/deprecated/plugins/gbp/gbp_policy_node.c create mode 100644 extras/deprecated/plugins/gbp/gbp_recirc.c create mode 100644 extras/deprecated/plugins/gbp/gbp_recirc.h create mode 100644 extras/deprecated/plugins/gbp/gbp_route_domain.c create mode 100644 extras/deprecated/plugins/gbp/gbp_route_domain.h create mode 100644 extras/deprecated/plugins/gbp/gbp_scanner.c create mode 100644 extras/deprecated/plugins/gbp/gbp_scanner.h create mode 100644 extras/deprecated/plugins/gbp/gbp_subnet.c create mode 100644 extras/deprecated/plugins/gbp/gbp_subnet.h create mode 100644 extras/deprecated/plugins/gbp/gbp_types.h create mode 100644 extras/deprecated/plugins/gbp/gbp_vxlan.c create mode 100644 extras/deprecated/plugins/gbp/gbp_vxlan.h create mode 100644 extras/deprecated/plugins/gbp/gbp_vxlan_node.c create mode 100644 extras/deprecated/plugins/gbp/test_gbp.py create mode 100644 extras/deprecated/plugins/l2e/CMakeLists.txt create mode 100644 extras/deprecated/plugins/l2e/l2e.api create mode 100644 extras/deprecated/plugins/l2e/l2e.c create mode 100644 extras/deprecated/plugins/l2e/l2e.h create mode 100644 extras/deprecated/plugins/l2e/l2e_api.c create mode 100644 extras/deprecated/plugins/l2e/l2e_node.c create mode 100644 extras/deprecated/vnet/vxlan-gbp/decap.c create mode 100644 extras/deprecated/vnet/vxlan-gbp/dir.dox create mode 100644 extras/deprecated/vnet/vxlan-gbp/encap.c create mode 100644 extras/deprecated/vnet/vxlan-gbp/test_vxlan_gbp.py create mode 100644 extras/deprecated/vnet/vxlan-gbp/vpp_vxlan_gbp_tunnel.py create mode 100644 extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.api create mode 100644 extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.c create mode 100644 extras/deprecated/vnet/vxlan-gbp/vxlan_gbp.h create mode 100644 extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_api.c create mode 100644 extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_error.def create mode 100644 extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_packet.c create mode 100644 extras/deprecated/vnet/vxlan-gbp/vxlan_gbp_packet.h delete mode 100644 src/plugins/gbp/CMakeLists.txt delete mode 100644 src/plugins/gbp/gbp.api delete mode 100644 src/plugins/gbp/gbp.h delete mode 100644 src/plugins/gbp/gbp_api.c delete mode 100644 src/plugins/gbp/gbp_bridge_domain.c delete mode 100644 src/plugins/gbp/gbp_bridge_domain.h delete mode 100644 src/plugins/gbp/gbp_classify.c delete mode 100644 src/plugins/gbp/gbp_classify.h delete mode 100644 src/plugins/gbp/gbp_classify_node.c delete mode 100644 src/plugins/gbp/gbp_contract.c delete mode 100644 src/plugins/gbp/gbp_contract.h delete mode 100644 src/plugins/gbp/gbp_endpoint.c delete mode 100644 src/plugins/gbp/gbp_endpoint.h delete mode 100644 src/plugins/gbp/gbp_endpoint_group.c delete mode 100644 src/plugins/gbp/gbp_endpoint_group.h delete mode 100644 src/plugins/gbp/gbp_ext_itf.c delete mode 100644 src/plugins/gbp/gbp_ext_itf.h delete mode 100644 src/plugins/gbp/gbp_fwd.c delete mode 100644 src/plugins/gbp/gbp_fwd_dpo.c delete mode 100644 src/plugins/gbp/gbp_fwd_dpo.h delete mode 100644 src/plugins/gbp/gbp_fwd_node.c delete mode 100644 src/plugins/gbp/gbp_itf.c delete mode 100644 src/plugins/gbp/gbp_itf.h delete mode 100644 src/plugins/gbp/gbp_learn.c delete mode 100644 src/plugins/gbp/gbp_learn.h delete mode 100644 src/plugins/gbp/gbp_learn_node.c delete mode 100644 src/plugins/gbp/gbp_policy.c delete mode 100644 src/plugins/gbp/gbp_policy.h delete mode 100644 src/plugins/gbp/gbp_policy_dpo.c delete mode 100644 src/plugins/gbp/gbp_policy_dpo.h delete mode 100644 src/plugins/gbp/gbp_policy_node.c delete mode 100644 src/plugins/gbp/gbp_recirc.c delete mode 100644 src/plugins/gbp/gbp_recirc.h delete mode 100644 src/plugins/gbp/gbp_route_domain.c delete mode 100644 src/plugins/gbp/gbp_route_domain.h delete mode 100644 src/plugins/gbp/gbp_scanner.c delete mode 100644 src/plugins/gbp/gbp_scanner.h delete mode 100644 src/plugins/gbp/gbp_subnet.c delete mode 100644 src/plugins/gbp/gbp_subnet.h delete mode 100644 src/plugins/gbp/gbp_types.h delete mode 100644 src/plugins/gbp/gbp_vxlan.c delete mode 100644 src/plugins/gbp/gbp_vxlan.h delete mode 100644 src/plugins/gbp/gbp_vxlan_node.c delete mode 100644 src/plugins/l2e/CMakeLists.txt delete mode 100644 src/plugins/l2e/l2e.api delete mode 100644 src/plugins/l2e/l2e.c delete mode 100644 src/plugins/l2e/l2e.h delete mode 100644 src/plugins/l2e/l2e_api.c delete mode 100644 src/plugins/l2e/l2e_node.c delete mode 100644 src/vnet/vxlan-gbp/decap.c delete mode 100644 src/vnet/vxlan-gbp/dir.dox delete mode 100644 src/vnet/vxlan-gbp/encap.c delete mode 100644 src/vnet/vxlan-gbp/vxlan_gbp.api delete mode 100644 src/vnet/vxlan-gbp/vxlan_gbp.c delete mode 100644 src/vnet/vxlan-gbp/vxlan_gbp.h delete mode 100644 src/vnet/vxlan-gbp/vxlan_gbp_api.c delete mode 100644 src/vnet/vxlan-gbp/vxlan_gbp_error.def delete mode 100644 src/vnet/vxlan-gbp/vxlan_gbp_packet.c delete mode 100644 src/vnet/vxlan-gbp/vxlan_gbp_packet.h delete mode 100644 test/patches/scapy-2.4/vxlan.py.patch delete mode 100644 test/test_gbp.py delete mode 100644 test/test_vxlan_gbp.py delete mode 100644 test/vpp_vxlan_gbp_tunnel.py diff --git a/MAINTAINERS b/MAINTAINERS index ddcacae84ca..fc0439514f4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -286,12 +286,6 @@ I: vxlan-gpe M: Hongjun Ni F: src/vnet/vxlan-gpe/ -VNET VXLAN-GBP -I: vxlan-gbp -M: Mohsin Kazmi -M: Neale Ranns -F: src/vnet/vxlan-gbp/ - VNET IPIP I: ipip Y: src/vnet/ipip/FEATURE.yaml @@ -433,11 +427,6 @@ I: builtinurl M: Dave Barach F: src/plugins/builtinurl/ -Plugin - Group Based Policy (GBP) -I: gbp -M: Neale Ranns -F: src/plugins/gbp/ - Plugin - GTPU I: gtpu M: Hongjun Ni @@ -693,11 +682,6 @@ I: svs M: Neale Ranns F: src/plugins/svs/ -Plugin - LAN Emulation -I: l2e -M: Neale Ranns -F: src/plugins/l2e/ - Plugin - IPv6 Connection Tracker I: ct6 M: Dave Barach 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 + +#include +#include +#include +#include +#include + +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 +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* define message IDs */ +#include +#include +#include +#include +#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 +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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/** + * 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 bvi [uu-fwd ] [bm-flood ] [flags ]} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_bridge_domain_cli_node, static) = { + .path = "gbp bridge-domain", + .short_help = "gbp bridge-domain [del] bd bvi [uu-fwd ] [bm-flood ] [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 +#include + +#include +#include + +/** + * 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 +#include +#include + +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 +#include + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * 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 +#include +#include +#include +#include + +#include +#include + +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 dst-epg acl-index } + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_contract_cli_node, static) = +{ + .path = "gbp contract", + .short_help = + "gbp contract [del] src-epg dst-epg acl-index ", + .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 ] [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 +#include + +#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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 | [add] sclass ip mac [flags ]} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_endpoint_cli_node, static) = { + .path = "gbp endpoint", + .short_help = "gbp endpoint del | [add] sclass ip mac [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 +#include +#include +#include + +#include +#include +#include +#include + +/** + * 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 +#include +#include +#include +#include + +#include +#include +#include + +/** + * 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 bd rd [sclass ] []} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_endpoint_group_cli_node, static) = { + .path = "gbp endpoint-group", + .short_help = "gbp endpoint-group [del] epg bd rd [sclass ] []", + .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 +#include + +#include + +/** + * 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 +#include +#include +#include + +/** + * 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 bd } + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_itf_anon_l3out_add_del_node, static) = { + .path = "gbp ext-itf", + .short_help = "gbp ext-itf [del] bd rd [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 + +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 +#include +#include + +/** + * 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 +#include + +#include + + +#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 + +/** + * @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 +#include + +#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 +#include +#include + +#include + +#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 +#include +#include + + +#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 +#include +#include + +#include + +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 + +#include + +/** + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 +#include +#include + +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 + +/** + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 +#include +#include +#include + +/** + * @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 +#include +#include +#include +#include +#include +#include + +#include +#include + +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 +#include +#include +#include + +#include +#include + +#include + +/** + * 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 +#include +#include + +/** + * 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 +#include + +#include +#include + +/** + * 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 ip4-table-id ip6-table-id [ip4-uu ] [ip6-uu ]} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_route_domain_cli_node, static) = { + .path = "gbp route-domain", + .short_help = "gbp route-domain [del] rd ip4-table-id ip6-table-id [ip4-uu ] [ip6-uu ]", + .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 + +#include +#include + +/** + * 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 +#include +#include + +/** + * 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 + +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 +#include +#include +#include + +#include +#include + +/** + * a key for the DB + */ +typedef struct gbp_subnet_key_t_ +{ + fib_prefix_t gsk_pfx; + u32 gsk_fib_index; +} gbp_subnet_key_t; + +/** + * Subnet + */ +typedef struct gbp_subnet_t_ +{ + gbp_subnet_key_t *gs_key; + gbp_subnet_type_t gs_type; + index_t gs_rd; + + union + { + struct + { + sclass_t gs_sclass; + u32 gs_sw_if_index; + } gs_stitched_external; + struct + { + sclass_t gs_sclass; + } gs_l3_out; + }; + + fib_node_index_t gs_fei; +} gbp_subnet_t; + +/** + * A DB of the subnets; key={pfx,fib-index} + */ +uword *gbp_subnet_db; + +/** + * pool of subnets + */ +gbp_subnet_t *gbp_subnet_pool; + +static fib_source_t gbp_fib_source; + +static index_t +gbp_subnet_db_find (u32 fib_index, const fib_prefix_t * pfx) +{ + gbp_subnet_key_t key = { + .gsk_pfx = *pfx, + .gsk_fib_index = fib_index, + }; + uword *p; + + p = hash_get_mem (gbp_subnet_db, &key); + + if (NULL != p) + return p[0]; + + return (INDEX_INVALID); +} + +static void +gbp_subnet_db_add (u32 fib_index, const fib_prefix_t * pfx, gbp_subnet_t * gs) +{ + gbp_subnet_key_t *key; + + key = clib_mem_alloc (sizeof (*key)); + + clib_memcpy (&(key->gsk_pfx), pfx, sizeof (*pfx)); + key->gsk_fib_index = fib_index; + + hash_set_mem (gbp_subnet_db, key, (gs - gbp_subnet_pool)); + + gs->gs_key = key; +} + +static void +gbp_subnet_db_del (gbp_subnet_t * gs) +{ + hash_unset_mem (gbp_subnet_db, gs->gs_key); + + clib_mem_free (gs->gs_key); + gs->gs_key = NULL; +} + + +static int +gbp_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 prefix type [] [sclass ]} + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gbp_subnet_add_del, static) = { + .path = "gbp subnet", + .short_help = "gbp subnet [del] rd prefix type [] [sclass ]\n", + .function = gbp_subnet_add_del_cli, +}; +/* *INDENT-ON* */ + + + +void +gbp_subnet_walk (gbp_subnet_cb_t cb, void *ctx) +{ + 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 + +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 + +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 +#include +#include +#include + +#include +#include +#include +#include + +/** + * 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 +#include + +#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 +#include +#include +#include +#include + +#include +#include +#include + +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 '' % ( + 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 +#include +#include +#include + +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 [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 [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 +#include + +/** + * 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 +#include + +#include +#include +#include + +#include + +#include +#include + +/* define message IDs */ +#include +#include + +#include + +#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 +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 +#include +#include + +#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 + +#include + +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 +#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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @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, ""); + + if (dev_instance >= vec_len (vxm->tunnels)) + return format (s, ""); + + 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 " + " {dst |group } vni " + " [instance ]" + " [encap-vrf-id ] [decap-next [l2|node ]] [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 [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 [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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* *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 +#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 +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define REPLY_MSG_ID_BASE msg_id_base +#include + +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 +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 + +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 + +/* + * 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: + */ diff --git a/src/plugins/gbp/CMakeLists.txt b/src/plugins/gbp/CMakeLists.txt deleted file mode 100644 index 95f664ff08e..00000000000 --- a/src/plugins/gbp/CMakeLists.txt +++ /dev/null @@ -1,54 +0,0 @@ -# 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/src/plugins/gbp/gbp.api b/src/plugins/gbp/gbp.api deleted file mode 100644 index 525e70536bd..00000000000 --- a/src/plugins/gbp/gbp.api +++ /dev/null @@ -1,470 +0,0 @@ -/* 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/src/plugins/gbp/gbp.h b/src/plugins/gbp/gbp.h deleted file mode 100644 index 50039b3bdcf..00000000000 --- a/src/plugins/gbp/gbp.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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 - -#include -#include -#include -#include -#include - -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/src/plugins/gbp/gbp_api.c b/src/plugins/gbp/gbp_api.c deleted file mode 100644 index ab89172b1af..00000000000 --- a/src/plugins/gbp/gbp_api.c +++ /dev/null @@ -1,1154 +0,0 @@ -/* - *------------------------------------------------------------------ - * 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 -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -/* define message IDs */ -#include -#include -#include -#include -#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 -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/src/plugins/gbp/gbp_bridge_domain.c b/src/plugins/gbp/gbp_bridge_domain.c deleted file mode 100644 index 279169abb1d..00000000000 --- a/src/plugins/gbp/gbp_bridge_domain.c +++ /dev/null @@ -1,503 +0,0 @@ -/* - * 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 -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/** - * 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 bvi [uu-fwd ] [bm-flood ] [flags ]} - * @cliexend - ?*/ -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (gbp_bridge_domain_cli_node, static) = { - .path = "gbp bridge-domain", - .short_help = "gbp bridge-domain [del] bd bvi [uu-fwd ] [bm-flood ] [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/src/plugins/gbp/gbp_bridge_domain.h b/src/plugins/gbp/gbp_bridge_domain.h deleted file mode 100644 index 0449240083c..00000000000 --- a/src/plugins/gbp/gbp_bridge_domain.h +++ /dev/null @@ -1,156 +0,0 @@ -/* - * 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 -#include - -#include -#include - -/** - * 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/src/plugins/gbp/gbp_classify.c b/src/plugins/gbp/gbp_classify.c deleted file mode 100644 index 255db252871..00000000000 --- a/src/plugins/gbp/gbp_classify.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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 -#include -#include - -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/src/plugins/gbp/gbp_classify.h b/src/plugins/gbp/gbp_classify.h deleted file mode 100644 index ca7db94a2c0..00000000000 --- a/src/plugins/gbp/gbp_classify.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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 -#include - -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/src/plugins/gbp/gbp_classify_node.c b/src/plugins/gbp/gbp_classify_node.c deleted file mode 100644 index a2058a21284..00000000000 --- a/src/plugins/gbp/gbp_classify_node.c +++ /dev/null @@ -1,628 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * 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/src/plugins/gbp/gbp_contract.c b/src/plugins/gbp/gbp_contract.c deleted file mode 100644 index dd433f28a84..00000000000 --- a/src/plugins/gbp/gbp_contract.c +++ /dev/null @@ -1,819 +0,0 @@ -/* - * 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 -#include -#include -#include -#include - -#include -#include - -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 dst-epg acl-index } - * @cliexend - ?*/ -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (gbp_contract_cli_node, static) = -{ - .path = "gbp contract", - .short_help = - "gbp contract [del] src-epg dst-epg acl-index ", - .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 ] [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/src/plugins/gbp/gbp_contract.h b/src/plugins/gbp/gbp_contract.h deleted file mode 100644 index 1e74db60116..00000000000 --- a/src/plugins/gbp/gbp_contract.h +++ /dev/null @@ -1,362 +0,0 @@ -/* - * 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 -#include - -#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/src/plugins/gbp/gbp_endpoint.c b/src/plugins/gbp/gbp_endpoint.c deleted file mode 100644 index b0cf64ced2d..00000000000 --- a/src/plugins/gbp/gbp_endpoint.c +++ /dev/null @@ -1,1597 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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 | [add] sclass ip mac [flags ]} - * @cliexend - ?*/ -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (gbp_endpoint_cli_node, static) = { - .path = "gbp endpoint", - .short_help = "gbp endpoint del | [add] sclass ip mac [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/src/plugins/gbp/gbp_endpoint.h b/src/plugins/gbp/gbp_endpoint.h deleted file mode 100644 index 3155e7be4e0..00000000000 --- a/src/plugins/gbp/gbp_endpoint.h +++ /dev/null @@ -1,376 +0,0 @@ -/* - * 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 -#include -#include -#include - -#include -#include -#include -#include - -/** - * 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/src/plugins/gbp/gbp_endpoint_group.c b/src/plugins/gbp/gbp_endpoint_group.c deleted file mode 100644 index b9044378e3b..00000000000 --- a/src/plugins/gbp/gbp_endpoint_group.c +++ /dev/null @@ -1,402 +0,0 @@ -/* - * 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 -#include -#include -#include -#include - -#include -#include -#include - -/** - * 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 bd rd [sclass ] []} - * @cliexend - ?*/ -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (gbp_endpoint_group_cli_node, static) = { - .path = "gbp endpoint-group", - .short_help = "gbp endpoint-group [del] epg bd rd [sclass ] []", - .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/src/plugins/gbp/gbp_endpoint_group.h b/src/plugins/gbp/gbp_endpoint_group.h deleted file mode 100644 index c5fdff8463d..00000000000 --- a/src/plugins/gbp/gbp_endpoint_group.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - * 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 -#include - -#include - -/** - * 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/src/plugins/gbp/gbp_ext_itf.c b/src/plugins/gbp/gbp_ext_itf.c deleted file mode 100644 index c5506661c2d..00000000000 --- a/src/plugins/gbp/gbp_ext_itf.c +++ /dev/null @@ -1,293 +0,0 @@ -/* - * 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 -#include -#include -#include - -/** - * 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 bd } - * @cliexend - ?*/ -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (gbp_itf_anon_l3out_add_del_node, static) = { - .path = "gbp ext-itf", - .short_help = "gbp ext-itf [del] bd rd [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/src/plugins/gbp/gbp_ext_itf.h b/src/plugins/gbp/gbp_ext_itf.h deleted file mode 100644 index 03b1992ca45..00000000000 --- a/src/plugins/gbp/gbp_ext_itf.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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 - -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/src/plugins/gbp/gbp_fwd.c b/src/plugins/gbp/gbp_fwd.c deleted file mode 100644 index 4ecc4779b92..00000000000 --- a/src/plugins/gbp/gbp_fwd.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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 -#include -#include - -/** - * 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/src/plugins/gbp/gbp_fwd_dpo.c b/src/plugins/gbp/gbp_fwd_dpo.c deleted file mode 100644 index b1023f5e78f..00000000000 --- a/src/plugins/gbp/gbp_fwd_dpo.c +++ /dev/null @@ -1,306 +0,0 @@ -/* - * 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 -#include - -#include - - -#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/src/plugins/gbp/gbp_fwd_dpo.h b/src/plugins/gbp/gbp_fwd_dpo.h deleted file mode 100644 index 6092d6241b5..00000000000 --- a/src/plugins/gbp/gbp_fwd_dpo.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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 - -/** - * @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/src/plugins/gbp/gbp_fwd_node.c b/src/plugins/gbp/gbp_fwd_node.c deleted file mode 100644 index 6ea56fd8074..00000000000 --- a/src/plugins/gbp/gbp_fwd_node.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - * 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 -#include - -#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/src/plugins/gbp/gbp_itf.c b/src/plugins/gbp/gbp_itf.c deleted file mode 100644 index 738a7ac2e39..00000000000 --- a/src/plugins/gbp/gbp_itf.c +++ /dev/null @@ -1,574 +0,0 @@ -/* - * 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 -#include -#include - -#include - -#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/src/plugins/gbp/gbp_itf.h b/src/plugins/gbp/gbp_itf.h deleted file mode 100644 index 23a09b2a9ff..00000000000 --- a/src/plugins/gbp/gbp_itf.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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 -#include -#include - - -#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/src/plugins/gbp/gbp_learn.c b/src/plugins/gbp/gbp_learn.c deleted file mode 100644 index af3a6fb52ac..00000000000 --- a/src/plugins/gbp/gbp_learn.c +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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 -#include -#include - -#include - -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/src/plugins/gbp/gbp_learn.h b/src/plugins/gbp/gbp_learn.h deleted file mode 100644 index b4f3ae0a23d..00000000000 --- a/src/plugins/gbp/gbp_learn.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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 - -#include - -/** - * 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/src/plugins/gbp/gbp_learn_node.c b/src/plugins/gbp/gbp_learn_node.c deleted file mode 100644 index a6c54971956..00000000000 --- a/src/plugins/gbp/gbp_learn_node.c +++ /dev/null @@ -1,718 +0,0 @@ -/* - * 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 -#include -#include -#include - -#include -#include -#include -#include -#include - -#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/src/plugins/gbp/gbp_policy.c b/src/plugins/gbp/gbp_policy.c deleted file mode 100644 index 127c6d3f059..00000000000 --- a/src/plugins/gbp/gbp_policy.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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 -#include -#include - -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/src/plugins/gbp/gbp_policy.h b/src/plugins/gbp/gbp_policy.h deleted file mode 100644 index 6f87f2ec7c4..00000000000 --- a/src/plugins/gbp/gbp_policy.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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 - -/** - * 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/src/plugins/gbp/gbp_policy_dpo.c b/src/plugins/gbp/gbp_policy_dpo.c deleted file mode 100644 index 9f26b9c67ab..00000000000 --- a/src/plugins/gbp/gbp_policy_dpo.c +++ /dev/null @@ -1,420 +0,0 @@ -/* - * 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 -#include -#include -#include - -#include -#include -#include -#include -#include - -#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/src/plugins/gbp/gbp_policy_dpo.h b/src/plugins/gbp/gbp_policy_dpo.h deleted file mode 100644 index 77ca5d93bd0..00000000000 --- a/src/plugins/gbp/gbp_policy_dpo.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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 -#include -#include -#include - -/** - * @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/src/plugins/gbp/gbp_policy_node.c b/src/plugins/gbp/gbp_policy_node.c deleted file mode 100644 index 8c6ef5c2b94..00000000000 --- a/src/plugins/gbp/gbp_policy_node.c +++ /dev/null @@ -1,341 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include - -#include -#include - -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/src/plugins/gbp/gbp_recirc.c b/src/plugins/gbp/gbp_recirc.c deleted file mode 100644 index 8d56f11b4e3..00000000000 --- a/src/plugins/gbp/gbp_recirc.c +++ /dev/null @@ -1,292 +0,0 @@ -/* - * 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 -#include -#include -#include - -#include -#include - -#include - -/** - * 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/src/plugins/gbp/gbp_recirc.h b/src/plugins/gbp/gbp_recirc.h deleted file mode 100644 index 2f3354b794e..00000000000 --- a/src/plugins/gbp/gbp_recirc.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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 -#include -#include - -/** - * 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/src/plugins/gbp/gbp_route_domain.c b/src/plugins/gbp/gbp_route_domain.c deleted file mode 100644 index 6cc595d0fa9..00000000000 --- a/src/plugins/gbp/gbp_route_domain.c +++ /dev/null @@ -1,447 +0,0 @@ -/* - * 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 -#include - -#include -#include - -/** - * 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 ip4-table-id ip6-table-id [ip4-uu ] [ip6-uu ]} - * @cliexend - ?*/ -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (gbp_route_domain_cli_node, static) = { - .path = "gbp route-domain", - .short_help = "gbp route-domain [del] rd ip4-table-id ip6-table-id [ip4-uu ] [ip6-uu ]", - .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/src/plugins/gbp/gbp_route_domain.h b/src/plugins/gbp/gbp_route_domain.h deleted file mode 100644 index 897c1bdd7ac..00000000000 --- a/src/plugins/gbp/gbp_route_domain.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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 - -#include -#include - -/** - * 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/src/plugins/gbp/gbp_scanner.c b/src/plugins/gbp/gbp_scanner.c deleted file mode 100644 index 9ae962b7449..00000000000 --- a/src/plugins/gbp/gbp_scanner.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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 -#include -#include - -/** - * 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/src/plugins/gbp/gbp_scanner.h b/src/plugins/gbp/gbp_scanner.h deleted file mode 100644 index 1133167d927..00000000000 --- a/src/plugins/gbp/gbp_scanner.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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 - -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/src/plugins/gbp/gbp_subnet.c b/src/plugins/gbp/gbp_subnet.c deleted file mode 100644 index 8d3b571657c..00000000000 --- a/src/plugins/gbp/gbp_subnet.c +++ /dev/null @@ -1,598 +0,0 @@ -/* - * 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 -#include -#include -#include - -#include -#include - -/** - * a key for the DB - */ -typedef struct gbp_subnet_key_t_ -{ - fib_prefix_t gsk_pfx; - u32 gsk_fib_index; -} gbp_subnet_key_t; - -/** - * Subnet - */ -typedef struct gbp_subnet_t_ -{ - gbp_subnet_key_t *gs_key; - gbp_subnet_type_t gs_type; - index_t gs_rd; - - union - { - struct - { - sclass_t gs_sclass; - u32 gs_sw_if_index; - } gs_stitched_external; - struct - { - sclass_t gs_sclass; - } gs_l3_out; - }; - - fib_node_index_t gs_fei; -} gbp_subnet_t; - -/** - * A DB of the subnets; key={pfx,fib-index} - */ -uword *gbp_subnet_db; - -/** - * pool of subnets - */ -gbp_subnet_t *gbp_subnet_pool; - -static fib_source_t gbp_fib_source; - -static index_t -gbp_subnet_db_find (u32 fib_index, const fib_prefix_t * pfx) -{ - gbp_subnet_key_t key = { - .gsk_pfx = *pfx, - .gsk_fib_index = fib_index, - }; - uword *p; - - p = hash_get_mem (gbp_subnet_db, &key); - - if (NULL != p) - return p[0]; - - return (INDEX_INVALID); -} - -static void -gbp_subnet_db_add (u32 fib_index, const fib_prefix_t * pfx, gbp_subnet_t * gs) -{ - gbp_subnet_key_t *key; - - key = clib_mem_alloc (sizeof (*key)); - - clib_memcpy (&(key->gsk_pfx), pfx, sizeof (*pfx)); - key->gsk_fib_index = fib_index; - - hash_set_mem (gbp_subnet_db, key, (gs - gbp_subnet_pool)); - - gs->gs_key = key; -} - -static void -gbp_subnet_db_del (gbp_subnet_t * gs) -{ - hash_unset_mem (gbp_subnet_db, gs->gs_key); - - clib_mem_free (gs->gs_key); - gs->gs_key = NULL; -} - - -static int -gbp_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 prefix type [] [sclass ]} - * @cliexend - ?*/ -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (gbp_subnet_add_del, static) = { - .path = "gbp subnet", - .short_help = "gbp subnet [del] rd prefix type [] [sclass ]\n", - .function = gbp_subnet_add_del_cli, -}; -/* *INDENT-ON* */ - - - -void -gbp_subnet_walk (gbp_subnet_cb_t cb, void *ctx) -{ - 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/src/plugins/gbp/gbp_subnet.h b/src/plugins/gbp/gbp_subnet.h deleted file mode 100644 index 6fbef01ceba..00000000000 --- a/src/plugins/gbp/gbp_subnet.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 - -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/src/plugins/gbp/gbp_types.h b/src/plugins/gbp/gbp_types.h deleted file mode 100644 index ac983b1cdd2..00000000000 --- a/src/plugins/gbp/gbp_types.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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 - -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/src/plugins/gbp/gbp_vxlan.c b/src/plugins/gbp/gbp_vxlan.c deleted file mode 100644 index 77e4d7ac11b..00000000000 --- a/src/plugins/gbp/gbp_vxlan.c +++ /dev/null @@ -1,654 +0,0 @@ -/* - * 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 -#include -#include -#include - -#include -#include -#include -#include - -/** - * 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/src/plugins/gbp/gbp_vxlan.h b/src/plugins/gbp/gbp_vxlan.h deleted file mode 100644 index 706fe2a0e85..00000000000 --- a/src/plugins/gbp/gbp_vxlan.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * 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 -#include - -#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/src/plugins/gbp/gbp_vxlan_node.c b/src/plugins/gbp/gbp_vxlan_node.c deleted file mode 100644 index 413a9f47e1b..00000000000 --- a/src/plugins/gbp/gbp_vxlan_node.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * 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 -#include -#include -#include -#include - -#include -#include -#include - -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/src/plugins/l2e/CMakeLists.txt b/src/plugins/l2e/CMakeLists.txt deleted file mode 100644 index 2bfb05a43e6..00000000000 --- a/src/plugins/l2e/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -# 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/src/plugins/l2e/l2e.api b/src/plugins/l2e/l2e.api deleted file mode 100644 index 586e2bae5ca..00000000000 --- a/src/plugins/l2e/l2e.api +++ /dev/null @@ -1,39 +0,0 @@ -/* 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/src/plugins/l2e/l2e.c b/src/plugins/l2e/l2e.c deleted file mode 100644 index 4c6eac50446..00000000000 --- a/src/plugins/l2e/l2e.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * 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 -#include -#include -#include - -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 [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 [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/src/plugins/l2e/l2e.h b/src/plugins/l2e/l2e.h deleted file mode 100644 index e548d333f9d..00000000000 --- a/src/plugins/l2e/l2e.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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 -#include - -/** - * 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/src/plugins/l2e/l2e_api.c b/src/plugins/l2e/l2e_api.c deleted file mode 100644 index fe2fb7ee06e..00000000000 --- a/src/plugins/l2e/l2e_api.c +++ /dev/null @@ -1,89 +0,0 @@ -/* - *------------------------------------------------------------------ - * 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 -#include - -#include -#include -#include - -#include - -#include -#include - -/* define message IDs */ -#include -#include - -#include - -#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 -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/src/plugins/l2e/l2e_node.c b/src/plugins/l2e/l2e_node.c deleted file mode 100644 index 71c9b4bc6af..00000000000 --- a/src/plugins/l2e/l2e_node.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * 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 -#include -#include - -#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/src/vnet/CMakeLists.txt b/src/vnet/CMakeLists.txt index 5de06da971f..5dd7cdb24ab 100644 --- a/src/vnet/CMakeLists.txt +++ b/src/vnet/CMakeLists.txt @@ -778,30 +778,6 @@ list(APPEND VNET_HEADERS list(APPEND VNET_API_FILES mpls/mpls.api) -############################################################################## -# Tunnel protocol: vxlan-gbp -############################################################################## -list(APPEND VNET_SOURCES - vxlan-gbp/decap.c - vxlan-gbp/encap.c - vxlan-gbp/vxlan_gbp_api.c - vxlan-gbp/vxlan_gbp.c - vxlan-gbp/vxlan_gbp_packet.c -) - -list (APPEND VNET_MULTIARCH_SOURCES - vxlan-gbp/decap.c - vxlan-gbp/encap.c -) - -list(APPEND VNET_HEADERS - vxlan-gbp/vxlan_gbp.h - vxlan-gbp/vxlan_gbp_packet.h - vxlan-gbp/vxlan_gbp_error.def -) - -list(APPEND VNET_API_FILES vxlan-gbp/vxlan_gbp.api) - ############################################################################## # Tunnel protocol: vxlan-gpe ############################################################################## diff --git a/src/vnet/l2/l2_fwd.c b/src/vnet/l2/l2_fwd.c index 3414f6c490e..1ee3a534cd7 100644 --- a/src/vnet/l2/l2_fwd.c +++ b/src/vnet/l2/l2_fwd.c @@ -215,8 +215,7 @@ l2fwd_process (vlib_main_t * vm, * unless some other feature is inserted before uu_flood */ if (vnet_buffer (b0)->l2.feature_bitmap & - (L2INPUT_FEAT_UU_FLOOD | - L2INPUT_FEAT_UU_FWD | L2INPUT_FEAT_GBP_FWD)) + (L2INPUT_FEAT_UU_FLOOD | L2INPUT_FEAT_UU_FWD)) { *next0 = vnet_l2_feature_next (b0, msm->feat_next_node_index, L2INPUT_FEAT_FWD); diff --git a/src/vnet/l2/l2_input.h b/src/vnet/l2/l2_input.h index ba4c4b6ed31..7d1dc9c1d05 100644 --- a/src/vnet/l2/l2_input.h +++ b/src/vnet/l2/l2_input.h @@ -136,17 +136,10 @@ l2input_bd_config (u32 bd_index) _(ARP_UFWD, "l2-uu-fwd") \ _(ARP_TERM, "arp-term-l2bd") \ _(UU_FLOOD, "l2-flood") \ - _(GBP_FWD, "gbp-fwd") \ _(UU_FWD, "l2-uu-fwd") \ _(FWD, "l2-fwd") \ _(RW, "l2-rw") \ _(LEARN, "l2-learn") \ - _(L2_EMULATION, "l2-emulation") \ - _(GBP_LEARN, "gbp-learn-l2") \ - _(GBP_LPM_ANON_CLASSIFY, "l2-gbp-lpm-anon-classify") \ - _(GBP_NULL_CLASSIFY, "gbp-null-classify") \ - _(GBP_SRC_CLASSIFY, "gbp-src-classify") \ - _(GBP_LPM_CLASSIFY, "l2-gbp-lpm-classify") \ _(VTR, "l2-input-vtr") \ _(L2_IP_QOS_RECORD, "l2-ip-qos-record") \ _(VPATH, "vpath-input-l2") \ diff --git a/src/vnet/l2/l2_input_node.c b/src/vnet/l2/l2_input_node.c index 3638a8aa00d..74625b2ec99 100644 --- a/src/vnet/l2/l2_input_node.c +++ b/src/vnet/l2/l2_input_node.c @@ -141,9 +141,8 @@ classify_and_dispatch (l2input_main_t * msm, vlib_buffer_t * b0, u16 * next0) u8 protocol = ((ip6_header_t *) l3h0)->protocol; /* Disable bridge forwarding (flooding will execute instead if not xconnect) */ - feat_mask &= ~(L2INPUT_FEAT_FWD | - L2INPUT_FEAT_UU_FLOOD | - L2INPUT_FEAT_UU_FWD | L2INPUT_FEAT_GBP_FWD); + feat_mask &= + ~(L2INPUT_FEAT_FWD | L2INPUT_FEAT_UU_FLOOD | L2INPUT_FEAT_UU_FWD); if (ethertype != ETHERNET_TYPE_ARP) feat_mask &= ~(L2INPUT_FEAT_ARP_UFWD); diff --git a/src/vnet/l2/l2_output.h b/src/vnet/l2/l2_output.h index 1cc1e738841..201f5e195a4 100644 --- a/src/vnet/l2/l2_output.h +++ b/src/vnet/l2/l2_output.h @@ -81,9 +81,6 @@ extern vlib_node_registration_t l2output_node; #define foreach_l2output_feat \ _(OUTPUT, "interface-output") \ _(SPAN, "span-l2-output") \ - _(GBP_POLICY_LPM, "gbp-policy-lpm") \ - _(GBP_POLICY_PORT, "gbp-policy-port") \ - _(GBP_POLICY_MAC, "gbp-policy-mac") \ _(CFM, "feature-bitmap-drop") \ _(QOS, "feature-bitmap-drop") \ _(ACL, "l2-output-acl") \ diff --git a/src/vnet/vxlan-gbp/decap.c b/src/vnet/vxlan-gbp/decap.c deleted file mode 100644 index 927c778b211..00000000000 --- a/src/vnet/vxlan-gbp/decap.c +++ /dev/null @@ -1,1050 +0,0 @@ -/* - * 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 - -#include - -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 -#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/src/vnet/vxlan-gbp/dir.dox b/src/vnet/vxlan-gbp/dir.dox deleted file mode 100644 index 6e63c90b17b..00000000000 --- a/src/vnet/vxlan-gbp/dir.dox +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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/src/vnet/vxlan-gbp/encap.c b/src/vnet/vxlan-gbp/encap.c deleted file mode 100644 index 2a4e8a8e312..00000000000 --- a/src/vnet/vxlan-gbp/encap.c +++ /dev/null @@ -1,601 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -/* 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/src/vnet/vxlan-gbp/vxlan_gbp.api b/src/vnet/vxlan-gbp/vxlan_gbp.api deleted file mode 100644 index 68566697000..00000000000 --- a/src/vnet/vxlan-gbp/vxlan_gbp.api +++ /dev/null @@ -1,100 +0,0 @@ -/* 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/src/vnet/vxlan-gbp/vxlan_gbp.c b/src/vnet/vxlan-gbp/vxlan_gbp.c deleted file mode 100644 index eb685b8a40c..00000000000 --- a/src/vnet/vxlan-gbp/vxlan_gbp.c +++ /dev/null @@ -1,1193 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * @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, ""); - - if (dev_instance >= vec_len (vxm->tunnels)) - return format (s, ""); - - 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 " - " {dst |group } vni " - " [instance ]" - " [encap-vrf-id ] [decap-next [l2|node ]] [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 [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 [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/src/vnet/vxlan-gbp/vxlan_gbp.h b/src/vnet/vxlan-gbp/vxlan_gbp.h deleted file mode 100644 index fe93587cb00..00000000000 --- a/src/vnet/vxlan-gbp/vxlan_gbp.h +++ /dev/null @@ -1,250 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* *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 -#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/src/vnet/vxlan-gbp/vxlan_gbp_api.c b/src/vnet/vxlan-gbp/vxlan_gbp_api.c deleted file mode 100644 index a3f2246f463..00000000000 --- a/src/vnet/vxlan-gbp/vxlan_gbp_api.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - *------------------------------------------------------------------ - * 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 -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define REPLY_MSG_ID_BASE msg_id_base -#include - -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 -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/src/vnet/vxlan-gbp/vxlan_gbp_error.def b/src/vnet/vxlan-gbp/vxlan_gbp_error.def deleted file mode 100644 index 43ad4dac064..00000000000 --- a/src/vnet/vxlan-gbp/vxlan_gbp_error.def +++ /dev/null @@ -1,17 +0,0 @@ -/* - * 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/src/vnet/vxlan-gbp/vxlan_gbp_packet.c b/src/vnet/vxlan-gbp/vxlan_gbp_packet.c deleted file mode 100644 index 01c7a19bfb9..00000000000 --- a/src/vnet/vxlan-gbp/vxlan_gbp_packet.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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 - -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/src/vnet/vxlan-gbp/vxlan_gbp_packet.h b/src/vnet/vxlan-gbp/vxlan_gbp_packet.h deleted file mode 100644 index e655b333b89..00000000000 --- a/src/vnet/vxlan-gbp/vxlan_gbp_packet.h +++ /dev/null @@ -1,173 +0,0 @@ -/* - * 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 - -/* - * 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: - */ diff --git a/test/patches/scapy-2.3.3/vxlan.patch b/test/patches/scapy-2.3.3/vxlan.patch index 71493dd742c..556df4dd70a 100644 --- a/test/patches/scapy-2.3.3/vxlan.patch +++ b/test/patches/scapy-2.3.3/vxlan.patch @@ -8,5 +8,4 @@ diff --git a/scapy/layers/vxlan.py b/scapy/layers/vxlan.py +bind_layers(UDP, VXLAN, dport=4790) # RFC standard vxlan-gpe port bind_layers(UDP, VXLAN, dport=6633) # New IANA assigned port for use with NSH bind_layers(UDP, VXLAN, dport=8472) # Linux implementation port -+bind_layers(UDP, VXLAN, dport=48879) # RFC standard vxlan-gbp port bind_layers(VXLAN, Ether, {'flags': 0x8}) diff --git a/test/patches/scapy-2.4/vxlan.py.patch b/test/patches/scapy-2.4/vxlan.py.patch deleted file mode 100644 index c5876a4adbd..00000000000 --- a/test/patches/scapy-2.4/vxlan.py.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/scapy/layers/vxlan.py b/scapy/layers/vxlan.py -index 03c3a5a..ee30f28 100644 ---- a/scapy/layers/vxlan.py -+++ b/scapy/layers/vxlan.py -@@ -69,6 +69,7 @@ bind_layers(UDP, VXLAN, dport=4789) # RFC standard vxlan port - bind_layers(UDP, VXLAN, dport=4790) # RFC standard vxlan-gpe port - bind_layers(UDP, VXLAN, dport=6633) # New IANA assigned port for use with NSH - bind_layers(UDP, VXLAN, dport=8472) # Linux implementation port -+bind_layers(UDP, VXLAN, dport=48879) # iVXLAN port - bind_layers(UDP, VXLAN, sport=4789) - bind_layers(UDP, VXLAN, sport=4790) - bind_layers(UDP, VXLAN, sport=6633) diff --git a/test/test_dvr.py b/test/test_dvr.py index 8531b8553ca..c0424a3047f 100644 --- a/test/test_dvr.py +++ b/test/test_dvr.py @@ -252,159 +252,6 @@ class TestDVR(VppTestCase): sub_if_on_pg3.remove_vpp_config() sub_if_on_pg2.remove_vpp_config() - def test_l2_emulation(self): - """ L2 Emulation """ - - # - # non distinct L3 packets, in the tag/non-tag combos - # - pkt_no_tag = (Ether(src=self.pg0.remote_mac, - dst=self.pg1.remote_mac) / - IP(src="2.2.2.2", - dst="1.1.1.1") / - UDP(sport=1234, dport=1234) / - Raw(b'\xa5' * 100)) - pkt_to_tag = (Ether(src=self.pg0.remote_mac, - dst=self.pg2.remote_mac) / - IP(src="2.2.2.2", - dst="1.1.1.2") / - UDP(sport=1234, dport=1234) / - Raw(b'\xa5' * 100)) - pkt_from_tag = (Ether(src=self.pg3.remote_mac, - dst=self.pg2.remote_mac) / - Dot1Q(vlan=93) / - IP(src="2.2.2.2", - dst="1.1.1.1") / - UDP(sport=1234, dport=1234) / - Raw(b'\xa5' * 100)) - pkt_from_to_tag = (Ether(src=self.pg3.remote_mac, - dst=self.pg2.remote_mac) / - Dot1Q(vlan=93) / - IP(src="2.2.2.2", - dst="1.1.1.2") / - UDP(sport=1234, dport=1234) / - Raw(b'\xa5' * 100)) - pkt_bcast = (Ether(src=self.pg0.remote_mac, - dst="ff:ff:ff:ff:ff:ff") / - IP(src="2.2.2.2", - dst="255.255.255.255") / - UDP(sport=1234, dport=1234) / - Raw(b'\xa5' * 100)) - - # - # A couple of sub-interfaces for tags - # - sub_if_on_pg2 = VppDot1QSubint(self, self.pg2, 92) - sub_if_on_pg3 = VppDot1QSubint(self, self.pg3, 93) - sub_if_on_pg2.admin_up() - sub_if_on_pg3.admin_up() - - # - # Put all the interfaces into a new bridge domain - # - self.vapi.sw_interface_set_l2_bridge( - rx_sw_if_index=self.pg0.sw_if_index, bd_id=1) - self.vapi.sw_interface_set_l2_bridge( - rx_sw_if_index=self.pg1.sw_if_index, bd_id=1) - self.vapi.sw_interface_set_l2_bridge( - rx_sw_if_index=sub_if_on_pg2.sw_if_index, bd_id=1) - self.vapi.sw_interface_set_l2_bridge( - rx_sw_if_index=sub_if_on_pg3.sw_if_index, bd_id=1) - self.vapi.l2_interface_vlan_tag_rewrite( - sw_if_index=sub_if_on_pg2.sw_if_index, vtr_op=L2_VTR_OP.L2_POP_1, - push_dot1q=92) - self.vapi.l2_interface_vlan_tag_rewrite( - sw_if_index=sub_if_on_pg3.sw_if_index, vtr_op=L2_VTR_OP.L2_POP_1, - push_dot1q=93) - - # - # Disable UU flooding, learning and ARP termination. makes this test - # easier as unicast packets are dropped if not extracted. - # - self.vapi.bridge_flags(bd_id=1, is_set=0, - flags=(1 << 0) | (1 << 3) | (1 << 4)) - - # - # Add a DVR route to steer traffic at L3 - # - route_1 = VppIpRoute( - self, "1.1.1.1", 32, - [VppRoutePath("0.0.0.0", - self.pg1.sw_if_index, - type=FibPathType.FIB_PATH_TYPE_DVR)]) - route_2 = VppIpRoute( - self, "1.1.1.2", 32, - [VppRoutePath("0.0.0.0", - sub_if_on_pg2.sw_if_index, - type=FibPathType.FIB_PATH_TYPE_DVR)]) - route_1.add_vpp_config() - route_2.add_vpp_config() - - # - # packets are dropped because bridge does not flood unknown unicast - # - self.send_and_assert_no_replies(self.pg0, pkt_no_tag) - - # - # Enable L3 extraction on pgs - # - self.vapi.l2_emulation(self.pg0.sw_if_index) - self.vapi.l2_emulation(self.pg1.sw_if_index) - self.vapi.l2_emulation(sub_if_on_pg2.sw_if_index) - self.vapi.l2_emulation(sub_if_on_pg3.sw_if_index) - - # - # now we expect the packet forward according to the DVR route - # - rx = self.send_and_expect(self.pg0, pkt_no_tag * NUM_PKTS, self.pg1) - self.assert_same_mac_addr(pkt_no_tag, rx) - self.assert_has_no_tag(rx) - - rx = self.send_and_expect(self.pg0, pkt_to_tag * NUM_PKTS, self.pg2) - self.assert_same_mac_addr(pkt_to_tag, rx) - self.assert_has_vlan_tag(92, rx) - - rx = self.send_and_expect(self.pg3, pkt_from_tag * NUM_PKTS, self.pg1) - self.assert_same_mac_addr(pkt_from_tag, rx) - self.assert_has_no_tag(rx) - - rx = self.send_and_expect(self.pg3, - pkt_from_to_tag * NUM_PKTS, - self.pg2) - self.assert_same_mac_addr(pkt_from_tag, rx) - self.assert_has_vlan_tag(92, rx) - - # - # but broadcast packets are still flooded - # - self.send_and_expect(self.pg0, pkt_bcast * 33, self.pg2) - - # - # cleanup - # - self.vapi.l2_emulation(self.pg0.sw_if_index, - enable=0) - self.vapi.l2_emulation(self.pg1.sw_if_index, - enable=0) - self.vapi.l2_emulation(sub_if_on_pg2.sw_if_index, - enable=0) - self.vapi.l2_emulation(sub_if_on_pg3.sw_if_index, - enable=0) - - self.vapi.sw_interface_set_l2_bridge( - rx_sw_if_index=self.pg0.sw_if_index, bd_id=1, enable=0) - self.vapi.sw_interface_set_l2_bridge( - rx_sw_if_index=self.pg1.sw_if_index, bd_id=1, enable=0) - self.vapi.sw_interface_set_l2_bridge( - rx_sw_if_index=sub_if_on_pg2.sw_if_index, bd_id=1, enable=0) - self.vapi.sw_interface_set_l2_bridge( - rx_sw_if_index=sub_if_on_pg3.sw_if_index, bd_id=1, enable=0) - - route_1.remove_vpp_config() - route_2.remove_vpp_config() - sub_if_on_pg3.remove_vpp_config() - sub_if_on_pg2.remove_vpp_config() - if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) diff --git a/test/test_gbp.py b/test/test_gbp.py deleted file mode 100644 index c30a729519d..00000000000 --- a/test/test_gbp.py +++ /dev/null @@ -1,5926 +0,0 @@ -#!/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 '' % ( - 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/test/test_vxlan_gbp.py b/test/test_vxlan_gbp.py deleted file mode 100644 index f332aced7d8..00000000000 --- a/test/test_vxlan_gbp.py +++ /dev/null @@ -1,293 +0,0 @@ -#!/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/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 0d521455abf..1c5b6c5084f 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -652,10 +652,6 @@ class VppPapiProvider(object): 'is_ip6': is_ip6 }}) - def vxlan_gbp_tunnel_dump(self, sw_if_index=0xffffffff): - return self.api(self.papi.vxlan_gbp_tunnel_dump, - {'sw_if_index': sw_if_index}) - def pppoe_add_del_session( self, client_ip, @@ -960,30 +956,6 @@ class VppPapiProvider(object): return self.api(self.papi.punt_socket_deregister, {'punt': reg}) - def gbp_endpoint_dump(self): - """ GBP endpoint Dump """ - return self.api(self.papi.gbp_endpoint_dump, {}) - - def gbp_recirc_dump(self): - """ GBP recirc Dump """ - return self.api(self.papi.gbp_recirc_dump, {}) - - def gbp_ext_itf_dump(self): - """ GBP recirc Dump """ - return self.api(self.papi.gbp_ext_itf_dump, {}) - - def gbp_subnet_dump(self): - """ GBP Subnet Dump """ - return self.api(self.papi.gbp_subnet_dump, {}) - - def gbp_contract_dump(self): - """ GBP contract Dump """ - return self.api(self.papi.gbp_contract_dump, {}) - - def gbp_vxlan_tunnel_dump(self): - """ GBP VXLAN tunnel add/del """ - return self.api(self.papi.gbp_vxlan_tunnel_dump, {}) - def igmp_enable_disable(self, sw_if_index, enable, host): """ Enable/disable IGMP on a given interface """ return self.api(self.papi.igmp_enable_disable, diff --git a/test/vpp_vxlan_gbp_tunnel.py b/test/vpp_vxlan_gbp_tunnel.py deleted file mode 100644 index 0898bd9f810..00000000000 --- a/test/vpp_vxlan_gbp_tunnel.py +++ /dev/null @@ -1,75 +0,0 @@ - -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) -- cgit 1.2.3-korg