VOM: ACL: Add Object Model for acl ethertype 81/10481/6
authorMohsin Kazmi <sykazmi@cisco.com>
Wed, 7 Feb 2018 19:20:36 +0000 (20:20 +0100)
committerMohsin Kazmi <sykazmi@cisco.com>
Tue, 13 Feb 2018 10:16:32 +0000 (11:16 +0100)
Change-Id: I2b572ebd4b7bb26381f127912a4cc0825c04fc34
Signed-off-by: Mohsin Kazmi <sykazmi@cisco.com>
src/vpp-api/vom/Makefile.am
src/vpp-api/vom/acl_ethertype.cpp [new file with mode: 0644]
src/vpp-api/vom/acl_ethertype.hpp [new file with mode: 0644]
src/vpp-api/vom/acl_ethertype_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/acl_ethertype_cmds.hpp [new file with mode: 0644]
src/vpp-api/vom/types.cpp
src/vpp-api/vom/types.hpp
test/ext/vom_test.cpp

index 2cb7f83..d2a3dde 100644 (file)
@@ -37,6 +37,8 @@ libvom_la_SOURCES =                   \
        types.cpp                       \
        acl_binding_cmds.cpp            \
        acl_binding.cpp                 \
+       acl_ethertype_cmds.cpp          \
+       acl_ethertype.cpp               \
        acl_l2_rule.cpp                 \
        acl_l3_rule.cpp                 \
        acl_list_cmds.cpp               \
@@ -110,6 +112,7 @@ vomincludedir = $(includedir)/vom
 
 vominclude_HEADERS =                   \
        acl_binding.hpp                 \
+       acl_ethertype.hpp               \
        acl_l2_rule.hpp                 \
        acl_l3_rule.hpp                 \
        acl_list.hpp                    \
diff --git a/src/vpp-api/vom/acl_ethertype.cpp b/src/vpp-api/vom/acl_ethertype.cpp
new file mode 100644 (file)
index 0000000..2f01633
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/acl_ethertype.hpp"
+#include "vom/acl_ethertype_cmds.hpp"
+
+namespace VOM {
+namespace ACL {
+
+ethertype_rule_t::ethertype_rule_t(const ethertype_t& eth,
+                                   const direction_t& dir)
+  : m_eth(eth)
+  , m_dir(dir)
+{
+}
+
+std::string
+ethertype_rule_t::to_string() const
+{
+  std::ostringstream s;
+
+  s << "["
+    << "ethertype:" << m_eth.to_string() << " dir:" << m_dir.to_string()
+    << "],";
+
+  return (s.str());
+}
+
+bool
+ethertype_rule_t::operator<(const ethertype_rule_t& other) const
+{
+  return (m_dir > other.m_dir);
+}
+
+bool
+ethertype_rule_t::operator==(const ethertype_rule_t& other) const
+{
+  return (m_dir == other.m_dir && m_eth == other.m_eth);
+}
+
+uint16_t
+ethertype_rule_t::getEthertype() const
+{
+  return m_eth.value();
+}
+
+const direction_t&
+ethertype_rule_t::getDirection() const
+{
+  return m_dir;
+}
+
+/**
+ * A DB of all acl ethertype bindings configs
+ */
+singular_db<interface::key_t, acl_ethertype> acl_ethertype::m_db;
+
+acl_ethertype::event_handler acl_ethertype::m_evh;
+
+acl_ethertype::acl_ethertype(const interface& itf,
+                             acl_ethertype::ethertype_rules_t le)
+  : m_itf(itf.singular())
+  , m_le(le)
+  , m_binding(true)
+{
+}
+
+acl_ethertype::acl_ethertype(const acl_ethertype& o)
+  : m_itf(o.m_itf)
+  , m_le(o.m_le)
+  , m_binding(o.m_binding)
+{
+}
+
+acl_ethertype::~acl_ethertype()
+{
+  sweep();
+
+  // not in the DB anymore.
+  m_db.release(m_itf->key(), this);
+}
+
+void
+acl_ethertype::sweep()
+{
+}
+
+const acl_ethertype::key_t&
+acl_ethertype::key() const
+{
+  return (m_itf->key());
+}
+
+bool
+acl_ethertype::operator==(const acl_ethertype& other) const
+{
+  return (m_itf->key() == other.m_itf->key() && m_le == other.m_le);
+}
+
+std::shared_ptr<acl_ethertype>
+acl_ethertype::find(const key_t& key)
+{
+  return (m_db.find(key));
+}
+
+void
+acl_ethertype::dump(std::ostream& os)
+{
+  m_db.dump(os);
+}
+
+void
+acl_ethertype::replay()
+{
+  if (m_binding) {
+    HW::enqueue(
+      new acl_ethertype_cmds::bind_cmd(m_binding, m_itf->handle(), m_le));
+  }
+}
+
+std::string
+acl_ethertype::to_string() const
+{
+  std::ostringstream s;
+  s << "Acl-Ethertype:" << m_itf->to_string() << " ethertype-rules:";
+  auto it = m_le.cbegin();
+  while (it != m_le.cend()) {
+    s << it->to_string();
+    ++it;
+  }
+  s << " rules-size:" << m_le.size();
+
+  return (s.str());
+}
+
+void
+acl_ethertype::update(const acl_ethertype& old)
+{
+  /*
+   * always update the instance with the latest rules
+   */
+  if (!m_binding || old.m_le != m_le) {
+    HW::enqueue(
+      new acl_ethertype_cmds::bind_cmd(m_binding, m_itf->handle(), m_le));
+  }
+}
+
+std::shared_ptr<acl_ethertype>
+acl_ethertype::find_or_add(const acl_ethertype& temp)
+{
+  return (m_db.find_or_add(temp.m_itf->key(), temp));
+}
+
+std::shared_ptr<acl_ethertype>
+acl_ethertype::singular() const
+{
+  return find_or_add(*this);
+}
+
+acl_ethertype::event_handler::event_handler()
+{
+  OM::register_listener(this);
+  inspect::register_handler({ "acl-ethertype" }, "ACL Ethertype bindings",
+                            this);
+}
+
+void
+acl_ethertype::event_handler::handle_replay()
+{
+  m_db.replay();
+}
+
+void
+acl_ethertype::event_handler::handle_populate(const client_db::key_t& key)
+{
+  // FIXME
+}
+
+dependency_t
+acl_ethertype::event_handler::order() const
+{
+  return (dependency_t::BINDING);
+}
+
+void
+acl_ethertype::event_handler::show(std::ostream& os)
+{
+  m_db.dump(os);
+}
+};
+};
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/src/vpp-api/vom/acl_ethertype.hpp b/src/vpp-api/vom/acl_ethertype.hpp
new file mode 100644 (file)
index 0000000..42897ae
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_ACL_ETHERTYPE_H__
+#define __VOM_ACL_ETHERTYPE_H__
+
+#include <set>
+
+#include "vom/hw.hpp"
+#include "vom/inspect.hpp"
+#include "vom/interface.hpp"
+#include "vom/object_base.hpp"
+#include "vom/om.hpp"
+#include "vom/singular_db.hpp"
+
+namespace VOM {
+namespace ACL {
+/**
+ * An ACL ethertype list comprises a set of inbound ether types and out bound
+ * ether types
+ * to be applied to packets.
+ * A list is bound to a given interface.
+ */
+
+struct ethertype_rule_t
+{
+public:
+  /**
+   * Constructor
+   */
+  ethertype_rule_t(const ethertype_t& eth, const direction_t& dir);
+
+  /**
+   * Destructor
+   */
+  ~ethertype_rule_t() = default;
+
+  /**
+   * convert to string
+   */
+  std::string to_string() const;
+
+  /**
+   * comparision operator
+   */
+  bool operator<(const ethertype_rule_t& other) const;
+
+  /**
+   * comparision operator (for testing)
+   */
+  bool operator==(const ethertype_rule_t& other) const;
+
+  /**
+   * get the ether value
+   */
+  uint16_t getEthertype(void) const;
+
+  /**
+   * get the direction
+   */
+  const direction_t& getDirection(void) const;
+
+private:
+  /**
+   * ethertype for this rule
+   */
+  const ethertype_t& m_eth;
+
+  /**
+   * direction in which ethertype will be applied w.r.t. intf
+   */
+  const direction_t& m_dir;
+};
+
+class acl_ethertype : public object_base
+{
+public:
+  /**
+   * The KEY can be used to uniquely identify the ACL ethertype.
+   * (other choices for keys, like the summation of the properties
+   * of the rules, are rather too cumbersome to use
+   */
+  typedef std::string key_t;
+
+  /**
+   * The ethertype container
+   */
+  typedef std::multiset<ethertype_rule_t> ethertype_rules_t;
+
+  /**
+   * Construct a new object matching the desried state
+   */
+  acl_ethertype(const interface& itf, ethertype_rules_t le);
+
+  /**
+   * Copy Constructor
+   */
+  acl_ethertype(const acl_ethertype& o);
+
+  /**
+   * Destructor
+   */
+  ~acl_ethertype();
+
+  /**
+   * Return the binding's key
+   */
+  const key_t& key() const;
+
+  /**
+   * comparision operator (for testing)
+   */
+  bool operator==(const acl_ethertype& o) const;
+
+  /**
+   * Return the 'singular' of the acl ethertype that matches this object
+   */
+  std::shared_ptr<acl_ethertype> singular() const;
+
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * Dump all acl ethertype into the stream provided
+   */
+  static void dump(std::ostream& os);
+
+  /**
+   * Static function to find the acl_ethertype in the model
+   */
+  static std::shared_ptr<acl_ethertype> find(const key_t& key);
+
+private:
+  /**
+   * Class definition for listeners to OM events
+   */
+  class event_handler : public OM::listener, public inspect::command_handler
+  {
+  public:
+    event_handler();
+    virtual ~event_handler() = default;
+
+    /**
+     * Handle a populate event
+     */
+    void handle_populate(const client_db::key_t& key);
+
+    /**
+     * Handle a replay event
+     */
+    void handle_replay();
+
+    /**
+     * Show the object in the Singular DB
+     */
+    void show(std::ostream& os);
+
+    /**
+     * Get the sortable Id of the listener
+     */
+    dependency_t order() const;
+  };
+
+  /**
+   * event_handler to register with OM
+   */
+  static event_handler m_evh;
+
+  /**
+   * Enque commands to the VPP command Q for the update
+   */
+  void update(const acl_ethertype& obj);
+
+  /**
+   * Find or add acl ethertype to the OM
+   */
+  static std::shared_ptr<acl_ethertype> find_or_add(const acl_ethertype& temp);
+
+  /*
+   * It's the OM class that calls singular()
+   */
+  friend class VOM::OM;
+
+  /**
+   * It's the singular_db class that calls replay()
+   */
+  friend class singular_db<interface::key_t, acl_ethertype>;
+
+  /**
+   * Sweep/reap the object if still stale
+   */
+  void sweep(void);
+
+  /**
+   * replay the object to create it in hardware
+   */
+  void replay(void);
+
+  /**
+   * A reference counting pointer to the interface on which acl ethertype
+   * resides. By holding the reference here, we can guarantee that
+   * this object will outlive the interface
+   */
+  const std::shared_ptr<interface> m_itf;
+
+  /**
+   * Inbound and outbound ethers list applied on given interface
+   */
+  ethertype_rules_t m_le;
+
+  /**
+   * HW configuration for the binding. The bool representing the
+   * do/don't bind.
+   */
+  HW::item<bool> m_binding;
+
+  /**
+   * A map of all acl ethertype keyed against the interface.
+   */
+  static singular_db<interface::key_t, acl_ethertype> m_db;
+};
+};
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/src/vpp-api/vom/acl_ethertype_cmds.cpp b/src/vpp-api/vom/acl_ethertype_cmds.cpp
new file mode 100644 (file)
index 0000000..91fc7ee
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/acl_ethertype_cmds.hpp"
+
+namespace VOM {
+namespace ACL {
+namespace acl_ethertype_cmds {
+
+bind_cmd::bind_cmd(HW::item<bool>& item,
+                   const handle_t& itf,
+                   const acl_ethertype::ethertype_rules_t& le)
+  : rpc_cmd(item)
+  , m_itf(itf)
+  , m_le(le)
+{
+}
+
+bool
+bind_cmd::operator==(const bind_cmd& other) const
+{
+  return (m_itf == other.m_itf && m_le == other.m_le);
+}
+
+rc_t
+bind_cmd::issue(connection& con)
+{
+  msg_t req(con.ctx(), m_le.size(), std::ref(*this));
+  uint32_t i = 0;
+  uint8_t n_input = 0;
+
+  auto& payload = req.get_request().get_payload();
+  payload.sw_if_index = m_itf.value();
+  payload.count = m_le.size();
+
+  auto it = m_le.cbegin();
+  while (it != m_le.cend()) {
+    payload.whitelist[i] = it->getEthertype();
+    if (it->getDirection() == direction_t::INPUT)
+      n_input++;
+    ++it;
+    ++i;
+  }
+
+  payload.n_input = n_input;
+
+  VAPI_CALL(req.execute());
+
+  m_hw_item.set(wait());
+
+  return rc_t::OK;
+}
+
+std::string
+bind_cmd::to_string() const
+{
+  std::ostringstream s;
+  s << "ACL-Ethertype: " << m_hw_item.to_string()
+    << " itf:" << m_itf.to_string() << " ethertype-rules:";
+  auto it = m_le.cbegin();
+  while (it != m_le.cend()) {
+    s << it->to_string();
+    ++it;
+  }
+
+  s << " rules-size:" << m_le.size();
+
+  return (s.str());
+}
+
+}; // namespace acl_ethertype_cmds
+}; // namespace ACL
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/src/vpp-api/vom/acl_ethertype_cmds.hpp b/src/vpp-api/vom/acl_ethertype_cmds.hpp
new file mode 100644 (file)
index 0000000..a0af50e
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_ACL_ETHERTYPE_CMDS_H__
+#define __VOM_ACL_ETHERTYPE_CMDS_H__
+
+#include "vom/acl_ethertype.hpp"
+#include "vom/dump_cmd.hpp"
+#include "vom/rpc_cmd.hpp"
+
+#include <vapi/acl.api.vapi.hpp>
+
+namespace VOM {
+namespace ACL {
+namespace acl_ethertype_cmds {
+/**
+ * A command class that binds the ethertype list to the interface
+ */
+class bind_cmd : public rpc_cmd<HW::item<bool>,
+                                rc_t,
+                                vapi::Acl_interface_set_etype_whitelist>
+{
+public:
+  /**
+   * Constructor
+   */
+  bind_cmd(HW::item<bool>& item,
+           const handle_t& itf,
+           const acl_ethertype::ethertype_rules_t& le);
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * Comparison operator - only used for UT
+   */
+  bool operator==(const bind_cmd& i) const;
+
+private:
+  /**
+   * Reference to the HW::item of the interface to bind
+   */
+  const handle_t& m_itf;
+
+  /**
+   * Ethertype list applied to interface
+   */
+  const acl_ethertype::ethertype_rules_t& m_le;
+};
+};
+};
+};
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
index 4f88846..82f0aec 100644 (file)
@@ -274,6 +274,55 @@ operator<<(std::ostream& os, const direction_t& dir)
   return os;
 }
 
+const ethertype_t ethertype_t::ARP(0x0806, "arp");
+const ethertype_t ethertype_t::FCOE(0x8906, "fcoe");
+const ethertype_t ethertype_t::IPV4(0x0800, "ipv4");
+const ethertype_t ethertype_t::IPV6(0x86DD, "ipv6");
+const ethertype_t ethertype_t::MAC_SECURITY(0x88E5, "mac-security");
+const ethertype_t ethertype_t::MPLS_UNICAST(0x8847, "mpls-unicast");
+const ethertype_t ethertype_t::TRILL(0x22F3, "trill");
+const ethertype_t ethertype_t::UNSPECIFIED(0x0, "unspecified");
+
+ethertype_t::ethertype_t(int v, const std::string s)
+  : enum_base(v, s)
+{
+}
+
+std::ostream&
+operator<<(std::ostream& os, const ethertype_t& ether)
+{
+  os << ether.to_string();
+  return os;
+}
+
+const ethertype_t&
+ethertype_t::from_numeric_val(uint16_t numeric)
+{
+  if (0x0806 == numeric) {
+    return (ethertype_t::ARP);
+  }
+  if (0x8906 == numeric) {
+    return (ethertype_t::FCOE);
+  }
+  if (0x0800 == numeric) {
+    return (ethertype_t::IPV4);
+  }
+  if (0x86DD == numeric) {
+    return (ethertype_t::IPV6);
+  }
+  if (0x88E5 == numeric) {
+    return (ethertype_t::MAC_SECURITY);
+  }
+  if (0x8847 == numeric) {
+    return (ethertype_t::MPLS_UNICAST);
+  }
+  if (0x22F3 == numeric) {
+    return (ethertype_t::TRILL);
+  }
+
+  return (ethertype_t::UNSPECIFIED);
+}
+
 }; // namespace VOM
 
 /*
index 4deb6c4..8543a66 100644 (file)
@@ -158,6 +158,72 @@ struct direction_t : public enum_base<direction_t>
  */
 std::ostream& operator<<(std::ostream& os, const direction_t& dir);
 
+/**
+ * Feature Ethertype
+ */
+struct ethertype_t : public enum_base<ethertype_t>
+{
+  /**
+   * Constructor
+   */
+  ethertype_t(int v, const std::string s);
+
+  /**
+   * Destructor
+   */
+  ~ethertype_t() = default;
+
+  /**
+   * Ethertype Arp
+   */
+  const static ethertype_t ARP;
+
+  /**
+   * Ethertype FCoE
+   */
+  const static ethertype_t FCOE;
+
+  /**
+   * Ethertype IPv4
+   */
+  const static ethertype_t IPV4;
+
+  /**
+   * Ethertype Ipv6
+   */
+  const static ethertype_t IPV6;
+
+  /**
+   * Ethertype MAC Security
+   */
+  const static ethertype_t MAC_SECURITY;
+
+  /**
+   * Ethertype MPLS unicast
+   */
+  const static ethertype_t MPLS_UNICAST;
+
+  /**
+   * Ethertype TRILL
+   */
+  const static ethertype_t TRILL;
+
+  /**
+   * Ethertype Unspecified
+   */
+  const static ethertype_t UNSPECIFIED;
+
+  /**
+   * Get the ethertype from the numeric value
+   */
+  static const ethertype_t& from_numeric_val(uint16_t numeric);
+};
+
+/**
+ * Output ostream for ethertype_t
+ */
+std::ostream& operator<<(std::ostream& os, const ethertype_t& eth);
+
 /**
  * A type declaration of an interface handle in VPP
  */
index 0a687c5..d9bd679 100644 (file)
@@ -39,6 +39,8 @@
 #include "vom/vxlan_tunnel_cmds.hpp"
 #include "vom/sub_interface.hpp"
 #include "vom/sub_interface_cmds.hpp"
+#include "vom/acl_ethertype.hpp"
+#include "vom/acl_ethertype_cmds.hpp"
 #include "vom/acl_list.hpp"
 #include "vom/acl_binding.hpp"
 #include "vom/acl_list_cmds.hpp"
@@ -280,6 +282,10 @@ public:
                     {
                         rc = handle_derived<sub_interface_cmds::delete_cmd>(f_exp, f_act);
                     }
+                    else if (typeid(*f_exp) == typeid(ACL::acl_ethertype_cmds::bind_cmd))
+                    {
+                        rc = handle_derived<ACL::acl_ethertype_cmds::bind_cmd>(f_exp, f_act);
+                    }
                     else if (typeid(*f_exp) == typeid(ACL::list_cmds::l3_update_cmd))
                     {
                         rc = handle_derived<ACL::list_cmds::l3_update_cmd>(f_exp, f_act);
@@ -984,6 +990,15 @@ BOOST_AUTO_TEST_CASE(test_acl) {
     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh));
     TRY_CHECK_RC(OM::write(fyodor, itf1));
 
+    ACL::ethertype_rule_t e1(ethertype_t::ARP, direction_t::INPUT);
+    ACL::ethertype_rule_t e2(ethertype_t::ARP, direction_t::OUTPUT);
+    ACL::ethertype_rule_t e3(ethertype_t::IPV4, direction_t::INPUT);
+    ACL::acl_ethertype::ethertype_rules_t l_e = {e1, e2, e3};
+    ACL::acl_ethertype *a_e = new ACL::acl_ethertype(itf1, l_e);
+    HW::item<bool> ae_binding(true, rc_t::OK);
+    ADD_EXPECT(ACL::acl_ethertype_cmds::bind_cmd(ae_binding, hw_ifh.data(), l_e));
+    TRY_CHECK_RC(OM::write(fyodor, *a_e));
+
     route::prefix_t src("10.10.10.10", 32);
     ACL::l3_rule r1(10, ACL::action_t::PERMIT, src, route::prefix_t::ZERO);
     ACL::l3_rule r2(20, ACL::action_t::DENY, route::prefix_t::ZERO, route::prefix_t::ZERO);
@@ -1038,6 +1053,7 @@ BOOST_AUTO_TEST_CASE(test_acl) {
     TRY_CHECK(OM::remove(leo));
 
     delete l3b;
+    delete a_e;
     HW::item<interface::admin_state_t> hw_as_down(interface::admin_state_t::DOWN,
                                                   rc_t::OK);
     STRICT_ORDER_OFF();