diff options
author | Neale Ranns <neale.ranns@cisco.com> | 2018-05-16 04:12:18 -0700 |
---|---|---|
committer | Neale Ranns <nranns@cisco.com> | 2018-06-07 03:11:10 -0400 |
commit | daff1784037376f4a5caec2f5975f9b5fc23d5a4 (patch) | |
tree | 186701f7fcd64d691de5b5d11b477a5d8b7840a2 | |
parent | 0e969ac8431c80ff4bca5f6985876b1c584eefcd (diff) |
DHCP Client Dump
- 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>
-rw-r--r-- | extras/vom/vom/Makefile.am | 7 | ||||
-rw-r--r-- | extras/vom/vom/dhcp_client.cpp | 346 | ||||
-rw-r--r-- | extras/vom/vom/dhcp_client.hpp (renamed from extras/vom/vom/dhcp_config.hpp) | 186 | ||||
-rw-r--r-- | extras/vom/vom/dhcp_client_cmds.cpp (renamed from extras/vom/vom/dhcp_config_cmds.cpp) | 105 | ||||
-rw-r--r-- | extras/vom/vom/dhcp_client_cmds.hpp (renamed from extras/vom/vom/dhcp_config_cmds.hpp) | 50 | ||||
-rw-r--r-- | extras/vom/vom/dhcp_config.cpp | 194 | ||||
-rw-r--r-- | extras/vom/vom/l3_binding.cpp | 29 | ||||
-rw-r--r-- | extras/vom/vom/l3_binding.hpp | 5 | ||||
-rw-r--r-- | extras/vom/vom/neighbour_cmds.cpp | 6 | ||||
-rw-r--r-- | src/vat/api_format.c | 19 | ||||
-rw-r--r-- | src/vnet/dhcp/client.c | 34 | ||||
-rw-r--r-- | src/vnet/dhcp/client.h | 51 | ||||
-rw-r--r-- | src/vnet/dhcp/dhcp.api | 71 | ||||
-rw-r--r-- | src/vnet/dhcp/dhcp_api.c | 135 | ||||
-rw-r--r-- | src/vpp/api/custom_dump.c | 8 | ||||
-rw-r--r-- | test/test_dhcp.py | 23 | ||||
-rw-r--r-- | test/vpp_papi_provider.py | 16 |
17 files changed, 888 insertions, 397 deletions
diff --git a/extras/vom/vom/Makefile.am b/extras/vom/vom/Makefile.am index 892f437976e..2abf3463c8f 100644 --- a/extras/vom/vom/Makefile.am +++ b/extras/vom/vom/Makefile.am @@ -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 index 00000000000..fcadfa69ded --- /dev/null +++ b/extras/vom/vom/dhcp_client.cpp @@ -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: + */ diff --git a/extras/vom/vom/dhcp_config.hpp b/extras/vom/vom/dhcp_client.hpp index 8ea608d809d..17c626ed0f1 100644 --- a/extras/vom/vom/dhcp_config.hpp +++ b/extras/vom/vom/dhcp_client.hpp @@ -13,60 +13,130 @@ * 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; }; }; diff --git a/extras/vom/vom/dhcp_config_cmds.cpp b/extras/vom/vom/dhcp_client_cmds.cpp index 76ce58b6b92..181a15f8f96 100644 --- a/extras/vom/vom/dhcp_config_cmds.cpp +++ b/extras/vom/vom/dhcp_client_cmds.cpp @@ -13,12 +13,12 @@ * 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 * diff --git a/extras/vom/vom/dhcp_config_cmds.hpp b/extras/vom/vom/dhcp_client_cmds.hpp index 726ff992577..e7db38f4e3b 100644 --- a/extras/vom/vom/dhcp_config_cmds.hpp +++ b/extras/vom/vom/dhcp_client_cmds.hpp @@ -13,17 +13,18 @@ * 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 index 7d97fa15d6e..00000000000 --- a/extras/vom/vom/dhcp_config.cpp +++ /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: - */ diff --git a/extras/vom/vom/l3_binding.cpp b/extras/vom/vom/l3_binding.cpp index 13bc1ffd575..6b8d36209a7 100644 --- a/extras/vom/vom/l3_binding.cpp +++ b/extras/vom/vom/l3_binding.cpp @@ -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); diff --git a/extras/vom/vom/l3_binding.hpp b/extras/vom/vom/l3_binding.hpp index 0177e56ea2b..a2a46263dbd 100644 --- a/extras/vom/vom/l3_binding.hpp +++ b/extras/vom/vom/l3_binding.hpp @@ -95,11 +95,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 */ static std::shared_ptr<l3_binding> find(const key_t& k); diff --git a/extras/vom/vom/neighbour_cmds.cpp b/extras/vom/vom/neighbour_cmds.cpp index 2f3c200d5fb..63534f3dd94 100644 --- a/extras/vom/vom/neighbour_cmds.cpp +++ b/extras/vom/vom/neighbour_cmds.cpp @@ -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 diff --git a/src/vat/api_format.c b/src/vat/api_format.c index dfb0284955e..f17802d778f 100644 --- a/src/vat/api_format.c +++ b/src/vat/api_format.c @@ -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); diff --git a/src/vnet/dhcp/client.c b/src/vnet/dhcp/client.c index 11e47f9ad7a..98f212334f6 100644 --- a/src/vnet/dhcp/client.c +++ b/src/vnet/dhcp/client.c @@ -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, diff --git a/src/vnet/dhcp/client.h b/src/vnet/dhcp/client.h index d3be3ebf271..72cbf66733f 100644 --- a/src/vnet/dhcp/client.h +++ b/src/vnet/dhcp/client.h @@ -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 */ diff --git a/src/vnet/dhcp/dhcp.api b/src/vnet/dhcp/dhcp.api index 975aa6ee9e7..30ead320089 100644 --- a/src/vnet/dhcp/dhcp.api +++ b/src/vnet/dhcp/dhcp.api @@ -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 diff --git a/src/vnet/dhcp/dhcp_api.c b/src/vnet/dhcp/dhcp_api.c index 401f6b75edc..1dacf1178c2 100644 --- a/src/vnet/dhcp/dhcp_api.c +++ b/src/vnet/dhcp/dhcp_api.c @@ -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. diff --git a/src/vpp/api/custom_dump.c b/src/vpp/api/custom_dump.c index 4c88b8ff959..e8d143b3609 100644 --- a/src/vpp/api/custom_dump.c +++ b/src/vpp/api/custom_dump.c @@ -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 "); diff --git a/test/test_dhcp.py b/test/test_dhcp.py index c5b1fa5fe3b..39133a35bd1 100644 --- a/test/test_dhcp.py +++ b/test/test_dhcp.py @@ -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 # diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 6dbed297de7..d13508ba4e8 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -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, |