VOM: mroutes 13/16713/3
authorNeale Ranns <nranns@cisco.com>
Thu, 27 Dec 2018 11:21:28 +0000 (03:21 -0800)
committerFlorin Coras <florin.coras@gmail.com>
Mon, 7 Jan 2019 21:56:08 +0000 (21:56 +0000)
- fixes in ip.api for dumping mroute path flags

Change-Id: I13b0cfb15d374250ed71bd4e13dda9b798c18204
Signed-off-by: Neale Ranns <nranns@cisco.com>
17 files changed:
extras/vom/vom/CMakeLists.txt
extras/vom/vom/api_types.cpp
extras/vom/vom/api_types.hpp
extras/vom/vom/mroute_cmds.cpp [new file with mode: 0644]
extras/vom/vom/mroute_cmds.hpp [new file with mode: 0644]
extras/vom/vom/prefix.cpp
extras/vom/vom/prefix.hpp
extras/vom/vom/route.cpp
extras/vom/vom/route.hpp
extras/vom/vom/route_api_types.cpp [new file with mode: 0644]
extras/vom/vom/route_api_types.hpp [new file with mode: 0644]
extras/vom/vom/route_cmds.cpp
src/vnet/fib/fib_types.h
src/vnet/ip/ip.api
src/vnet/ip/ip_api.c
src/vnet/mfib/mfib_entry.c
test/ext/vom_test.cpp

index bd8986a..475672a 100644 (file)
@@ -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
index 53cd047..4a81a41 100644 (file)
@@ -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;
+}
 };
 
 /*
index 784ace2..5856c22 100644 (file)
@@ -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 (file)
index 0000000..e4df00a
--- /dev/null
@@ -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 (file)
index 0000000..b8f18f6
--- /dev/null
@@ -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
index abd589e..fdad679 100644 (file)
@@ -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
 
 /*
index 8365541..1b6a068 100644 (file)
@@ -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);
 };
 
 /*
index ec56c44..c713de9 100644 (file)
  */
 
 #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)
 {
@@ -525,6 +710,14 @@ operator<<(std::ostream& os, const ip_route::key_t& key)
   return (os);
 }
 
+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)
 {
@@ -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
  *
index 65797b7..a09f704 100644 (file)
@@ -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 (file)
index 0000000..85fca05
--- /dev/null
@@ -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 (file)
index 0000000..0901548
--- /dev/null
@@ -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:
+ */
index 240592f..26d6593 100644 (file)
 
 #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,
index 714a1d9..472ce88 100644 (file)
@@ -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.
index 4b418be..025fd57 100644 (file)
@@ -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
index d59aa23..f58ca07 100644 (file)
@@ -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++;
   }
 
index 79d2f9a..1856221 100644 (file)
@@ -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 *
index 7d73254..69de5f1 100644 (file)
@@ -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));