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>
This commit is contained in:
Neale Ranns
2018-05-16 04:12:18 -07:00
committed by Neale Ranns
parent 0e969ac843
commit daff178403
17 changed files with 902 additions and 411 deletions

View 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 \

File diff suppressed because it is too large Load Diff

View File

@ -13,89 +13,73 @@
* 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;
/**
* Construct a new object matching the desried state
*/
dhcp_config(const interface& itf,
const std::string& hostname,
bool set_broadcast_flag = true);
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);
};
/**
* Construct a new object matching the desried state
* A DHCP lease data
*/
dhcp_config(const interface& itf,
const std::string& hostname,
const l2_address_t& client_id,
bool set_broadcast_flag = true);
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;
};
/**
* Copy Constructor
* A class that listens to DHCP Events
*/
dhcp_config(const dhcp_config& o);
/**
* Destructor
*/
~dhcp_config();
/**
* Comparison operator - for UT
*/
bool operator==(const dhcp_config& d) const;
/**
* Return the object's key
*/
const key_t& key() const;
/**
* Return the 'singular' of the DHCP config that matches this object
*/
std::shared_ptr<dhcp_config> singular() const;
/**
* convert to string format for debug purposes
*/
std::string to_string() const;
/**
* Dump all DHCP configs into the stream provided
*/
static void dump(std::ostream& os);
/**
* Find a DHCP config from its key
*/
static std::shared_ptr<dhcp_config> find(const key_t& k);
/**
* A class that listens to DHCP Events
*/
class event_listener
{
public:
@ -108,7 +92,7 @@ public:
* 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;
virtual void handle_dhcp_event(std::shared_ptr<lease_t> e) = 0;
/**
* Return the HW::item associated with this command
@ -122,6 +106,68 @@ public:
HW::item<bool> m_status;
};
/**
* Construct a new object matching the desried state
*/
dhcp_client(const interface& itf,
const std::string& hostname,
bool set_broadcast_flag = true,
event_listener* ev = nullptr);
/**
* Construct a new object matching the desried state
*/
dhcp_client(const interface& itf,
const std::string& hostname,
const l2_address_t& client_id,
bool set_broadcast_flag = true,
event_listener* ev = nullptr);
/**
* Copy Constructor
*/
dhcp_client(const dhcp_client& o);
/**
* Destructor
*/
~dhcp_client();
/**
* Comparison operator - for UT
*/
bool operator==(const dhcp_client& d) const;
/**
* Return the object's key
*/
const key_t& key() const;
/**
* Return the 'singular' of the DHCP client that matches this object
*/
std::shared_ptr<dhcp_client> singular() const;
/**
* convert to string format for debug purposes
*/
std::string to_string() const;
/**
* Dump all DHCP clients into the stream provided
*/
static void dump(std::ostream& os);
/**
* Find a DHCP client from its key
*/
static std::shared_ptr<dhcp_client> find(const key_t& k);
/**
* return the current lease data
*/
const std::shared_ptr<lease_t> lease() const;
private:
/**
* Class definition for listeners to OM events
@ -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;
};
};

View File

@ -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
*

View File

@ -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
*

View File

@ -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:
*/

View 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);

View 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
*/

View 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

View 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);

View 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,

View 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 */

View 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

View 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.

View 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 ");

View 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
#

View 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,