diff options
-rw-r--r-- | extras/vom/vom/CMakeLists.txt | 2 | ||||
-rw-r--r-- | extras/vom/vom/api_types.cpp | 54 | ||||
-rw-r--r-- | extras/vom/vom/api_types.hpp | 7 | ||||
-rw-r--r-- | extras/vom/vom/mroute_cmds.cpp | 193 | ||||
-rw-r--r-- | extras/vom/vom/mroute_cmds.hpp | 181 | ||||
-rw-r--r-- | extras/vom/vom/prefix.cpp | 131 | ||||
-rw-r--r-- | extras/vom/vom/prefix.hpp | 118 | ||||
-rw-r--r-- | extras/vom/vom/route.cpp | 345 | ||||
-rw-r--r-- | extras/vom/vom/route.hpp | 194 | ||||
-rw-r--r-- | extras/vom/vom/route_api_types.cpp | 119 | ||||
-rw-r--r-- | extras/vom/vom/route_api_types.hpp | 35 | ||||
-rw-r--r-- | extras/vom/vom/route_cmds.cpp | 44 | ||||
-rw-r--r-- | src/vnet/fib/fib_types.h | 4 | ||||
-rw-r--r-- | src/vnet/ip/ip.api | 10 | ||||
-rw-r--r-- | src/vnet/ip/ip_api.c | 10 | ||||
-rw-r--r-- | src/vnet/mfib/mfib_entry.c | 13 | ||||
-rw-r--r-- | test/ext/vom_test.cpp | 41 |
17 files changed, 1378 insertions, 123 deletions
diff --git a/extras/vom/vom/CMakeLists.txt b/extras/vom/vom/CMakeLists.txt index bd8986a4864..475672a001a 100644 --- a/extras/vom/vom/CMakeLists.txt +++ b/extras/vom/vom/CMakeLists.txt @@ -158,6 +158,7 @@ list(APPEND VOM_SOURCES neighbour.cpp neighbour_cmds.cpp object_base.cpp + mroute_cmds.cpp om.cpp pipe.cpp pipe_cmds.cpp @@ -165,6 +166,7 @@ list(APPEND VOM_SOURCES ra_config.cpp ra_prefix.cpp route.cpp + route_api_types.cpp route_cmds.cpp route_domain.cpp route_domain_cmds.cpp diff --git a/extras/vom/vom/api_types.cpp b/extras/vom/vom/api_types.cpp index 53cd047332a..4a81a41daa9 100644 --- a/extras/vom/vom/api_types.cpp +++ b/extras/vom/vom/api_types.cpp @@ -28,6 +28,21 @@ to_api(const ip_address_t& a, vapi_type_address& v) memcpy(v.un.ip6, a.to_v6().to_bytes().data(), 16); } } + +void +to_api(const ip_address_t& a, + vapi_union_address_union& u, + vapi_enum_address_family& af) +{ + if (a.is_v4()) { + af = ADDRESS_IP4; + memcpy(u.ip4, a.to_v4().to_bytes().data(), 4); + } else { + af = ADDRESS_IP6; + memcpy(u.ip6, a.to_v6().to_bytes().data(), 16); + } +} + void to_api(const boost::asio::ip::address& a, vapi_type_ip4_address& v) { @@ -54,6 +69,26 @@ from_api(const vapi_type_address& v) return addr; } +ip_address_t +from_api(const vapi_union_address_union& u, vapi_enum_address_family af) +{ + boost::asio::ip::address addr; + + if (ADDRESS_IP6 == af) { + std::array<uint8_t, 16> a; + std::copy(u.ip6, u.ip6 + 16, std::begin(a)); + boost::asio::ip::address_v6 v6(a); + addr = v6; + } else { + std::array<uint8_t, 4> a; + std::copy(u.ip6, u.ip6 + 4, std::begin(a)); + boost::asio::ip::address_v4 v4(a); + addr = v4; + } + + return addr; +} + void to_api(const mac_address_t& a, vapi_type_mac_address& v) { @@ -80,6 +115,25 @@ to_api(const route::prefix_t& p) v.address_length = p.mask_width(); return v; } + +route::mprefix_t +from_api(const vapi_type_mprefix& v) +{ + return route::mprefix_t(from_api(v.src_address, v.af), + from_api(v.grp_address, v.af), v.grp_address_length); +} + +vapi_type_mprefix +to_api(const route::mprefix_t& p) +{ + vapi_enum_address_family af; + vapi_type_mprefix v; + to_api(p.grp_address(), v.grp_address, af); + to_api(p.src_address(), v.src_address, af); + v.grp_address_length = p.mask_width(); + v.af = af; + return v; +} }; /* diff --git a/extras/vom/vom/api_types.hpp b/extras/vom/vom/api_types.hpp index 784ace2c293..5856c22d339 100644 --- a/extras/vom/vom/api_types.hpp +++ b/extras/vom/vom/api_types.hpp @@ -25,17 +25,24 @@ typedef boost::asio::ip::address ip_address_t; void to_api(const ip_address_t& a, vapi_type_address& v); void to_api(const boost::asio::ip::address& a, vapi_type_ip4_address& v); +void to_api(const boost::asio::ip::address& a, + vapi_union_address_union& u, + vapi_enum_address_family& af); ip_address_t from_api(const vapi_type_address& v); ip_address_t from_api(const vapi_type_ip4_address& v); +ip_address_t from_api(const vapi_union_address_union& u, + vapi_enum_address_family af); void to_api(const mac_address_t& a, vapi_type_mac_address& m); mac_address_t from_api(const vapi_type_mac_address& v); route::prefix_t from_api(const vapi_type_prefix&); +route::mprefix_t from_api(const vapi_type_mprefix&); vapi_type_prefix to_api(const route::prefix_t&); +vapi_type_mprefix to_api(const route::mprefix_t&); }; /* diff --git a/extras/vom/vom/mroute_cmds.cpp b/extras/vom/vom/mroute_cmds.cpp new file mode 100644 index 00000000000..e4df00a6ddd --- /dev/null +++ b/extras/vom/vom/mroute_cmds.cpp @@ -0,0 +1,193 @@ +/* + * 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. + */ + +#include <sstream> + +#include "vom/api_types.hpp" +#include "vom/mroute_cmds.hpp" +#include "vom/route_api_types.hpp" + +namespace VOM { +namespace route { +namespace ip_mroute_cmds { + +update_cmd::update_cmd(HW::item<bool>& item, + table_id_t id, + const mprefix_t& mprefix, + const path& path, + const itf_flags_t& flags) + : rpc_cmd(item) + , m_id(id) + , m_mprefix(mprefix) + , m_path(path) + , m_flags(flags) +{ +} + +bool +update_cmd::operator==(const update_cmd& other) const +{ + return ((m_mprefix == other.m_mprefix) && (m_id == other.m_id)); +} + +rc_t +update_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + + payload.table_id = m_id; + payload.is_add = 1; + + m_mprefix.to_vpp(&payload.is_ipv6, payload.grp_address, payload.src_address, + &payload.grp_address_length); + + to_vpp(m_path, payload); + payload.itf_flags = m_flags.value(); + + VAPI_CALL(req.execute()); + + return (wait()); +} + +std::string +update_cmd::to_string() const +{ + std::ostringstream s; + s << "ip-mroute-create: " << m_hw_item.to_string() << " table-id:" << m_id + << " mprefix:" << m_mprefix.to_string() << " path:" << m_path.to_string() + << " flags:" << m_flags; + + return (s.str()); +} + +delete_cmd::delete_cmd(HW::item<bool>& item, + table_id_t id, + const mprefix_t& mprefix, + const path& path, + const itf_flags_t& flags) + : rpc_cmd(item) + , m_id(id) + , m_mprefix(mprefix) + , m_path(path) + , m_flags(flags) +{ +} + +bool +delete_cmd::operator==(const delete_cmd& other) const +{ + return ((m_mprefix == other.m_mprefix) && (m_id == other.m_id)); +} + +rc_t +delete_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.table_id = m_id; + payload.is_add = 0; + + m_mprefix.to_vpp(&payload.is_ipv6, payload.grp_address, payload.src_address, + &payload.grp_address_length); + + to_vpp(m_path, payload); + payload.itf_flags = m_flags.value(); + + VAPI_CALL(req.execute()); + + wait(); + m_hw_item.set(rc_t::NOOP); + + return rc_t::OK; +} + +std::string +delete_cmd::to_string() const +{ + std::ostringstream s; + s << "ip-mroute-delete: " << m_hw_item.to_string() << " id:" << m_id + << " mprefix:" << m_mprefix.to_string(); + + return (s.str()); +} + +dump_v4_cmd::dump_v4_cmd() +{ +} + +bool +dump_v4_cmd::operator==(const dump_v4_cmd& other) const +{ + return (true); +} + +rc_t +dump_v4_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_v4_cmd::to_string() const +{ + return ("ip-mroute-v4-dump"); +} + +dump_v6_cmd::dump_v6_cmd() +{ +} + +bool +dump_v6_cmd::operator==(const dump_v6_cmd& other) const +{ + return (true); +} + +rc_t +dump_v6_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_v6_cmd::to_string() const +{ + return ("ip-mroute-v6-dump"); +} +} // namespace ip_mroute_cmds +} // namespace mroute +} // namespace vom + /* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/extras/vom/vom/mroute_cmds.hpp b/extras/vom/vom/mroute_cmds.hpp new file mode 100644 index 00000000000..b8f18f6b45b --- /dev/null +++ b/extras/vom/vom/mroute_cmds.hpp @@ -0,0 +1,181 @@ +/* + * 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_MROUTE_CMDS_H__ +#define __VOM_MROUTE_CMDS_H__ + +#include "vom/dump_cmd.hpp" +#include "vom/route.hpp" +#include "vom/rpc_cmd.hpp" + +#include <vapi/ip.api.vapi.hpp> + +namespace VOM { +namespace route { +namespace ip_mroute_cmds { + +/** + * A command class that creates or updates the route + */ +class update_cmd : public rpc_cmd<HW::item<bool>, vapi::Ip_mroute_add_del> +{ +public: + /** + * Constructor + */ + update_cmd(HW::item<bool>& item, + table_id_t id, + const mprefix_t& mprefix, + const path& path, + const itf_flags_t& flags); + + /** + * 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 update_cmd& i) const; + +private: + route::table_id_t m_id; + mprefix_t m_mprefix; + const path m_path; + const itf_flags_t& m_flags; +}; + +/** + * A cmd class that deletes a route + */ +class delete_cmd : public rpc_cmd<HW::item<bool>, vapi::Ip_mroute_add_del> +{ +public: + /** + * Constructor + */ + delete_cmd(HW::item<bool>& item, + table_id_t id, + const mprefix_t& mprefix, + const path& path, + const itf_flags_t& flags); + + /** + * 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: + route::table_id_t m_id; + mprefix_t m_mprefix; + const path m_path; + const itf_flags_t& m_flags; +}; + +/** + * A cmd class that Dumps ipv4 fib + */ +class dump_v4_cmd : public VOM::dump_cmd<vapi::Ip_mfib_dump> +{ +public: + /** + * Constructor + */ + dump_v4_cmd(); + dump_v4_cmd(const dump_cmd& d); + + /** + * 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_v4_cmd& i) const; + +private: + /** + * HW reutrn code + */ + HW::item<bool> item; +}; + +/** + * A cmd class that Dumps ipv6 fib + */ +class dump_v6_cmd : public VOM::dump_cmd<vapi::Ip6_mfib_dump> +{ +public: + /** + * Constructor + */ + dump_v6_cmd(); + dump_v6_cmd(const dump_cmd& d); + + /** + * 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_v6_cmd& i) const; + +private: + /** + * HW reutrn code + */ + HW::item<bool> item; +}; + +}; // namespace ip_mroute_cmds +}; // namespace route +}; // namespace VOM + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/extras/vom/vom/prefix.cpp b/extras/vom/vom/prefix.cpp index abd589eef9f..fdad67998d1 100644 --- a/extras/vom/vom/prefix.cpp +++ b/extras/vom/vom/prefix.cpp @@ -190,7 +190,7 @@ route::prefix_t::to_string() const } boost::asio::ip::address -from_bytes(uint8_t is_ip6, uint8_t* bytes) +from_bytes(uint8_t is_ip6, const uint8_t* bytes) { boost::asio::ip::address addr; @@ -420,6 +420,135 @@ route::prefix_t::high() const return (pfx); } +/** + * The all Zeros prefix + */ +const route::mprefix_t route::mprefix_t::ZERO; +const route::mprefix_t route::mprefix_t::ZEROv6; + +route::mprefix_t::mprefix_t(const boost::asio::ip::address& gaddr, uint8_t len) + : m_gaddr(gaddr) + , m_saddr() + , m_len(len) +{ +} + +route::mprefix_t::mprefix_t(const boost::asio::ip::address& gaddr) + : m_gaddr(gaddr) + , m_saddr() + , m_len(VOM::mask_width(gaddr)) +{ +} + +route::mprefix_t::mprefix_t(const boost::asio::ip::address& saddr, + const boost::asio::ip::address& gaddr) + : m_gaddr(gaddr) + , m_saddr(saddr) + , m_len(2 * VOM::mask_width(gaddr)) +{ +} + +route::mprefix_t::mprefix_t(const boost::asio::ip::address& saddr, + const boost::asio::ip::address& gaddr, + uint16_t len) + : m_gaddr(gaddr) + , m_saddr(saddr) + , m_len(len) +{ +} + +route::mprefix_t::mprefix_t(const mprefix_t& o) + : m_gaddr(o.m_gaddr) + , m_saddr(o.m_saddr) + , m_len(o.m_len) +{ +} +route::mprefix_t::mprefix_t() + : m_gaddr() + , m_saddr() + , m_len(0) +{ +} + +route::mprefix_t::~mprefix_t() +{ +} + +const boost::asio::ip::address& +route::mprefix_t::grp_address() const +{ + return (m_gaddr); +} + +const boost::asio::ip::address& +route::mprefix_t::src_address() const +{ + return (m_saddr); +} + +uint8_t +route::mprefix_t::mask_width() const +{ + return (m_len); +} + +void +route::mprefix_t::to_vpp(uint8_t* is_ip6, + uint8_t* saddr, + uint8_t* gaddr, + uint16_t* len) const +{ + *len = m_len; + to_bytes(m_saddr, is_ip6, saddr); + to_bytes(m_gaddr, is_ip6, gaddr); +} + +route::mprefix_t& +route::mprefix_t::operator=(const route::mprefix_t& o) +{ + m_gaddr = o.m_gaddr; + m_saddr = o.m_saddr; + m_len = o.m_len; + + return (*this); +} + +bool +route::mprefix_t::operator<(const route::mprefix_t& o) const +{ + if (m_len == o.m_len) { + if (m_saddr == o.m_saddr) + return (m_gaddr < o.m_gaddr); + else + return (m_saddr < o.m_saddr); + } else { + return (m_len < o.m_len); + } +} + +bool +route::mprefix_t::operator==(const route::mprefix_t& o) const +{ + return (m_len == o.m_len && m_gaddr == o.m_gaddr && m_saddr == o.m_saddr); +} + +bool +route::mprefix_t::operator!=(const route::mprefix_t& o) const +{ + return (!(*this == o)); +} + +std::string +route::mprefix_t::to_string() const +{ + std::ostringstream s; + + s << "(" << m_saddr.to_string() << "," << m_gaddr.to_string() << "/" + << std::to_string(m_len) << ")"; + + return (s.str()); +} + }; // namespace VOM /* diff --git a/extras/vom/vom/prefix.hpp b/extras/vom/vom/prefix.hpp index 836554123f5..1b6a06874d3 100644 --- a/extras/vom/vom/prefix.hpp +++ b/extras/vom/vom/prefix.hpp @@ -171,8 +171,8 @@ public: const static prefix_t ZEROv6; /** - * Convert the prefix into VPP API parameters - */ + * Convert the prefix into VPP API parameters + */ void to_vpp(uint8_t* is_ip6, uint8_t* addr, uint8_t* len) const; /** @@ -206,8 +206,120 @@ private: */ uint8_t m_len; }; + +/** +* A prefix defintion. Address + length +*/ +class mprefix_t +{ +public: + /** + * Default Constructor - creates ::/0 + */ + mprefix_t(); + /** + * Constructor for (S,G) + */ + mprefix_t(const boost::asio::ip::address& saddr, + const boost::asio::ip::address& gaddr); + /* + * Constructor for (*,G) + */ + mprefix_t(const boost::asio::ip::address& gaddr); + + /* + * Constructor for (*,G/n) + */ + mprefix_t(const boost::asio::ip::address& gaddr, uint8_t len); + + /** +*Constructor for (S,G) +*/ + mprefix_t(const boost::asio::ip::address& saddr, + const boost::asio::ip::address& gaddr, + uint16_t len); + + /** + * Copy Constructor + */ + mprefix_t(const mprefix_t&); + + /** + * Destructor + */ + ~mprefix_t(); + + /** + * Get the address + */ + const boost::asio::ip::address& grp_address() const; + const boost::asio::ip::address& src_address() const; + + /** + * Get the network mask width + */ + uint8_t mask_width() const; + + /** + * Assignement + */ + mprefix_t& operator=(const mprefix_t&); + + /** + * Less than operator + */ + bool operator<(const mprefix_t& o) const; + + /** + * equals operator + */ + bool operator==(const mprefix_t& o) const; + + /** + * not equal opartor + */ + bool operator!=(const mprefix_t& o) const; + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * The all Zeros prefix + */ + const static mprefix_t ZERO; + + /** + * The all Zeros v6 prefix + */ + const static mprefix_t ZEROv6; + + /** + * Get the L3 protocol + */ + l3_proto_t l3_proto() const; + + void to_vpp(uint8_t* is_ip6, + uint8_t* saddr, + uint8_t* gaddr, + uint16_t* len) const; + +private: + /** + * The address + */ + boost::asio::ip::address m_gaddr; + boost::asio::ip::address m_saddr; + + /** + * The prefix length + */ + uint8_t m_len; }; +}; // namespace route + boost::asio::ip::address_v4 operator|(const boost::asio::ip::address_v4& addr1, const boost::asio::ip::address_v4& addr2); @@ -254,7 +366,7 @@ uint32_t mask_width(const boost::asio::ip::address& addr); /** * Convert a VPP byte stinrg into a boost addresss */ -boost::asio::ip::address from_bytes(uint8_t is_ip6, uint8_t* array); +boost::asio::ip::address from_bytes(uint8_t is_ip6, const uint8_t* array); }; /* diff --git a/extras/vom/vom/route.cpp b/extras/vom/vom/route.cpp index ec56c44a0d7..c713de9ee99 100644 --- a/extras/vom/vom/route.cpp +++ b/extras/vom/vom/route.cpp @@ -14,19 +14,24 @@ */ #include "vom/route.hpp" +#include "vom/api_types.hpp" +#include "vom/mroute_cmds.hpp" +#include "vom/route_api_types.hpp" #include "vom/route_cmds.hpp" #include "vom/singular_db_funcs.hpp" namespace VOM { namespace route { ip_route::event_handler ip_route::m_evh; +ip_mroute::event_handler ip_mroute::m_evh; singular_db<ip_route::key_t, ip_route> ip_route::m_db; +singular_db<ip_mroute::key_t, ip_mroute> ip_mroute::m_db; const path::special_t path::special_t::STANDARD(0, "standard"); -const path::special_t path::special_t::LOCAL(0, "local"); -const path::special_t path::special_t::DROP(0, "standard"); -const path::special_t path::special_t::UNREACH(0, "unreachable"); -const path::special_t path::special_t::PROHIBIT(0, "prohibit"); +const path::special_t path::special_t::LOCAL(1, "local"); +const path::special_t path::special_t::DROP(2, "standard"); +const path::special_t path::special_t::UNREACH(3, "unreachable"); +const path::special_t path::special_t::PROHIBIT(4, "prohibit"); path::special_t::special_t(int v, const std::string& s) : enum_base<path::special_t>(v, s) @@ -41,6 +46,23 @@ path::flags_t::flags_t(int v, const std::string& s) { } +const itf_flags_t itf_flags_t::NONE(0, "none"); +const itf_flags_t itf_flags_t::ACCEPT((1 << 2), "accept"); +const itf_flags_t itf_flags_t::FORWARD((1 << 3), "forward"); + +itf_flags_t::itf_flags_t(int v, const std::string& s) + : enum_base<itf_flags_t>(v, s) +{ +} +const itf_flags_t& +itf_flags_t::from_vpp(uint32_t val) +{ + if (itf_flags_t::ACCEPT == (int)val) + return itf_flags_t::ACCEPT; + else + return itf_flags_t::FORWARD; +} + path::path(special_t special) : m_type(special) , m_nh_proto(nh_proto_t::IPV4) @@ -124,16 +146,24 @@ path::operator<(const path& p) const return false; if (!m_rd && p.m_rd) return true; - if (m_rd->table_id() < p.m_rd->table_id()) - return true; + if (m_rd && p.m_rd) { + if (m_rd->table_id() < p.m_rd->table_id()) + return true; + else if (m_rd->table_id() > p.m_rd->table_id()) + return false; + } if (m_nh < p.m_nh) return true; if (m_interface && !p.m_interface) return false; if (!m_interface && p.m_interface) return true; - if (m_interface->handle() < p.m_interface->handle()) - return true; + if (m_interface && p.m_interface) { + if (m_interface->handle() < p.m_interface->handle()) + return true; + if (p.m_interface->handle() < m_interface->handle()) + return false; + } return (false); } @@ -410,36 +440,7 @@ ip_route::event_handler::handle_populate(const client_db::key_t& key) ip_route ip_r(rd_temp, pfx); for (unsigned int i = 0; i < payload.count; i++) { - vapi_type_fib_path p = payload.path[i]; - if (p.is_local) { - path path_v4(path::special_t::LOCAL); - ip_r.add(path_v4); - } else if (p.is_drop) { - path path_v4(path::special_t::DROP); - ip_r.add(path_v4); - } else if (p.is_unreach) { - path path_v4(path::special_t::UNREACH); - ip_r.add(path_v4); - } else if (p.is_prohibit) { - path path_v4(path::special_t::PROHIBIT); - ip_r.add(path_v4); - } else { - boost::asio::ip::address address = from_bytes(0, p.next_hop); - std::shared_ptr<interface> itf = interface::find(p.sw_if_index); - if (itf) { - if (p.is_dvr) { - path path_v4(*itf, nh_proto_t::IPV4, route::path::flags_t::DVR, - p.weight, p.preference); - ip_r.add(path_v4); - } else { - path path_v4(address, *itf, p.weight, p.preference); - ip_r.add(path_v4); - } - } else { - path path_v4(rd_temp, address, p.weight, p.preference); - ip_r.add(path_v4); - } - } + ip_r.add(from_vpp(payload.path[i], nh_proto_t::IPV4)); } VOM_LOG(log_level_t::DEBUG) << "ip-route-dump: " << ip_r.to_string(); @@ -463,36 +464,7 @@ ip_route::event_handler::handle_populate(const client_db::key_t& key) ip_route ip_r(rd_temp, pfx); for (unsigned int i = 0; i < payload.count; i++) { - vapi_type_fib_path p = payload.path[i]; - if (p.is_local) { - path path_v6(path::special_t::LOCAL); - ip_r.add(path_v6); - } else if (p.is_drop) { - path path_v6(path::special_t::DROP); - ip_r.add(path_v6); - } else if (p.is_unreach) { - path path_v6(path::special_t::UNREACH); - ip_r.add(path_v6); - } else if (p.is_prohibit) { - path path_v6(path::special_t::PROHIBIT); - ip_r.add(path_v6); - } else { - std::shared_ptr<interface> itf = interface::find(p.sw_if_index); - boost::asio::ip::address address = from_bytes(1, p.next_hop); - if (itf) { - if (p.is_dvr) { - path path_v6(*itf, nh_proto_t::IPV6, route::path::flags_t::DVR, - p.weight, p.preference); - ip_r.add(path_v6); - } else { - path path_v6(address, *itf, p.weight, p.preference); - ip_r.add(path_v6); - } - } else { - path path_v6(rd_temp, address, p.weight, p.preference); - ip_r.add(path_v6); - } - } + ip_r.add(from_vpp(payload.path[i], nh_proto_t::IPV6)); } VOM_LOG(log_level_t::DEBUG) << "ip-route-dump: " << ip_r.to_string(); @@ -517,6 +489,219 @@ ip_route::event_handler::show(std::ostream& os) db_dump(m_db, os); } +ip_mroute::ip_mroute(const mprefix_t& mprefix) + : m_hw(false) + , m_rd(route_domain::get_default()) + , m_mprefix(mprefix) + , m_paths() +{ +} + +ip_mroute::ip_mroute(const ip_mroute& r) + : m_hw(r.m_hw) + , m_rd(r.m_rd) + , m_mprefix(r.m_mprefix) + , m_paths(r.m_paths) +{ +} + +ip_mroute::ip_mroute(const route_domain& rd, const mprefix_t& mprefix) + : m_hw(false) + , m_rd(rd.singular()) + , m_mprefix(mprefix) + , m_paths() +{ +} + +void +ip_mroute::add(const path& path, const itf_flags_t& flag) +{ + m_paths.insert(std::make_pair(path, flag)); +} + +ip_mroute::~ip_mroute() +{ + sweep(); + m_db.release(key(), this); +} + +const ip_mroute::key_t +ip_mroute::key() const +{ + return (std::make_pair(m_rd->table_id(), m_mprefix)); +} + +bool +ip_mroute::operator==(const ip_mroute& i) const +{ + return ((key() == i.key()) && (m_paths == i.m_paths)); +} + +void +ip_mroute::sweep() +{ + if (m_hw) { + for (auto& p : m_paths) + HW::enqueue(new ip_mroute_cmds::delete_cmd(m_hw, m_rd->table_id(), + m_mprefix, p.first, p.second)); + } + HW::write(); +} + +void +ip_mroute::replay() +{ + if (m_hw) { + for (auto& p : m_paths) + HW::enqueue(new ip_mroute_cmds::update_cmd(m_hw, m_rd->table_id(), + m_mprefix, p.first, p.second)); + } +} +std::string +ip_mroute::to_string() const +{ + std::ostringstream s; + s << "route:[" << m_rd->to_string() << ", " << m_mprefix.to_string() << " [" + << m_paths << "]" + << "]"; + + return (s.str()); +} + +void +ip_mroute::update(const ip_mroute& r) +{ + if (rc_t::OK != m_hw.rc()) { + for (auto& p : m_paths) + HW::enqueue(new ip_mroute_cmds::update_cmd(m_hw, m_rd->table_id(), + m_mprefix, p.first, p.second)); + } +} + +std::shared_ptr<ip_mroute> +ip_mroute::find_or_add(const ip_mroute& temp) +{ + return (m_db.find_or_add(temp.key(), temp)); +} + +std::shared_ptr<ip_mroute> +ip_mroute::find(const key_t& k) +{ + return (m_db.find(k)); +} + +std::shared_ptr<ip_mroute> +ip_mroute::singular() const +{ + return find_or_add(*this); +} + +void +ip_mroute::dump(std::ostream& os) +{ + db_dump(m_db, os); +} + +ip_mroute::event_handler::event_handler() +{ + OM::register_listener(this); + inspect::register_handler({ "ip-route" }, "ip route configurations", this); +} + +void +ip_mroute::event_handler::handle_replay() +{ + m_db.replay(); +} + +void +ip_mroute::event_handler::handle_populate(const client_db::key_t& key) +{ + std::shared_ptr<ip_mroute_cmds::dump_v4_cmd> cmd_v4 = + std::make_shared<ip_mroute_cmds::dump_v4_cmd>(); + std::shared_ptr<ip_mroute_cmds::dump_v6_cmd> cmd_v6 = + std::make_shared<ip_mroute_cmds::dump_v6_cmd>(); + + HW::enqueue(cmd_v4); + HW::enqueue(cmd_v6); + HW::write(); + + VOM_LOG(log_level_t::INFO) << "ip-mroute-dump: "; + + for (auto& record : *cmd_v4) { + auto& payload = record.get_payload(); + + ip_address_t gaddr = from_bytes(0, payload.grp_address); + ip_address_t saddr = from_bytes(0, payload.src_address); + mprefix_t pfx(saddr, gaddr, payload.address_length); + + /** + * populating the route domain here + */ + route_domain rd_temp(payload.table_id); + std::shared_ptr<route_domain> rd = route_domain::find(payload.table_id); + if (!rd) { + OM::commit(key, rd_temp); + } + ip_mroute ip_r(rd_temp, pfx); + + for (unsigned int i = 0; i < payload.count; i++) { + vapi_type_mfib_path& p = payload.path[i]; + ip_r.add(from_vpp(p.path, nh_proto_t::IPV4), + itf_flags_t::from_vpp(p.itf_flags)); + } + VOM_LOG(log_level_t::INFO) << "ip-mroute-dump: " << ip_r.to_string(); + + /* + * Write each of the discovered interfaces into the OM, + * but disable the HW Command q whilst we do, so that no + * commands are sent to VPP + */ + OM::commit(key, ip_r); + } + + for (auto& record : *cmd_v6) { + auto& payload = record.get_payload(); + + ip_address_t gaddr = from_bytes(1, payload.grp_address); + ip_address_t saddr = from_bytes(1, payload.src_address); + mprefix_t pfx(saddr, gaddr, payload.address_length); + + route_domain rd_temp(payload.table_id); + std::shared_ptr<route_domain> rd = route_domain::find(payload.table_id); + if (!rd) { + OM::commit(key, rd_temp); + } + ip_mroute ip_r(rd_temp, pfx); + + for (unsigned int i = 0; i < payload.count; i++) { + vapi_type_mfib_path& p = payload.path[i]; + ip_r.add(from_vpp(p.path, nh_proto_t::IPV6), + itf_flags_t::from_vpp(p.itf_flags)); + } + VOM_LOG(log_level_t::INFO) << "ip-mroute-dump: " << ip_r.to_string(); + + /* + * Write each of the discovered interfaces into the OM, + * but disable the HW Command q whilst we do, so that no + * commands are sent to VPP + */ + OM::commit(key, ip_r); + } +} + +dependency_t +ip_mroute::event_handler::order() const +{ + return (dependency_t::TABLE); +} + +void +ip_mroute::event_handler::show(std::ostream& os) +{ + db_dump(m_db, os); +} + std::ostream& operator<<(std::ostream& os, const ip_route::key_t& key) { @@ -526,6 +711,14 @@ operator<<(std::ostream& os, const ip_route::key_t& key) } std::ostream& +operator<<(std::ostream& os, const ip_mroute::key_t& key) +{ + os << "[" << key.first << ", " << key.second.to_string() << "]"; + + return (os); +} + +std::ostream& operator<<(std::ostream& os, const path_list_t& key) { os << "["; @@ -536,8 +729,22 @@ operator<<(std::ostream& os, const path_list_t& key) return (os); } + +std::ostream& +operator<<(std::ostream& os, const mpath_list_t& key) +{ + os << "["; + for (auto k : key) { + os << "[" << k.first.to_string() << ", " << k.second.to_string() << "]"; + } + os << "]"; + + return (os); } -} + +}; // namespace route +}; // namespace VOM + /* * fd.io coding-style-patch-verification: ON * diff --git a/extras/vom/vom/route.hpp b/extras/vom/vom/route.hpp index 65797b7c2f9..a09f704f60c 100644 --- a/extras/vom/vom/route.hpp +++ b/extras/vom/vom/route.hpp @@ -204,15 +204,44 @@ private: uint8_t m_preference; }; +class itf_flags_t : public enum_base<itf_flags_t> +{ +public: + const static itf_flags_t NONE; + /** + * Path is accepting multicast traffic + */ + const static itf_flags_t ACCEPT; + + /** + * A local/for-us/recieve + */ + const static itf_flags_t FORWARD; + + static const itf_flags_t& from_vpp(uint32_t val); + +private: + /** + * Private constructor taking the value and the string name + */ + itf_flags_t(int v, const std::string& s); +}; + /** * A path-list is a set of paths */ typedef std::set<path> path_list_t; /** + * A mpath-list is a set of paths and interface flags + */ +typedef std::set<std::pair<path, itf_flags_t>> mpath_list_t; + +/** * ostream output for iterator */ std::ostream& operator<<(std::ostream& os, const path_list_t& path_list); +std::ostream& operator<<(std::ostream& os, const mpath_list_t& path_list); /** * A IP route @@ -392,10 +421,171 @@ private: static singular_db<key_t, ip_route> m_db; }; -std::ostream& operator<<(std::ostream& os, const ip_route::key_t& key); -}; +/** + * A IP multicast route + */ +class ip_mroute : public object_base +{ +public: + /** + * The key for a route + */ + typedef std::pair<route::table_id_t, mprefix_t> key_t; + + /** + * Construct a route in the default table + */ + ip_mroute(const mprefix_t& mprefix); + + /** + * Copy Construct + */ + ip_mroute(const ip_mroute& r); + + /** + * Construct a route in the given route domain + */ + ip_mroute(const route_domain& rd, const mprefix_t& mprefix); + + /** + * Destructor + */ + ~ip_mroute(); + + /** + * Get the route's key + */ + const key_t key() const; + + /** + * Comparison operator + */ + bool operator==(const ip_mroute& i) const; + + /** + * Return the matching 'singular instance' + */ + std::shared_ptr<ip_mroute> singular() const; + + /** + * Find the instnace of the route domain in the OM + */ + static std::shared_ptr<ip_mroute> find(const ip_mroute& temp); + + /** + * Dump all route-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 matching 'singular instance' + */ + static std::shared_ptr<ip_mroute> find(const key_t& k); + + void add(const path& path, const itf_flags_t& flag); + +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; + + /** + * Find or add the instnace of the route domain in the OM + */ + static std::shared_ptr<ip_mroute> find_or_add(const ip_mroute& temp); + + /* + * It's the OM class that updates the objects in HW + */ + friend class VOM::OM; + + /** + * It's the singular_db class that calls replay() + */ + friend class singular_db<key_t, ip_mroute>; + + /** + * Commit the acculmulated changes into VPP. i.e. to a 'HW" write. + */ + void update(const ip_mroute& obj); + + /** + * Sweep/reap the object if still stale + */ + void sweep(void); + + /** + * HW configuration for the result of creating the route + */ + HW::item<bool> m_hw; + + /** + * The route domain the route is in. + */ + std::shared_ptr<route_domain> m_rd; + + /** + * The mprefix to match + */ + mprefix_t m_mprefix; + + /** + * The set of paths + */ + mpath_list_t m_paths; + + /** + * A map of all routes + */ + static singular_db<key_t, ip_mroute> m_db; }; +std::ostream& operator<<(std::ostream& os, const ip_route::key_t& key); +std::ostream& operator<<(std::ostream& os, const ip_mroute::key_t& key); +}; // namespace route +}; // namesapce VPP + /* * fd.io coding-style-patch-verification: ON * diff --git a/extras/vom/vom/route_api_types.cpp b/extras/vom/vom/route_api_types.cpp new file mode 100644 index 00000000000..85fca05b35a --- /dev/null +++ b/extras/vom/vom/route_api_types.cpp @@ -0,0 +1,119 @@ +/* + * 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/route.hpp> +#include <vom/route_api_types.hpp> + +namespace VOM { + +void +to_vpp(const route::path& p, vapi_payload_ip_add_del_route& payload) +{ + payload.is_drop = 0; + payload.is_unreach = 0; + payload.is_prohibit = 0; + payload.is_local = 0; + payload.is_classify = 0; + payload.is_multipath = 0; + payload.is_resolve_host = 0; + payload.is_resolve_attached = 0; + + if (route::path::flags_t::DVR & p.flags()) { + payload.is_dvr = 1; + } + + if (route::path::special_t::STANDARD == p.type()) { + uint8_t path_v6; + to_bytes(p.nh(), &path_v6, payload.next_hop_address); + + if (p.rd()) { + payload.next_hop_table_id = p.rd()->table_id(); + } + if (p.itf()) { + payload.next_hop_sw_if_index = p.itf()->handle().value(); + } + } else if (route::path::special_t::DROP == p.type()) { + payload.is_drop = 1; + } else if (route::path::special_t::UNREACH == p.type()) { + payload.is_unreach = 1; + } else if (route::path::special_t::PROHIBIT == p.type()) { + payload.is_prohibit = 1; + } else if (route::path::special_t::LOCAL == p.type()) { + payload.is_local = 1; + } + payload.next_hop_weight = p.weight(); + payload.next_hop_preference = p.preference(); + payload.next_hop_via_label = 0; + payload.classify_table_index = 0; +} + +void +to_vpp(const route::path& p, vapi_payload_ip_mroute_add_del& payload) +{ + if (route::path::special_t::STANDARD == p.type()) { + uint8_t path_v6; + to_bytes(p.nh(), &path_v6, payload.nh_address); + + if (p.itf()) { + payload.next_hop_sw_if_index = p.itf()->handle().value(); + } + + payload.next_hop_afi = p.nh_proto(); + } +} + +route::path +from_vpp(const vapi_type_fib_path& p, const nh_proto_t& nhp) +{ + if (p.is_local) { + return route::path(route::path::special_t::LOCAL); + } else if (p.is_drop) { + return route::path(route::path::special_t::DROP); + } else if (p.is_unreach) { + return route::path(route::path::special_t::UNREACH); + } else if (p.is_prohibit) { + return route::path(route::path::special_t::PROHIBIT); + } else { + boost::asio::ip::address address = + from_bytes(nh_proto_t::IPV6 == nhp, p.next_hop); + std::shared_ptr<interface> itf = interface::find(p.sw_if_index); + if (itf) { + if (p.is_dvr) { + return route::path(*itf, nhp, route::path::flags_t::DVR, p.weight, + p.preference); + } else { + return route::path(address, *itf, p.weight, p.preference); + } + } else { + std::shared_ptr<route_domain> rd = route_domain::find(p.table_id); + if (rd) { + return route::path(*rd, address, p.weight, p.preference); + } + } + } + + VOM_LOG(log_level_t::ERROR) << "cannot decode: "; + + return route::path(route::path::special_t::DROP); +} +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/extras/vom/vom/route_api_types.hpp b/extras/vom/vom/route_api_types.hpp new file mode 100644 index 00000000000..09015485cdb --- /dev/null +++ b/extras/vom/vom/route_api_types.hpp @@ -0,0 +1,35 @@ +/* + * 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/route.hpp> + +#include <vapi/ip.api.vapi.hpp> + +namespace VOM { + +void to_vpp(const route::path& p, vapi_payload_ip_mroute_add_del& payload); +void to_vpp(const route::path& p, vapi_payload_ip_add_del_route& payload); + +route::path from_vpp(const vapi_type_fib_path& p, const nh_proto_t& nh); + +}; // namespace VOM + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/extras/vom/vom/route_cmds.cpp b/extras/vom/vom/route_cmds.cpp index 240592f909f..26d6593df78 100644 --- a/extras/vom/vom/route_cmds.cpp +++ b/extras/vom/vom/route_cmds.cpp @@ -15,53 +15,13 @@ #include <sstream> -#include "vom/route_cmds.hpp" +#include <vom/route_api_types.hpp> +#include <vom/route_cmds.hpp> namespace VOM { namespace route { namespace ip_route_cmds { -static void -to_vpp(const route::path& p, vapi_payload_ip_add_del_route& payload) -{ - payload.is_drop = 0; - payload.is_unreach = 0; - payload.is_prohibit = 0; - payload.is_local = 0; - payload.is_classify = 0; - payload.is_multipath = 0; - payload.is_resolve_host = 0; - payload.is_resolve_attached = 0; - - if (route::path::flags_t::DVR & p.flags()) { - payload.is_dvr = 1; - } - - if (route::path::special_t::STANDARD == p.type()) { - uint8_t path_v6; - to_bytes(p.nh(), &path_v6, payload.next_hop_address); - - if (p.rd()) { - payload.next_hop_table_id = p.rd()->table_id(); - } - if (p.itf()) { - payload.next_hop_sw_if_index = p.itf()->handle().value(); - } - } else if (route::path::special_t::DROP == p.type()) { - payload.is_drop = 1; - } else if (route::path::special_t::UNREACH == p.type()) { - payload.is_unreach = 1; - } else if (route::path::special_t::PROHIBIT == p.type()) { - payload.is_prohibit = 1; - } else if (route::path::special_t::LOCAL == p.type()) { - payload.is_local = 1; - } - payload.next_hop_weight = p.weight(); - payload.next_hop_preference = p.preference(); - payload.next_hop_via_label = 0; - payload.classify_table_index = 0; -} - update_cmd::update_cmd(HW::item<bool>& item, table_id_t id, const prefix_t& prefix, diff --git a/src/vnet/fib/fib_types.h b/src/vnet/fib/fib_types.h index 714a1d9e9a8..472ce888b4e 100644 --- a/src/vnet/fib/fib_types.h +++ b/src/vnet/fib/fib_types.h @@ -528,6 +528,10 @@ typedef struct fib_route_path_t_ { * Exclusive DPO */ dpo_id_t dpo; + /** + * MFIB interface flags + */ + u32 frp_mitf_flags; }; /** * A path that resolves via a BIER Table. diff --git a/src/vnet/ip/ip.api b/src/vnet/ip/ip.api index 4b418bea71f..025fd577cf5 100644 --- a/src/vnet/ip/ip.api +++ b/src/vnet/ip/ip.api @@ -502,6 +502,12 @@ define ip_mfib_dump @param count - the number of fib_path in path @param path - array of of fib_path structures */ +typedef mfib_path +{ + vl_api_fib_path_t path; + u32 itf_flags; +}; + manual_endian manual_print define ip_mfib_details { u32 context; @@ -513,7 +519,7 @@ manual_endian manual_print define ip_mfib_details u8 src_address[4]; u32 count; u32 stats_index; - vl_api_fib_path_t path[count]; + vl_api_mfib_path_t path[count]; }; /** \brief Dump IP6 multicast fib table @@ -541,7 +547,7 @@ manual_endian manual_print define ip6_mfib_details u8 grp_address[16]; u8 src_address[16]; u32 count; - vl_api_fib_path_t path[count]; + vl_api_mfib_path_t path[count]; }; define ip_address_details diff --git a/src/vnet/ip/ip_api.c b/src/vnet/ip/ip_api.c index d59aa23d7e5..f58ca079b04 100644 --- a/src/vnet/ip/ip_api.c +++ b/src/vnet/ip/ip_api.c @@ -410,7 +410,7 @@ send_ip_mfib_details (vl_api_registration_t * reg, vl_api_ip_mfib_details_t *mp; const mfib_prefix_t *pfx; mfib_entry_t *mfib_entry; - vl_api_fib_path_t *fp; + vl_api_mfib_path_t *fp; int path_count; mfib_entry = mfib_entry_get (mfei); @@ -438,7 +438,8 @@ send_ip_mfib_details (vl_api_registration_t * reg, fp = mp->path; vec_foreach (api_rpath, api_rpaths) { - fib_api_path_encode (api_rpath, fp); + fib_api_path_encode (api_rpath, &fp->path); + fp->itf_flags = ntohl (api_rpath->rpath.frp_mitf_flags); fp++; } vec_free (api_rpaths); @@ -508,7 +509,7 @@ send_ip6_mfib_details (vpe_api_main_t * am, { vl_api_ip6_mfib_details_t *mp; fib_route_path_encode_t *api_rpath; - vl_api_fib_path_t *fp; + vl_api_mfib_path_t *fp; int path_count; path_count = vec_len (api_rpaths); @@ -530,7 +531,8 @@ send_ip6_mfib_details (vpe_api_main_t * am, fp = mp->path; vec_foreach (api_rpath, api_rpaths) { - fib_api_path_encode (api_rpath, fp); + fib_api_path_encode (api_rpath, &fp->path); + fp->itf_flags = ntohl (api_rpath->rpath.frp_mitf_flags); fp++; } diff --git a/src/vnet/mfib/mfib_entry.c b/src/vnet/mfib/mfib_entry.c index 79d2f9a50a3..18562219ce2 100644 --- a/src/vnet/mfib/mfib_entry.c +++ b/src/vnet/mfib/mfib_entry.c @@ -1325,6 +1325,7 @@ void mfib_entry_encode (fib_node_index_t mfib_entry_index, fib_route_path_encode_t **api_rpaths) { + fib_route_path_encode_t *api_rpath; mfib_entry_t *mfib_entry; mfib_entry_src_t *bsrc; @@ -1338,6 +1339,18 @@ mfib_entry_encode (fib_node_index_t mfib_entry_index, fib_path_encode, api_rpaths); } + + vec_foreach(api_rpath, *api_rpaths) + { + mfib_itf_t *mfib_itf; + + mfib_itf = mfib_entry_itf_find(bsrc->mfes_itfs, + api_rpath->rpath.frp_sw_if_index); + if (mfib_itf) + { + api_rpath->rpath.frp_mitf_flags = mfib_itf->mfi_flags; + } + } } const mfib_prefix_t * diff --git a/test/ext/vom_test.cpp b/test/ext/vom_test.cpp index 7d7325413bd..69de5f1ad3d 100644 --- a/test/ext/vom_test.cpp +++ b/test/ext/vom_test.cpp @@ -38,6 +38,7 @@ #include "vom/prefix.hpp" #include "vom/route.hpp" #include "vom/route_cmds.hpp" +#include "vom/mroute_cmds.hpp" #include "vom/route_domain.hpp" #include "vom/route_domain_cmds.hpp" #include "vom/vxlan_tunnel.hpp" @@ -251,6 +252,14 @@ public: { rc = handle_derived<route::ip_route_cmds::delete_cmd>(f_exp, f_act); } + else if (typeid(*f_exp) == typeid(route::ip_mroute_cmds::update_cmd)) + { + rc = handle_derived<route::ip_mroute_cmds::update_cmd>(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(route::ip_mroute_cmds::delete_cmd)) + { + rc = handle_derived<route::ip_mroute_cmds::delete_cmd>(f_exp, f_act); + } else if (typeid(*f_exp) == typeid(neighbour_cmds::create_cmd)) { rc = handle_derived<neighbour_cmds::create_cmd>(f_exp, f_act); @@ -1762,6 +1771,26 @@ BOOST_AUTO_TEST_CASE(test_routing) { ADD_EXPECT(route::ip_route_cmds::update_cmd(hw_route_dvr, 0, pfx_6, {*path_l2})); TRY_CHECK_RC(OM::write(ian, *route_dvr)); + /* + * a multicast route + */ + route::mprefix_t mpfx_4(boost::asio::ip::address::from_string("232.1.1.1"), 32); + route::ip_mroute *mroute_4 = new route::ip_mroute(mpfx_4); + + route::path *mp1 = new route::path(itf1, nh_proto_t::IPV4); + route::path *mp2 = new route::path(*itf2, nh_proto_t::IPV4); + mroute_4->add(*mp1, route::itf_flags_t::FORWARD); + mroute_4->add(*mp1, route::itf_flags_t::ACCEPT); + mroute_4->add(*mp2, route::itf_flags_t::FORWARD); + HW::item<bool> hw_mroute_4(true, rc_t::OK); + ADD_EXPECT(route::ip_mroute_cmds::update_cmd(hw_mroute_4, 0, mpfx_4, + *mp1, route::itf_flags_t::FORWARD)); + ADD_EXPECT(route::ip_mroute_cmds::update_cmd(hw_mroute_4, 0, mpfx_4, + *mp2, route::itf_flags_t::FORWARD)); + ADD_EXPECT(route::ip_mroute_cmds::update_cmd(hw_mroute_4, 0, mpfx_4, + *mp1, route::itf_flags_t::ACCEPT)); + TRY_CHECK_RC(OM::write(ian, *mroute_4)); + STRICT_ORDER_OFF(); // delete the stack objects that hold references to others // so the OM::remove is the call that removes the last reference @@ -1775,6 +1804,18 @@ BOOST_AUTO_TEST_CASE(test_routing) { delete route_dvr; delete path_l2; delete ne; + delete mroute_4; + + ADD_EXPECT(route::ip_mroute_cmds::delete_cmd(hw_mroute_4, 0, mpfx_4, + *mp1, route::itf_flags_t::FORWARD)); + ADD_EXPECT(route::ip_mroute_cmds::delete_cmd(hw_mroute_4, 0, mpfx_4, + *mp2, route::itf_flags_t::FORWARD)); + ADD_EXPECT(route::ip_mroute_cmds::delete_cmd(hw_mroute_4, 0, mpfx_4, + *mp1, route::itf_flags_t::ACCEPT)); + + delete mp1; + delete mp2; + ADD_EXPECT(neighbour_cmds::delete_cmd(hw_neighbour, hw_ifh.data(), mac_n, nh_10)); ADD_EXPECT(route::ip_route_cmds::delete_cmd(hw_route_dvr, 0, pfx_6)); ADD_EXPECT(route::ip_route_cmds::delete_cmd(hw_route_5_2, 1, pfx_5)); |