From 49c7f0ca1770304183a8bdfa23a5df751a056401 Mon Sep 17 00:00:00 2001 From: Mohsin Kazmi Date: Wed, 7 Feb 2018 20:20:36 +0100 Subject: VOM: ACL: Add Object Model for acl ethertype Change-Id: I2b572ebd4b7bb26381f127912a4cc0825c04fc34 Signed-off-by: Mohsin Kazmi --- src/vpp-api/vom/Makefile.am | 3 + src/vpp-api/vom/acl_ethertype.cpp | 210 ++++++++++++++++++++++++++++ src/vpp-api/vom/acl_ethertype.hpp | 247 +++++++++++++++++++++++++++++++++ src/vpp-api/vom/acl_ethertype_cmds.cpp | 93 +++++++++++++ src/vpp-api/vom/acl_ethertype_cmds.hpp | 79 +++++++++++ src/vpp-api/vom/types.cpp | 49 +++++++ src/vpp-api/vom/types.hpp | 66 +++++++++ test/ext/vom_test.cpp | 16 +++ 8 files changed, 763 insertions(+) create mode 100644 src/vpp-api/vom/acl_ethertype.cpp create mode 100644 src/vpp-api/vom/acl_ethertype.hpp create mode 100644 src/vpp-api/vom/acl_ethertype_cmds.cpp create mode 100644 src/vpp-api/vom/acl_ethertype_cmds.hpp diff --git a/src/vpp-api/vom/Makefile.am b/src/vpp-api/vom/Makefile.am index 2cb7f837571..d2a3dde70c5 100644 --- a/src/vpp-api/vom/Makefile.am +++ b/src/vpp-api/vom/Makefile.am @@ -37,6 +37,8 @@ libvom_la_SOURCES = \ types.cpp \ acl_binding_cmds.cpp \ acl_binding.cpp \ + acl_ethertype_cmds.cpp \ + acl_ethertype.cpp \ acl_l2_rule.cpp \ acl_l3_rule.cpp \ acl_list_cmds.cpp \ @@ -110,6 +112,7 @@ vomincludedir = $(includedir)/vom vominclude_HEADERS = \ acl_binding.hpp \ + acl_ethertype.hpp \ acl_l2_rule.hpp \ acl_l3_rule.hpp \ acl_list.hpp \ diff --git a/src/vpp-api/vom/acl_ethertype.cpp b/src/vpp-api/vom/acl_ethertype.cpp new file mode 100644 index 00000000000..2f0163398d0 --- /dev/null +++ b/src/vpp-api/vom/acl_ethertype.cpp @@ -0,0 +1,210 @@ +/* + * 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/acl_ethertype.hpp" +#include "vom/acl_ethertype_cmds.hpp" + +namespace VOM { +namespace ACL { + +ethertype_rule_t::ethertype_rule_t(const ethertype_t& eth, + const direction_t& dir) + : m_eth(eth) + , m_dir(dir) +{ +} + +std::string +ethertype_rule_t::to_string() const +{ + std::ostringstream s; + + s << "[" + << "ethertype:" << m_eth.to_string() << " dir:" << m_dir.to_string() + << "],"; + + return (s.str()); +} + +bool +ethertype_rule_t::operator<(const ethertype_rule_t& other) const +{ + return (m_dir > other.m_dir); +} + +bool +ethertype_rule_t::operator==(const ethertype_rule_t& other) const +{ + return (m_dir == other.m_dir && m_eth == other.m_eth); +} + +uint16_t +ethertype_rule_t::getEthertype() const +{ + return m_eth.value(); +} + +const direction_t& +ethertype_rule_t::getDirection() const +{ + return m_dir; +} + +/** + * A DB of all acl ethertype bindings configs + */ +singular_db acl_ethertype::m_db; + +acl_ethertype::event_handler acl_ethertype::m_evh; + +acl_ethertype::acl_ethertype(const interface& itf, + acl_ethertype::ethertype_rules_t le) + : m_itf(itf.singular()) + , m_le(le) + , m_binding(true) +{ +} + +acl_ethertype::acl_ethertype(const acl_ethertype& o) + : m_itf(o.m_itf) + , m_le(o.m_le) + , m_binding(o.m_binding) +{ +} + +acl_ethertype::~acl_ethertype() +{ + sweep(); + + // not in the DB anymore. + m_db.release(m_itf->key(), this); +} + +void +acl_ethertype::sweep() +{ +} + +const acl_ethertype::key_t& +acl_ethertype::key() const +{ + return (m_itf->key()); +} + +bool +acl_ethertype::operator==(const acl_ethertype& other) const +{ + return (m_itf->key() == other.m_itf->key() && m_le == other.m_le); +} + +std::shared_ptr +acl_ethertype::find(const key_t& key) +{ + return (m_db.find(key)); +} + +void +acl_ethertype::dump(std::ostream& os) +{ + m_db.dump(os); +} + +void +acl_ethertype::replay() +{ + if (m_binding) { + HW::enqueue( + new acl_ethertype_cmds::bind_cmd(m_binding, m_itf->handle(), m_le)); + } +} + +std::string +acl_ethertype::to_string() const +{ + std::ostringstream s; + s << "Acl-Ethertype:" << m_itf->to_string() << " ethertype-rules:"; + auto it = m_le.cbegin(); + while (it != m_le.cend()) { + s << it->to_string(); + ++it; + } + s << " rules-size:" << m_le.size(); + + return (s.str()); +} + +void +acl_ethertype::update(const acl_ethertype& old) +{ + /* + * always update the instance with the latest rules + */ + if (!m_binding || old.m_le != m_le) { + HW::enqueue( + new acl_ethertype_cmds::bind_cmd(m_binding, m_itf->handle(), m_le)); + } +} + +std::shared_ptr +acl_ethertype::find_or_add(const acl_ethertype& temp) +{ + return (m_db.find_or_add(temp.m_itf->key(), temp)); +} + +std::shared_ptr +acl_ethertype::singular() const +{ + return find_or_add(*this); +} + +acl_ethertype::event_handler::event_handler() +{ + OM::register_listener(this); + inspect::register_handler({ "acl-ethertype" }, "ACL Ethertype bindings", + this); +} + +void +acl_ethertype::event_handler::handle_replay() +{ + m_db.replay(); +} + +void +acl_ethertype::event_handler::handle_populate(const client_db::key_t& key) +{ + // FIXME +} + +dependency_t +acl_ethertype::event_handler::order() const +{ + return (dependency_t::BINDING); +} + +void +acl_ethertype::event_handler::show(std::ostream& os) +{ + m_db.dump(os); +} +}; +}; +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/acl_ethertype.hpp b/src/vpp-api/vom/acl_ethertype.hpp new file mode 100644 index 00000000000..42897aebd6a --- /dev/null +++ b/src/vpp-api/vom/acl_ethertype.hpp @@ -0,0 +1,247 @@ +/* + * 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_ACL_ETHERTYPE_H__ +#define __VOM_ACL_ETHERTYPE_H__ + +#include + +#include "vom/hw.hpp" +#include "vom/inspect.hpp" +#include "vom/interface.hpp" +#include "vom/object_base.hpp" +#include "vom/om.hpp" +#include "vom/singular_db.hpp" + +namespace VOM { +namespace ACL { +/** + * An ACL ethertype list comprises a set of inbound ether types and out bound + * ether types + * to be applied to packets. + * A list is bound to a given interface. + */ + +struct ethertype_rule_t +{ +public: + /** + * Constructor + */ + ethertype_rule_t(const ethertype_t& eth, const direction_t& dir); + + /** + * Destructor + */ + ~ethertype_rule_t() = default; + + /** + * convert to string + */ + std::string to_string() const; + + /** + * comparision operator + */ + bool operator<(const ethertype_rule_t& other) const; + + /** + * comparision operator (for testing) + */ + bool operator==(const ethertype_rule_t& other) const; + + /** + * get the ether value + */ + uint16_t getEthertype(void) const; + + /** + * get the direction + */ + const direction_t& getDirection(void) const; + +private: + /** + * ethertype for this rule + */ + const ethertype_t& m_eth; + + /** + * direction in which ethertype will be applied w.r.t. intf + */ + const direction_t& m_dir; +}; + +class acl_ethertype : public object_base +{ +public: + /** + * The KEY can be used to uniquely identify the ACL ethertype. + * (other choices for keys, like the summation of the properties + * of the rules, are rather too cumbersome to use + */ + typedef std::string key_t; + + /** + * The ethertype container + */ + typedef std::multiset ethertype_rules_t; + + /** + * Construct a new object matching the desried state + */ + acl_ethertype(const interface& itf, ethertype_rules_t le); + + /** + * Copy Constructor + */ + acl_ethertype(const acl_ethertype& o); + + /** + * Destructor + */ + ~acl_ethertype(); + + /** + * Return the binding's key + */ + const key_t& key() const; + + /** + * comparision operator (for testing) + */ + bool operator==(const acl_ethertype& o) const; + + /** + * Return the 'singular' of the acl ethertype that matches this object + */ + std::shared_ptr singular() const; + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Dump all acl ethertype into the stream provided + */ + static void dump(std::ostream& os); + + /** + * Static function to find the acl_ethertype in the model + */ + static std::shared_ptr find(const key_t& key); + +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; + + /** + * Enque commands to the VPP command Q for the update + */ + void update(const acl_ethertype& obj); + + /** + * Find or add acl ethertype to the OM + */ + static std::shared_ptr find_or_add(const acl_ethertype& temp); + + /* + * It's the OM class that calls singular() + */ + friend class VOM::OM; + + /** + * It's the singular_db class that calls replay() + */ + friend class singular_db; + + /** + * Sweep/reap the object if still stale + */ + void sweep(void); + + /** + * replay the object to create it in hardware + */ + void replay(void); + + /** + * A reference counting pointer to the interface on which acl ethertype + * resides. By holding the reference here, we can guarantee that + * this object will outlive the interface + */ + const std::shared_ptr m_itf; + + /** + * Inbound and outbound ethers list applied on given interface + */ + ethertype_rules_t m_le; + + /** + * HW configuration for the binding. The bool representing the + * do/don't bind. + */ + HW::item m_binding; + + /** + * A map of all acl ethertype keyed against the interface. + */ + static singular_db m_db; +}; +}; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/acl_ethertype_cmds.cpp b/src/vpp-api/vom/acl_ethertype_cmds.cpp new file mode 100644 index 00000000000..91fc7ee6b01 --- /dev/null +++ b/src/vpp-api/vom/acl_ethertype_cmds.cpp @@ -0,0 +1,93 @@ +/* + * 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/acl_ethertype_cmds.hpp" + +namespace VOM { +namespace ACL { +namespace acl_ethertype_cmds { + +bind_cmd::bind_cmd(HW::item& item, + const handle_t& itf, + const acl_ethertype::ethertype_rules_t& le) + : rpc_cmd(item) + , m_itf(itf) + , m_le(le) +{ +} + +bool +bind_cmd::operator==(const bind_cmd& other) const +{ + return (m_itf == other.m_itf && m_le == other.m_le); +} + +rc_t +bind_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), m_le.size(), std::ref(*this)); + uint32_t i = 0; + uint8_t n_input = 0; + + auto& payload = req.get_request().get_payload(); + payload.sw_if_index = m_itf.value(); + payload.count = m_le.size(); + + auto it = m_le.cbegin(); + while (it != m_le.cend()) { + payload.whitelist[i] = it->getEthertype(); + if (it->getDirection() == direction_t::INPUT) + n_input++; + ++it; + ++i; + } + + payload.n_input = n_input; + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return rc_t::OK; +} + +std::string +bind_cmd::to_string() const +{ + std::ostringstream s; + s << "ACL-Ethertype: " << m_hw_item.to_string() + << " itf:" << m_itf.to_string() << " ethertype-rules:"; + auto it = m_le.cbegin(); + while (it != m_le.cend()) { + s << it->to_string(); + ++it; + } + + s << " rules-size:" << m_le.size(); + + return (s.str()); +} + +}; // namespace acl_ethertype_cmds +}; // namespace ACL +}; // namespace VOM + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/acl_ethertype_cmds.hpp b/src/vpp-api/vom/acl_ethertype_cmds.hpp new file mode 100644 index 00000000000..a0af50e8297 --- /dev/null +++ b/src/vpp-api/vom/acl_ethertype_cmds.hpp @@ -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. + */ + +#ifndef __VOM_ACL_ETHERTYPE_CMDS_H__ +#define __VOM_ACL_ETHERTYPE_CMDS_H__ + +#include "vom/acl_ethertype.hpp" +#include "vom/dump_cmd.hpp" +#include "vom/rpc_cmd.hpp" + +#include + +namespace VOM { +namespace ACL { +namespace acl_ethertype_cmds { +/** + * A command class that binds the ethertype list to the interface + */ +class bind_cmd : public rpc_cmd, + rc_t, + vapi::Acl_interface_set_etype_whitelist> +{ +public: + /** + * Constructor + */ + bind_cmd(HW::item& item, + const handle_t& itf, + const acl_ethertype::ethertype_rules_t& le); + + /** + * 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 bind_cmd& i) const; + +private: + /** + * Reference to the HW::item of the interface to bind + */ + const handle_t& m_itf; + + /** + * Ethertype list applied to interface + */ + const acl_ethertype::ethertype_rules_t& m_le; +}; +}; +}; +}; +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/types.cpp b/src/vpp-api/vom/types.cpp index 4f8884615e2..82f0aec20b2 100644 --- a/src/vpp-api/vom/types.cpp +++ b/src/vpp-api/vom/types.cpp @@ -274,6 +274,55 @@ operator<<(std::ostream& os, const direction_t& dir) return os; } +const ethertype_t ethertype_t::ARP(0x0806, "arp"); +const ethertype_t ethertype_t::FCOE(0x8906, "fcoe"); +const ethertype_t ethertype_t::IPV4(0x0800, "ipv4"); +const ethertype_t ethertype_t::IPV6(0x86DD, "ipv6"); +const ethertype_t ethertype_t::MAC_SECURITY(0x88E5, "mac-security"); +const ethertype_t ethertype_t::MPLS_UNICAST(0x8847, "mpls-unicast"); +const ethertype_t ethertype_t::TRILL(0x22F3, "trill"); +const ethertype_t ethertype_t::UNSPECIFIED(0x0, "unspecified"); + +ethertype_t::ethertype_t(int v, const std::string s) + : enum_base(v, s) +{ +} + +std::ostream& +operator<<(std::ostream& os, const ethertype_t& ether) +{ + os << ether.to_string(); + return os; +} + +const ethertype_t& +ethertype_t::from_numeric_val(uint16_t numeric) +{ + if (0x0806 == numeric) { + return (ethertype_t::ARP); + } + if (0x8906 == numeric) { + return (ethertype_t::FCOE); + } + if (0x0800 == numeric) { + return (ethertype_t::IPV4); + } + if (0x86DD == numeric) { + return (ethertype_t::IPV6); + } + if (0x88E5 == numeric) { + return (ethertype_t::MAC_SECURITY); + } + if (0x8847 == numeric) { + return (ethertype_t::MPLS_UNICAST); + } + if (0x22F3 == numeric) { + return (ethertype_t::TRILL); + } + + return (ethertype_t::UNSPECIFIED); +} + }; // namespace VOM /* diff --git a/src/vpp-api/vom/types.hpp b/src/vpp-api/vom/types.hpp index 4deb6c489a6..8543a6604bb 100644 --- a/src/vpp-api/vom/types.hpp +++ b/src/vpp-api/vom/types.hpp @@ -158,6 +158,72 @@ struct direction_t : public enum_base */ std::ostream& operator<<(std::ostream& os, const direction_t& dir); +/** + * Feature Ethertype + */ +struct ethertype_t : public enum_base +{ + /** + * Constructor + */ + ethertype_t(int v, const std::string s); + + /** + * Destructor + */ + ~ethertype_t() = default; + + /** + * Ethertype Arp + */ + const static ethertype_t ARP; + + /** + * Ethertype FCoE + */ + const static ethertype_t FCOE; + + /** + * Ethertype IPv4 + */ + const static ethertype_t IPV4; + + /** + * Ethertype Ipv6 + */ + const static ethertype_t IPV6; + + /** + * Ethertype MAC Security + */ + const static ethertype_t MAC_SECURITY; + + /** + * Ethertype MPLS unicast + */ + const static ethertype_t MPLS_UNICAST; + + /** + * Ethertype TRILL + */ + const static ethertype_t TRILL; + + /** + * Ethertype Unspecified + */ + const static ethertype_t UNSPECIFIED; + + /** + * Get the ethertype from the numeric value + */ + static const ethertype_t& from_numeric_val(uint16_t numeric); +}; + +/** + * Output ostream for ethertype_t + */ +std::ostream& operator<<(std::ostream& os, const ethertype_t& eth); + /** * A type declaration of an interface handle in VPP */ diff --git a/test/ext/vom_test.cpp b/test/ext/vom_test.cpp index 0a687c5017c..d9bd67984f1 100644 --- a/test/ext/vom_test.cpp +++ b/test/ext/vom_test.cpp @@ -39,6 +39,8 @@ #include "vom/vxlan_tunnel_cmds.hpp" #include "vom/sub_interface.hpp" #include "vom/sub_interface_cmds.hpp" +#include "vom/acl_ethertype.hpp" +#include "vom/acl_ethertype_cmds.hpp" #include "vom/acl_list.hpp" #include "vom/acl_binding.hpp" #include "vom/acl_list_cmds.hpp" @@ -280,6 +282,10 @@ public: { rc = handle_derived(f_exp, f_act); } + else if (typeid(*f_exp) == typeid(ACL::acl_ethertype_cmds::bind_cmd)) + { + rc = handle_derived(f_exp, f_act); + } else if (typeid(*f_exp) == typeid(ACL::list_cmds::l3_update_cmd)) { rc = handle_derived(f_exp, f_act); @@ -984,6 +990,15 @@ BOOST_AUTO_TEST_CASE(test_acl) { ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh)); TRY_CHECK_RC(OM::write(fyodor, itf1)); + ACL::ethertype_rule_t e1(ethertype_t::ARP, direction_t::INPUT); + ACL::ethertype_rule_t e2(ethertype_t::ARP, direction_t::OUTPUT); + ACL::ethertype_rule_t e3(ethertype_t::IPV4, direction_t::INPUT); + ACL::acl_ethertype::ethertype_rules_t l_e = {e1, e2, e3}; + ACL::acl_ethertype *a_e = new ACL::acl_ethertype(itf1, l_e); + HW::item ae_binding(true, rc_t::OK); + ADD_EXPECT(ACL::acl_ethertype_cmds::bind_cmd(ae_binding, hw_ifh.data(), l_e)); + TRY_CHECK_RC(OM::write(fyodor, *a_e)); + route::prefix_t src("10.10.10.10", 32); ACL::l3_rule r1(10, ACL::action_t::PERMIT, src, route::prefix_t::ZERO); ACL::l3_rule r2(20, ACL::action_t::DENY, route::prefix_t::ZERO, route::prefix_t::ZERO); @@ -1038,6 +1053,7 @@ BOOST_AUTO_TEST_CASE(test_acl) { TRY_CHECK(OM::remove(leo)); delete l3b; + delete a_e; HW::item hw_as_down(interface::admin_state_t::DOWN, rc_t::OK); STRICT_ORDER_OFF(); -- cgit 1.2.3-korg