vom: Add support for redirect contracts in gbp 13/16013/5
authorMohsin Kazmi <sykazmi@cisco.com>
Sun, 18 Nov 2018 23:12:11 +0000 (00:12 +0100)
committerNeale Ranns <nranns@cisco.com>
Tue, 20 Nov 2018 11:05:44 +0000 (11:05 +0000)
Change-Id: I18543785166811ddbd628d19065d3dfad3f948e9
Signed-off-by: Mohsin Kazmi <sykazmi@cisco.com>
extras/vom/vom/CMakeLists.txt
extras/vom/vom/gbp_contract.cpp
extras/vom/vom/gbp_contract.hpp
extras/vom/vom/gbp_contract_cmds.cpp
extras/vom/vom/gbp_contract_cmds.hpp
extras/vom/vom/gbp_rule.cpp [new file with mode: 0644]
extras/vom/vom/gbp_rule.hpp [new file with mode: 0644]
src/plugins/gbp/gbp.api
src/plugins/gbp/gbp_api.c
src/plugins/gbp/gbp_contract.h

index 82c2211..7413df8 100644 (file)
@@ -80,6 +80,7 @@ if(GBP_FILE)
     gbp_recirc.cpp
     gbp_route_domain_cmds.cpp
     gbp_route_domain.cpp
+    gbp_rule.cpp
     gbp_subnet_cmds.cpp
     gbp_subnet.cpp
     gbp_vxlan.cpp
@@ -197,6 +198,7 @@ if(GBP_FILE)
     gbp_endpoint_group.hpp
     gbp_recirc.hpp
     gbp_route_domain.hpp
+    gbp_rule.hpp
     gbp_subnet.hpp
     gbp_vxlan.hpp
   )
index 8b27269..87b5ed8 100644 (file)
@@ -14,6 +14,7 @@
  */
 
 #include "vom/gbp_contract.hpp"
+#include "vom/api_types.hpp"
 #include "vom/gbp_contract_cmds.hpp"
 #include "vom/singular_db_funcs.hpp"
 
@@ -76,7 +77,7 @@ gbp_contract::replay()
 {
   if (m_hw) {
     HW::enqueue(new gbp_contract_cmds::create_cmd(
-      m_hw, m_src_epg_id, m_dst_epg_id, m_acl->handle()));
+      m_hw, m_src_epg_id, m_dst_epg_id, m_acl->handle(), m_gbp_rules));
   }
 }
 
@@ -85,20 +86,34 @@ gbp_contract::to_string() const
 {
   std::ostringstream s;
   s << "gbp-contract:[{" << m_src_epg_id << ", " << m_dst_epg_id << "}, "
-    << m_acl->to_string() << "]";
+    << m_acl->to_string();
+  if (m_gbp_rules.size()) {
+    auto it = m_gbp_rules.cbegin();
+    while (it != m_gbp_rules.cend()) {
+      s << it->to_string();
+      ++it;
+    }
+  }
+  s << "]";
 
   return (s.str());
 }
 
+void
+gbp_contract::set_gbp_rules(const gbp_contract::gbp_rules_t& gbp_rules)
+{
+  m_gbp_rules = gbp_rules;
+}
+
 void
 gbp_contract::update(const gbp_contract& r)
 {
   /*
- * create the table if it is not yet created
- */
  * create the table if it is not yet created
  */
   if (rc_t::OK != m_hw.rc()) {
     HW::enqueue(new gbp_contract_cmds::create_cmd(
-      m_hw, m_src_epg_id, m_dst_epg_id, m_acl->handle()));
+      m_hw, m_src_epg_id, m_dst_epg_id, m_acl->handle(), m_gbp_rules));
   }
 }
 
@@ -157,7 +172,28 @@ gbp_contract::event_handler::handle_populate(const client_db::key_t& key)
       gbp_contract gbpc(payload.contract.src_epg, payload.contract.dst_epg,
                         *acl);
       OM::commit(key, gbpc);
-
+      if (payload.contract.n_rules) {
+        gbp_contract::gbp_rules_t rules;
+        for (u8 i = 0; i < payload.contract.n_rules; i++) {
+          const gbp_rule::action_t action =
+            gbp_rule::action_t::from_int(payload.contract.rules[i].action);
+          const gbp_rule::hash_mode_t hm = gbp_rule::hash_mode_t::from_int(
+            payload.contract.rules[i].nh_set.hash_mode);
+          gbp_rule::next_hops_t nhs;
+          for (u8 j = 0; j < payload.contract.rules[i].nh_set.n_nhs; j++) {
+            gbp_rule::next_hop_t nh(
+              from_api(payload.contract.rules[i].nh_set.nhs[j].ip),
+              from_api(payload.contract.rules[i].nh_set.nhs[j].mac),
+              payload.contract.rules[i].nh_set.nhs[j].bd_id,
+              payload.contract.rules[i].nh_set.nhs[j].rd_id);
+            nhs.insert(nh);
+          }
+          gbp_rule::next_hop_set_t next_hop_set(hm, nhs);
+          gbp_rule gr(i, next_hop_set, action);
+          rules.insert(gr);
+        }
+        gbpc.set_gbp_rules(rules);
+      }
       VOM_LOG(log_level_t::DEBUG) << "read: " << gbpc.to_string();
     }
   }
index 7a0696d..53f8f36 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "vom/acl_list.hpp"
 #include "vom/gbp_endpoint.hpp"
+#include "vom/gbp_rule.hpp"
 #include "vom/interface.hpp"
 #include "vom/singular_db.hpp"
 #include "vom/types.hpp"
@@ -30,6 +31,11 @@ namespace VOM {
 class gbp_contract : public object_base
 {
 public:
+  /**
+   * set of gbp rules
+   */
+  typedef std::set<gbp_rule> gbp_rules_t;
+
   /**
    * The key for a contract is the pari of EPG-IDs
    */
@@ -87,6 +93,11 @@ public:
    */
   std::string to_string() const;
 
+  /**
+   * Set gbp_rules in case of Redirect Contract
+   */
+  void set_gbp_rules(const gbp_rules_t& gbp_rules);
+
 private:
   /**
    * Class definition for listeners to OM events
@@ -168,6 +179,11 @@ private:
    */
   std::shared_ptr<ACL::l3_list> m_acl;
 
+  /**
+   * The gbp rules applied to traffic between the gourps
+   */
+  gbp_rules_t m_gbp_rules;
+
   /**
    * A map of all bridge_domains
    */
index f990924..db49f97 100644 (file)
@@ -14,6 +14,7 @@
  */
 
 #include "vom/gbp_contract_cmds.hpp"
+#include "vom/api_types.hpp"
 
 namespace VOM {
 namespace gbp_contract_cmds {
@@ -21,11 +22,13 @@ namespace gbp_contract_cmds {
 create_cmd::create_cmd(HW::item<bool>& item,
                        epg_id_t src_epg_id,
                        epg_id_t dst_epg_id,
-                       const handle_t& acl)
+                       const handle_t& acl,
+                       const gbp_contract::gbp_rules_t& gbp_rules)
   : rpc_cmd(item)
   , m_src_epg_id(src_epg_id)
   , m_dst_epg_id(dst_epg_id)
   , m_acl(acl)
+  , m_gbp_rules(gbp_rules)
 {
 }
 
@@ -33,20 +36,59 @@ bool
 create_cmd::operator==(const create_cmd& other) const
 {
   return ((m_acl == other.m_acl) && (m_src_epg_id == other.m_src_epg_id) &&
-          (m_dst_epg_id == other.m_dst_epg_id));
+          (m_dst_epg_id == other.m_dst_epg_id) &&
+          (m_gbp_rules == other.m_gbp_rules));
 }
 
 rc_t
 create_cmd::issue(connection& con)
 {
-  msg_t req(con.ctx(), 1, std::ref(*this));
+  u8 size = m_gbp_rules.empty() ? 1 : m_gbp_rules.size();
+  msg_t req(con.ctx(), size, std::ref(*this));
 
   auto& payload = req.get_request().get_payload();
   payload.is_add = 1;
   payload.contract.acl_index = m_acl.value();
   payload.contract.src_epg = m_src_epg_id;
   payload.contract.dst_epg = m_dst_epg_id;
-
+  if (size > 1) {
+    u32 ii = 0;
+    auto it = m_gbp_rules.cbegin();
+    payload.contract.n_rules = m_gbp_rules.size();
+    while (it != m_gbp_rules.cend()) {
+      if (it->action() == gbp_rule::action_t::REDIRECT)
+        payload.contract.rules[ii].action = GBP_API_RULE_REDIRECT;
+      else if (it->action() == gbp_rule::action_t::PERMIT)
+        payload.contract.rules[ii].action = GBP_API_RULE_PERMIT;
+      else
+        payload.contract.rules[ii].action = GBP_API_RULE_DENY;
+
+      if (it->nhs().getHashMode() == gbp_rule::hash_mode_t::SYMMETRIC)
+        payload.contract.rules[ii].nh_set.hash_mode =
+          GBP_API_HASH_MODE_SYMMETRIC;
+      else if (it->nhs().getHashMode() == gbp_rule::hash_mode_t::SRC_IP)
+        payload.contract.rules[ii].nh_set.hash_mode = GBP_API_HASH_MODE_SRC_IP;
+      else
+        payload.contract.rules[ii].nh_set.hash_mode = GBP_API_HASH_MODE_DST_IP;
+
+      const gbp_rule::next_hops_t& next_hops = it->nhs().getNextHops();
+      u8 jj = 0, nh_size = (next_hops.size() > 8) ? 8 : next_hops.size();
+      auto nh_it = next_hops.cbegin();
+
+      payload.contract.rules[ii].nh_set.n_nhs = nh_size;
+      while (jj < nh_size) {
+        payload.contract.rules[ii].nh_set.nhs[jj].ip = to_api(nh_it->getIp());
+        payload.contract.rules[ii].nh_set.nhs[jj].mac = to_api(nh_it->getMac());
+        payload.contract.rules[ii].nh_set.nhs[jj].bd_id = nh_it->getBdId();
+        payload.contract.rules[ii].nh_set.nhs[jj].rd_id = nh_it->getRdId();
+        ++nh_it;
+        ++jj;
+      }
+
+      ++it;
+      ++ii;
+    }
+  }
   VAPI_CALL(req.execute());
 
   return (wait());
index 7e44476..4f921f6 100644 (file)
@@ -25,8 +25,8 @@ namespace VOM {
 namespace gbp_contract_cmds {
 
 /**
-* A command class that creates or updates the GBP contract
-*/
+ * A command class that creates or updates the GBP contract
+ */
 class create_cmd : public rpc_cmd<HW::item<bool>, vapi::Gbp_contract_add_del>
 {
 public:
@@ -36,7 +36,8 @@ public:
   create_cmd(HW::item<bool>& item,
              epg_id_t src_epg_id,
              epg_id_t dst_epg_id,
-             const handle_t& acl);
+             const handle_t& acl,
+             const gbp_contract::gbp_rules_t& gbp_rules);
 
   /**
    * Issue the command to VPP/HW
@@ -57,6 +58,7 @@ private:
   const epg_id_t m_src_epg_id;
   const epg_id_t m_dst_epg_id;
   const handle_t m_acl;
+  const gbp_contract::gbp_rules_t& m_gbp_rules;
 };
 
 /**
diff --git a/extras/vom/vom/gbp_rule.cpp b/extras/vom/vom/gbp_rule.cpp
new file mode 100644 (file)
index 0000000..7aa7990
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * 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 <sstream>
+
+#include "vom/gbp_rule.hpp"
+
+namespace VOM {
+gbp_rule::next_hop_t::next_hop_t(const boost::asio::ip::address& ip,
+                                 const mac_address_t& mac,
+                                 uint32_t bd_id,
+                                 uint32_t rd_id)
+  : m_ip(ip)
+  , m_mac(mac)
+  , m_bd_id(bd_id)
+  , m_rd_id(rd_id)
+{
+}
+
+std::string
+gbp_rule::next_hop_t::to_string() const
+{
+  std::ostringstream s;
+
+  s << "["
+    << "ip:" << m_ip << " mac:" << m_mac.to_string() << " bd:" << m_bd_id
+    << " rd:" << m_rd_id << "]";
+
+  return (s.str());
+}
+
+bool
+gbp_rule::next_hop_t::operator<(const gbp_rule::next_hop_t& nh) const
+{
+  return (nh.m_ip < m_ip);
+}
+
+bool
+gbp_rule::next_hop_t::operator==(const gbp_rule::next_hop_t& nh) const
+{
+  return ((m_ip == nh.m_ip) && (m_mac == nh.m_mac) && (m_bd_id == nh.m_bd_id) &&
+          (m_rd_id == nh.m_rd_id));
+}
+
+const boost::asio::ip::address&
+gbp_rule::next_hop_t::getIp() const
+{
+  return m_ip;
+}
+
+const mac_address_t&
+gbp_rule::next_hop_t::getMac() const
+{
+  return m_mac;
+}
+
+const uint32_t
+gbp_rule::next_hop_t::getBdId() const
+{
+  return m_bd_id;
+}
+
+const uint32_t
+gbp_rule::next_hop_t::getRdId() const
+{
+  return m_rd_id;
+}
+
+const gbp_rule::hash_mode_t gbp_rule::hash_mode_t::SRC_IP(1, "src-ip");
+const gbp_rule::hash_mode_t gbp_rule::hash_mode_t::DST_IP(0, "dst-ip");
+const gbp_rule::hash_mode_t gbp_rule::hash_mode_t::SYMMETRIC(2, "symmetric");
+
+gbp_rule::hash_mode_t::hash_mode_t(int v, const std::string s)
+  : enum_base(v, s)
+{
+}
+
+const gbp_rule::hash_mode_t&
+gbp_rule::hash_mode_t::from_int(vapi_enum_gbp_hash_mode i)
+{
+  if (i == GBP_API_HASH_MODE_SYMMETRIC)
+    return gbp_rule::hash_mode_t::SYMMETRIC;
+  else if (i == GBP_API_HASH_MODE_SRC_IP)
+    return gbp_rule::hash_mode_t::SRC_IP;
+
+  return gbp_rule::hash_mode_t::DST_IP;
+}
+
+gbp_rule::next_hop_set_t::next_hop_set_t(const gbp_rule::hash_mode_t& hm,
+                                         gbp_rule::next_hops_t& nhs)
+  : m_hm(hm)
+  , m_nhs(nhs)
+{
+}
+
+std::string
+gbp_rule::next_hop_set_t::to_string() const
+{
+  std::ostringstream s;
+
+  s << "hash-mode:" << m_hm.to_string() << " next-hops:[";
+  auto it = m_nhs.cbegin();
+  while (it != m_nhs.cend()) {
+    s << " " << it->to_string();
+    ++it;
+  }
+  s << " ] next-hop-size:" << m_nhs.size();
+
+  return (s.str());
+}
+
+bool
+gbp_rule::next_hop_set_t::operator==(const next_hop_set_t& nhs) const
+{
+  return ((m_hm == nhs.m_hm) && (m_nhs == nhs.m_nhs));
+}
+
+const gbp_rule::hash_mode_t&
+gbp_rule::next_hop_set_t::getHashMode() const
+{
+  return m_hm;
+}
+
+const gbp_rule::next_hops_t&
+gbp_rule::next_hop_set_t::getNextHops() const
+{
+  return m_nhs;
+}
+
+const gbp_rule::action_t gbp_rule::action_t::REDIRECT(2, "redirect");
+const gbp_rule::action_t gbp_rule::action_t::PERMIT(1, "permit");
+const gbp_rule::action_t gbp_rule::action_t::DENY(0, "deny");
+
+gbp_rule::action_t::action_t(int v, const std::string s)
+  : enum_base(v, s)
+{
+}
+
+const gbp_rule::action_t&
+gbp_rule::action_t::from_int(vapi_enum_gbp_rule_action i)
+{
+  if (i == GBP_API_RULE_REDIRECT)
+    return gbp_rule::action_t::REDIRECT;
+  else if (i == GBP_API_RULE_PERMIT)
+    return gbp_rule::action_t::PERMIT;
+
+  return gbp_rule::action_t::DENY;
+}
+
+gbp_rule::gbp_rule(uint32_t priority,
+                   const gbp_rule::next_hop_set_t& nhs,
+                   const gbp_rule::action_t& a)
+  : m_priority(priority)
+  , m_nhs(nhs)
+  , m_action(a)
+{
+}
+
+bool
+gbp_rule::operator<(const gbp_rule& other) const
+{
+  return (other.m_priority < m_priority);
+}
+
+bool
+gbp_rule::operator==(const gbp_rule& rule) const
+{
+  return ((m_action == rule.m_action) && (m_nhs == rule.m_nhs) &&
+          (m_priority == rule.m_priority));
+}
+
+std::string
+gbp_rule::to_string() const
+{
+  std::ostringstream s;
+
+  s << "gbp-rule:["
+    << "priority:" << m_priority << " action:" << m_action.to_string()
+    << " next-hop-set:[" << m_nhs.to_string() << "]]";
+
+  return (s.str());
+}
+
+uint32_t
+gbp_rule::priority() const
+{
+  return m_priority;
+}
+
+const gbp_rule::action_t&
+gbp_rule::action() const
+{
+  return m_action;
+}
+
+const gbp_rule::next_hop_set_t&
+gbp_rule::nhs() const
+{
+  return m_nhs;
+}
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/gbp_rule.hpp b/extras/vom/vom/gbp_rule.hpp
new file mode 100644 (file)
index 0000000..bda0409
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_GBP_RULE_H__
+#define __VOM_GBP_RULE_H__
+
+#include <set>
+
+#include "vom/types.hpp"
+#include <vapi/gbp.api.vapi.h>
+namespace VOM {
+class gbp_rule
+{
+public:
+  /**
+   * Representation of next hop
+   */
+  struct next_hop_t
+  {
+    /**
+     * Constructor for next_hop_t
+     */
+    next_hop_t(const boost::asio::ip::address& ip,
+               const mac_address_t& mac,
+               uint32_t bd_id,
+               uint32_t rd_id);
+
+    /**
+     * default destructor
+     */
+    ~next_hop_t() = default;
+
+    /**
+     * convert to string
+     */
+    std::string to_string() const;
+
+    /**
+     * less-than operator
+     */
+    bool operator<(const next_hop_t& nh) const;
+
+    /**
+     * comparison operator (for testing)
+     */
+    bool operator==(const next_hop_t& nh) const;
+
+    /**
+     * get the IP address
+     */
+    const boost::asio::ip::address& getIp(void) const;
+
+    /**
+     * get the mac address
+     */
+    const mac_address_t& getMac(void) const;
+
+    /**
+     * get the bridge domain Id
+     */
+    const uint32_t getBdId(void) const;
+
+    /**
+     * get the route domain Id
+     */
+    const uint32_t getRdId(void) const;
+
+  private:
+    /**
+     * IP address for next hop
+     */
+    const boost::asio::ip::address m_ip;
+
+    /**
+     * mac address for interface lookup
+     */
+    const mac_address_t m_mac;
+
+    /**
+     * bridge domain in which redirected endpoints exist
+     */
+    const uint32_t m_bd_id;
+
+    /**
+     * route domain in which redirected endpoints exist
+     */
+    const uint32_t m_rd_id;
+  };
+
+  /**
+   * hash mode enum
+   */
+  struct hash_mode_t : public enum_base<hash_mode_t>
+  {
+    /**
+     * Flow Hash is calculated based on SRC IP
+     * in case of load balancing
+     */
+    const static hash_mode_t SRC_IP;
+
+    /**
+     * Flow hash is calculated based on DST IP
+     */
+    const static hash_mode_t DST_IP;
+
+    /**
+     * Flow hash is calculated based on SRC IP,
+     * DST IP and Protocol. SRC IP and DST IP
+     * addresses are sorted before hash such that
+     * a same hash is generated in both directions.
+     */
+    const static hash_mode_t SYMMETRIC;
+
+    /**
+     * create the hash mode from int value
+     */
+    static const hash_mode_t& from_int(vapi_enum_gbp_hash_mode i);
+
+  private:
+    hash_mode_t(int v, const std::string s);
+  };
+
+  /**
+   * unordered set of next hops
+   */
+  typedef std::set<next_hop_t> next_hops_t;
+
+  /**
+   * Representation of set of next hops and
+   * associated hash mode profile
+   */
+  struct next_hop_set_t
+  {
+    /**
+     * Constructor for next_hop_set_t
+     */
+    next_hop_set_t(const hash_mode_t& hm, next_hops_t& nhs);
+
+    /**
+     * Destructor for next_hop_set_t
+     */
+    ~next_hop_set_t() = default;
+
+    /**
+     * convert to string
+     */
+    std::string to_string() const;
+
+    /**
+     * Comparison operator
+     */
+    bool operator==(const next_hop_set_t& nhs) const;
+
+    /**
+     * get the hash mode
+     */
+    const hash_mode_t& getHashMode(void) const;
+
+    /**
+     * get the set of next hops
+     */
+    const next_hops_t& getNextHops(void) const;
+
+  private:
+    /**
+     * hash mode for this rule
+     */
+    const hash_mode_t m_hm;
+
+    /**
+     * set of next hops
+     */
+    const next_hops_t m_nhs;
+  };
+
+  /**
+   * ACL rule action enum
+   */
+  struct action_t : public enum_base<action_t>
+  {
+    /**
+     * Permit action
+     */
+    const static action_t PERMIT;
+
+    /**
+     * Deny action
+     */
+    const static action_t DENY;
+
+    /**
+     * Redirect action
+     */
+    const static action_t REDIRECT;
+
+    /**
+     * create the action from int value
+     */
+    static const action_t& from_int(vapi_enum_gbp_rule_action i);
+
+  private:
+    action_t(int v, const std::string s);
+  };
+
+  /**
+   * Construct a new object matching the desried state
+   */
+  gbp_rule(uint32_t priority, const next_hop_set_t& nhs, const action_t& a);
+
+  /**
+   * Copy Constructor
+   */
+  gbp_rule(const gbp_rule& o) = default;
+
+  /**
+   * Destructor
+   */
+  ~gbp_rule() = default;
+
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * less-than operator
+   */
+  bool operator<(const gbp_rule& rule) const;
+
+  /**
+   * comparison operator (for testing)
+   */
+  bool operator==(const gbp_rule& rule) const;
+
+  /**
+   * Getters
+   */
+  uint32_t priority() const;
+  const next_hop_set_t& nhs() const;
+  const action_t& action() const;
+
+private:
+  /**
+   * Priority. Used to sort the rules in a list in the order
+   * in which they are applied
+   */
+  uint32_t m_priority;
+
+  /**
+   * set of next hops along with hash mode profile
+   */
+  const next_hop_set_t m_nhs;
+
+  /**
+   * Action on match
+   */
+  const action_t m_action;
+};
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
index 9af8b35..6bdcc5d 100644 (file)
@@ -263,6 +263,7 @@ enum gbp_hash_mode
 {
   GBP_API_HASH_MODE_SRC_IP,
   GBP_API_HASH_MODE_DST_IP,
+  GBP_API_HASH_MODE_SYMMETRIC,
 };
 
 typedef gbp_next_hop_set
index 6a00072..4402ec1 100644 (file)
@@ -718,6 +718,9 @@ gbp_hash_mode_decode (vl_api_gbp_hash_mode_t in, gbp_hash_mode_t * out)
     case GBP_API_HASH_MODE_DST_IP:
       *out = GBP_HASH_MODE_DST_IP;
       return (0);
+    case GBP_API_HASH_MODE_SYMMETRIC:
+      *out = GBP_HASH_MODE_SYMMETRIC;
+      return (0);
     }
 
   return (-2);
index c107351..21e7265 100644 (file)
@@ -52,7 +52,8 @@ typedef struct gbp_next_hop_t_
 
 #define foreach_gbp_hash_mode   \
   _(SRC_IP, "src-ip")           \
-  _(DST_IP, "dst-ip")
+  _(DST_IP, "dst-ip")          \
+  _(SYMMETRIC, "symmetric")
 
 typedef enum gbp_hash_mode_t_
 {