From b6a47953973f7c94239c394b649100e91bdb2152 Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Wed, 21 Nov 2018 05:44:35 -0800 Subject: [PATCH] GBP: l3-out subnets Change-Id: Id4a20066fc5be716c61a497dfcb4d00dc1dbb28d Signed-off-by: Neale Ranns --- extras/vom/vom/CMakeLists.txt | 3 + extras/vom/vom/gbp_ext_itf.cpp | 193 +++++++++++++++++++ extras/vom/vom/gbp_ext_itf.hpp | 186 +++++++++++++++++++ extras/vom/vom/gbp_ext_itf_cmds.cpp | 135 ++++++++++++++ extras/vom/vom/gbp_ext_itf_cmds.hpp | 134 ++++++++++++++ extras/vom/vom/gbp_subnet.cpp | 21 +++ extras/vom/vom/gbp_subnet.hpp | 14 +- src/plugins/gbp/CMakeLists.txt | 1 + src/plugins/gbp/gbp.api | 39 +++- src/plugins/gbp/gbp_api.c | 95 +++++++++- src/plugins/gbp/gbp_bridge_domain.c | 10 + src/plugins/gbp/gbp_bridge_domain.h | 1 + src/plugins/gbp/gbp_classify.c | 283 +++++++++++++++++++++++++++- src/plugins/gbp/gbp_endpoint.c | 36 +++- src/plugins/gbp/gbp_endpoint.h | 9 +- src/plugins/gbp/gbp_ext_itf.c | 203 ++++++++++++++++++++ src/plugins/gbp/gbp_ext_itf.h | 81 ++++++++ src/plugins/gbp/gbp_itf.c | 6 +- src/plugins/gbp/gbp_policy_dpo.c | 213 +-------------------- src/plugins/gbp/gbp_policy_dpo.h | 18 +- src/plugins/gbp/gbp_recirc.c | 28 ++- src/plugins/gbp/gbp_recirc.h | 9 +- src/plugins/gbp/gbp_route_domain.c | 10 + src/plugins/gbp/gbp_route_domain.h | 1 + src/plugins/gbp/gbp_scanner.c | 43 ++++- src/plugins/gbp/gbp_scanner.h | 3 +- src/plugins/gbp/gbp_subnet.c | 97 +++++++--- src/plugins/gbp/gbp_subnet.h | 1 + src/vnet/l2/l2_input.h | 1 + test/test_gbp.py | 359 ++++++++++++++++++++++++++++++++++-- test/vpp_papi_provider.py | 13 ++ 31 files changed, 1955 insertions(+), 291 deletions(-) create mode 100644 extras/vom/vom/gbp_ext_itf.cpp create mode 100644 extras/vom/vom/gbp_ext_itf.hpp create mode 100644 extras/vom/vom/gbp_ext_itf_cmds.cpp create mode 100644 extras/vom/vom/gbp_ext_itf_cmds.hpp create mode 100644 src/plugins/gbp/gbp_ext_itf.c create mode 100644 src/plugins/gbp/gbp_ext_itf.h diff --git a/extras/vom/vom/CMakeLists.txt b/extras/vom/vom/CMakeLists.txt index 7413df8d834..f68a7409362 100644 --- a/extras/vom/vom/CMakeLists.txt +++ b/extras/vom/vom/CMakeLists.txt @@ -76,6 +76,8 @@ if(GBP_FILE) gbp_endpoint.cpp gbp_endpoint_group_cmds.cpp gbp_endpoint_group.cpp + gbp_ext_itf.cpp + gbp_ext_itf_cmds.cpp gbp_recirc_cmds.cpp gbp_recirc.cpp gbp_route_domain_cmds.cpp @@ -196,6 +198,7 @@ if(GBP_FILE) gbp_bridge_domain.hpp gbp_endpoint.hpp gbp_endpoint_group.hpp + gbp_ext_itf.hpp gbp_recirc.hpp gbp_route_domain.hpp gbp_rule.hpp diff --git a/extras/vom/vom/gbp_ext_itf.cpp b/extras/vom/vom/gbp_ext_itf.cpp new file mode 100644 index 00000000000..4b7302ddaaa --- /dev/null +++ b/extras/vom/vom/gbp_ext_itf.cpp @@ -0,0 +1,193 @@ +/* + * 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 "vom/gbp_ext_itf.hpp" +#include "vom/gbp_ext_itf_cmds.hpp" +#include "vom/singular_db_funcs.hpp" + +namespace VOM { + +singular_db gbp_ext_itf::m_db; + +gbp_ext_itf::event_handler gbp_ext_itf::m_evh; + +gbp_ext_itf::gbp_ext_itf(const interface& itf, + const gbp_bridge_domain& gbd, + const gbp_route_domain& grd) + : m_hw(false) + , m_itf(itf.singular()) + , m_bd(gbd.singular()) + , m_rd(grd.singular()) +{ +} + +gbp_ext_itf::gbp_ext_itf(const gbp_ext_itf& gbpe) + : m_hw(gbpe.m_hw) + , m_itf(gbpe.m_itf) + , m_bd(gbpe.m_bd) + , m_rd(gbpe.m_rd) +{ +} + +gbp_ext_itf::~gbp_ext_itf() +{ + sweep(); + m_db.release(key(), this); +} + +const gbp_ext_itf::key_t +gbp_ext_itf::key() const +{ + return (m_itf->key()); +} + +const handle_t& +gbp_ext_itf::handle() const +{ + return m_itf->handle(); +} + +bool +gbp_ext_itf::operator==(const gbp_ext_itf& gei) const +{ + return ((key() == gei.key()) && (m_itf == gei.m_itf) && (m_rd == gei.m_rd) && + (m_bd == gei.m_bd)); +} + +void +gbp_ext_itf::sweep() +{ + if (m_hw) { + HW::enqueue(new gbp_ext_itf_cmds::delete_cmd(m_hw, m_itf->handle())); + } + HW::write(); +} + +void +gbp_ext_itf::replay() +{ + if (m_hw) { + HW::enqueue(new gbp_ext_itf_cmds::create_cmd(m_hw, m_itf->handle(), + m_bd->id(), m_rd->id())); + } +} + +std::string +gbp_ext_itf::to_string() const +{ + std::ostringstream s; + s << "gbp-ext_itf:[" << m_itf->to_string() << ", " << m_bd->to_string() + << ", " << m_rd->to_string() << "]"; + + return (s.str()); +} + +void +gbp_ext_itf::update(const gbp_ext_itf& r) +{ + if (rc_t::OK != m_hw.rc()) { + HW::enqueue(new gbp_ext_itf_cmds::create_cmd(m_hw, m_itf->handle(), + m_bd->id(), m_rd->id())); + } +} + +std::shared_ptr +gbp_ext_itf::find_or_add(const gbp_ext_itf& temp) +{ + return (m_db.find_or_add(temp.key(), temp)); +} + +std::shared_ptr +gbp_ext_itf::find(const key_t& k) +{ + return (m_db.find(k)); +} + +std::shared_ptr +gbp_ext_itf::singular() const +{ + return find_or_add(*this); +} + +void +gbp_ext_itf::dump(std::ostream& os) +{ + db_dump(m_db, os); +} + +gbp_ext_itf::event_handler::event_handler() +{ + OM::register_listener(this); + inspect::register_handler({ "gbp-ext-itf" }, "GBP External-Itfs", this); +} + +void +gbp_ext_itf::event_handler::handle_replay() +{ + m_db.replay(); +} + +void +gbp_ext_itf::event_handler::handle_populate(const client_db::key_t& key) +{ + std::shared_ptr cmd = + std::make_shared(); + + HW::enqueue(cmd); + HW::write(); + + for (auto& record : *cmd) { + auto& payload = record.get_payload(); + + std::shared_ptr itf = + interface::find(payload.ext_itf.sw_if_index); + std::shared_ptr gbd = + gbp_bridge_domain::find(payload.ext_itf.bd_id); + std::shared_ptr grd = + gbp_route_domain::find(payload.ext_itf.rd_id); + + VOM_LOG(log_level_t::DEBUG) << "data: [" << payload.ext_itf.sw_if_index + << ", " << payload.ext_itf.bd_id << ", " + << payload.ext_itf.rd_id << "]"; + + if (itf && gbd && grd) { + gbp_ext_itf ext_itf(*itf, *gbd, *grd); + OM::commit(key, ext_itf); + + VOM_LOG(log_level_t::DEBUG) << "read: " << ext_itf.to_string(); + } + } +} + +dependency_t +gbp_ext_itf::event_handler::order() const +{ + return (dependency_t::BINDING); +} + +void +gbp_ext_itf::event_handler::show(std::ostream& os) +{ + db_dump(m_db, os); +} +} // namespace VOM + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/extras/vom/vom/gbp_ext_itf.hpp b/extras/vom/vom/gbp_ext_itf.hpp new file mode 100644 index 00000000000..20d9b6eff9b --- /dev/null +++ b/extras/vom/vom/gbp_ext_itf.hpp @@ -0,0 +1,186 @@ +/* + * 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 __VOM_GBP_EXT_ITF_H__ +#define __VOM_GBP_EXT_ITF_H__ + +#include "vom/gbp_bridge_domain.hpp" +#include "vom/gbp_route_domain.hpp" +#include "vom/interface.hpp" +#include "vom/singular_db.hpp" + +namespace VOM { +/** + * A enternal interface for GBP + */ +class gbp_ext_itf : public object_base +{ +public: + /** + * The key for a GBP ext_itf interface + */ + typedef interface::key_t key_t; + + /** + * Construct a GBP ext_itf + */ + gbp_ext_itf(const interface& itf, + const gbp_bridge_domain& gbd, + const gbp_route_domain& grd); + + /** + * Copy Construct + */ + gbp_ext_itf(const gbp_ext_itf& r); + + /** + * Destructor + */ + ~gbp_ext_itf(); + + /** + * Return the object's key + */ + const key_t key() const; + + /** + * comparison operator + */ + bool operator==(const gbp_ext_itf& bdae) const; + + /** + * Return the matching 'singular instance' + */ + std::shared_ptr singular() const; + + /** + * Find the instnace of the ext_itf interface in the OM + */ + static std::shared_ptr find(const key_t& k); + + /** + * Dump all bridge_domain-doamin into the stream provided + */ + static void dump(std::ostream& os); + + /** + * replay the object to create it in hardware + */ + void replay(void); + + /** + * Convert to string for debugging + */ + std::string to_string() const; + + /** + * return the ext_itfulation interface's handle + */ + const handle_t& handle() const; + +private: + /** + * Class definition for listeners to OM events + */ + class event_handler : public OM::listener, public inspect::command_handler + { + public: + event_handler(); + virtual ~event_handler() = default; + + /** + * Handle a populate event + */ + void handle_populate(const client_db::key_t& key); + + /** + * Handle a replay event + */ + void handle_replay(); + + /** + * Show the object in the Singular DB + */ + void show(std::ostream& os); + + /** + * Get the sortable Id of the listener + */ + dependency_t order() const; + }; + + /** + * event_handler to register with OM + */ + static event_handler m_evh; + + /** + * Commit the acculmulated changes into VPP. i.e. to a 'HW" write. + */ + void update(const gbp_ext_itf& obj); + + /** + * Find or add the instnace of the bridge_domain domain in the OM + */ + static std::shared_ptr find_or_add(const gbp_ext_itf& temp); + + /* + * It's the VPPHW class that updates the objects in HW + */ + friend class OM; + + /** + * It's the singular_db class that calls replay() + */ + friend class singular_db; + + /** + * Sweep/reap the object if still stale + */ + void sweep(void); + + /** + * HW configuration for the result of creating the ext_itf + */ + HW::item m_hw; + + /** + * The interface the ext_itf is attached to. + */ + std::shared_ptr m_itf; + + /** + * The BD & RD the ext_itf is in + */ + std::shared_ptr m_bd; + std::shared_ptr m_rd; + + /** + * A map of all bridge_domains + */ + static singular_db m_db; +}; + +}; // namespace VOM + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/extras/vom/vom/gbp_ext_itf_cmds.cpp b/extras/vom/vom/gbp_ext_itf_cmds.cpp new file mode 100644 index 00000000000..b2090ea0fa5 --- /dev/null +++ b/extras/vom/vom/gbp_ext_itf_cmds.cpp @@ -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. + */ + +#include "vom/gbp_ext_itf_cmds.hpp" + +namespace VOM { +namespace gbp_ext_itf_cmds { + +create_cmd::create_cmd(HW::item& item, + const handle_t& itf, + uint32_t bd_id, + uint32_t rd_id) + : rpc_cmd(item) + , m_itf(itf) + , m_bd_id(bd_id) + , m_rd_id(rd_id) +{ +} + +bool +create_cmd::operator==(const create_cmd& other) const +{ + return ((m_itf == other.m_itf) && (m_bd_id == other.m_bd_id) && + (m_rd_id == other.m_rd_id)); +} + +rc_t +create_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.is_add = 1; + payload.ext_itf.sw_if_index = m_itf.value(); + payload.ext_itf.bd_id = m_bd_id; + payload.ext_itf.rd_id = m_rd_id; + + VAPI_CALL(req.execute()); + + return (wait()); +} + +std::string +create_cmd::to_string() const +{ + std::ostringstream s; + s << "gbp-ext_itf-create: " << m_hw_item.to_string() << " itf:" << m_itf + << " bd-id:" << m_bd_id << " rd-id:" << m_rd_id; + + return (s.str()); +} + +delete_cmd::delete_cmd(HW::item& item, const handle_t& itf) + : rpc_cmd(item) + , m_itf(itf) +{ +} + +bool +delete_cmd::operator==(const delete_cmd& other) const +{ + return (m_itf == other.m_itf); +} + +rc_t +delete_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.is_add = 0; + payload.ext_itf.sw_if_index = m_itf.value(); + payload.ext_itf.bd_id = ~0; + payload.ext_itf.rd_id = ~0; + + VAPI_CALL(req.execute()); + + return (wait()); +} + +std::string +delete_cmd::to_string() const +{ + std::ostringstream s; + s << "gbp-ext-itf-delete: " << m_hw_item.to_string() << " itf:" << m_itf; + + return (s.str()); +} + +bool +dump_cmd::operator==(const dump_cmd& other) const +{ + return (true); +} + +rc_t +dump_cmd::issue(connection& con) +{ + m_dump.reset(new msg_t(con.ctx(), std::ref(*this))); + + VAPI_CALL(m_dump->execute()); + + wait(); + + return rc_t::OK; +} + +std::string +dump_cmd::to_string() const +{ + return ("gbp-ext-itf-dump"); +} + +}; // namespace gbp_ext_itf_cmds +}; // namespace VOM + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/extras/vom/vom/gbp_ext_itf_cmds.hpp b/extras/vom/vom/gbp_ext_itf_cmds.hpp new file mode 100644 index 00000000000..51052b9a360 --- /dev/null +++ b/extras/vom/vom/gbp_ext_itf_cmds.hpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2017 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 __VOM_GBP_EXT_ITF_CMDS_H__ +#define __VOM_GBP_EXT_ITF_CMDS_H__ + +#include "vom/dump_cmd.hpp" +#include "vom/gbp_ext_itf.hpp" + +#include + +namespace VOM { +namespace gbp_ext_itf_cmds { + +/** +* A command class that creates or updates the GBP ext_itf +*/ +class create_cmd : public rpc_cmd, vapi::Gbp_ext_itf_add_del> +{ +public: + /** + * Constructor + */ + create_cmd(HW::item& item, + const handle_t& itf, + uint32_t bd_id, + uint32_t rd_id); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const create_cmd& i) const; + +private: + const handle_t m_itf; + const uint32_t m_bd_id; + const uint32_t m_rd_id; +}; + +/** + * A cmd class that deletes a GBP ext_itf + */ +class delete_cmd : public rpc_cmd, vapi::Gbp_ext_itf_add_del> +{ +public: + /** + * Constructor + */ + delete_cmd(HW::item& item, const handle_t& itf); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const delete_cmd& i) const; + +private: + const handle_t m_itf; +}; + +/** + * A cmd class that Dumps all the GBP ext_itfs + */ +class dump_cmd : public VOM::dump_cmd +{ +public: + /** + * Constructor + */ + dump_cmd() = default; + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const dump_cmd& i) const; + +private: + /** + * HW reutrn code + */ + HW::item item; +}; +}; // namespace gbp_ext_itf_cmds +}; // namespace VOM + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/extras/vom/vom/gbp_subnet.cpp b/extras/vom/vom/gbp_subnet.cpp index 2221c616dbb..8404dcd2e65 100644 --- a/extras/vom/vom/gbp_subnet.cpp +++ b/extras/vom/vom/gbp_subnet.cpp @@ -62,6 +62,18 @@ gbp_subnet::gbp_subnet(const gbp_route_domain& rd, { } +gbp_subnet::gbp_subnet(const gbp_route_domain& rd, + const route::prefix_t& prefix, + const gbp_endpoint_group& epg) + : m_hw(false) + , m_rd(rd.singular()) + , m_prefix(prefix) + , m_type(type_t::L3_OUT) + , m_recirc(nullptr) + , m_epg(epg.singular()) +{ +} + gbp_subnet::gbp_subnet(const gbp_subnet& o) : m_hw(o.m_hw) , m_rd(o.m_rd) @@ -215,6 +227,15 @@ gbp_subnet::event_handler::handle_populate(const client_db::key_t& key) VOM_LOG(log_level_t::DEBUG) << "read: " << gs.to_string(); break; } + case GBP_API_SUBNET_L3_OUT: { + std::shared_ptr epg = + gbp_endpoint_group::find(payload.subnet.epg_id); + + gbp_subnet gs(*rd, pfx, *epg); + OM::commit(key, gs); + VOM_LOG(log_level_t::DEBUG) << "read: " << gs.to_string(); + break; + } case GBP_API_SUBNET_STITCHED_EXTERNAL: { std::shared_ptr itf = interface::find(payload.subnet.sw_if_index); diff --git a/extras/vom/vom/gbp_subnet.hpp b/extras/vom/vom/gbp_subnet.hpp index e08f1a25e11..f18aa01431e 100644 --- a/extras/vom/vom/gbp_subnet.hpp +++ b/extras/vom/vom/gbp_subnet.hpp @@ -53,6 +53,11 @@ public: */ const static type_t TRANSPORT; + /** + * A transport subnet, sent via the RD's UU-fwd interface + */ + const static type_t L3_OUT; + private: type_t(int v, const std::string s); }; @@ -65,13 +70,20 @@ public: const type_t& type); /** - * Construct an external GBP subnet + * Construct an stitched external GBP subnet */ gbp_subnet(const gbp_route_domain& rd, const route::prefix_t& prefix, const gbp_recirc& recirc, const gbp_endpoint_group& epg); + /** + * Construct an l3-out GBP subnet + */ + gbp_subnet(const gbp_route_domain& rd, + const route::prefix_t& prefix, + const gbp_endpoint_group& epg); + /** * Copy Construct */ diff --git a/src/plugins/gbp/CMakeLists.txt b/src/plugins/gbp/CMakeLists.txt index 377197a7c08..4b511413b82 100644 --- a/src/plugins/gbp/CMakeLists.txt +++ b/src/plugins/gbp/CMakeLists.txt @@ -19,6 +19,7 @@ add_vpp_plugin(gbp gbp_contract.c gbp_endpoint.c gbp_endpoint_group.c + gbp_ext_itf.c gbp_fwd.c gbp_fwd_dpo.c gbp_itf.c diff --git a/src/plugins/gbp/gbp.api b/src/plugins/gbp/gbp.api index 6bdcc5d7e0c..bf7c167a42f 100644 --- a/src/plugins/gbp/gbp.api +++ b/src/plugins/gbp/gbp.api @@ -95,12 +95,13 @@ define gbp_route_domain_details enum gbp_endpoint_flags { - NONE = 0, - BOUNCE = 0x1, - REMOTE = 0x2, - LEARNT = 0x4, + GBP_API_ENDPOINT_FLAG_NONE = 0, + GBP_API_ENDPOINT_FLAG_BOUNCE = 0x1, + GBP_API_ENDPOINT_FLAG_REMOTE = 0x2, + GBP_API_ENDPOINT_FLAG_LEARNT = 0x4, /* hey Ole WTF */ - REMOTE_LEARNT = 0x6, + GBP_API_ENDPOINT_FLAG_REMOTE_LEARNT = 0x6, + GBP_API_ENDPOINT_FLAG_EXTERNAL = 0x8, }; typedef gbp_endpoint_tun @@ -220,6 +221,7 @@ enum gbp_subnet_type GBP_API_SUBNET_TRANSPORT, GBP_API_SUBNET_STITCHED_INTERNAL, GBP_API_SUBNET_STITCHED_EXTERNAL, + GBP_API_SUBNET_L3_OUT, }; typeonly define gbp_subnet @@ -379,6 +381,33 @@ define gbp_vxlan_tunnel_details vl_api_gbp_vxlan_tunnel_t tunnel; }; +typeonly define gbp_ext_itf +{ + u32 sw_if_index; + u32 bd_id; + u32 rd_id; +}; + +autoreply define gbp_ext_itf_add_del +{ + u32 client_index; + u32 context; + u8 is_add; + vl_api_gbp_ext_itf_t ext_itf; +}; + +define gbp_ext_itf_dump +{ + u32 client_index; + u32 context; +}; + +define gbp_ext_itf_details +{ + u32 context; + vl_api_gbp_ext_itf_t ext_itf; +}; + /* * Local Variables: * eval: (c-set-style "gnu") diff --git a/src/plugins/gbp/gbp_api.c b/src/plugins/gbp/gbp_api.c index 4402ec15b8c..5f87db8f83c 100644 --- a/src/plugins/gbp/gbp_api.c +++ b/src/plugins/gbp/gbp_api.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -75,6 +76,8 @@ _(GBP_ROUTE_DOMAIN_DUMP, gbp_route_domain_dump) \ _(GBP_RECIRC_ADD_DEL, gbp_recirc_add_del) \ _(GBP_RECIRC_DUMP, gbp_recirc_dump) \ + _(GBP_EXT_ITF_ADD_DEL, gbp_ext_itf_add_del) \ + _(GBP_EXT_ITF_DUMP, gbp_ext_itf_dump) \ _(GBP_CONTRACT_ADD_DEL, gbp_contract_add_del) \ _(GBP_CONTRACT_DUMP, gbp_contract_dump) \ _(GBP_ENDPOINT_LEARN_SET_INACTIVE_THRESHOLD, gbp_endpoint_learn_set_inactive_threshold) \ @@ -95,12 +98,14 @@ gbp_endpoint_flags_decode (vl_api_gbp_endpoint_flags_t v) v = ntohl (v); - if (v & BOUNCE) + if (v & GBP_API_ENDPOINT_FLAG_BOUNCE) f |= GBP_ENDPOINT_FLAG_BOUNCE; - if (v & REMOTE) + if (v & GBP_API_ENDPOINT_FLAG_REMOTE) f |= GBP_ENDPOINT_FLAG_REMOTE; - if (v & LEARNT) + 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); } @@ -112,11 +117,13 @@ gbp_endpoint_flags_encode (gbp_endpoint_flags_t f) if (f & GBP_ENDPOINT_FLAG_BOUNCE) - v |= BOUNCE; + v |= GBP_API_ENDPOINT_FLAG_BOUNCE; if (f & GBP_ENDPOINT_FLAG_REMOTE) - v |= REMOTE; + v |= GBP_API_ENDPOINT_FLAG_REMOTE; if (f & GBP_ENDPOINT_FLAG_LEARNT) - v |= LEARNT; + v |= GBP_API_ENDPOINT_FLAG_LEARNT; + if (f & GBP_ENDPOINT_FLAG_EXTERNAL) + v |= GBP_API_ENDPOINT_FLAG_EXTERNAL; v = htonl (v); @@ -386,6 +393,9 @@ gub_subnet_type_from_api (vl_api_gbp_subnet_type_t a, gbp_subnet_type_t * t) 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_STITCHED_INTERNAL: *t = GBP_SUBNET_STITCHED_INTERNAL; return (0); @@ -440,6 +450,9 @@ gub_subnet_type_to_api (gbp_subnet_type_t t) case GBP_SUBNET_STITCHED_EXTERNAL: a = GBP_API_SUBNET_STITCHED_EXTERNAL; break; + case GBP_SUBNET_L3_OUT: + a = GBP_API_SUBNET_L3_OUT; + break; } a = clib_host_to_net_u32 (a); @@ -642,7 +655,7 @@ vl_api_gbp_recirc_add_del_t_handler (vl_api_gbp_recirc_add_del_t * mp) REPLY_MACRO (VL_API_GBP_RECIRC_ADD_DEL_REPLY + GBP_MSG_BASE); } -static int +static walk_rc_t gbp_recirc_send_details (gbp_recirc_t * gr, void *args) { vl_api_gbp_recirc_details_t *mp; @@ -651,7 +664,7 @@ gbp_recirc_send_details (gbp_recirc_t * gr, void *args) ctx = args; mp = vl_msg_api_alloc (sizeof (*mp)); if (!mp) - return 1; + return (WALK_STOP); clib_memset (mp, 0, sizeof (*mp)); mp->_vl_msg_id = ntohs (VL_API_GBP_RECIRC_DETAILS + GBP_MSG_BASE); @@ -663,7 +676,7 @@ gbp_recirc_send_details (gbp_recirc_t * gr, void *args) vl_api_send_msg (ctx->reg, (u8 *) mp); - return (1); + return (WALK_CONTINUE); } static void @@ -683,6 +696,70 @@ vl_api_gbp_recirc_dump_t_handler (vl_api_gbp_recirc_dump_t * mp) 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; + int rv = 0; + + sw_if_index = ntohl (mp->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 (mp->ext_itf.bd_id), + ntohl (mp->ext_itf.rd_id)); + 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.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 (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) diff --git a/src/plugins/gbp/gbp_bridge_domain.c b/src/plugins/gbp/gbp_bridge_domain.c index 298819f87f1..21ffe9cc314 100644 --- a/src/plugins/gbp/gbp_bridge_domain.c +++ b/src/plugins/gbp/gbp_bridge_domain.c @@ -56,6 +56,16 @@ gbp_bridge_domain_lock (index_t 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) { diff --git a/src/plugins/gbp/gbp_bridge_domain.h b/src/plugins/gbp/gbp_bridge_domain.h index cc03320871a..65f133c84da 100644 --- a/src/plugins/gbp/gbp_bridge_domain.h +++ b/src/plugins/gbp/gbp_bridge_domain.h @@ -78,6 +78,7 @@ 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); diff --git a/src/plugins/gbp/gbp_classify.c b/src/plugins/gbp/gbp_classify.c index 4846911ed65..94044ae7214 100644 --- a/src/plugins/gbp/gbp_classify.c +++ b/src/plugins/gbp/gbp_classify.c @@ -16,6 +16,11 @@ */ #include +#include +#include +#include +#include +#include #include #include #include @@ -25,9 +30,10 @@ typedef enum gbp_src_classify_type_t_ { GBP_SRC_CLASSIFY_NULL, GBP_SRC_CLASSIFY_PORT, + GBP_SRC_CLASSIFY_LPM, } gbp_src_classify_type_t; -#define GBP_SRC_N_CLASSIFY (GBP_SRC_CLASSIFY_PORT + 1) +#define GBP_SRC_N_CLASSIFY (GBP_SRC_CLASSIFY_LPM + 1) /** * Grouping of global data for the GBP source EPG classification feature @@ -297,6 +303,276 @@ VNET_FEATURE_INIT (gbp_ip6_src_classify_feat_node, static) = .runs_before = VNET_FEATURES ("nat66-out2in"), }; +/* *INDENT-ON* */ + +typedef enum gbp_lpm_classify_next_t_ +{ + GPB_LPM_CLASSIFY_DROP, +} gbp_lpm_classify_next_t; + +always_inline dpo_proto_t +ethertype_to_dpo_proto (const ethernet_header_t * eh0) +{ + u16 etype = clib_net_to_host_u16 (eh0->type); + + switch (etype) + { + case ETHERNET_TYPE_IP4: + return (DPO_PROTO_IP4); + case ETHERNET_TYPE_IP6: + return (DPO_PROTO_IP6); + case ETHERNET_TYPE_VLAN: + { + ethernet_vlan_header_t *vh0; + + vh0 = (ethernet_vlan_header_t *) (eh0 + 1); + + switch (clib_net_to_host_u16 (vh0->type)) + { + case ETHERNET_TYPE_IP4: + return (DPO_PROTO_IP4); + case ETHERNET_TYPE_IP6: + return (DPO_PROTO_IP6); + } + } + } + + return (DPO_PROTO_NONE); +} + +/* + * 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, + dpo_proto_t dproto, u8 is_recirc) +{ + 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; + gbp_lpm_classify_next_t next0; + const gbp_policy_dpo_t *gpd0; + const gbp_ext_itf_t *gx0; + const gbp_recirc_t *gr0; + const dpo_id_t *dpo0; + load_balance_t *lb0; + ip4_header_t *ip4_0; + ip6_header_t *ip6_0; + vlib_buffer_t *b0; + epg_id_t src_epg0; + + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + next0 = GPB_LPM_CLASSIFY_DROP; + + 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 = vlib_buffer_get_current (b0); + else if (DPO_PROTO_IP4 == dproto) + ip6_0 = vlib_buffer_get_current (b0); + else if (DPO_PROTO_ETHERNET == dproto) + { + const ethernet_header_t *eh0; + + eh0 = vlib_buffer_get_current (b0); + + dproto = ethertype_to_dpo_proto (eh0); + + switch (dproto) + { + case DPO_PROTO_IP4: + ip4_0 = (vlib_buffer_get_current (b0) + + vnet_buffer (b0)->l2.l2_len); + break; + case DPO_PROTO_IP6: + ip6_0 = (vlib_buffer_get_current (b0) + + vnet_buffer (b0)->l2.l2_len); + break; + default: + /* not IP so no LPM classify possible */ + src_epg0 = EPG_INVALID; + goto trace; + } + } + + if (is_recirc) + { + gr0 = gbp_recirc_get (sw_if_index0); + fib_index0 = gr0->gr_fib_index[dproto]; + + vnet_feature_next (&next0, b0); + } + else + { + gx0 = gbp_ext_itf_get (sw_if_index0); + fib_index0 = gx0->gx_fib_index[dproto]; + + next0 = vnet_l2_feature_next + (b0, gscm->l2_input_feat_next[GBP_SRC_CLASSIFY_LPM], + L2INPUT_FEAT_GBP_LPM_CLASSIFY); + } + + if (DPO_PROTO_IP4 == dproto) + { + lbi0 = ip4_fib_forwarding_lookup (fib_index0, + &ip4_0->src_address); + } + else if (DPO_PROTO_IP4 == dproto) + { + lbi0 = ip6_fib_table_fwding_lookup (&ip6_main, fib_index0, + &ip6_0->src_address); + } + else + { + /* not IP so no LPM classify possible */ + src_epg0 = EPG_INVALID; + goto trace; + } + lb0 = load_balance_get (lbi0); + dpo0 = load_balance_get_bucket_i (lb0, 0); + + if (gbp_policy_dpo_type == dpo0->dpoi_type) + { + gpd0 = gbp_policy_dpo_get (dpo0->dpoi_index); + src_epg0 = gpd0->gpd_epg; + } + else + { + /* could not classify => drop */ + src_epg0 = EPG_INVALID; + next0 = GPB_LPM_CLASSIFY_DROP; + } + + trace: + vnet_buffer2 (b0)->gbp.src_epg = src_epg0; + + if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED))) + { + gbp_classify_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->src_epg = src_epg0; + } + + 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; +} + +static uword +gbp_ip4_lpm_classify (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return (gbp_lpm_classify_inline (vm, node, frame, DPO_PROTO_IP4, 1)); +} + +static uword +gbp_ip6_lpm_classify (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return (gbp_lpm_classify_inline (vm, node, frame, DPO_PROTO_IP6, 1)); +} + +static uword +gbp_l2_lpm_classify (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return (gbp_lpm_classify_inline (vm, node, frame, DPO_PROTO_ETHERNET, 0)); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (gbp_ip4_lpm_classify_node) = { + .function = gbp_ip4_lpm_classify, + .name = "ip4-gbp-lpm-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 = { + [GPB_LPM_CLASSIFY_DROP] = "ip4-drop" + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (gbp_ip4_lpm_classify_node, gbp_ip4_lpm_classify); + +VLIB_REGISTER_NODE (gbp_ip6_lpm_classify_node) = { + .function = gbp_ip6_lpm_classify, + .name = "ip6-gbp-lpm-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 = { + [GPB_LPM_CLASSIFY_DROP] = "ip6-drop" + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (gbp_ip6_lpm_classify_node, gbp_ip6_lpm_classify); + +VLIB_REGISTER_NODE (gbp_l2_lpm_classify_node) = { + .function = gbp_l2_lpm_classify, + .name = "l2-gbp-lpm-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 = { + [GPB_LPM_CLASSIFY_DROP] = "error-drop" + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (gbp_l2_lpm_classify_node, gbp_l2_lpm_classify); + +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* */ + static clib_error_t * gbp_src_classify_init (vlib_main_t * vm) { @@ -313,6 +589,11 @@ gbp_src_classify_init (vlib_main_t * vm) L2INPUT_N_FEAT, l2input_get_feat_names (), em->l2_input_feat_next[GBP_SRC_CLASSIFY_PORT]); + feat_bitmap_init_next_nodes (vm, + gbp_l2_lpm_classify_node.index, + L2INPUT_N_FEAT, + l2input_get_feat_names (), + em->l2_input_feat_next[GBP_SRC_CLASSIFY_LPM]); return 0; } diff --git a/src/plugins/gbp/gbp_endpoint.c b/src/plugins/gbp/gbp_endpoint.c index e9ced3f9e03..0746b70d57f 100644 --- a/src/plugins/gbp/gbp_endpoint.c +++ b/src/plugins/gbp/gbp_endpoint.c @@ -92,7 +92,19 @@ format_gbp_endpoint_flags (u8 * s, va_list * args) int gbp_endpoint_is_remote (const gbp_endpoint_t * ge) { - return (ge->ge_fwd.gef_flags & GBP_ENDPOINT_FLAG_REMOTE); + 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)); } static void @@ -625,8 +637,12 @@ gbb_endpoint_fwd_recalc (gbp_endpoint_t * ge) { gbp_itf_set_l2_input_feature (gef->gef_itf, gei, L2INPUT_FEAT_GBP_FWD); - if (gbp_endpoint_is_remote (ge)) + 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_set_l2_output_feature (gef->gef_itf, gei, L2OUTPUT_FEAT_GBP_POLICY_MAC); } @@ -711,6 +727,12 @@ gbb_endpoint_fwd_recalc (gbp_endpoint_t * ge) 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)) @@ -728,6 +750,7 @@ gbb_endpoint_fwd_recalc (gbp_endpoint_t * ge) FIB_SOURCE_PLUGIN_HI, FIB_ENTRY_FLAG_INTERPOSE, &policy_dpo); + dpo_reset (&policy_dpo); } /* @@ -735,7 +758,7 @@ gbb_endpoint_fwd_recalc (gbp_endpoint_t * ge) * that if this EP has moved from some other place in the * 'fabric', upstream devices are informed */ - if (!gbp_endpoint_is_remote (ge) && ~0 != gg->gg_uplink_sw_if_index) + if (gbp_endpoint_is_local (ge) && ~0 != gg->gg_uplink_sw_if_index) { gbp_endpoint_add_itf (gef->gef_itf, gei); if (FIB_PROTOCOL_IP4 == pfx->fp_proto) @@ -750,13 +773,14 @@ gbb_endpoint_fwd_recalc (gbp_endpoint_t * ge) } } - if (!gbp_endpoint_is_remote (ge)) + if (gbp_endpoint_is_local (ge) && !gbp_endpoint_is_external (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; @@ -1444,6 +1468,10 @@ void gbp_learn_set_inactive_threshold (u32 threshold) { GBP_ENDPOINT_INACTIVE_TIME = threshold; + + vlib_process_signal_event (vlib_get_main (), + gbp_scanner_node.index, + GBP_ENDPOINT_SCAN_SET_TIME, 0); } f64 diff --git a/src/plugins/gbp/gbp_endpoint.h b/src/plugins/gbp/gbp_endpoint.h index d56e91daa99..4b4c2e0b627 100644 --- a/src/plugins/gbp/gbp_endpoint.h +++ b/src/plugins/gbp/gbp_endpoint.h @@ -32,8 +32,9 @@ typedef enum gbp_endpoint_attr_t_ { GBP_ENDPOINT_ATTR_FIRST = 0, GBP_ENDPOINT_ATTR_BOUNCE = GBP_ENDPOINT_ATTR_FIRST, - GBP_ENDPOINT_ATTR_REMOTE = 1, - GBP_ENDPOINT_ATTR_LEARNT = 2, + GBP_ENDPOINT_ATTR_REMOTE, + GBP_ENDPOINT_ATTR_LEARNT, + GBP_ENDPOINT_ATTR_EXTERNAL, GBP_ENDPOINT_ATTR_LAST, } gbp_endpoint_attr_t; @@ -43,12 +44,14 @@ typedef enum gbp_endpoint_flags_t_ 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); @@ -235,6 +238,8 @@ extern void gbp_endpoint_walk (gbp_endpoint_cb_t cb, void *ctx); extern void gbp_endpoint_scan (vlib_main_t * vm); extern f64 gbp_endpoint_scan_threshold (void); 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); diff --git a/src/plugins/gbp/gbp_ext_itf.c b/src/plugins/gbp/gbp_ext_itf.c new file mode 100644 index 00000000000..57ff625d64a --- /dev/null +++ b/src/plugins/gbp/gbp_ext_itf.c @@ -0,0 +1,203 @@ +/* + * 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 in %U", + format_gbp_itf, gx->gx_itf, + format_gbp_bridge_domain, gx->gx_bd)); +} + +int +gbp_ext_itf_add (u32 sw_if_index, u32 bd_id, u32 rd_id) +{ + 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_bridge_domain_t *gb; + 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; + + gb = gbp_bridge_domain_get (gbi); + gr = gbp_route_domain_get (gri); + + gx->gx_bd = gbi; + gx->gx_rd = gri; + + FOR_EACH_FIB_IP_PROTOCOL (fproto) + { + gx->gx_fib_index[fproto] = + gr->grd_fib_index[fib_proto_to_dpo (fproto)]; + } + + gx->gx_itf = gbp_itf_add_and_lock (sw_if_index, gb->gb_bd_index); + gbp_itf_set_l2_input_feature (gx->gx_itf, (gxi | GBP_EXT_ITF_ID), + L2INPUT_FEAT_GBP_LPM_CLASSIFY); + + 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; + + 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_set_l2_input_feature (gx->gx_itf, + (gxi | GBP_EXT_ITF_ID), + L2INPUT_FEAT_NONE); + 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); +} + +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 new file mode 100644 index 00000000000..dafcd08a2e4 --- /dev/null +++ b/src/plugins/gbp/gbp_ext_itf.h @@ -0,0 +1,81 @@ +/* + * 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 + +/** + * 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 + */ + u32 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]; + +} gbp_ext_itf_t; + + +extern int gbp_ext_itf_add (u32 sw_if_index, u32 bd_id, u32 rd_id); +extern int gbp_ext_itf_delete (u32 sw_if_index); + +extern 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_itf.c b/src/plugins/gbp/gbp_itf.c index e1afdfb94dc..7cfe9da0ab0 100644 --- a/src/plugins/gbp/gbp_itf.c +++ b/src/plugins/gbp/gbp_itf.c @@ -196,9 +196,9 @@ format_gbp_itf (u8 * s, va_list * args) s = format (s, "%U locks:%d input-feats:%U output-feats:%U", format_vnet_sw_if_index_name, vnet_get_main (), - gi->gi_sw_if_index, gi->gi_locks, format_l2_input_features, 0, - gi->gi_l2_input_fb, format_l2_output_features, 0, - gi->gi_l2_output_fb); + gi->gi_sw_if_index, gi->gi_locks, + format_l2_input_features, gi->gi_l2_input_fb, 0, + format_l2_output_features, gi->gi_l2_output_fb, 0); return (s); } diff --git a/src/plugins/gbp/gbp_policy_dpo.c b/src/plugins/gbp/gbp_policy_dpo.c index 7c53d1bcedb..523e23c4363 100644 --- a/src/plugins/gbp/gbp_policy_dpo.c +++ b/src/plugins/gbp/gbp_policy_dpo.c @@ -14,9 +14,6 @@ */ #include -#include -#include -#include #include #include @@ -27,31 +24,19 @@ /** * DPO pool */ -static gbp_policy_dpo_t *gbp_policy_dpo_pool; +gbp_policy_dpo_t *gbp_policy_dpo_pool; /** * DPO type registered for these GBP FWD */ -static dpo_type_t gbp_policy_dpo_type; - -static inline gbp_policy_dpo_t * -gbp_policy_dpo_get_i (index_t index) -{ - return (pool_elt_at_index (gbp_policy_dpo_pool, index)); -} - -gbp_policy_dpo_t * -gbp_policy_dpo_get (index_t index) -{ - return (gbp_policy_dpo_get_i (index)); -} +dpo_type_t gbp_policy_dpo_type; static gbp_policy_dpo_t * gbp_policy_dpo_alloc (void) { gbp_policy_dpo_t *gpd; - pool_get_zero (gbp_policy_dpo_pool, gpd); + pool_get_aligned_zero (gbp_policy_dpo_pool, gpd, CLIB_CACHE_LINE_BYTES); return (gpd); } @@ -61,7 +46,7 @@ gbp_policy_dpo_get_from_dpo (const dpo_id_t * dpo) { ASSERT (gbp_policy_dpo_type == dpo->dpoi_type); - return (gbp_policy_dpo_get_i (dpo->dpoi_index)); + return (gbp_policy_dpo_get (dpo->dpoi_index)); } static inline index_t @@ -138,7 +123,7 @@ 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_i (index); + gbp_policy_dpo_t *gpd = gbp_policy_dpo_get (index); vnet_main_t *vnm = vnet_get_main (); s = format (s, "gbp-policy-dpo: %U, epg:%d out:%U", @@ -293,8 +278,7 @@ gbp_policy_dpo_inline (vlib_main_t * vm, b0 = vlib_get_buffer (vm, bi0); gc0 = NULL; - gpd0 = - gbp_policy_dpo_get_i (vnet_buffer (b0)->ip.adj_index[VLIB_TX]); + 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; if (vnet_buffer2 (b0)->gbp.flags & VXLAN_GBP_GPFLAGS_A) @@ -451,191 +435,6 @@ VLIB_REGISTER_NODE (ip6_gbp_policy_dpo_node) = { VLIB_NODE_FUNCTION_MULTIARCH (ip4_gbp_policy_dpo_node, ip4_gbp_policy_dpo) VLIB_NODE_FUNCTION_MULTIARCH (ip6_gbp_policy_dpo_node, ip6_gbp_policy_dpo) -/* *INDENT-ON* */ - - /** - * per-packet trace data - */ -typedef struct gbp_classify_trace_t_ -{ - /* per-pkt trace data */ - epg_id_t src_epg; -} gbp_classify_trace_t; - -typedef enum gbp_lpm_classify_next_t_ -{ - GPB_LPM_CLASSIFY_DROP, -} gbp_lpm_classify_next_t; - -/* - * 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, fib_protocol_t fproto) -{ - 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; - gbp_lpm_classify_next_t next0; - const gbp_policy_dpo_t *gpd0; - const gbp_recirc_t *gr0; - const dpo_id_t *dpo0; - load_balance_t *lb0; - ip4_header_t *ip4_0; - ip6_header_t *ip6_0; - vlib_buffer_t *b0; - epg_id_t src_epg0; - - bi0 = from[0]; - to_next[0] = bi0; - from += 1; - to_next += 1; - n_left_from -= 1; - n_left_to_next -= 1; - next0 = GPB_LPM_CLASSIFY_DROP; - - b0 = vlib_get_buffer (vm, bi0); - - sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; - gr0 = gbp_recirc_get (sw_if_index0); - fib_index0 = gr0->gr_fib_index[fproto]; - - if (FIB_PROTOCOL_IP4 == fproto) - { - ip4_0 = vlib_buffer_get_current (b0); - lbi0 = ip4_fib_forwarding_lookup (fib_index0, - &ip4_0->src_address); - } - else - { - ip6_0 = vlib_buffer_get_current (b0); - lbi0 = ip6_fib_table_fwding_lookup (&ip6_main, fib_index0, - &ip6_0->src_address); - } - - lb0 = load_balance_get (lbi0); - dpo0 = load_balance_get_bucket_i (lb0, 0); - - if (gbp_policy_dpo_type == dpo0->dpoi_type) - { - gpd0 = gbp_policy_dpo_get_i (dpo0->dpoi_index); - src_epg0 = gpd0->gpd_epg; - vnet_feature_next (&next0, b0); - } - else - { - /* could not classify => drop */ - src_epg0 = 0; - } - - vnet_buffer2 (b0)->gbp.src_epg = src_epg0; - - if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED))) - { - gbp_classify_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->src_epg = src_epg0; - } - - 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; -} - -static uword -gbp_ip4_lpm_classify (vlib_main_t * vm, - vlib_node_runtime_t * node, vlib_frame_t * frame) -{ - return (gbp_lpm_classify_inline (vm, node, frame, FIB_PROTOCOL_IP4)); -} - -static uword -gbp_ip6_lpm_classify (vlib_main_t * vm, - vlib_node_runtime_t * node, vlib_frame_t * frame) -{ - return (gbp_lpm_classify_inline (vm, node, frame, FIB_PROTOCOL_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, "src-epg:%d", t->src_epg); - - return s; -} - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (gbp_ip4_lpm_classify_node) = { - .function = gbp_ip4_lpm_classify, - .name = "ip4-gbp-lpm-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 = { - [GPB_LPM_CLASSIFY_DROP] = "ip4-drop" - }, -}; - -VLIB_NODE_FUNCTION_MULTIARCH (gbp_ip4_lpm_classify_node, gbp_ip4_lpm_classify); - -VLIB_REGISTER_NODE (gbp_ip6_lpm_classify_node) = { - .function = gbp_ip6_lpm_classify, - .name = "ip6-gbp-lpm-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 = { - [GPB_LPM_CLASSIFY_DROP] = "ip6-drop" - }, -}; - -VLIB_NODE_FUNCTION_MULTIARCH (gbp_ip6_lpm_classify_node, gbp_ip6_lpm_classify); - -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* */ /* diff --git a/src/plugins/gbp/gbp_policy_dpo.h b/src/plugins/gbp/gbp_policy_dpo.h index 8ee086d1cb0..097205b8255 100644 --- a/src/plugins/gbp/gbp_policy_dpo.h +++ b/src/plugins/gbp/gbp_policy_dpo.h @@ -25,6 +25,8 @@ */ typedef struct gbp_policy_dpo_t_ { + CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); + /** * The protocol of packets using this DPO */ @@ -46,7 +48,7 @@ typedef struct gbp_policy_dpo_t_ u16 gpd_locks; /** - * Stacked DPO on DVR of output interface + * Stacked DPO on DVR/ADJ of output interface */ dpo_id_t gpd_dpo; } gbp_policy_dpo_t; @@ -55,14 +57,24 @@ extern void gbp_policy_dpo_add_or_lock (dpo_proto_t dproto, epg_id_t epg, u32 sw_if_index, dpo_id_t * dpo); -extern gbp_policy_dpo_t *gbp_policy_dpo_get (index_t index); - 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)); +} + /* * fd.io coding-style-patch-verification: ON * diff --git a/src/plugins/gbp/gbp_recirc.c b/src/plugins/gbp/gbp_recirc.c index 59588eafacd..d907be01b3c 100644 --- a/src/plugins/gbp/gbp_recirc.c +++ b/src/plugins/gbp/gbp_recirc.c @@ -21,6 +21,8 @@ #include #include +#include + /** * Pool of GBP recircs */ @@ -36,6 +38,12 @@ index_t *gbp_recirc_db; */ 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__); @@ -72,8 +80,7 @@ gbp_recirc_add (u32 sw_if_index, epg_id_t epg_id, u8 is_ext) return (VNET_API_ERROR_NO_SUCH_ENTRY); gbp_endpoint_group_lock (ggi); - pool_get (gbp_recirc_pool, gr); - clib_memset (gr, 0, sizeof (*gr)); + pool_get_zero (gbp_recirc_pool, gr); gri = gr - gbp_recirc_pool; gr->gr_epg = epg_id; @@ -94,7 +101,7 @@ gbp_recirc_add (u32 sw_if_index, epg_id_t epg_id, u8 is_ext) gg = gbp_endpoint_group_get (gr->gr_epgi); FOR_EACH_FIB_IP_PROTOCOL (fproto) { - gr->gr_fib_index[fproto] = + gr->gr_fib_index[fib_proto_to_dpo (fproto)] = gbp_endpoint_group_get_fib_index (gg, fproto); } @@ -103,6 +110,11 @@ gbp_recirc_add (u32 sw_if_index, epg_id_t epg_id, u8 is_ext) */ gr->gr_itf = gbp_itf_add_and_lock (gr->gr_sw_if_index, gg->gg_bd_index); + /* + * 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. @@ -195,6 +207,7 @@ gbp_recirc_delete (u32 sw_if_index) 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); @@ -218,12 +231,12 @@ gbp_recirc_walk (gbp_recirc_cb_t cb, void *ctx) /* *INDENT-ON* */ } -static int +static walk_rc_t gbp_recirc_show_one (gbp_recirc_t * gr, void *ctx) { vlib_cli_output (ctx, " %U", format_gbp_recirc, gr); - return (1); + return (WALK_CONTINUE); } static clib_error_t * @@ -256,6 +269,11 @@ 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); } diff --git a/src/plugins/gbp/gbp_recirc.h b/src/plugins/gbp/gbp_recirc.h index 1d1a88a396b..c577a5f3ea9 100644 --- a/src/plugins/gbp/gbp_recirc.h +++ b/src/plugins/gbp/gbp_recirc.h @@ -20,7 +20,10 @@ #include /** - * An Endpoint Group representation + * 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_ { @@ -37,7 +40,7 @@ typedef struct gpb_recirc_t_ /** * FIB indices the EPG is mapped to */ - u32 gr_fib_index[FIB_PROTOCOL_IP_MAX]; + u32 gr_fib_index[DPO_PROTO_NUM]; /** * Is the interface for packets post-NAT translation (i.e. ext) @@ -59,7 +62,7 @@ typedef struct gpb_recirc_t_ extern int gbp_recirc_add (u32 sw_if_index, epg_id_t epg_id, u8 is_ext); extern void gbp_recirc_delete (u32 sw_if_index); -typedef int (*gbp_recirc_cb_t) (gbp_recirc_t * gbpe, void *ctx); +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); /** diff --git a/src/plugins/gbp/gbp_route_domain.c b/src/plugins/gbp/gbp_route_domain.c index df3959edca6..67b6915b463 100644 --- a/src/plugins/gbp/gbp_route_domain.c +++ b/src/plugins/gbp/gbp_route_domain.c @@ -231,6 +231,16 @@ gbp_route_domain_unlock (index_t index) } } +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); +} + int gbp_route_domain_delete (u32 rd_id) { diff --git a/src/plugins/gbp/gbp_route_domain.h b/src/plugins/gbp/gbp_route_domain.h index ba5d5e49aca..b83d598ad38 100644 --- a/src/plugins/gbp/gbp_route_domain.h +++ b/src/plugins/gbp/gbp_route_domain.h @@ -66,6 +66,7 @@ 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); 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); diff --git a/src/plugins/gbp/gbp_scanner.c b/src/plugins/gbp/gbp_scanner.c index a2d0c9a98cb..90507a60568 100644 --- a/src/plugins/gbp/gbp_scanner.c +++ b/src/plugins/gbp/gbp_scanner.c @@ -19,8 +19,16 @@ #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__); @@ -28,13 +36,13 @@ static uword gbp_scanner (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f) { uword event_type, *event_data = 0; - bool enabled = 0, do_scan = 0; + bool do_scan = 0; while (1) { do_scan = 0; - if (enabled) + if (gs_enabled) { /* scan every 'inactive threshold' seconds */ vlib_process_wait_for_event_or_clock (vm, @@ -55,11 +63,14 @@ gbp_scanner (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f) break; case GBP_ENDPOINT_SCAN_START: - enabled = 1; + gs_enabled = 1; break; case GBP_ENDPOINT_SCAN_STOP: - enabled = 0; + gs_enabled = 0; + break; + + case GBP_ENDPOINT_SCAN_SET_TIME: break; default: @@ -84,6 +95,30 @@ VLIB_REGISTER_NODE (gbp_scanner_node) = { }; /* *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:%f", + gs_enabled, gbp_endpoint_scan_threshold ()); + + 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) diff --git a/src/plugins/gbp/gbp_scanner.h b/src/plugins/gbp/gbp_scanner.h index 070da3892ca..1133167d927 100644 --- a/src/plugins/gbp/gbp_scanner.h +++ b/src/plugins/gbp/gbp_scanner.h @@ -22,8 +22,7 @@ typedef enum gbp_scan_event_t_ { GBP_ENDPOINT_SCAN_START, GBP_ENDPOINT_SCAN_STOP, - GBP_VXLAN_SCAN_START, - GBP_VXLAN_SCAN_STOP, + GBP_ENDPOINT_SCAN_SET_TIME, } gbp_scan_event_t; extern vlib_node_registration_t gbp_scanner_node; diff --git a/src/plugins/gbp/gbp_subnet.c b/src/plugins/gbp/gbp_subnet.c index d9d42997aa7..b0b6db8040e 100644 --- a/src/plugins/gbp/gbp_subnet.c +++ b/src/plugins/gbp/gbp_subnet.c @@ -46,7 +46,13 @@ typedef struct gbp_subnet_t_ epg_id_t gs_epg; u32 gs_sw_if_index; } gs_stitched_external; + struct + { + epg_id_t gs_epg; + } gs_l3_out; }; + + fib_node_index_t gs_fei; } gbp_subnet_t; /** @@ -102,7 +108,7 @@ gbp_subnet_db_del (gbp_subnet_t * gs) static int -gbp_subnet_transport_add (const gbp_subnet_t * gs) +gbp_subnet_transport_add (gbp_subnet_t * gs) { dpo_id_t gfd = DPO_INVALID; gbp_route_domain_t *grd; @@ -111,14 +117,15 @@ gbp_subnet_transport_add (const gbp_subnet_t * gs) fproto = gs->gs_key->gsk_pfx.fp_proto; grd = gbp_route_domain_get (gs->gs_rd); - fib_table_entry_update_one_path (gs->gs_key->gsk_fib_index, - &gs->gs_key->gsk_pfx, - FIB_SOURCE_PLUGIN_HI, - 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); + gs->gs_fei = fib_table_entry_update_one_path (gs->gs_key->gsk_fib_index, + &gs->gs_key->gsk_pfx, + FIB_SOURCE_PLUGIN_HI, + 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); @@ -126,17 +133,18 @@ gbp_subnet_transport_add (const gbp_subnet_t * gs) } static int -gbp_subnet_internal_add (const gbp_subnet_t * gs) +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); - fib_table_entry_special_dpo_update (gs->gs_key->gsk_fib_index, - &gs->gs_key->gsk_pfx, - FIB_SOURCE_PLUGIN_HI, - FIB_ENTRY_FLAG_EXCLUSIVE, &gfd); + gs->gs_fei = fib_table_entry_special_dpo_update (gs->gs_key->gsk_fib_index, + &gs->gs_key->gsk_pfx, + FIB_SOURCE_PLUGIN_HI, + FIB_ENTRY_FLAG_EXCLUSIVE, + &gfd); dpo_reset (&gfd); @@ -155,12 +163,33 @@ gbp_subnet_external_add (gbp_subnet_t * gs, u32 sw_if_index, epg_id_t epg) gs->gs_stitched_external.gs_epg, gs->gs_stitched_external.gs_sw_if_index, &gpd); - fib_table_entry_special_dpo_update (gs->gs_key->gsk_fib_index, - &gs->gs_key->gsk_pfx, - FIB_SOURCE_PLUGIN_HI, - (FIB_ENTRY_FLAG_EXCLUSIVE | - FIB_ENTRY_FLAG_LOOSE_URPF_EXEMPT), - &gpd); + gs->gs_fei = fib_table_entry_special_dpo_update (gs->gs_key->gsk_fib_index, + &gs->gs_key->gsk_pfx, + FIB_SOURCE_PLUGIN_HI, + (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, u32 sw_if_index, epg_id_t epg) +{ + dpo_id_t gpd = DPO_INVALID; + + gs->gs_l3_out.gs_epg = epg; + + gbp_policy_dpo_add_or_lock (fib_proto_to_dpo (gs->gs_key->gsk_pfx.fp_proto), + gs->gs_l3_out.gs_epg, ~0, &gpd); + + gs->gs_fei = fib_table_entry_special_dpo_add (gs->gs_key->gsk_fib_index, + &gs->gs_key->gsk_pfx, + FIB_SOURCE_SPECIAL, + FIB_ENTRY_FLAG_INTERPOSE, + &gpd); dpo_reset (&gpd); @@ -190,7 +219,10 @@ gbp_subnet_del (u32 rd_id, const fib_prefix_t * pfx) gs = pool_elt_at_index (gbp_subnet_pool, gsi); - fib_table_entry_delete (fib_index, pfx, FIB_SOURCE_PLUGIN_HI); + if (GBP_SUBNET_L3_OUT == gs->gs_type) + fib_table_entry_delete (fib_index, pfx, FIB_SOURCE_SPECIAL); + else + fib_table_entry_delete (fib_index, pfx, FIB_SOURCE_PLUGIN_HI); gbp_subnet_db_del (gs); gbp_route_domain_unlock (gs->gs_rd); @@ -243,6 +275,9 @@ gbp_subnet_add (u32 rd_id, case GBP_SUBNET_TRANSPORT: rv = gbp_subnet_transport_add (gs); break; + case GBP_SUBNET_L3_OUT: + rv = gbp_subnet_l3_out_add (gs, sw_if_index, epg); + break; } return (rv); @@ -274,10 +309,13 @@ gbp_subnet_walk (gbp_subnet_cb_t cb, void *ctx) sw_if_index = gs->gs_stitched_external.gs_sw_if_index; epg = gs->gs_stitched_external.gs_epg; break; + case GBP_SUBNET_L3_OUT: + epg = gs->gs_l3_out.gs_epg; + break; } if (WALK_STOP == cb (grd->grd_id, &gs->gs_key->gsk_pfx, - gs->gs_type, epg, sw_if_index, ctx)) + gs->gs_type, sw_if_index, epg, ctx)) break; })); /* *INDENT-ON* */ @@ -302,6 +340,8 @@ format_gbp_subnet_type (u8 * s, va_list * args) return (format (s, "stitched-external")); case GBP_SUBNET_TRANSPORT: return (format (s, "transport")); + case GBP_SUBNET_L3_OUT: + return (format (s, "l3-out")); } return (format (s, "unknown")); @@ -334,20 +374,17 @@ format_gbp_subnet (u8 * s, va_list * args) format_vnet_sw_if_index_name, vnet_get_main (), gs->gs_stitched_external.gs_sw_if_index); break; + case GBP_SUBNET_L3_OUT: + s = format (s, " {epg:%d}", gs->gs_l3_out.gs_epg); + break; } switch (flags) { case GBP_SUBNET_SHOW_DETAILS: { - fib_node_index_t fei; - - fei = fib_table_lookup_exact_match (gs->gs_key->gsk_fib_index, - &gs->gs_key->gsk_pfx); - - s = - format (s, "\n %U", format_fib_entry, fei, - FIB_ENTRY_FORMAT_DETAIL); + s = format (s, "\n %U", format_fib_entry, gs->gs_fei, + FIB_ENTRY_FORMAT_DETAIL); } case GBP_SUBNET_SHOW_BRIEF: break; diff --git a/src/plugins/gbp/gbp_subnet.h b/src/plugins/gbp/gbp_subnet.h index b6906dee674..5fbd4b23df0 100644 --- a/src/plugins/gbp/gbp_subnet.h +++ b/src/plugins/gbp/gbp_subnet.h @@ -23,6 +23,7 @@ typedef enum gbp_subnet_type_t_ GBP_SUBNET_TRANSPORT, GBP_SUBNET_STITCHED_INTERNAL, GBP_SUBNET_STITCHED_EXTERNAL, + GBP_SUBNET_L3_OUT, } gbp_subnet_type_t; extern int gbp_subnet_add (u32 rd_id, diff --git a/src/vnet/l2/l2_input.h b/src/vnet/l2/l2_input.h index 4a5cf6c8748..93da1277e67 100644 --- a/src/vnet/l2/l2_input.h +++ b/src/vnet/l2/l2_input.h @@ -113,6 +113,7 @@ l2input_bd_config (u32 bd_index) _(GBP_LEARN, "gbp-learn-l2") \ _(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/test/test_gbp.py b/test/test_gbp.py index 68bbfe4a7c9..4a5969213be 100644 --- a/test/test_gbp.py +++ b/test/test_gbp.py @@ -196,6 +196,46 @@ class VppGbpRecirc(VppObject): return False +class VppGbpExtItf(VppObject): + """ + GBP ExtItfulation Interface + """ + + def __init__(self, test, itf, bd, rd): + self._test = test + self.itf = itf + self.bd = bd + self.rd = rd + + def add_vpp_config(self): + self._test.vapi.gbp_ext_itf_add_del( + 1, + self.itf.sw_if_index, + self.bd.bd_id, + self.rd.rd_id) + self._test.registry.register(self, self._test.logger) + + def remove_vpp_config(self): + self._test.vapi.gbp_ext_itf_add_del( + 0, + self.itf.sw_if_index, + self.bd.bd_id, + self.rd.rd_id) + + def __str__(self): + return self.object_id() + + def object_id(self): + return "gbp-ext-itf:[%d]" % (self.itf.sw_if_index) + + 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 @@ -650,6 +690,8 @@ class TestGBP(VppTestCase): def test_gbp(self): """ Group Based Policy """ + ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t + # # Bridge Domains # @@ -794,8 +836,6 @@ class TestGBP(VppTestCase): VppIpInterfaceBind(self, recirc.recirc, recirc.epg.rd.t6).add_vpp_config() - self.vapi.sw_interface_set_l2_emulation( - recirc.recirc.sw_if_index) self.vapi.nat44_interface_add_del_feature( recirc.recirc.sw_if_index, is_inside=0, @@ -1375,8 +1415,6 @@ class TestGBP(VppTestCase): is_add=0) for recirc in recircs: - self.vapi.sw_interface_set_l2_emulation( - recirc.recirc.sw_if_index, enable=0) self.vapi.nat44_interface_add_del_feature( recirc.recirc.sw_if_index, is_inside=0, @@ -1389,6 +1427,7 @@ class TestGBP(VppTestCase): def test_gbp_learn_l2(self): """ GBP L2 Endpoint Learning """ + 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'}, @@ -1550,13 +1589,8 @@ class TestGBP(VppTestCase): self.sleep(2) for l in learnt: self.assertFalse(find_gbp_endpoint(self, - tep1_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")) - # # repeat. the do not learn bit is set so the EPs are not learnt # @@ -1791,6 +1825,7 @@ class TestGBP(VppTestCase): 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'}, @@ -1960,6 +1995,7 @@ class TestGBP(VppTestCase): self.vapi.cli("set logging class gbp 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" @@ -2218,7 +2254,7 @@ class TestGBP(VppTestCase): # # Add a route to static EP's v4 and v6 subnet - # packets should be send on the v4/v6 uu=fwd interface resp. + # packets should be sent on the v4/v6 uu=fwd interface resp. # se_10_24 = VppGbpSubnet( self, rd1, "10.0.0.0", 24, @@ -2283,7 +2319,7 @@ class TestGBP(VppTestCase): epg_220, None, "10.0.0.88", "11.0.0.88", "2001:10::88", "3001::88", - VppEnum.vl_api_gbp_endpoint_flags_t.REMOTE, + ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE, self.pg2.local_ip4, self.pg2.remote_hosts[1].ip4, mac=None) @@ -2296,7 +2332,7 @@ class TestGBP(VppTestCase): epg_220, None, learnt[0]['ip'], "11.0.0.101", learnt[0]['ip6'], "3001::101", - VppEnum.vl_api_gbp_endpoint_flags_t.REMOTE, + ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE, self.pg2.local_ip4, self.pg2.remote_hosts[1].ip4, mac=None) @@ -2407,6 +2443,7 @@ class TestGBP(VppTestCase): self.vapi.cli("set logging class gbp 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" @@ -2960,6 +2997,304 @@ class TestGBP(VppTestCase): self.assertEqual(rx[IPv6].src, ep1.ip6.address) self.assertEqual(rx[IPv6].dst, "2001:10::88") + # + # cleanup + # + 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 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, t4, t6) + rd1.add_vpp_config() + + self.loop0.set_mac(self.router_mac.address) + + # + # Bind the BVI to the RD + # + VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config() + 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, self.loop0) + gbd1.add_vpp_config() + + # + # The Endpoint-groups in which the external endpoints exist + # + epg_220 = VppGbpEndpointGroup(self, 220, rd1, gbd1, + None, gbd1.bvi, + "10.0.0.128", + "2001:10::128") + epg_220.add_vpp_config() + + # the BVIs have the subnets applied ... + ip4_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", 24) + ip4_addr.add_vpp_config() + ip6_addr = VppIpInterfaceAddress(self, gbd1.bvi, "2001:10::128", 64) + ip6_addr.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, + epg=200) + l3o_1.add_vpp_config() + + # + # an external interface attached to the outside world and the + # external BD + # + vlan_100 = VppDot1QSubint(self, self.pg0, 100) + vlan_100.admin_up() + ext_itf = VppGbpExtItf(self, vlan_100, bd1, rd1) + ext_itf.add_vpp_config() + + # + # 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() + bp_bm = VppBridgeDomainPort(self, bd1, tun_bm, + port_type=L2_PORT_TYPE.NORMAL) + bp_bm.add_vpp_config() + + # + # an unicast vxlan-gbp for inter-BD traffic + # + vx_tun_l3 = VppGbpVxlanTunnel( + self, 444, rd1.rd_id, + VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3) + vx_tun_l3.add_vpp_config() + + # + # packets destined to unkown addresses in the BVI's subnet + # are ARP'd for + # + p4 = (Ether(src=self.pg0.remote_mac, dst=self.router_mac.address) / + Dot1Q(vlan=100) / + IP(src="10.0.0.1", dst="10.0.0.88") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + p6 = (Ether(src=self.pg0.remote_mac, dst=self.router_mac.address) / + Dot1Q(vlan=100) / + IPv6(src="2001:10::1", dst="2001:10::88") / + UDP(sport=1234, dport=1234) / + Raw('\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 is not applied since we don't know where it's going + self.assertFalse(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + + inner = rx[VXLAN].payload + + self.assertTrue(inner.haslayer(ARP)) + + # + # An external Endpoint + # + eep = VppGbpEndpoint(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) + eep.add_vpp_config() + + # + # A remote 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() + + # + # 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=220, flags=0x88) / + Ether(src=self.pg0.remote_mac, dst=self.router_mac.address) / + IP(src="10.0.0.101", dst="10.0.0.1") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rxs = self.send_and_expect(self.pg7, p * 1, self.pg0) + + # + # A subnet reachable through the external EP + # + ip_220 = VppIpRoute(self, "10.220.0.0", 24, + [VppRoutePath(eep.ip4.address, + eep.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, + epg=220) + l3o_220.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=220, flags=0x88) / + Ether(src=self.pg0.remote_mac, dst=self.router_mac.address) / + IP(src="10.0.0.101", dst="10.220.0.1") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rxs = self.send_and_expect(self.pg7, p * 1, self.pg0) + + # + # another external subnet, this time in a different EPG + # + ip_200 = VppIpRoute(self, "10.200.0.0", 24, + [VppRoutePath(eep.ip4.address, + eep.epg.bvi.sw_if_index)], + table_id=t4.table_id) + ip_200.add_vpp_config() + + l3o_200 = VppGbpSubnet( + self, rd1, "10.200.0.0", 24, + VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT, + epg=200) + l3o_200.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=220, flags=0x88) / + Ether(src=self.pg0.remote_mac, dst=self.router_mac.address) / + IP(src="10.0.0.101", dst="10.200.0.1") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + # + # packets dropped due to lack of contract. + # + rxs = self.send_and_assert_no_replies(self.pg7, p * 1) + + # + # from the the subnet in EPG 220 beyond the external to remote + # + p4 = (Ether(src=self.pg0.remote_mac, dst=self.router_mac.address) / + Dot1Q(vlan=100) / + IP(src="10.220.0.1", dst=rep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\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, self.pg7.remote_ip4) + self.assertEqual(rx[VXLAN].vni, 444) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + self.assertTrue(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + + # + # from the the subnet in EPG 200 beyond the external to remote + # dropped due to no contract + # + p4 = (Ether(src=self.pg0.remote_mac, dst=self.router_mac.address) / + Dot1Q(vlan=100) / + IP(src="10.200.0.1", dst=rep.ip4.address) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + rxs = self.send_and_assert_no_replies(self.pg0, p4 * 1) + + # + # add a contract + # + acl = VppGbpAcl(self) + rule = acl.create_rule(permit_deny=1, proto=17) + rule2 = acl.create_rule(is_ipv6=1, permit_deny=1, proto=17) + acl_index = acl.add_vpp_config([rule, rule2]) + c1 = VppGbpContract( + self, 200, 220, acl_index, + [VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + []), + VppGbpContractRule( + VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT, + [])]) + c1.add_vpp_config() + + 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, self.pg7.remote_ip4) + self.assertEqual(rx[VXLAN].vni, 444) + self.assertTrue(rx[VXLAN].flags.G) + self.assertTrue(rx[VXLAN].flags.Instance) + self.assertTrue(rx[VXLAN].gpflags.A) + self.assertFalse(rx[VXLAN].gpflags.D) + + # + # cleanup + # + self.pg7.unconfig_ip4() + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 662732ffb30..370758764df 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -3624,6 +3624,19 @@ class VppPapiProvider(object): """ GBP recirc Dump """ return self.api(self.papi.gbp_recirc_dump, {}) + def gbp_ext_itf_add_del(self, is_add, sw_if_index, bd_id, rd_id): + """ GBP recirc Add/Del """ + return self.api(self.papi.gbp_ext_itf_add_del, + {'is_add': is_add, + 'ext_itf': { + 'sw_if_index': sw_if_index, + 'bd_id': bd_id, + 'rd_id': rd_id}}) + + def gbp_ext_itf_dump(self): + """ GBP recirc Dump """ + return self.api(self.papi.gbp_ext_itf_dump, {}) + def gbp_subnet_add_del(self, is_add, rd_id, prefix, type, sw_if_index=0xffffffff, -- 2.16.6