DHCP Client Dump 06/12606/4
authorNeale Ranns <neale.ranns@cisco.com>
Wed, 16 May 2018 11:12:18 +0000 (04:12 -0700)
committerNeale Ranns <nranns@cisco.com>
Thu, 7 Jun 2018 07:11:10 +0000 (03:11 -0400)
- use types on the DHCP API so that the same data is sent in comfing messages and in dumps
- add the DHCP client dump API
- update VOM to refelct API changes
- rename VOM class dhcp_config* dhcp_client*
- the VOM dhcp_client class maintains the lease data (which it reads on a dump) for clients to read

Change-Id: I2a43463937cbd80c01d45798e74b21288d8b8ead
Signed-off-by: Neale Ranns <neale.ranns@cisco.com>
17 files changed:
extras/vom/vom/Makefile.am
extras/vom/vom/dhcp_client.cpp [new file with mode: 0644]
extras/vom/vom/dhcp_client.hpp [moved from extras/vom/vom/dhcp_config.hpp with 54% similarity]
extras/vom/vom/dhcp_client_cmds.cpp [moved from extras/vom/vom/dhcp_config_cmds.cpp with 55% similarity]
extras/vom/vom/dhcp_client_cmds.hpp [moved from extras/vom/vom/dhcp_config_cmds.hpp with 79% similarity]
extras/vom/vom/dhcp_config.cpp [deleted file]
extras/vom/vom/l3_binding.cpp
extras/vom/vom/l3_binding.hpp
extras/vom/vom/neighbour_cmds.cpp
src/vat/api_format.c
src/vnet/dhcp/client.c
src/vnet/dhcp/client.h
src/vnet/dhcp/dhcp.api
src/vnet/dhcp/dhcp_api.c
src/vpp/api/custom_dump.c
test/test_dhcp.py
test/vpp_papi_provider.py

index 892f437..2abf346 100644 (file)
@@ -98,8 +98,8 @@ libvom_la_SOURCES =                   \
        client_db.cpp                   \
        cmd.cpp                         \
        connection.cpp                  \
-       dhcp_config_cmds.cpp            \
-       dhcp_config.cpp                 \
+       dhcp_client_cmds.cpp            \
+       dhcp_client.cpp                 \
        hw_cmds.cpp                     \
        hw.cpp                          \
        inspect.cpp                     \
@@ -192,8 +192,7 @@ vominclude_HEADERS =                        \
        client_db.hpp                   \
        cmd.hpp                         \
        connection.hpp                  \
-       dhcp_config.hpp                 \
-       dhcp_config_cmds.hpp            \
+       dhcp_client.hpp                 \
        dump_cmd.hpp                    \
        enum_base.hpp                   \
        event_cmd.hpp                   \
diff --git a/extras/vom/vom/dhcp_client.cpp b/extras/vom/vom/dhcp_client.cpp
new file mode 100644 (file)
index 0000000..fcadfa6
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ * 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 "vom/dhcp_client.hpp"
+#include "vom/dhcp_client_cmds.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+const dhcp_client::state_t dhcp_client::state_t::DISCOVER(0, "discover");
+const dhcp_client::state_t dhcp_client::state_t::REQUEST(1, "request");
+const dhcp_client::state_t dhcp_client::state_t::BOUND(2, "bound");
+
+dhcp_client::state_t::state_t(int v, const std::string& s)
+  : enum_base<dhcp_client::state_t>(v, s)
+{
+}
+
+const dhcp_client::state_t&
+dhcp_client::state_t::from_vpp(int n)
+{
+  if (REQUEST == n)
+    return (REQUEST);
+  if (BOUND == n)
+    return (BOUND);
+
+  return (DISCOVER);
+}
+
+singular_db<interface::key_t, dhcp_client> dhcp_client::m_db;
+std::weak_ptr<dhcp_client_cmds::events_cmd> dhcp_client::m_s_event_cmd;
+dhcp_client::dhcp_client_listener dhcp_client::m_listener;
+
+dhcp_client::event_handler dhcp_client::m_evh;
+
+dhcp_client::dhcp_client(const interface& itf,
+                         const std::string& hostname,
+                         bool set_broadcast_flag,
+                         event_listener* ev)
+  : m_itf(itf.singular())
+  , m_hostname(hostname)
+  , m_client_id(l2_address_t::ZERO)
+  , m_set_broadcast_flag(set_broadcast_flag)
+  , m_binding(0)
+  , m_evl(ev)
+  , m_event_cmd(get_event_cmd())
+{
+}
+
+dhcp_client::dhcp_client(const interface& itf,
+                         const std::string& hostname,
+                         const l2_address_t& client_id,
+                         bool set_broadcast_flag,
+                         event_listener* ev)
+  : m_itf(itf.singular())
+  , m_hostname(hostname)
+  , m_client_id(client_id)
+  , m_set_broadcast_flag(set_broadcast_flag)
+  , m_binding(0)
+  , m_evl(ev)
+  , m_event_cmd(get_event_cmd())
+{
+}
+
+dhcp_client::dhcp_client(const dhcp_client& o)
+  : m_itf(o.m_itf)
+  , m_hostname(o.m_hostname)
+  , m_client_id(o.m_client_id)
+  , m_set_broadcast_flag(o.m_set_broadcast_flag)
+  , m_binding(0)
+  , m_evl(o.m_evl)
+  , m_event_cmd(o.m_event_cmd)
+{
+}
+
+dhcp_client::~dhcp_client()
+{
+  sweep();
+
+  // not in the DB anymore.
+  m_db.release(m_itf->key(), this);
+}
+
+bool
+dhcp_client::operator==(const dhcp_client& l) const
+{
+  return ((key() == l.key()) && (m_hostname == l.m_hostname) &&
+          (m_client_id == l.m_client_id));
+}
+
+const dhcp_client::key_t&
+dhcp_client::key() const
+{
+  return (m_itf->key());
+}
+
+void
+dhcp_client::sweep()
+{
+  if (m_binding) {
+    HW::enqueue(
+      new dhcp_client_cmds::unbind_cmd(m_binding, m_itf->handle(), m_hostname));
+  }
+  HW::write();
+}
+
+void
+dhcp_client::dump(std::ostream& os)
+{
+  db_dump(m_db, os);
+}
+
+void
+dhcp_client::replay()
+{
+  if (m_binding) {
+    HW::enqueue(new dhcp_client_cmds::bind_cmd(m_binding, m_itf->handle(),
+                                               m_hostname, m_client_id));
+  }
+}
+
+std::string
+dhcp_client::to_string() const
+{
+  std::ostringstream s;
+  s << "DHCP-client: " << m_itf->to_string() << " hostname:" << m_hostname
+    << " client_id:[" << m_client_id << "] " << m_binding.to_string();
+  if (m_lease)
+    s << " " << m_lease->to_string();
+  else
+    s << " no-lease";
+
+  return (s.str());
+}
+
+void
+dhcp_client::update(const dhcp_client& desired)
+{
+  /*
+   * the desired state is always that the interface should be created
+   */
+  if (!m_binding) {
+    HW::enqueue(new dhcp_client_cmds::bind_cmd(m_binding, m_itf->handle(),
+                                               m_hostname, m_client_id));
+  }
+
+  if (desired.m_lease)
+    m_lease = desired.m_lease;
+  if (m_evl != desired.m_evl) {
+    m_evl = desired.m_evl;
+  }
+}
+
+const std::shared_ptr<dhcp_client::lease_t>
+dhcp_client::lease() const
+{
+  return (m_lease);
+}
+
+void
+dhcp_client::lease(std::shared_ptr<dhcp_client::lease_t> lease)
+{
+  m_lease = lease;
+}
+
+std::shared_ptr<dhcp_client>
+dhcp_client::find_or_add(const dhcp_client& temp)
+{
+  return (m_db.find_or_add(temp.m_itf->key(), temp));
+}
+
+std::shared_ptr<dhcp_client>
+dhcp_client::find(const key_t& k)
+{
+  return (m_db.find(k));
+}
+
+std::shared_ptr<dhcp_client>
+dhcp_client::singular() const
+{
+  return find_or_add(*this);
+}
+
+dhcp_client::lease_t::lease_t()
+  : state(state_t::DISCOVER)
+  , mac(mac_address_t::ZERO)
+{
+}
+
+dhcp_client::lease_t::lease_t(const state_t& state,
+                              std::shared_ptr<interface> itf,
+                              const boost::asio::ip::address& router_address,
+                              const route::prefix_t& host_prefix,
+                              const std::string& hostname,
+                              const mac_address_t& mac)
+  : state(state)
+  , itf(itf)
+  , router_address(router_address)
+  , host_prefix(host_prefix)
+  , hostname(hostname)
+  , mac(mac)
+{
+}
+
+std::string
+dhcp_client::lease_t::to_string() const
+{
+  std::stringstream ss;
+
+  ss << "lease:[" << itf->to_string() << " state: " << state.to_string()
+     << " host: " << host_prefix.to_string() << " router: " << router_address
+     << " mac: " << mac.to_string() << "]";
+
+  return (ss.str());
+}
+
+dhcp_client::event_listener::event_listener()
+  : m_status(rc_t::NOOP)
+{
+}
+
+HW::item<bool>&
+dhcp_client::event_listener::status()
+{
+  return (m_status);
+}
+
+dhcp_client::event_handler::event_handler()
+{
+  OM::register_listener(this);
+  inspect::register_handler({ "dhcp" }, "DHCP clients", this);
+}
+
+void
+dhcp_client::event_handler::handle_replay()
+{
+  m_db.replay();
+}
+
+void
+dhcp_client::event_handler::handle_populate(const client_db::key_t& key)
+{
+  std::shared_ptr<dhcp_client_cmds::dump_cmd> cmd =
+    std::make_shared<dhcp_client_cmds::dump_cmd>();
+
+  HW::enqueue(cmd);
+  HW::write();
+
+  for (auto& record : *cmd) {
+    auto& payload = record.get_payload();
+
+    std::shared_ptr<interface> itf =
+      interface::find(payload.client.sw_if_index);
+
+    if (!itf) {
+      VOM_LOG(log_level_t::ERROR) << "dhcp-client dump:"
+                                  << " itf:" << payload.client.sw_if_index;
+      continue;
+    }
+
+    const dhcp_client::state_t& s =
+      dhcp_client::state_t::from_vpp(payload.lease.state);
+    route::prefix_t pfx(payload.lease.is_ipv6, payload.lease.host_address,
+                        payload.lease.mask_width);
+    std::string hostname =
+      reinterpret_cast<const char*>(payload.lease.hostname);
+    l2_address_t l2(payload.client.id + 1);
+    dhcp_client dc(*itf, hostname, l2, payload.client.set_broadcast_flag);
+    dc.lease(std::make_shared<dhcp_client::lease_t>(
+      s, itf, from_bytes(0, payload.lease.router_address), pfx, hostname,
+      mac_address_t(payload.lease.host_mac)));
+    OM::commit(key, dc);
+  }
+}
+
+dependency_t
+dhcp_client::event_handler::order() const
+{
+  return (dependency_t::BINDING);
+}
+
+void
+dhcp_client::event_handler::show(std::ostream& os)
+{
+  db_dump(m_db, os);
+}
+
+std::shared_ptr<dhcp_client_cmds::events_cmd>
+dhcp_client::get_event_cmd()
+{
+  if (m_s_event_cmd.expired()) {
+    std::shared_ptr<dhcp_client_cmds::events_cmd> c =
+      std::make_shared<dhcp_client_cmds::events_cmd>(m_listener);
+
+    m_s_event_cmd = c;
+
+    HW::enqueue(c);
+    HW::write();
+
+    return c;
+  }
+
+  return (m_s_event_cmd.lock());
+}
+
+void
+dhcp_client::handle_dhcp_event(std::shared_ptr<lease_t> lease)
+{
+  m_lease = lease;
+  if (m_evl)
+    m_evl->handle_dhcp_event(m_lease);
+}
+
+void
+dhcp_client::dhcp_client_listener::handle_dhcp_event(std::shared_ptr<lease_t> e)
+{
+  /*
+   * Find the client the event references
+   */
+  std::shared_ptr<dhcp_client> client = find(e->itf->key());
+
+  if (client) {
+    client->handle_dhcp_event(e);
+  }
+}
+
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
similarity index 54%
rename from extras/vom/vom/dhcp_config.hpp
rename to extras/vom/vom/dhcp_client.hpp
index 8ea608d..17c626e 100644 (file)
  * limitations under the License.
  */
 
-#ifndef __VOM_DHCP_CONFIG_H__
-#define __VOM_DHCP_CONFIG_H__
+#ifndef __VOM_DHCP_CLIENT_H__
+#define __VOM_DHCP_CLIENT_H__
 
 #include "vom/hw.hpp"
 #include "vom/inspect.hpp"
 #include "vom/interface.hpp"
 #include "vom/object_base.hpp"
 #include "vom/om.hpp"
+#include "vom/prefix.hpp"
 #include "vom/singular_db.hpp"
 
 namespace VOM {
-namespace dhcp_config_cmds {
+namespace dhcp_client_cmds {
 class events_cmd;
 };
 /**
- * A representation of DHCP client configuration on an interface
+ * A representation of DHCP client on an interface
  */
-class dhcp_config : public object_base
+class dhcp_client : public object_base
 {
 public:
   /**
-   * typedef for the DHCP config key type
+   * typedef for the DHCP client key type
    */
   typedef interface::key_t key_t;
 
+  struct state_t : enum_base<state_t>
+  {
+    const static state_t DISCOVER;
+    const static state_t REQUEST;
+    const static state_t BOUND;
+
+    static const state_t& from_vpp(int i);
+
+  private:
+    /**
+     * Private constructor taking the value and the string name
+     */
+    state_t(int v, const std::string& s);
+  };
+
+  /**
+   * A DHCP lease data
+   */
+  struct lease_t
+  {
+    lease_t();
+    lease_t(const state_t& state,
+            std::shared_ptr<interface> itf,
+            const boost::asio::ip::address& router_address,
+            const route::prefix_t& host_prefix,
+            const std::string& hostname,
+            const mac_address_t& mac);
+
+    std::string to_string() const;
+
+    const state_t& state;
+    std::shared_ptr<interface> itf;
+    boost::asio::ip::address router_address;
+    route::prefix_t host_prefix;
+    std::string hostname;
+    mac_address_t mac;
+  };
+
+  /**
+   * A class that listens to DHCP Events
+   */
+  class event_listener
+  {
+  public:
+    /**
+     * Constructor
+     */
+    event_listener();
+
+    /**
+     * listener's virtual function invoked when a DHCP event is
+     * available to read
+     */
+    virtual void handle_dhcp_event(std::shared_ptr<lease_t> e) = 0;
+
+    /**
+     * Return the HW::item associated with this command
+     */
+    HW::item<bool>& status();
+
+  protected:
+    /**
+     * The HW::item associated with this command
+     */
+    HW::item<bool> m_status;
+  };
+
   /**
    * Construct a new object matching the desried state
    */
-  dhcp_config(const interface& itf,
+  dhcp_client(const interface& itf,
               const std::string& hostname,
-              bool set_broadcast_flag = true);
+              bool set_broadcast_flag = true,
+              event_listener* ev = nullptr);
 
   /**
    * Construct a new object matching the desried state
    */
-  dhcp_config(const interface& itf,
+  dhcp_client(const interface& itf,
               const std::string& hostname,
               const l2_address_t& client_id,
-              bool set_broadcast_flag = true);
+              bool set_broadcast_flag = true,
+              event_listener* ev = nullptr);
 
   /**
    * Copy Constructor
    */
-  dhcp_config(const dhcp_config& o);
+  dhcp_client(const dhcp_client& o);
 
   /**
    * Destructor
    */
-  ~dhcp_config();
+  ~dhcp_client();
 
   /**
    * Comparison operator - for UT
    */
-  bool operator==(const dhcp_config& d) const;
+  bool operator==(const dhcp_client& d) const;
 
   /**
    * Return the object's key
@@ -74,9 +144,9 @@ public:
   const key_t& key() const;
 
   /**
-   * Return the 'singular' of the DHCP config that matches this object
+   * Return the 'singular' of the DHCP client that matches this object
    */
-  std::shared_ptr<dhcp_config> singular() const;
+  std::shared_ptr<dhcp_client> singular() const;
 
   /**
    * convert to string format for debug purposes
@@ -84,43 +154,19 @@ public:
   std::string to_string() const;
 
   /**
-   * Dump all DHCP configs into the stream provided
+   * Dump all DHCP clients into the stream provided
    */
   static void dump(std::ostream& os);
 
   /**
-   * Find a DHCP config from its key
+   * Find a DHCP client from its key
    */
-  static std::shared_ptr<dhcp_config> find(const key_t& k);
+  static std::shared_ptr<dhcp_client> find(const key_t& k);
 
   /**
- * A class that listens to DHCP Events
- */
-  class event_listener
-  {
-  public:
-    /**
-     * Constructor
-     */
-    event_listener();
-
-    /**
-     * listener's virtual function invoked when a DHCP event is
-     * available to read
-     */
-    virtual void handle_dhcp_event(dhcp_config_cmds::events_cmd* cmd) = 0;
-
-    /**
-     * Return the HW::item associated with this command
-     */
-    HW::item<bool>& status();
-
-  protected:
-    /**
-     * The HW::item associated with this command
-     */
-    HW::item<bool> m_status;
-  };
+   * return the current lease data
+   */
+  const std::shared_ptr<lease_t> lease() const;
 
 private:
   /**
@@ -161,12 +207,12 @@ private:
   /**
    * Enquue commonds to the VPP command Q for the update
    */
-  void update(const dhcp_config& obj);
+  void update(const dhcp_client& obj);
 
   /**
-   * Find or add DHCP config to the OM
+   * Find or add DHCP client to the OM
    */
-  static std::shared_ptr<dhcp_config> find_or_add(const dhcp_config& temp);
+  static std::shared_ptr<dhcp_client> find_or_add(const dhcp_client& temp);
 
   /*
    * It's the OM class that calls singular()
@@ -176,7 +222,7 @@ private:
   /**
    * It's the singular_db class that calls replay()
    */
-  friend class singular_db<key_t, dhcp_config>;
+  friend class singular_db<key_t, dhcp_client>;
 
   /**
    * Sweep/reap the object if still stale
@@ -188,20 +234,22 @@ private:
    */
   void replay(void);
 
+  void lease(std::shared_ptr<lease_t> l);
+
   /**
-   * A reference counting pointer to the interface on which DHCP config
+   * A reference counting pointer to the interface on which DHCP client
    * resides. By holding the reference here, we can guarantee that
    * this object will outlive the interface
    */
   const std::shared_ptr<interface> m_itf;
 
   /**
-   * The hostname in the DHCP configuration
+   * The hostname in the DHCP client
    */
   const std::string m_hostname;
 
   /**
-   * The option-61 client_id in the DHCP configuration
+   * The option-61 client_id in the DHCP client
    */
   const l2_address_t m_client_id;
 
@@ -213,13 +261,41 @@ private:
   /**
    * HW configuration for the binding. The bool representing the
    * do/don't bind.
- */
  */
   HW::item<bool> m_binding;
 
   /**
-   * A map of all Dhcp configs keyed against the interface.
+   * A pointer to an event listener for client events
    */
-  static singular_db<key_t, dhcp_config> m_db;
+  event_listener* m_evl;
+
+  /**
+   * Current lease state for this client
+   */
+  std::shared_ptr<lease_t> m_lease;
+
+  std::shared_ptr<dhcp_client_cmds::events_cmd> m_event_cmd;
+
+  void handle_dhcp_event(std::shared_ptr<lease_t> e);
+
+  /**
+   * A map of all Dhcp clients keyed against the interface.
+   */
+  static singular_db<key_t, dhcp_client> m_db;
+
+  static std::weak_ptr<dhcp_client_cmds::events_cmd> m_s_event_cmd;
+  static std::shared_ptr<dhcp_client_cmds::events_cmd> get_event_cmd();
+
+  class dhcp_client_listener : public event_listener
+  {
+  public:
+    /**
+     * listener's virtual function invoked when a DHCP event is
+     * available to read
+     */
+    void handle_dhcp_event(std::shared_ptr<lease_t> e);
+  };
+  static dhcp_client_listener m_listener;
 };
 };
 
similarity index 55%
rename from extras/vom/vom/dhcp_config_cmds.cpp
rename to extras/vom/vom/dhcp_client_cmds.cpp
index 76ce58b..181a15f 100644 (file)
  * limitations under the License.
  */
 
-#include "vom/dhcp_config_cmds.hpp"
+#include "vom/dhcp_client_cmds.hpp"
 
 DEFINE_VAPI_MSG_IDS_DHCP_API_JSON;
 
 namespace VOM {
-namespace dhcp_config_cmds {
+namespace dhcp_client_cmds {
 
 bind_cmd::bind_cmd(HW::item<bool>& item,
                    const handle_t& itf,
@@ -45,21 +45,21 @@ bind_cmd::issue(connection& con)
   msg_t req(con.ctx(), std::ref(*this));
 
   auto& payload = req.get_request().get_payload();
-  payload.sw_if_index = m_itf.value();
   payload.is_add = 1;
-  payload.pid = getpid();
-  payload.want_dhcp_event = 1;
-  payload.set_broadcast_flag = m_set_broadcast_flag;
+  payload.client.sw_if_index = m_itf.value();
+  payload.client.pid = getpid();
+  payload.client.want_dhcp_event = 1;
+  payload.client.set_broadcast_flag = m_set_broadcast_flag;
 
-  memset(payload.hostname, 0, sizeof(payload.hostname));
-  memcpy(payload.hostname, m_hostname.c_str(),
-         std::min(sizeof(payload.hostname), m_hostname.length()));
+  memset(payload.client.hostname, 0, sizeof(payload.client.hostname));
+  memcpy(payload.client.hostname, m_hostname.c_str(),
+         std::min(sizeof(payload.client.hostname), m_hostname.length()));
 
-  memset(payload.client_id, 0, sizeof(payload.client_id));
-  payload.client_id[0] = 1;
+  memset(payload.client.id, 0, sizeof(payload.client.id));
+  payload.client.id[0] = 1;
   std::copy_n(begin(m_client_id.bytes),
-              std::min(sizeof(payload.client_id), m_client_id.bytes.size()),
-              payload.client_id + 1);
+              std::min(sizeof(payload.client.id), m_client_id.bytes.size()),
+              payload.client.id + 1);
 
   VAPI_CALL(req.execute());
 
@@ -72,7 +72,7 @@ std::string
 bind_cmd::to_string() const
 {
   std::ostringstream s;
-  s << "Dhcp-config-bind: " << m_hw_item.to_string()
+  s << "Dhcp-client-bind: " << m_hw_item.to_string()
     << " itf:" << m_itf.to_string() << " hostname:" << m_hostname;
 
   return (s.str());
@@ -99,13 +99,13 @@ unbind_cmd::issue(connection& con)
   msg_t req(con.ctx(), std::ref(*this));
 
   auto& payload = req.get_request().get_payload();
-  payload.sw_if_index = m_itf.value();
   payload.is_add = 0;
-  payload.pid = getpid();
-  payload.want_dhcp_event = 0;
+  payload.client.sw_if_index = m_itf.value();
+  payload.client.pid = getpid();
+  payload.client.want_dhcp_event = 0;
 
-  memcpy(payload.hostname, m_hostname.c_str(),
-         std::min(sizeof(payload.hostname), m_hostname.length()));
+  memcpy(payload.client.hostname, m_hostname.c_str(),
+         std::min(sizeof(payload.client.hostname), m_hostname.length()));
 
   VAPI_CALL(req.execute());
 
@@ -119,18 +119,23 @@ std::string
 unbind_cmd::to_string() const
 {
   std::ostringstream s;
-  s << "Dhcp-config-unbind: " << m_hw_item.to_string()
+  s << "Dhcp-client-unbind: " << m_hw_item.to_string()
     << " itf:" << m_itf.to_string() << " hostname:" << m_hostname;
 
   return (s.str());
 }
 
-events_cmd::events_cmd(dhcp_config::event_listener& el)
+events_cmd::events_cmd(dhcp_client::event_listener& el)
   : event_cmd(el.status())
   , m_listener(el)
 {
 }
 
+events_cmd::~events_cmd()
+{
+  VOM_LOG(log_level_t::INFO) << "DHCP events destroyed";
+}
+
 bool
 events_cmd::operator==(const events_cmd& other) const
 {
@@ -159,7 +164,31 @@ events_cmd::retire(connection& con)
 void
 events_cmd::notify()
 {
-  m_listener.handle_dhcp_event(this);
+  for (auto& msg : *this) {
+    auto& payload = msg.get_payload();
+
+    const dhcp_client::state_t& s =
+      dhcp_client::state_t::from_vpp(payload.lease.state);
+    route::prefix_t pfx(payload.lease.is_ipv6, payload.lease.host_address,
+                        payload.lease.mask_width);
+    std::shared_ptr<interface> itf = interface::find(payload.lease.sw_if_index);
+
+    if (itf) {
+      std::shared_ptr<dhcp_client::lease_t> ev =
+        std::make_shared<dhcp_client::lease_t>(
+          s, itf, from_bytes(0, payload.lease.router_address), pfx,
+          reinterpret_cast<const char*>(payload.lease.hostname),
+          mac_address_t(payload.lease.host_mac));
+      m_listener.handle_dhcp_event(ev);
+
+      VOM_LOG(log_level_t::INFO) << "DHCP: " << ev->to_string();
+    } else {
+      VOM_LOG(log_level_t::ERROR) << "DHCP: no interface: "
+                                  << payload.lease.sw_if_index;
+    }
+  }
+
+  flush();
 }
 
 std::string
@@ -167,8 +196,38 @@ events_cmd::to_string() const
 {
   return ("dhcp-events");
 }
+
+dump_cmd::dump_cmd()
+{
 }
-};
+
+bool
+dump_cmd::operator==(const dump_cmd& other) const
+{
+  return (true);
+}
+
+rc_t
+dump_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_cmd::to_string() const
+{
+  return ("dhcp-client-dump");
+}
+
+}; // namespace dhcp_client_cmds
+}; // namespace VOM
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
similarity index 79%
rename from extras/vom/vom/dhcp_config_cmds.hpp
rename to extras/vom/vom/dhcp_client_cmds.hpp
index 726ff99..e7db38f 100644 (file)
  * limitations under the License.
  */
 
-#ifndef __VOM_DHCP_CONFIG_CMDS_H__
-#define __VOM_DHCP_CONFIG_CMDS_H__
+#ifndef __VOM_DHCP_CLIENT_CMDS_H__
+#define __VOM_DHCP_CLIENT_CMDS_H__
 
-#include "vom/dhcp_config.hpp"
+#include "vom/dhcp_client.hpp"
+#include "vom/dump_cmd.hpp"
 #include "vom/event_cmd.hpp"
 
 #include <vapi/dhcp.api.vapi.hpp>
 #include <vapi/vpe.api.vapi.hpp>
 
 namespace VOM {
-namespace dhcp_config_cmds {
+namespace dhcp_client_cmds {
 
 /**
   * A command class that binds the DHCP config to the interface
@@ -125,7 +126,8 @@ public:
   /**
    * Constructor
    */
-  events_cmd(dhcp_config::event_listener& el);
+  events_cmd(dhcp_client::event_listener& el);
+  ~events_cmd();
 
   /**
    * Issue the command to VPP/HW - subscribe to DHCP events
@@ -156,11 +158,45 @@ private:
   /**
    * The listner of this command
    */
-  dhcp_config::event_listener& m_listener;
-};
+  dhcp_client::event_listener& m_listener;
 };
+
+/**
+ * A cmd class that Dumps all the DHCP clients
+ */
+class dump_cmd : public VOM::dump_cmd<vapi::Dhcp_client_dump>
+{
+public:
+  /**
+   * Constructor
+   */
+  dump_cmd();
+  dump_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_cmd& i) const;
+
+private:
+  /**
+   * HW reutrn code
+   */
+  HW::item<bool> item;
 };
 
+}; // namespace dhcp_client_cmds
+}; // namespace VOM
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
diff --git a/extras/vom/vom/dhcp_config.cpp b/extras/vom/vom/dhcp_config.cpp
deleted file mode 100644 (file)
index 7d97fa1..0000000
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * 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 "vom/dhcp_config.hpp"
-#include "vom/dhcp_config_cmds.hpp"
-#include "vom/singular_db_funcs.hpp"
-
-namespace VOM {
-/**
- * A DB of all DHCP configs
- */
-singular_db<interface::key_t, dhcp_config> dhcp_config::m_db;
-
-dhcp_config::event_handler dhcp_config::m_evh;
-
-dhcp_config::dhcp_config(const interface& itf,
-                         const std::string& hostname,
-                         bool set_broadcast_flag)
-  : m_itf(itf.singular())
-  , m_hostname(hostname)
-  , m_client_id(l2_address_t::ZERO)
-  , m_set_broadcast_flag(set_broadcast_flag)
-  , m_binding(0)
-{
-}
-
-dhcp_config::dhcp_config(const interface& itf,
-                         const std::string& hostname,
-                         const l2_address_t& client_id,
-                         bool set_broadcast_flag)
-  : m_itf(itf.singular())
-  , m_hostname(hostname)
-  , m_client_id(client_id)
-  , m_set_broadcast_flag(set_broadcast_flag)
-  , m_binding(0)
-{
-}
-
-dhcp_config::dhcp_config(const dhcp_config& o)
-  : m_itf(o.m_itf)
-  , m_hostname(o.m_hostname)
-  , m_client_id(o.m_client_id)
-  , m_set_broadcast_flag(o.m_set_broadcast_flag)
-  , m_binding(0)
-{
-}
-
-dhcp_config::~dhcp_config()
-{
-  sweep();
-
-  // not in the DB anymore.
-  m_db.release(m_itf->key(), this);
-}
-
-bool
-dhcp_config::operator==(const dhcp_config& l) const
-{
-  return ((key() == l.key()) && (m_hostname == l.m_hostname) &&
-          (m_client_id == l.m_client_id));
-}
-
-const dhcp_config::key_t&
-dhcp_config::key() const
-{
-  return (m_itf->key());
-}
-
-void
-dhcp_config::sweep()
-{
-  if (m_binding) {
-    HW::enqueue(
-      new dhcp_config_cmds::unbind_cmd(m_binding, m_itf->handle(), m_hostname));
-  }
-  HW::write();
-}
-
-void
-dhcp_config::dump(std::ostream& os)
-{
-  db_dump(m_db, os);
-}
-
-void
-dhcp_config::replay()
-{
-  if (m_binding) {
-    HW::enqueue(new dhcp_config_cmds::bind_cmd(m_binding, m_itf->handle(),
-                                               m_hostname, m_client_id));
-  }
-}
-
-std::string
-dhcp_config::to_string() const
-{
-  std::ostringstream s;
-  s << "Dhcp-config: " << m_itf->to_string() << " hostname:" << m_hostname
-    << " client_id:[" << m_client_id << "] " << m_binding.to_string();
-
-  return (s.str());
-}
-
-void
-dhcp_config::update(const dhcp_config& desired)
-{
-  /*
- * the desired state is always that the interface should be created
- */
-  if (!m_binding) {
-    HW::enqueue(new dhcp_config_cmds::bind_cmd(m_binding, m_itf->handle(),
-                                               m_hostname, m_client_id));
-  }
-}
-
-std::shared_ptr<dhcp_config>
-dhcp_config::find_or_add(const dhcp_config& temp)
-{
-  return (m_db.find_or_add(temp.m_itf->key(), temp));
-}
-
-std::shared_ptr<dhcp_config>
-dhcp_config::find(const key_t& k)
-{
-  return (m_db.find(k));
-}
-
-std::shared_ptr<dhcp_config>
-dhcp_config::singular() const
-{
-  return find_or_add(*this);
-}
-
-dhcp_config::event_listener::event_listener()
-  : m_status(rc_t::NOOP)
-{
-}
-
-HW::item<bool>&
-dhcp_config::event_listener::status()
-{
-  return (m_status);
-}
-
-dhcp_config::event_handler::event_handler()
-{
-  OM::register_listener(this);
-  inspect::register_handler({ "dhcp" }, "DHCP configurations", this);
-}
-
-void
-dhcp_config::event_handler::handle_replay()
-{
-  m_db.replay();
-}
-
-void
-dhcp_config::event_handler::handle_populate(const client_db::key_t& key)
-{
-  // FIXME
-}
-
-dependency_t
-dhcp_config::event_handler::order() const
-{
-  return (dependency_t::BINDING);
-}
-
-void
-dhcp_config::event_handler::show(std::ostream& os)
-{
-  db_dump(m_db, os);
-}
-}
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "mozilla")
- * End:
- */
index 13bc1ff..6b8d362 100644 (file)
@@ -158,35 +158,6 @@ operator<<(std::ostream& os, const l3_binding::key_t& key)
   return (os);
 }
 
-std::deque<std::shared_ptr<l3_binding>>
-l3_binding::find(const interface& i)
-{
-  /*
- * Loop throught the entire map looking for matching interface.
- * not the most efficient algorithm, but it will do for now. The
- * number of L3 configs is low and this is only called during bootup
- */
-  std::deque<std::shared_ptr<l3_binding>> l3s;
-
-  auto it = m_db.begin();
-
-  while (it != m_db.end()) {
-    /*
-     * The key in the DB is a pair of the interface's name and prefix.
-     * If the keys match, save the L3-config
-     */
-    auto key = it->first;
-
-    if (i.key() == key.first) {
-      l3s.push_back(it->second.lock());
-    }
-
-    ++it;
-  }
-
-  return (l3s);
-}
-
 l3_binding::event_handler::event_handler()
 {
   OM::register_listener(this);
index 0177e56..a2a4626 100644 (file)
@@ -94,11 +94,6 @@ public:
    */
   static void dump(std::ostream& os);
 
-  /**
-   * Find all bindings in the DB for the interface passed
-   */
-  static std::deque<std::shared_ptr<l3_binding>> find(const interface& i);
-
   /**
    * Find a binding from its key
    */
index 2f3c200..63534f3 100644 (file)
@@ -151,7 +151,11 @@ dump_cmd::issue(connection& con)
 std::string
 dump_cmd::to_string() const
 {
-  return ("neighbour-dump");
+  std::ostringstream s;
+
+  s << "neighbour-dump: " << m_itf.to_string() << " " << m_proto.to_string();
+
+  return (s.str());
 }
 } // namespace neighbour_cmds
 } // namespace vom
index dfb0284..f17802d 100644 (file)
@@ -2669,10 +2669,11 @@ vl_api_dhcp_compl_event_t_handler (vl_api_dhcp_compl_event_t * mp)
 {
   errmsg ("DHCP compl event: pid %d %s hostname %s host_addr %U "
          "router_addr %U host_mac %U",
-         ntohl (mp->pid), mp->is_ipv6 ? "ipv6" : "ipv4", mp->hostname,
-         format_ip4_address, &mp->host_address,
-         format_ip4_address, &mp->router_address,
-         format_ethernet_address, mp->host_mac);
+         ntohl (mp->pid), mp->lease.is_ipv6 ? "ipv6" : "ipv4",
+         mp->lease.hostname,
+         format_ip4_address, &mp->lease.host_address,
+         format_ip4_address, &mp->lease.router_address,
+         format_ethernet_address, mp->lease.host_mac);
 }
 
 static void vl_api_dhcp_compl_event_t_handler_json
@@ -10125,12 +10126,12 @@ api_dhcp_client_config (vat_main_t * vam)
   /* Construct the API message */
   M (DHCP_CLIENT_CONFIG, mp);
 
-  mp->sw_if_index = htonl (sw_if_index);
-  clib_memcpy (mp->hostname, hostname, vec_len (hostname));
-  vec_free (hostname);
   mp->is_add = is_add;
-  mp->want_dhcp_event = disable_event ? 0 : 1;
-  mp->pid = htonl (getpid ());
+  mp->client.sw_if_index = htonl (sw_if_index);
+  clib_memcpy (mp->client.hostname, hostname, vec_len (hostname));
+  vec_free (hostname);
+  mp->client.want_dhcp_event = disable_event ? 0 : 1;
+  mp->client.pid = htonl (getpid ());
 
   /* send it... */
   S (mp);
index 11e47f9..98f2123 100644 (file)
@@ -100,7 +100,6 @@ static void
 dhcp_client_addr_callback (dhcp_client_t * c)
 {
   dhcp_client_main_t *dcm = &dhcp_client_main;
-  void (*fp) (u32, u32, u8 *, u8, u8, u8 *, u8 *, u8 *) = c->event_callback;
 
   /* disable the feature */
   vnet_feature_enable_disable ("ip4-unicast",
@@ -147,12 +146,8 @@ dhcp_client_addr_callback (dhcp_client_t * c)
   /*
    * Call the user's event callback to report DHCP information
    */
-  if (fp)
-    (*fp) (c->client_index,    /* clinet index */
-          c->pid, c->hostname, c->subnet_mask_width, 0,        /* is_ipv6 */
-          (u8 *) & c->leased_address,  /* host IP address */
-          (u8 *) & c->router_address,  /* router IP address */
-          (u8 *) (c->l2_rewrite + 6)); /* host MAC address */
+  if (c->event_callback)
+    c->event_callback (c->client_index, c);
 }
 
 /*
@@ -977,13 +972,14 @@ dhcp_client_add_del (dhcp_client_add_del_args_t * a)
 }
 
 int
-dhcp_client_config (vlib_main_t * vm,
+dhcp_client_config (u32 is_add,
+                   u32 client_index,
+                   vlib_main_t * vm,
                    u32 sw_if_index,
                    u8 * hostname,
                    u8 * client_id,
-                   u32 is_add,
-                   u32 client_index,
-                   void *event_callback, u8 set_broadcast_flag, u32 pid)
+                   dhcp_event_cb_t event_callback,
+                   u8 set_broadcast_flag, u32 pid)
 {
   dhcp_client_add_del_args_t _a, *a = &_a;
   int rv;
@@ -1061,6 +1057,22 @@ dhcp_client_config (vlib_main_t * vm,
   return rv;
 }
 
+void
+dhcp_client_walk (dhcp_client_walk_cb_t cb, void *ctx)
+{
+  dhcp_client_main_t *dcm = &dhcp_client_main;
+  dhcp_client_t *c;
+
+  /* *INDENT-OFF* */
+  pool_foreach (c, dcm->clients,
+  ({
+    if (!cb(c, ctx))
+      break;
+  }));
+  /* *INDENT-ON* */
+
+}
+
 static clib_error_t *
 dhcp_client_set_command_fn (vlib_main_t * vm,
                            unformat_input_t * input,
index d3be3eb..72cbf66 100644 (file)
@@ -34,7 +34,15 @@ typedef enum
 #undef _
 } dhcp_client_state_t;
 
-typedef struct
+struct dhcp_client_t_;
+
+/**
+ * Callback function for DHCP complete events
+ */
+typedef void (*dhcp_event_cb_t) (u32 client_index,
+                                const struct dhcp_client_t_ * client);
+
+typedef struct dhcp_client_t_
 {
   dhcp_client_state_t state;
 
@@ -78,7 +86,7 @@ typedef struct
   u8 client_hardware_address[6];
   u8 client_detect_feature_enabled;
 
-  void *event_callback;
+  dhcp_event_cb_t event_callback;
 } dhcp_client_t;
 
 typedef struct
@@ -109,7 +117,7 @@ typedef struct
   /* Information used for event callback */
   u32 client_index;
   u32 pid;
-  void *event_callback;
+  dhcp_event_cb_t event_callback;
 } dhcp_client_add_del_args_t;
 
 extern dhcp_client_main_t dhcp_client_main;
@@ -121,13 +129,36 @@ int dhcp_client_for_us (u32 bi0,
                        ip4_header_t * ip0,
                        udp_header_t * u0, dhcp_header_t * dh0);
 
-int dhcp_client_config (vlib_main_t * vm,
-                       u32 sw_if_index,
-                       u8 * hostname,
-                       u8 * client_id,
-                       u32 is_add,
-                       u32 client_index,
-                       void *event_callback, u8 set_broadcast_flag, u32 pid);
+/**
+ * Add/Delete DHCP clients
+ */
+extern int dhcp_client_config (u32 is_add,
+                              u32 client_index,
+                              vlib_main_t * vm,
+                              u32 sw_if_index,
+                              u8 * hostname,
+                              u8 * client_id,
+                              dhcp_event_cb_t event_callback,
+                              u8 set_broadcast_flag, u32 pid);
+
+/**
+ * callback function for clients walking the DHCP client configurations
+ *
+ * @param client The client being visitsed
+ * @param data   The data passed during the call to 'walk'
+ * @return !0 to continue walking 0 to stop.
+ */
+typedef int (*dhcp_client_walk_cb_t) (const dhcp_client_t * client,
+                                     void *data);
+
+/**
+ * Walk (visit each) DHCP client configuration
+ *
+ * @param cb The callback function invoked as each client is visited
+ * @param ctx Context data passed back to the client in the invocation of
+ *             the callback.
+ */
+extern void dhcp_client_walk (dhcp_client_walk_cb_t cb, void *ctx);
 
 #endif /* included_dhcp_client_h */
 
index 975aa6e..30ead32 100644 (file)
@@ -13,7 +13,7 @@
  * limitations under the License.
  */
 
-option version = "1.0.1";
+option version = "2.0.0";
 
 /** \brief DHCP Proxy config add / del request
     @param client_index - opaque cookie to identify the sender
@@ -62,45 +62,53 @@ autoreply define dhcp_proxy_set_vss
   u8 is_add;
 };
 
-/** \brief DHCP Client config add / del request
-    @param client_index - opaque cookie to identify the sender
-    @param context - sender context, to match reply w/ request
+/** \brief DHCP Client config data
     @param sw_if_index - index of the interface for DHCP client
     @param hostname - hostname
-    @param client_id - Client ID - option 61
-    @param is_add - add the config if non-zero, else delete
+    @param id - Client ID - option 61
     @param want_dhcp_event - DHCP event sent to the sender
            via dhcp_compl_event API message if non-zero
     @param set_broadcast_flag - in the DHCP Discover to control
                                 how the resulting OFFER is addressed.
     @param pid - sender's pid
 */
-autoreply define dhcp_client_config
+typeonly define dhcp_client
 {
-  u32 client_index;
-  u32 context;
   u32 sw_if_index;
   u8 hostname[64];
-  u8 client_id[64];
-  u8 is_add;
+  u8 id[64];
   u8 want_dhcp_event;
   u8 set_broadcast_flag;
   u32 pid;
 };
 
-/** \brief Tell client about a DHCP completion event
+/** \brief DHCP Client config add / del request
     @param client_index - opaque cookie to identify the sender
-    @param pid - client pid registered to receive notification
+    @param context - sender context, to match reply w/ request
+    @param is_add - add the config if non-zero, else delete
+    @param client - client configuration data
+*/
+autoreply define dhcp_client_config
+{
+  u32 client_index;
+  u32 context;
+  u8 is_add;
+  vl_api_dhcp_client_t client;
+};
+
+/** \brief Data learned by the client during the DHCP process
+    @param sw_if_index - the interface on which the client is configured
+    @param state - the state of the lease (see dhcp_client_state_t)
     @param is_ipv6 - if non-zero the address is ipv6, else ipv4
     @param mask_width - The length of the subnet mask assigned
     @param host_address - Host IP address
     @param router_address - Router IP address
     @param host_mac - Host MAC address
 */
-define dhcp_compl_event
+typeonly define dhcp_lease
 {
-  u32 client_index;
-  u32 pid;
+  u32 sw_if_index;
+  u8 state;
   u8 hostname[64];
   u8 is_ipv6;
   u8 mask_width;
@@ -109,10 +117,41 @@ define dhcp_compl_event
   u8 host_mac[6];
 };
 
+/** \brief Tell client about a DHCP completion event
+    @param client_index - opaque cookie to identify the sender
+    @param pid - client pid registered to receive notification
+    @param lease - Data learned during the DHCP process;
+*/
+define dhcp_compl_event
+{
+  u32 client_index;
+  u32 pid;
+  vl_api_dhcp_lease_t lease;
+};
+
 service {
   rpc dhcp_client_config returns dhcp_client_config_reply events dhcp_compl_event;
 };
 
+/** \brief Dump the DHCP client configurations
+ */
+define dhcp_client_dump
+{
+  u32 client_index;
+  u32 context;
+};
+
+/** \brief DHCP Client details returned from dump
+ *  @param client - The configured client
+ *  @param lease - The learned lease data
+ */
+define dhcp_client_details
+{
+  u32 context;
+  vl_api_dhcp_client_t client;
+  vl_api_dhcp_lease_t lease;
+};
+
 /** \brief Dump DHCP proxy table
     @param client_index - opaque cookie to identify the sender
     @param True for IPv6 proxy table
index 401f6b7..1dacf11 100644 (file)
@@ -48,7 +48,8 @@
 _(DHCP_PROXY_CONFIG,dhcp_proxy_config)            \
 _(DHCP_PROXY_DUMP,dhcp_proxy_dump)                \
 _(DHCP_PROXY_SET_VSS,dhcp_proxy_set_vss)          \
-_(DHCP_CLIENT_CONFIG, dhcp_client_config)
+_(DHCP_CLIENT_CONFIG, dhcp_client_config)         \
+_(DHCP_CLIENT_DUMP, dhcp_client_dump)
 
 
 static void
@@ -203,14 +204,56 @@ dhcp_send_details (fib_protocol_t proto,
   vl_api_send_msg (reg, (u8 *) mp);
 }
 
-void
-dhcp_compl_event_callback (u32 client_index, u32 pid, u8 * hostname,
-                          u8 mask_width, u8 is_ipv6, u8 * host_address,
-                          u8 * router_address, u8 * host_mac)
+static void
+dhcp_client_lease_encode (vl_api_dhcp_lease_t * lease,
+                         const dhcp_client_t * client)
+{
+  size_t len;
+
+  lease->is_ipv6 = 0;          // only support IPv6 clients
+  lease->sw_if_index = ntohl (client->sw_if_index);
+  lease->state = client->state;
+  len = clib_min (sizeof (lease->hostname) - 1, vec_len (client->hostname));
+  clib_memcpy (&lease->hostname, client->hostname, len);
+  lease->hostname[len] = 0;
+
+  lease->mask_width = client->subnet_mask_width;
+  clib_memcpy (&lease->host_address[0], (u8 *) & client->leased_address, 4);
+  clib_memcpy (&lease->router_address[0], (u8 *) & client->router_address, 4);
+
+  if (NULL != client->l2_rewrite)
+    clib_memcpy (&lease->host_mac[0], client->l2_rewrite + 6, 6);
+}
+
+static void
+dhcp_client_data_encode (vl_api_dhcp_client_t * vclient,
+                        const dhcp_client_t * client)
+{
+  size_t len;
+
+  vclient->sw_if_index = ntohl (client->sw_if_index);
+  len = clib_min (sizeof (vclient->hostname) - 1, vec_len (client->hostname));
+  clib_memcpy (&vclient->hostname, client->hostname, len);
+  vclient->hostname[len] = 0;
+
+  len = clib_min (sizeof (vclient->id) - 1,
+                 vec_len (client->client_identifier));
+  clib_memcpy (&vclient->id, client->client_identifier, len);
+  vclient->id[len] = 0;
+
+  if (NULL != client->event_callback)
+    vclient->want_dhcp_event = 1;
+  else
+    vclient->want_dhcp_event = 0;
+  vclient->set_broadcast_flag = client->set_broadcast_flag;
+  vclient->pid = client->pid;
+}
+
+static void
+dhcp_compl_event_callback (u32 client_index, const dhcp_client_t * client)
 {
   vl_api_registration_t *reg;
   vl_api_dhcp_compl_event_t *mp;
-  u32 len;
 
   reg = vl_api_client_index_to_registration (client_index);
   if (!reg)
@@ -218,17 +261,8 @@ dhcp_compl_event_callback (u32 client_index, u32 pid, u8 * hostname,
 
   mp = vl_msg_api_alloc (sizeof (*mp));
   mp->client_index = client_index;
-  mp->pid = pid;
-  mp->is_ipv6 = is_ipv6;
-  len = (vec_len (hostname) < 63) ? vec_len (hostname) : 63;
-  clib_memcpy (&mp->hostname, hostname, len);
-  mp->hostname[len] = 0;
-  mp->mask_width = mask_width;
-  clib_memcpy (&mp->host_address[0], host_address, 16);
-  clib_memcpy (&mp->router_address[0], router_address, 16);
-
-  if (NULL != host_mac)
-    clib_memcpy (&mp->host_mac[0], host_mac, 6);
+  mp->pid = client->pid;
+  dhcp_client_lease_encode (&mp->lease, client);
 
   mp->_vl_msg_id = ntohs (VL_API_DHCP_COMPL_EVENT);
 
@@ -240,21 +274,76 @@ static void vl_api_dhcp_client_config_t_handler
 {
   vlib_main_t *vm = vlib_get_main ();
   vl_api_dhcp_client_config_reply_t *rmp;
+  u32 sw_if_index;
   int rv = 0;
 
-  VALIDATE_SW_IF_INDEX (mp);
+  sw_if_index = ntohl (mp->client.sw_if_index);
+  if (!vnet_sw_if_index_is_api_valid (sw_if_index))
+    {
+      rv = VNET_API_ERROR_INVALID_SW_IF_INDEX;
+      goto bad_sw_if_index;
+    }
 
-  rv = dhcp_client_config (vm, ntohl (mp->sw_if_index),
-                          mp->hostname, mp->client_id,
-                          mp->is_add, mp->client_index,
-                          mp->want_dhcp_event ? dhcp_compl_event_callback :
-                          NULL, mp->set_broadcast_flag, mp->pid);
+  rv = dhcp_client_config (mp->is_add,
+                          mp->client_index,
+                          vm,
+                          sw_if_index,
+                          mp->client.hostname,
+                          mp->client.id,
+                          (mp->client.want_dhcp_event ?
+                           dhcp_compl_event_callback :
+                           NULL),
+                          mp->client.set_broadcast_flag, mp->client.pid);
 
   BAD_SW_IF_INDEX_LABEL;
 
   REPLY_MACRO (VL_API_DHCP_CLIENT_CONFIG_REPLY);
 }
 
+typedef struct dhcp_client_send_walk_ctx_t_
+{
+  vl_api_registration_t *reg;
+  u32 context;
+} dhcp_client_send_walk_ctx_t;
+
+static int
+send_dhcp_client_entry (const dhcp_client_t * client, void *arg)
+{
+  dhcp_client_send_walk_ctx_t *ctx;
+  vl_api_dhcp_client_details_t *mp;
+
+  ctx = arg;
+
+  mp = vl_msg_api_alloc (sizeof (*mp));
+  memset (mp, 0, sizeof (*mp));
+
+  mp->_vl_msg_id = ntohs (VL_API_DHCP_CLIENT_DETAILS);
+  mp->context = ctx->context;
+
+  dhcp_client_data_encode (&mp->client, client);
+  dhcp_client_lease_encode (&mp->lease, client);
+
+  vl_api_send_msg (ctx->reg, (u8 *) mp);
+
+  return (1);
+}
+
+static void
+vl_api_dhcp_client_dump_t_handler (vl_api_dhcp_client_dump_t * mp)
+{
+  vl_api_registration_t *reg;
+
+  reg = vl_api_client_index_to_registration (mp->client_index);
+  if (!reg)
+    return;
+
+  dhcp_client_send_walk_ctx_t ctx = {
+    .reg = reg,
+    .context = mp->context,
+  };
+  dhcp_client_walk (send_dhcp_client_entry, &ctx);
+}
+
 /*
  * dhcp_api_hookup
  * Add vpe's API message handlers to the table.
index 4c88b8f..e8d143b 100644 (file)
@@ -1011,13 +1011,13 @@ static void *vl_api_dhcp_client_config_t_print
 
   s = format (0, "SCRIPT: dhcp_client_config ");
 
-  s = format (s, "sw_if_index %d ", ntohl (mp->sw_if_index));
+  s = format (s, "sw_if_index %d ", ntohl (mp->client.sw_if_index));
 
-  s = format (s, "hostname %s ", mp->hostname);
+  s = format (s, "hostname %s ", mp->client.hostname);
 
-  s = format (s, "want_dhcp_event %d ", mp->want_dhcp_event);
+  s = format (s, "want_dhcp_event %d ", mp->client.want_dhcp_event);
 
-  s = format (s, "pid %d ", ntohl (mp->pid));
+  s = format (s, "pid %d ", ntohl (mp->client.pid));
 
   if (mp->is_add == 0)
     s = format (s, "del ");
index c5b1fa5..39133a3 100644 (file)
@@ -1422,6 +1422,29 @@ class TestDHCP(VppTestCase):
                                       mactobinary(self.pg3.remote_mac),
                                       self.pg3.remote_ip4,
                                       is_add=0)
+
+        #
+        # read the DHCP client details from a dump
+        #
+        clients = self.vapi.dhcp_client_dump()
+
+        self.assertEqual(clients[0].client.sw_if_index,
+                         self.pg3.sw_if_index)
+        self.assertEqual(clients[0].lease.sw_if_index,
+                         self.pg3.sw_if_index)
+        self.assertEqual(clients[0].client.hostname.rstrip('\0'),
+                         hostname)
+        self.assertEqual(clients[0].lease.hostname.rstrip('\0'),
+                         hostname)
+        self.assertEqual(clients[0].lease.is_ipv6, 0)
+        # 0 = DISCOVER, 1 = REQUEST, 2 = BOUND
+        self.assertEqual(clients[0].lease.state, 2)
+        self.assertEqual(clients[0].lease.mask_width, 24)
+        self.assertEqual(clients[0].lease.router_address.rstrip('\0'),
+                         self.pg3.remote_ip4n)
+        self.assertEqual(clients[0].lease.host_address.rstrip('\0'),
+                         self.pg3.local_ip4n)
+
         #
         # remove the DHCP config
         #
index 6dbed29..d13508b 100644 (file)
@@ -2298,6 +2298,9 @@ class VppPapiProvider(object):
                 'is_ipv6': is_ip6,
             })
 
+    def dhcp_client_dump(self):
+        return self.api(self.papi.dhcp_client_dump, {})
+
     def dhcp_client(self,
                     sw_if_index,
                     hostname,
@@ -2308,13 +2311,14 @@ class VppPapiProvider(object):
         return self.api(
             self.papi.dhcp_client_config,
             {
-                'sw_if_index': sw_if_index,
-                'hostname': hostname,
-                'client_id': client_id,
                 'is_add': is_add,
-                'want_dhcp_event': want_dhcp_events,
-                'set_broadcast_flag': set_broadcast_flag,
-                'pid': os.getpid(),
+                'client': {
+                    'sw_if_index': sw_if_index,
+                    'hostname': hostname,
+                    'id': client_id,
+                    'want_dhcp_event': want_dhcp_events,
+                    'set_broadcast_flag': set_broadcast_flag,
+                    'pid': os.getpid()}
             })
 
     def ip_mroute_add_del(self,