mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-31 05:55:28 +00:00
[master] Merge branch 'trac3231'
Conflicts: src/lib/dhcp/iface_mgr.cc
This commit is contained in:
@@ -4378,20 +4378,15 @@ Dhcp4/subnet4 [] list (default)
|
||||
<para>
|
||||
The DHCPv4 protocol uses a "server identifier" for clients to be able
|
||||
to discriminate between several servers present on the same link: this
|
||||
value is an IPv4 address of the server. When started for the first time,
|
||||
the DHCPv4 server will choose one of its IPv4 addresses as its server-id,
|
||||
and store the chosen value to a file. That file will be read by the server
|
||||
and the contained value used whenever the server is subsequently started.
|
||||
value is an IPv4 address of the server. The server chooses the IPv4 address
|
||||
of the interface on which the message from the client (or relay) has been
|
||||
received. A single server instance will use multiple server identifiers
|
||||
if it is receiving queries on multiple interfaces.
|
||||
</para>
|
||||
<para>
|
||||
It is unlikely that this parameter should ever need to be changed.
|
||||
However, if such a need arises, stop the server, edit the file and restart
|
||||
the server. (The file is named b10-dhcp4-serverid and by default is
|
||||
stored in the "var" subdirectory of the directory in which BIND 10 is installed.
|
||||
This can be changed when BIND 10 is built by using "--localstatedir"
|
||||
on the "configure" command line.) The file is a text file that should
|
||||
contain an IPv4 address. Spaces are ignored, and no extra characters are allowed
|
||||
in this file.
|
||||
Currently there is no mechanism to override the default server identifiers
|
||||
by an administrator. In the future, the configuration mechanism will be used
|
||||
to specify the custom server identifier.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
@@ -289,26 +289,6 @@ both clones use the same client-id.
|
||||
% DHCP4_RESPONSE_DATA responding with packet type %1, data is <%2>
|
||||
A debug message listing the data returned to the client.
|
||||
|
||||
% DHCP4_SERVERID_GENERATED server-id %1 has been generated and will be stored in %2
|
||||
This informational messages indicates that the server was not able to
|
||||
read its server identifier and has generated a new one. This server-id
|
||||
will be stored in a file and will be read (and used) whenever the server
|
||||
is restarted. This is normal behavior when the server is started for the
|
||||
first time. If this message is printed every time the server is started,
|
||||
please check that the server has sufficient permission to write its
|
||||
server-id file and that the file is not corrupt.
|
||||
|
||||
% DHCP4_SERVERID_LOADED server-id %1 has been loaded from file %2
|
||||
This debug message indicates that the server loaded its server identifier.
|
||||
That value is sent in all server responses and clients use it to
|
||||
discriminate between servers. This is a part of normal startup or
|
||||
reconfiguration procedure.
|
||||
|
||||
% DHCP4_SERVERID_WRITE_FAIL server was not able to write its ID to file %1
|
||||
This warning message indicates that server was not able to write its
|
||||
server identifier to a file. The most likely cause is is that the server
|
||||
does not have permissions to write the server id file.
|
||||
|
||||
% DHCP4_SERVER_FAILED server failed: %1
|
||||
The IPv4 DHCP server has encountered a fatal error and is terminating.
|
||||
The reason for the failure is included in the message.
|
||||
|
@@ -37,12 +37,10 @@
|
||||
#include <hooks/hooks_manager.h>
|
||||
#include <util/strutil.h>
|
||||
|
||||
#include <boost/algorithm/string/erase.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
|
||||
using namespace isc;
|
||||
using namespace isc::asiolink;
|
||||
@@ -111,38 +109,28 @@ const bool FQDN_REPLACE_CLIENT_NAME = false;
|
||||
|
||||
}
|
||||
|
||||
/// @brief file name of a server-id file
|
||||
///
|
||||
/// Server must store its server identifier in persistent storage that must not
|
||||
/// change between restarts. This is name of the file that is created in dataDir
|
||||
/// (see isc::dhcp::CfgMgr::getDataDir()). It is a text file that uses
|
||||
/// regular IPv4 address, e.g. 192.0.2.1. Server will create it during
|
||||
/// first run and then use it afterwards.
|
||||
static const char* SERVER_ID_FILE = "b10-dhcp4-serverid";
|
||||
|
||||
// These are hardcoded parameters. Currently this is a skeleton server that only
|
||||
// grants those options and a single, fixed, hardcoded lease.
|
||||
|
||||
Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast,
|
||||
const bool direct_response_desired)
|
||||
: serverid_(), shutdown_(true), alloc_engine_(), port_(port),
|
||||
: shutdown_(true), alloc_engine_(), port_(port),
|
||||
use_bcast_(use_bcast), hook_index_pkt4_receive_(-1),
|
||||
hook_index_subnet4_select_(-1), hook_index_pkt4_send_(-1) {
|
||||
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port);
|
||||
try {
|
||||
// First call to instance() will create IfaceMgr (it's a singleton)
|
||||
// it may throw something if things go wrong.
|
||||
// The 'true' value of the call to setMatchingPacketFilter imposes
|
||||
// that IfaceMgr will try to use the mechanism to respond directly
|
||||
// to the client which doesn't have address assigned. This capability
|
||||
// may be lacking on some OSes, so there is no guarantee that server
|
||||
// will be able to respond directly.
|
||||
IfaceMgr::instance().setMatchingPacketFilter(direct_response_desired);
|
||||
|
||||
// Open sockets only if port is non-zero. Port 0 is used
|
||||
// for non-socket related testing.
|
||||
// Open sockets only if port is non-zero. Port 0 is used for testing
|
||||
// purposes in two cases:
|
||||
// - when non-socket related testing is performed
|
||||
// - when the particular test supplies its own packet filtering class.
|
||||
if (port) {
|
||||
// First call to instance() will create IfaceMgr (it's a singleton)
|
||||
// it may throw something if things go wrong.
|
||||
// The 'true' value of the call to setMatchingPacketFilter imposes
|
||||
// that IfaceMgr will try to use the mechanism to respond directly
|
||||
// to the client which doesn't have address assigned. This capability
|
||||
// may be lacking on some OSes, so there is no guarantee that server
|
||||
// will be able to respond directly.
|
||||
IfaceMgr::instance().setMatchingPacketFilter(direct_response_desired);
|
||||
|
||||
// Create error handler. This handler will be called every time
|
||||
// the socket opening operation fails. We use this handler to
|
||||
// log a warning.
|
||||
@@ -151,24 +139,6 @@ Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast,
|
||||
IfaceMgr::instance().openSockets4(port_, use_bcast_, error_handler);
|
||||
}
|
||||
|
||||
string srvid_file = CfgMgr::instance().getDataDir() + "/" + string(SERVER_ID_FILE);
|
||||
if (loadServerID(srvid_file)) {
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_SERVERID_LOADED)
|
||||
.arg(srvidToString(getServerID()))
|
||||
.arg(srvid_file);
|
||||
} else {
|
||||
generateServerID();
|
||||
LOG_INFO(dhcp4_logger, DHCP4_SERVERID_GENERATED)
|
||||
.arg(srvidToString(getServerID()))
|
||||
.arg(srvid_file);
|
||||
|
||||
if (!writeServerID(srvid_file)) {
|
||||
LOG_WARN(dhcp4_logger, DHCP4_SERVERID_WRITE_FAIL)
|
||||
.arg(srvid_file);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Instantiate LeaseMgr
|
||||
LeaseMgrFactory::create(dbconfig);
|
||||
LOG_INFO(dhcp4_logger, DHCP4_DB_BACKEND_STARTED)
|
||||
@@ -389,19 +359,6 @@ Dhcpv4Srv::run() {
|
||||
continue;
|
||||
}
|
||||
|
||||
adjustRemoteAddr(query, rsp);
|
||||
|
||||
if (!rsp->getHops()) {
|
||||
rsp->setRemotePort(DHCP4_CLIENT_PORT);
|
||||
} else {
|
||||
rsp->setRemotePort(DHCP4_SERVER_PORT);
|
||||
}
|
||||
|
||||
rsp->setLocalAddr(query->getLocalAddr());
|
||||
rsp->setLocalPort(DHCP4_SERVER_PORT);
|
||||
rsp->setIface(query->getIface());
|
||||
rsp->setIndex(query->getIndex());
|
||||
|
||||
// Specifies if server should do the packing
|
||||
bool skip_pack = false;
|
||||
|
||||
@@ -487,90 +444,6 @@ Dhcpv4Srv::run() {
|
||||
return (true);
|
||||
}
|
||||
|
||||
bool
|
||||
Dhcpv4Srv::loadServerID(const std::string& file_name) {
|
||||
|
||||
// load content of the file into a string
|
||||
fstream f(file_name.c_str(), ios::in);
|
||||
if (!f.is_open()) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
string hex_string;
|
||||
f >> hex_string;
|
||||
f.close();
|
||||
|
||||
// remove any spaces
|
||||
boost::algorithm::erase_all(hex_string, " ");
|
||||
|
||||
try {
|
||||
IOAddress addr(hex_string);
|
||||
|
||||
if (!addr.isV4()) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
// Now create server-id option
|
||||
serverid_.reset(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER, addr));
|
||||
|
||||
} catch(...) {
|
||||
// any kind of malformed input (empty string, IPv6 address, complete
|
||||
// garbate etc.)
|
||||
return (false);
|
||||
}
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
void
|
||||
Dhcpv4Srv::generateServerID() {
|
||||
|
||||
const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
|
||||
|
||||
// Let's find suitable interface.
|
||||
for (IfaceMgr::IfaceCollection::const_iterator iface = ifaces.begin();
|
||||
iface != ifaces.end(); ++iface) {
|
||||
|
||||
// Let's don't use loopback.
|
||||
if (iface->flag_loopback_) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Let's skip downed interfaces. It is better to use working ones.
|
||||
if (!iface->flag_up_) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const Iface::AddressCollection addrs = iface->getAddresses();
|
||||
|
||||
for (Iface::AddressCollection::const_iterator addr = addrs.begin();
|
||||
addr != addrs.end(); ++addr) {
|
||||
if (addr->getFamily() != AF_INET) {
|
||||
continue;
|
||||
}
|
||||
|
||||
serverid_ = OptionPtr(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER,
|
||||
*addr));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
isc_throw(BadValue, "No suitable interfaces for server-identifier found");
|
||||
}
|
||||
|
||||
bool
|
||||
Dhcpv4Srv::writeServerID(const std::string& file_name) {
|
||||
fstream f(file_name.c_str(), ios::out | ios::trunc);
|
||||
if (!f.good()) {
|
||||
return (false);
|
||||
}
|
||||
f << srvidToString(getServerID());
|
||||
f.close();
|
||||
return (true);
|
||||
}
|
||||
|
||||
string
|
||||
Dhcpv4Srv::srvidToString(const OptionPtr& srvid) {
|
||||
if (!srvid) {
|
||||
@@ -690,12 +563,21 @@ Dhcpv4Srv::appendDefaultOptions(Pkt4Ptr& msg, uint8_t msg_type) {
|
||||
// add Message Type Option (type 53)
|
||||
msg->setType(msg_type);
|
||||
|
||||
// DHCP Server Identifier (type 54)
|
||||
msg->addOption(getServerID());
|
||||
|
||||
// more options will be added here later
|
||||
}
|
||||
|
||||
void
|
||||
Dhcpv4Srv::appendServerID(const Pkt4Ptr& response) {
|
||||
// The source address for the outbound message should have been set already.
|
||||
// This is the address that to the best of the server's knowledge will be
|
||||
// available from the client.
|
||||
// @todo: perhaps we should consider some more sophisticated server id
|
||||
// generation, but for the current use cases, it should be ok.
|
||||
response->addOption(OptionPtr(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER,
|
||||
response->getLocalAddr()))
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
Dhcpv4Srv::appendRequestedOptions(const Pkt4Ptr& question, Pkt4Ptr& msg) {
|
||||
|
||||
@@ -1269,7 +1151,36 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
|
||||
}
|
||||
|
||||
void
|
||||
Dhcpv4Srv::adjustRemoteAddr(const Pkt4Ptr& question, Pkt4Ptr& msg) {
|
||||
Dhcpv4Srv::adjustIfaceData(const Pkt4Ptr& query, const Pkt4Ptr& response) {
|
||||
adjustRemoteAddr(query, response);
|
||||
|
||||
// For the non-relayed message, the destination port is the client's port.
|
||||
// For the relayed message, the server/relay port is a destination.
|
||||
// Note that the call to this function may throw if invalid combination
|
||||
// of hops and giaddr is found (hops = 0 if giaddr = 0 and hops != 0 if
|
||||
// giaddr != 0). The exception will propagate down and eventually cause the
|
||||
// packet to be discarded.
|
||||
response->setRemotePort(query->isRelayed() ? DHCP4_SERVER_PORT :
|
||||
DHCP4_CLIENT_PORT);
|
||||
|
||||
// In many cases the query is sent to a broadcast address. This address
|
||||
// appears as a local address in the query message. Therefore we can't
|
||||
// simply copy local address from the query and use it as a source
|
||||
// address for the response. Instead, we have to check what address our
|
||||
// socket is bound to and use it as a source address. This operation
|
||||
// may throw if for some reason the socket is closed.
|
||||
// @todo Consider an optimization that we use local address from
|
||||
// the query if this address is not broadcast.
|
||||
SocketInfo sock_info = IfaceMgr::instance().getSocket(*query);
|
||||
// Set local adddress, port and interface.
|
||||
response->setLocalAddr(sock_info.addr_);
|
||||
response->setLocalPort(DHCP4_SERVER_PORT);
|
||||
response->setIface(query->getIface());
|
||||
response->setIndex(query->getIndex());
|
||||
}
|
||||
|
||||
void
|
||||
Dhcpv4Srv::adjustRemoteAddr(const Pkt4Ptr& question, const Pkt4Ptr& response) {
|
||||
// Let's create static objects representing zeroed and broadcast
|
||||
// addresses. We will use them further in this function to test
|
||||
// other addresses against them. Since they are static, they will
|
||||
@@ -1278,22 +1189,22 @@ Dhcpv4Srv::adjustRemoteAddr(const Pkt4Ptr& question, Pkt4Ptr& msg) {
|
||||
static const IOAddress bcast_addr("255.255.255.255");
|
||||
|
||||
// If received relayed message, server responds to the relay address.
|
||||
if (question->getGiaddr() != zero_addr) {
|
||||
msg->setRemoteAddr(question->getGiaddr());
|
||||
if (question->isRelayed()) {
|
||||
response->setRemoteAddr(question->getGiaddr());
|
||||
|
||||
// If giaddr is 0 but client set ciaddr, server should unicast the
|
||||
// response to ciaddr.
|
||||
} else if (question->getCiaddr() != zero_addr) {
|
||||
msg->setRemoteAddr(question->getCiaddr());
|
||||
response->setRemoteAddr(question->getCiaddr());
|
||||
|
||||
// We can't unicast the response to the client when sending NAK,
|
||||
// because we haven't allocated address for him. Therefore,
|
||||
// NAK is broadcast.
|
||||
} else if (msg->getType() == DHCPNAK) {
|
||||
msg->setRemoteAddr(bcast_addr);
|
||||
} else if (response->getType() == DHCPNAK) {
|
||||
response->setRemoteAddr(bcast_addr);
|
||||
|
||||
// If yiaddr is set it means that we have created a lease for a client.
|
||||
} else if (msg->getYiaddr() != zero_addr) {
|
||||
} else if (response->getYiaddr() != zero_addr) {
|
||||
// If the broadcast bit is set in the flags field, we have to
|
||||
// send the response to broadcast address. Client may have requested it
|
||||
// because it doesn't support reception of messages on the interface
|
||||
@@ -1302,13 +1213,13 @@ Dhcpv4Srv::adjustRemoteAddr(const Pkt4Ptr& question, Pkt4Ptr& msg) {
|
||||
// directly to a client without address assigned.
|
||||
const bool bcast_flag = ((question->getFlags() & Pkt4::FLAG_BROADCAST_MASK) != 0);
|
||||
if (!IfaceMgr::instance().isDirectResponseSupported() || bcast_flag) {
|
||||
msg->setRemoteAddr(bcast_addr);
|
||||
response->setRemoteAddr(bcast_addr);
|
||||
|
||||
// Client cleared the broadcast bit and we support direct responses
|
||||
// so we should unicast the response to a newly allocated address -
|
||||
// yiaddr.
|
||||
} else {
|
||||
msg->setRemoteAddr(msg->getYiaddr());
|
||||
response->setRemoteAddr(response ->getYiaddr());
|
||||
|
||||
}
|
||||
|
||||
@@ -1316,7 +1227,7 @@ Dhcpv4Srv::adjustRemoteAddr(const Pkt4Ptr& question, Pkt4Ptr& msg) {
|
||||
// found ourselves at this point, the rational thing to do is to respond
|
||||
// to the address we got the query from.
|
||||
} else {
|
||||
msg->setRemoteAddr(question->getRemoteAddr());
|
||||
response->setRemoteAddr(question->getRemoteAddr());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1361,6 +1272,12 @@ Dhcpv4Srv::processDiscover(Pkt4Ptr& discover) {
|
||||
appendBasicOptions(discover, offer);
|
||||
}
|
||||
|
||||
// Set the src/dest IP address, port and interface for the outgoing
|
||||
// packet.
|
||||
adjustIfaceData(discover, offer);
|
||||
|
||||
appendServerID(offer);
|
||||
|
||||
return (offer);
|
||||
}
|
||||
|
||||
@@ -1397,6 +1314,12 @@ Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
|
||||
appendBasicOptions(request, ack);
|
||||
}
|
||||
|
||||
// Set the src/dest IP address, port and interface for the outgoing
|
||||
// packet.
|
||||
adjustIfaceData(request, ack);
|
||||
|
||||
appendServerID(ack);
|
||||
|
||||
return (ack);
|
||||
}
|
||||
|
||||
|
@@ -175,7 +175,7 @@ protected:
|
||||
/// @param pkt packet to be checked
|
||||
/// @param serverid expectation regarding server-id option
|
||||
/// @throw RFCViolation if any issues are detected
|
||||
void sanityCheck(const Pkt4Ptr& pkt, RequirementLevel serverid);
|
||||
static void sanityCheck(const Pkt4Ptr& pkt, RequirementLevel serverid);
|
||||
|
||||
/// @brief Processes incoming DISCOVER and returns response.
|
||||
///
|
||||
@@ -396,10 +396,68 @@ protected:
|
||||
|
||||
/// @brief Appends default options to a message
|
||||
///
|
||||
/// Currently it is only a Message Type option. This function does not add
|
||||
/// the Server Identifier option as this option must be added using
|
||||
/// @c Dhcpv4Srv::appendServerID.
|
||||
///
|
||||
///
|
||||
/// @param msg message object (options will be added to it)
|
||||
/// @param msg_type specifies message type
|
||||
void appendDefaultOptions(Pkt4Ptr& msg, uint8_t msg_type);
|
||||
|
||||
/// @brief Adds server identifier option to the server's response.
|
||||
///
|
||||
/// This method adds a server identifier to the DHCPv4 message. It epxects
|
||||
/// that the local (source) address is set for this message. If address is
|
||||
/// not set, it will throw an exception. This method also expects that the
|
||||
/// server identifier option is not present in the specified message.
|
||||
/// Otherwise, it will throw an exception on attempt to add a duplicate
|
||||
/// server identifier option.
|
||||
///
|
||||
/// @note This method doesn't throw exceptions by itself but the underlying
|
||||
/// classes being used my throw. The reason for this method to not sanity
|
||||
/// check the specified message is that it is meant to be called internally
|
||||
/// by the @c Dhcpv4Srv class.
|
||||
///
|
||||
/// @note This method is static because it is not dependent on the class
|
||||
/// state.
|
||||
///
|
||||
/// @param [out] response DHCPv4 message to which the server identifier
|
||||
/// option should be added.
|
||||
static void appendServerID(const Pkt4Ptr& response);
|
||||
|
||||
/// @brief Set IP/UDP and interface parameters for the DHCPv4 response.
|
||||
///
|
||||
/// This method sets the following parameters for the DHCPv4 message being
|
||||
/// sent to a client:
|
||||
/// - client unicast or a broadcast address,
|
||||
/// - client or relay port,
|
||||
/// - server address,
|
||||
/// - server port,
|
||||
/// - name and index of the interface which is to be used to send the
|
||||
/// message.
|
||||
///
|
||||
/// Internally it calls the @c Dhcpv4Srv::adjustRemoteAddr to figure
|
||||
/// out the destination address (client unicast address or broadcast
|
||||
/// address).
|
||||
///
|
||||
/// The destination port is always DHCPv4 client (68) or relay (67) port,
|
||||
/// depending if the response will be sent directly to a client.
|
||||
///
|
||||
/// The source port is always set to DHCPv4 server port (67).
|
||||
///
|
||||
/// The interface selected for the response is always the same as the
|
||||
/// one through which the query has been received.
|
||||
///
|
||||
/// The source address for the response is the IPv4 address assigned to
|
||||
/// the interface being used to send the response. This function uses
|
||||
/// @c IfaceMgr to get the socket bound to the IPv4 address on the
|
||||
/// particular interface.
|
||||
///
|
||||
/// @note This method is static because it is not dependent on the class
|
||||
/// state.
|
||||
static void adjustIfaceData(const Pkt4Ptr& query, const Pkt4Ptr& response);
|
||||
|
||||
/// @brief Sets remote addresses for outgoing packet.
|
||||
///
|
||||
/// This method sets the local and remote addresses on outgoing packet.
|
||||
@@ -413,42 +471,14 @@ protected:
|
||||
/// are valid. Make sure that pointers are correct before calling this
|
||||
/// function.
|
||||
///
|
||||
/// @note This method is static because it is not dependent on the class
|
||||
/// state.
|
||||
///
|
||||
/// @param question instance of a packet received by a server.
|
||||
/// @param [out] msg response packet which addresses are to be adjusted.
|
||||
void adjustRemoteAddr(const Pkt4Ptr& question, Pkt4Ptr& msg);
|
||||
|
||||
/// @brief Returns server-identifier option
|
||||
///
|
||||
/// @return server-id option
|
||||
OptionPtr getServerID() { return serverid_; }
|
||||
|
||||
/// @brief Sets server-identifier.
|
||||
///
|
||||
/// This method attempts to set server-identifier DUID. It tries to
|
||||
/// load previously stored IP from configuration. If there is no previously
|
||||
/// stored server identifier, it will pick up one address from configured
|
||||
/// and supported network interfaces.
|
||||
///
|
||||
/// @throws isc::Unexpected Failed to obtain server identifier (i.e. no
|
||||
// previously stored configuration and no network interfaces available)
|
||||
void generateServerID();
|
||||
|
||||
/// @brief attempts to load server-id from a file
|
||||
///
|
||||
/// Tries to load duid from a text file. If the load is successful,
|
||||
/// it creates server-id option and stores it in serverid_ (to be used
|
||||
/// later by getServerID()).
|
||||
///
|
||||
/// @param file_name name of the server-id file to load
|
||||
/// @return true if load was successful, false otherwise
|
||||
bool loadServerID(const std::string& file_name);
|
||||
|
||||
/// @brief attempts to write server-id to a file
|
||||
/// Tries to write server-id content (stored in serverid_) to a text file.
|
||||
///
|
||||
/// @param file_name name of the server-id file to write
|
||||
/// @return true if write was successful, false otherwise
|
||||
bool writeServerID(const std::string& file_name);
|
||||
/// @param [out] response response packet which addresses are to be
|
||||
/// adjusted.
|
||||
static void adjustRemoteAddr(const Pkt4Ptr& question,
|
||||
const Pkt4Ptr& response);
|
||||
|
||||
/// @brief converts server-id to text
|
||||
/// Converts content of server-id option to a text representation, e.g.
|
||||
@@ -486,9 +516,6 @@ protected:
|
||||
/// @return selected subnet (or NULL if no suitable subnet was found)
|
||||
isc::dhcp::Subnet4Ptr selectSubnet(const Pkt4Ptr& question);
|
||||
|
||||
/// server DUID (to be sent in server-identifier option)
|
||||
OptionPtr serverid_;
|
||||
|
||||
/// indicates if shutdown is in progress. Setting it to true will
|
||||
/// initiate server shutdown procedure.
|
||||
volatile bool shutdown_;
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -55,16 +55,6 @@ Dhcpv4SrvTest::Dhcpv4SrvTest()
|
||||
|
||||
// it's ok if that fails. There should not be such a file anyway
|
||||
unlink(SRVID_FILE);
|
||||
|
||||
const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
|
||||
|
||||
// There must be some interface detected
|
||||
if (ifaces.empty()) {
|
||||
// We can't use ASSERT in constructor
|
||||
ADD_FAILURE() << "No interfaces detected.";
|
||||
}
|
||||
|
||||
valid_iface_ = ifaces.begin()->getName();
|
||||
}
|
||||
|
||||
Dhcpv4SrvTest::~Dhcpv4SrvTest() {
|
||||
@@ -325,9 +315,6 @@ Lease4Ptr Dhcpv4SrvTest::checkLease(const Pkt4Ptr& rsp,
|
||||
return (lease);
|
||||
}
|
||||
|
||||
/// @brief Checks if server response (OFFER, ACK, NAK) includes proper server-id
|
||||
/// @param rsp response packet to be validated
|
||||
/// @param expected_srvid expected value of server-id
|
||||
void Dhcpv4SrvTest::checkServerId(const Pkt4Ptr& rsp, const OptionPtr& expected_srvid) {
|
||||
// Check that server included its server-id
|
||||
OptionPtr opt = rsp->getOption(DHO_DHCP_SERVER_IDENTIFIER);
|
||||
@@ -396,8 +383,88 @@ Dhcpv4SrvTest::createPacketFromBuffer(const Pkt4Ptr& src_pkt,
|
||||
return (::testing::AssertionSuccess());
|
||||
}
|
||||
|
||||
void Dhcpv4SrvTest::TearDown() {
|
||||
|
||||
void Dhcpv4SrvTest::testDiscoverRequest(const uint8_t msg_type) {
|
||||
CfgMgr::instance().deleteSubnets4();
|
||||
|
||||
// Let's clean up if there is such a file.
|
||||
unlink(SRVID_FILE);
|
||||
|
||||
// Close all open sockets.
|
||||
IfaceMgr::instance().closeSockets();
|
||||
|
||||
// Some unit tests override the default packet filtering class, used
|
||||
// by the IfaceMgr. The dummy class, called PktFilterTest, reports the
|
||||
// capability to directly respond to the clients without IP address
|
||||
// assigned. This capability is not supported by the default packet
|
||||
// filtering class: PktFilterInet. Therefore setting the dummy class
|
||||
// allows to test scenarios, when server responds to the broadcast address
|
||||
// on client's request, despite having support for direct response.
|
||||
// The following call restores the use of original packet filtering class
|
||||
// after the test.
|
||||
try {
|
||||
IfaceMgr::instance().setPacketFilter(PktFilterPtr(new PktFilterInet()));
|
||||
|
||||
} catch (const Exception& ex) {
|
||||
FAIL() << "Failed to restore the default (PktFilterInet) packet filtering"
|
||||
<< " class after the test. Exception has been caught: "
|
||||
<< ex.what();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Dhcpv4SrvFakeIfaceTest::Dhcpv4SrvFakeIfaceTest()
|
||||
: Dhcpv4SrvTest(), current_pkt_filter_() {
|
||||
// Remove current interface configuration. Instead we want to add
|
||||
// a couple of fake interfaces.
|
||||
IfaceMgr& ifacemgr = IfaceMgr::instance();
|
||||
ifacemgr.closeSockets();
|
||||
ifacemgr.clearIfaces();
|
||||
|
||||
// Add fake interfaces.
|
||||
ifacemgr.addInterface(createIface("lo", 0, "127.0.0.1"));
|
||||
ifacemgr.addInterface(createIface("eth0", 1, "192.0.3.1"));
|
||||
ifacemgr.addInterface(createIface("eth1", 2, "10.0.0.1"));
|
||||
|
||||
// In order to use fake interfaces we have to supply the custom
|
||||
// packet filtering class, which can mimic opening sockets on
|
||||
// fake interafaces.
|
||||
current_pkt_filter_.reset(new PktFilterTest());
|
||||
ifacemgr.setPacketFilter(current_pkt_filter_);
|
||||
ifacemgr.openSockets4();
|
||||
}
|
||||
|
||||
void
|
||||
Dhcpv4SrvFakeIfaceTest::TearDown() {
|
||||
// The base class function restores the original packet filtering class.
|
||||
Dhcpv4SrvTest::TearDown();
|
||||
// The base class however, doesn't re-detect real interfaces.
|
||||
try {
|
||||
IfaceMgr::instance().clearIfaces();
|
||||
IfaceMgr::instance().detectIfaces();
|
||||
|
||||
} catch (const Exception& ex) {
|
||||
FAIL() << "Failed to restore interface configuration after using"
|
||||
" fake interfaces";
|
||||
}
|
||||
}
|
||||
|
||||
Iface
|
||||
Dhcpv4SrvFakeIfaceTest::createIface(const std::string& name, const int ifindex,
|
||||
const std::string& addr) {
|
||||
Iface iface(name, ifindex);
|
||||
iface.addAddress(IOAddress(addr));
|
||||
if (name == "lo") {
|
||||
iface.flag_loopback_ = true;
|
||||
}
|
||||
iface.flag_up_ = true;
|
||||
iface.flag_running_ = true;
|
||||
iface.inactive4_ = false;
|
||||
return (iface);
|
||||
}
|
||||
|
||||
void
|
||||
Dhcpv4SrvFakeIfaceTest::testDiscoverRequest(const uint8_t msg_type) {
|
||||
// Create an instance of the tested class.
|
||||
boost::scoped_ptr<NakedDhcpv4Srv> srv(new NakedDhcpv4Srv(0));
|
||||
|
||||
@@ -432,8 +499,9 @@ void Dhcpv4SrvTest::testDiscoverRequest(const uint8_t msg_type) {
|
||||
req->setLocalHWAddr(1, 6, mac);
|
||||
// Set target IP address.
|
||||
req->setRemoteAddr(IOAddress("192.0.2.55"));
|
||||
// Set relay address.
|
||||
// Set relay address and hops.
|
||||
req->setGiaddr(IOAddress("192.0.2.10"));
|
||||
req->setHops(1);
|
||||
|
||||
// We are going to test that certain options are returned
|
||||
// in the response message when requested using 'Parameter
|
||||
@@ -446,6 +514,8 @@ void Dhcpv4SrvTest::testDiscoverRequest(const uint8_t msg_type) {
|
||||
// which was parsed from its wire format.
|
||||
Pkt4Ptr received;
|
||||
ASSERT_TRUE(createPacketFromBuffer(req, received));
|
||||
// Set interface. It is required for the server to generate server id.
|
||||
received->setIface("eth0");
|
||||
if (msg_type == DHCPDISCOVER) {
|
||||
ASSERT_NO_THROW(
|
||||
rsp = srv->processDiscover(received);
|
||||
@@ -479,6 +549,9 @@ void Dhcpv4SrvTest::testDiscoverRequest(const uint8_t msg_type) {
|
||||
ASSERT_TRUE(createPacketFromBuffer(req, received));
|
||||
ASSERT_TRUE(received->getOption(DHO_DHCP_PARAMETER_REQUEST_LIST));
|
||||
|
||||
// Set interface. It is required for the server to generate server id.
|
||||
received->setIface("eth0");
|
||||
|
||||
if (msg_type == DHCPDISCOVER) {
|
||||
ASSERT_NO_THROW(rsp = srv->processDiscover(received));
|
||||
|
||||
@@ -512,6 +585,9 @@ void Dhcpv4SrvTest::testDiscoverRequest(const uint8_t msg_type) {
|
||||
ASSERT_TRUE(createPacketFromBuffer(req, received));
|
||||
ASSERT_TRUE(received->getOption(DHO_DHCP_PARAMETER_REQUEST_LIST));
|
||||
|
||||
// Set interface. It is required for the server to generate server id.
|
||||
received->setIface("eth0");
|
||||
|
||||
if (msg_type == DHCPDISCOVER) {
|
||||
ASSERT_NO_THROW(rsp = srv->processDiscover(received));
|
||||
// Should return non-NULL packet.
|
||||
@@ -532,36 +608,6 @@ void Dhcpv4SrvTest::testDiscoverRequest(const uint8_t msg_type) {
|
||||
EXPECT_TRUE(noBasicOptions(rsp));
|
||||
}
|
||||
|
||||
/// @brief This function cleans up after the test.
|
||||
void Dhcpv4SrvTest::TearDown() {
|
||||
|
||||
CfgMgr::instance().deleteSubnets4();
|
||||
|
||||
// Let's clean up if there is such a file.
|
||||
unlink(SRVID_FILE);
|
||||
|
||||
// Close all open sockets.
|
||||
IfaceMgr::instance().closeSockets();
|
||||
|
||||
// Some unit tests override the default packet filtering class, used
|
||||
// by the IfaceMgr. The dummy class, called PktFilterTest, reports the
|
||||
// capability to directly respond to the clients without IP address
|
||||
// assigned. This capability is not supported by the default packet
|
||||
// filtering class: PktFilterInet. Therefore setting the dummy class
|
||||
// allows to test scenarios, when server responds to the broadcast address
|
||||
// on client's request, despite having support for direct response.
|
||||
// The following call restores the use of original packet filtering class
|
||||
// after the test.
|
||||
try {
|
||||
IfaceMgr::instance().setPacketFilter(PktFilterPtr(new PktFilterInet()));
|
||||
|
||||
} catch (const Exception& ex) {
|
||||
FAIL() << "Failed to restore the default (PktFilterInet) packet filtering"
|
||||
<< " class after the test. Exception has been caught: "
|
||||
<< ex.what();
|
||||
}
|
||||
}
|
||||
|
||||
}; // end of isc::dhcp::test namespace
|
||||
}; // end of isc::dhcp namespace
|
||||
}; // end of isc namespace
|
||||
|
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <dhcp/iface_mgr.h>
|
||||
#include <dhcp/option4_addrlst.h>
|
||||
#include <dhcp/pkt4.h>
|
||||
#include <dhcp/pkt_filter.h>
|
||||
#include <dhcp/pkt_filter_inet.h>
|
||||
@@ -31,6 +32,8 @@
|
||||
#include <config/ccsession.h>
|
||||
#include <list>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
namespace test {
|
||||
@@ -45,11 +48,18 @@ namespace test {
|
||||
class PktFilterTest : public PktFilter {
|
||||
public:
|
||||
|
||||
/// @brief Constructor.
|
||||
///
|
||||
/// Sets the 'direct response' capability to true.
|
||||
PktFilterTest()
|
||||
: direct_resp_supported_(true) {
|
||||
}
|
||||
|
||||
/// @brief Reports 'direct response' capability.
|
||||
///
|
||||
/// @return always true.
|
||||
virtual bool isDirectResponseSupported() const {
|
||||
return (true);
|
||||
return (direct_resp_supported_);
|
||||
}
|
||||
|
||||
/// Does nothing.
|
||||
@@ -69,8 +79,14 @@ public:
|
||||
return (0);
|
||||
}
|
||||
|
||||
/// @brief Holds a boolean value which indicates whether direct response
|
||||
/// capability is supported (true) or not (false).
|
||||
bool direct_resp_supported_;
|
||||
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<PktFilterTest> PktFilterTestPtr;
|
||||
|
||||
class Dhcpv4SrvTest : public ::testing::Test {
|
||||
public:
|
||||
|
||||
@@ -237,24 +253,12 @@ public:
|
||||
createPacketFromBuffer(const Pkt4Ptr& src_pkt,
|
||||
Pkt4Ptr& dst_pkt);
|
||||
|
||||
|
||||
/// @brief generates a DHCPv4 packet based on provided hex string
|
||||
///
|
||||
/// @return created packet
|
||||
Pkt4Ptr packetFromCapture(const std::string& hex_string);
|
||||
|
||||
/// @brief Tests if Discover or Request message is processed correctly
|
||||
///
|
||||
/// This test verifies that the Parameter Request List option is handled
|
||||
/// correctly, i.e. it checks that certain options are present in the
|
||||
/// server's response when they are requested and that they are not present
|
||||
/// when they are not requested or NAK occurs.
|
||||
///
|
||||
/// @todo We need an additional test for PRL option using real traffic
|
||||
/// capture.
|
||||
///
|
||||
/// @param msg_type DHCPDISCOVER or DHCPREQUEST
|
||||
void testDiscoverRequest(const uint8_t msg_type);
|
||||
|
||||
/// @brief This function cleans up after the test.
|
||||
virtual void TearDown();
|
||||
|
||||
@@ -271,8 +275,62 @@ public:
|
||||
|
||||
isc::data::ConstElementPtr comment_;
|
||||
|
||||
// Name of a valid network interface
|
||||
std::string valid_iface_;
|
||||
};
|
||||
|
||||
/// @brief Test fixture class to be used for tests which require fake
|
||||
/// interfaces.
|
||||
///
|
||||
/// The DHCPv4 server must always append the server identifier to its response.
|
||||
/// The server identifier is typically an IP address assigned to the interface
|
||||
/// on which the query has been received. The DHCPv4 server uses IfaceMgr to
|
||||
/// check this address. In order to test this functionality, a set of interfaces
|
||||
/// must be known to the test. This test fixture class creates a set of well
|
||||
/// known (fake) interfaces which can be assigned to the test DHCPv4 messages
|
||||
/// so as the response (including server identifier) can be validated.
|
||||
/// The real interfaces are removed from the IfaceMgr in the constructor and
|
||||
/// they are re-assigned in the destructor.
|
||||
class Dhcpv4SrvFakeIfaceTest : public Dhcpv4SrvTest {
|
||||
public:
|
||||
/// @brief Constructor.
|
||||
///
|
||||
/// Creates a set of fake interfaces:
|
||||
/// - lo, index: 0, address: 127.0.0.1
|
||||
/// - eth0, index: 1, address: 192.0.3.1
|
||||
/// - eth1, index: 2, address: 10.0.0.1
|
||||
///
|
||||
/// These interfaces replace the real interfaces detected by the IfaceMgr.
|
||||
Dhcpv4SrvFakeIfaceTest();
|
||||
|
||||
/// @brief Restores the original interface configuration.
|
||||
virtual void TearDown();
|
||||
|
||||
/// @brief Creates an instance of the interface.
|
||||
///
|
||||
/// @param name Name of the interface.
|
||||
/// @param ifindex Index of the interface.
|
||||
/// @param addr IP address assigned to the interface, represented as string.
|
||||
///
|
||||
/// @return Iface Instance of the interface.
|
||||
static Iface createIface(const std::string& name, const int ifindex,
|
||||
const std::string& addr);
|
||||
|
||||
/// @brief Tests if Discover or Request message is processed correctly
|
||||
///
|
||||
/// This test verifies that the Parameter Request List option is handled
|
||||
/// correctly, i.e. it checks that certain options are present in the
|
||||
/// server's response when they are requested and that they are not present
|
||||
/// when they are not requested or NAK occurs.
|
||||
///
|
||||
/// @todo We need an additional test for PRL option using real traffic
|
||||
/// capture.
|
||||
///
|
||||
/// @param msg_type DHCPDISCOVER or DHCPREQUEST
|
||||
void testDiscoverRequest(const uint8_t msg_type);
|
||||
|
||||
/// @brief Holds a pointer to the packet filter object currently used
|
||||
/// by the IfaceMgr.
|
||||
PktFilterTestPtr current_pkt_filter_;
|
||||
|
||||
};
|
||||
|
||||
/// @brief "Naked" DHCPv4 server, exposes internal fields
|
||||
@@ -306,6 +364,15 @@ public:
|
||||
/// that sockets should not be opened.
|
||||
NakedDhcpv4Srv(uint16_t port = 0)
|
||||
: Dhcpv4Srv(port, "type=memfile", false, false) {
|
||||
// Create fixed server id.
|
||||
server_id_.reset(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER,
|
||||
asiolink::IOAddress("192.0.3.1")));
|
||||
}
|
||||
|
||||
/// @brief Returns fixed server identifier assigned to the naked server
|
||||
/// instance.
|
||||
OptionPtr getServerID() const {
|
||||
return (server_id_);
|
||||
}
|
||||
|
||||
/// @brief fakes packet reception
|
||||
@@ -341,12 +408,16 @@ public:
|
||||
///
|
||||
/// See fake_received_ field for description
|
||||
void fakeReceive(const Pkt4Ptr& pkt) {
|
||||
pkt->setIface("eth0");
|
||||
fake_received_.push_back(pkt);
|
||||
}
|
||||
|
||||
virtual ~NakedDhcpv4Srv() {
|
||||
}
|
||||
|
||||
/// @brief Dummy server identifier option used by various tests.
|
||||
OptionPtr server_id_;
|
||||
|
||||
/// @brief packets we pretend to receive
|
||||
///
|
||||
/// Instead of setting up sockets on interfaces that change between OSes, it
|
||||
@@ -357,7 +428,8 @@ public:
|
||||
|
||||
std::list<Pkt4Ptr> fake_sent_;
|
||||
|
||||
using Dhcpv4Srv::adjustRemoteAddr;
|
||||
using Dhcpv4Srv::adjustIfaceData;
|
||||
using Dhcpv4Srv::appendServerID;
|
||||
using Dhcpv4Srv::processDiscover;
|
||||
using Dhcpv4Srv::processRequest;
|
||||
using Dhcpv4Srv::processRelease;
|
||||
@@ -366,10 +438,6 @@ public:
|
||||
using Dhcpv4Srv::processClientName;
|
||||
using Dhcpv4Srv::computeDhcid;
|
||||
using Dhcpv4Srv::createNameChangeRequests;
|
||||
using Dhcpv4Srv::getServerID;
|
||||
using Dhcpv4Srv::loadServerID;
|
||||
using Dhcpv4Srv::generateServerID;
|
||||
using Dhcpv4Srv::writeServerID;
|
||||
using Dhcpv4Srv::sanityCheck;
|
||||
using Dhcpv4Srv::srvidToString;
|
||||
using Dhcpv4Srv::unpackOptions;
|
||||
|
@@ -30,9 +30,9 @@ using namespace isc::dhcp_ddns;
|
||||
|
||||
namespace {
|
||||
|
||||
class NameDhcpv4SrvTest : public Dhcpv4SrvTest {
|
||||
class NameDhcpv4SrvTest : public Dhcpv4SrvFakeIfaceTest {
|
||||
public:
|
||||
NameDhcpv4SrvTest() : Dhcpv4SrvTest() {
|
||||
NameDhcpv4SrvTest() : Dhcpv4SrvFakeIfaceTest() {
|
||||
srv_ = new NakedDhcpv4Srv(0);
|
||||
}
|
||||
virtual ~NameDhcpv4SrvTest() {
|
||||
@@ -110,6 +110,7 @@ public:
|
||||
const bool include_clientid = true) {
|
||||
Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(msg_type, 1234));
|
||||
pkt->setRemoteAddr(IOAddress("192.0.2.3"));
|
||||
pkt->setIface("eth0");
|
||||
// For DISCOVER we don't include server id, because client broadcasts
|
||||
// the message to all servers.
|
||||
if (msg_type != DHCPDISCOVER) {
|
||||
@@ -580,7 +581,6 @@ TEST_F(NameDhcpv4SrvTest, processDiscover) {
|
||||
|
||||
Pkt4Ptr reply;
|
||||
ASSERT_NO_THROW(reply = srv_->processDiscover(req));
|
||||
|
||||
checkResponse(reply, DHCPOFFER, 1234);
|
||||
|
||||
EXPECT_TRUE(srv_->name_change_reqs_.empty());
|
||||
@@ -615,6 +615,9 @@ TEST_F(NameDhcpv4SrvTest, processRequestFqdnEmptyDomainName) {
|
||||
// to it when Hostname option carries the top level domain-name.
|
||||
TEST_F(NameDhcpv4SrvTest, processRequestEmptyHostname) {
|
||||
Pkt4Ptr req = generatePktWithHostname(DHCPREQUEST, ".");
|
||||
// Set interface for the incoming packet. The server requires it to
|
||||
// generate client id.
|
||||
req->setIface("eth0");
|
||||
|
||||
Pkt4Ptr reply;
|
||||
ASSERT_NO_THROW(reply = srv_->processRequest(req));
|
||||
@@ -691,6 +694,11 @@ TEST_F(NameDhcpv4SrvTest, processTwoRequestsFqdn) {
|
||||
TEST_F(NameDhcpv4SrvTest, processTwoRequestsHostname) {
|
||||
Pkt4Ptr req1 = generatePktWithHostname(DHCPREQUEST, "myhost.example.com.");
|
||||
|
||||
// Set interface for the incoming packet. The server requires it to
|
||||
// generate client id.
|
||||
req1->setIface("eth0");
|
||||
|
||||
|
||||
Pkt4Ptr reply;
|
||||
ASSERT_NO_THROW(reply = srv_->processRequest(req1));
|
||||
|
||||
@@ -709,6 +717,10 @@ TEST_F(NameDhcpv4SrvTest, processTwoRequestsHostname) {
|
||||
// another one to add new entry with updated domain-name.
|
||||
Pkt4Ptr req2 = generatePktWithHostname(DHCPREQUEST, "otherhost");
|
||||
|
||||
// Set interface for the incoming packet. The server requires it to
|
||||
// generate client id.
|
||||
req2->setIface("eth0");
|
||||
|
||||
ASSERT_NO_THROW(reply = srv_->processRequest(req2));
|
||||
|
||||
checkResponse(reply, DHCPACK, 1234);
|
||||
|
@@ -325,6 +325,8 @@ void IfaceMgr::stubDetectIfaces() {
|
||||
addInterface(iface);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool
|
||||
IfaceMgr::openSockets4(const uint16_t port, const bool use_bcast,
|
||||
IfaceMgrErrorMsgCallback error_handler) {
|
||||
@@ -585,6 +587,11 @@ IfaceMgr::getIface(const std::string& ifname) {
|
||||
return (NULL); // not found
|
||||
}
|
||||
|
||||
void
|
||||
IfaceMgr::clearIfaces() {
|
||||
ifaces_.clear();
|
||||
}
|
||||
|
||||
int IfaceMgr::openSocket(const std::string& ifname, const IOAddress& addr,
|
||||
const uint16_t port, const bool receive_bcast,
|
||||
const bool send_bcast) {
|
||||
@@ -794,7 +801,7 @@ IfaceMgr::send(const Pkt4Ptr& pkt) {
|
||||
}
|
||||
|
||||
// Assuming that packet filter is not NULL, because its modifier checks it.
|
||||
return (packet_filter_->send(*iface, getSocket(*pkt), pkt));
|
||||
return (packet_filter_->send(*iface, getSocket(*pkt).sockfd_, pkt));
|
||||
}
|
||||
|
||||
|
||||
@@ -994,8 +1001,7 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
|
||||
uint16_t IfaceMgr::getSocket(const isc::dhcp::Pkt6& pkt) {
|
||||
Iface* iface = getIface(pkt.getIface());
|
||||
if (iface == NULL) {
|
||||
isc_throw(BadValue, "Tried to find socket for non-existent interface "
|
||||
<< pkt.getIface());
|
||||
isc_throw(BadValue, "Tried to find socket for non-existent interface");
|
||||
}
|
||||
|
||||
|
||||
@@ -1048,18 +1054,18 @@ uint16_t IfaceMgr::getSocket(const isc::dhcp::Pkt6& pkt) {
|
||||
<< " does not have any suitable IPv6 sockets open.");
|
||||
}
|
||||
|
||||
uint16_t IfaceMgr::getSocket(isc::dhcp::Pkt4 const& pkt) {
|
||||
SocketInfo
|
||||
IfaceMgr::getSocket(isc::dhcp::Pkt4 const& pkt) {
|
||||
Iface* iface = getIface(pkt.getIface());
|
||||
if (iface == NULL) {
|
||||
isc_throw(BadValue, "Tried to find socket for non-existent interface "
|
||||
<< pkt.getIface());
|
||||
isc_throw(BadValue, "Tried to find socket for non-existent interface");
|
||||
}
|
||||
|
||||
const Iface::SocketCollection& socket_collection = iface->getSockets();
|
||||
Iface::SocketCollection::const_iterator s;
|
||||
for (s = socket_collection.begin(); s != socket_collection.end(); ++s) {
|
||||
if (s->family_ == AF_INET) {
|
||||
return (s->sockfd_);
|
||||
return (*s);
|
||||
}
|
||||
/// TODO: Add more checks here later. If remote address is
|
||||
/// not link-local, we can't use link local bound socket
|
||||
|
@@ -444,8 +444,7 @@ public:
|
||||
/// @return interface with requested name (or NULL if no such
|
||||
/// interface is present)
|
||||
///
|
||||
Iface*
|
||||
getIface(const std::string& ifname);
|
||||
Iface* getIface(const std::string& ifname);
|
||||
|
||||
/// @brief Returns container with all interfaces.
|
||||
///
|
||||
@@ -454,7 +453,22 @@ public:
|
||||
/// main() function completes, you should not worry much about this.
|
||||
///
|
||||
/// @return container with all interfaces.
|
||||
const IfaceCollection& getIfaces() { return ifaces_; }
|
||||
const IfaceCollection& getIfaces() { return (ifaces_); }
|
||||
|
||||
/// @brief Removes detected interfaces.
|
||||
///
|
||||
/// This method removes all detected interfaces. This method should be
|
||||
/// used by unit tests to supply a custom set of interfaces. For example:
|
||||
/// a unit test may create a pool of fake interfaces and use the custom
|
||||
/// @c PktFilter class to mimic socket operation on these interfaces.
|
||||
void clearIfaces();
|
||||
|
||||
/// @brief Detects network interfaces.
|
||||
///
|
||||
/// This method will eventually detect available interfaces. For now
|
||||
/// it offers stub implementation. First interface name and link-local
|
||||
/// IPv6 address is read from interfaces.txt file.
|
||||
void detectIfaces();
|
||||
|
||||
/// @brief Return most suitable socket for transmitting specified IPv6 packet.
|
||||
///
|
||||
@@ -469,7 +483,7 @@ public:
|
||||
/// @return a socket descriptor
|
||||
uint16_t getSocket(const isc::dhcp::Pkt6& pkt);
|
||||
|
||||
/// @brief Return most suitable socket for transmitting specified IPv6 packet.
|
||||
/// @brief Return most suitable socket for transmitting specified IPv4 packet.
|
||||
///
|
||||
/// This method takes Pkt4 (see overloaded implementation that takes
|
||||
/// Pkt6) and chooses appropriate socket to send it. This method
|
||||
@@ -479,8 +493,8 @@ public:
|
||||
///
|
||||
/// @param pkt a packet to be transmitted
|
||||
///
|
||||
/// @return a socket descriptor
|
||||
uint16_t getSocket(const isc::dhcp::Pkt4& pkt);
|
||||
/// @return A structure describing a socket.
|
||||
SocketInfo getSocket(const isc::dhcp::Pkt4& pkt);
|
||||
|
||||
/// Debugging method that prints out all available interfaces.
|
||||
///
|
||||
@@ -870,14 +884,6 @@ protected:
|
||||
int openSocket6(Iface& iface, const isc::asiolink::IOAddress& addr,
|
||||
uint16_t port, const bool join_multicast);
|
||||
|
||||
/// @brief Detects network interfaces.
|
||||
///
|
||||
/// This method will eventually detect available interfaces. For now
|
||||
/// it offers stub implementation. First interface name and link-local
|
||||
/// IPv6 address is read from interfaces.txt file.
|
||||
void
|
||||
detectIfaces();
|
||||
|
||||
/// @brief Stub implementation of network interface detection.
|
||||
///
|
||||
/// This implementations reads a single line from interfaces.txt file
|
||||
|
@@ -463,6 +463,26 @@ Pkt4::updateTimestamp() {
|
||||
timestamp_ = boost::posix_time::microsec_clock::universal_time();
|
||||
}
|
||||
|
||||
bool
|
||||
Pkt4::isRelayed() const {
|
||||
static const IOAddress zero_addr("0.0.0.0");
|
||||
// For non-relayed message both Giaddr and Hops are zero.
|
||||
if (getGiaddr() == zero_addr && getHops() == 0) {
|
||||
return (false);
|
||||
|
||||
// For relayed message, both Giaddr and Hops are non-zero.
|
||||
} else if (getGiaddr() != zero_addr && getHops() > 0) {
|
||||
return (true);
|
||||
}
|
||||
// In any other case, the packet is considered malformed.
|
||||
isc_throw(isc::BadValue, "invalid combination of giaddr = "
|
||||
<< getGiaddr().toText() << " and hops = "
|
||||
<< static_cast<int>(getHops()) << ". Valid values"
|
||||
" are: (giaddr = 0 and hops = 0) or (giaddr != 0 and"
|
||||
"hops != 0)");
|
||||
|
||||
}
|
||||
|
||||
} // end of namespace isc::dhcp
|
||||
|
||||
} // end of namespace isc
|
||||
|
@@ -484,6 +484,24 @@ public:
|
||||
/// @return remote port
|
||||
uint16_t getRemotePort() const { return (remote_port_); }
|
||||
|
||||
/// @brief Checks if a DHCPv4 message has been relayed.
|
||||
///
|
||||
/// This function returns a boolean value which indicates whether a DHCPv4
|
||||
/// message has been relayed (if true is returned) or not (if false).
|
||||
///
|
||||
/// This function uses a combination of Giaddr and Hops. It is expected that
|
||||
/// if Giaddr is not 0, the Hops is greater than 0. In this case the message
|
||||
/// is considered relayed. If Giaddr is 0, the Hops value must also be 0. In
|
||||
/// this case the message is considered non-relayed. For any other
|
||||
/// combination of Giaddr and Hops, an exception is thrown to indicate that
|
||||
/// the message is malformed.
|
||||
///
|
||||
/// @return Boolean value which indicates whether the message is relayed
|
||||
/// (true) or non-relayed (false).
|
||||
/// @throw isc::BadValue if invalid combination of Giaddr and Hops values is
|
||||
/// found.
|
||||
bool isRelayed() const;
|
||||
|
||||
/// @brief Set callback function to be used to parse options.
|
||||
///
|
||||
/// @param callback An instance of the callback function or NULL to
|
||||
|
@@ -258,26 +258,6 @@ PktFilterLPF::send(const Iface& iface, uint16_t sockfd, const Pkt4Ptr& pkt) {
|
||||
// are valid because they are checked by the function called.
|
||||
writeEthernetHeader(pkt, buf);
|
||||
|
||||
// This object represents broadcast address. We will compare the
|
||||
// local packet address with it a few lines below. Having static
|
||||
// variable guarantees that this object is created only once, not
|
||||
// every time this function is called.
|
||||
static const isc::asiolink::IOAddress bcast_addr("255.255.255.255");
|
||||
|
||||
// It is likely that the local address in pkt object is set to
|
||||
// broadcast address. This is the case if server received the
|
||||
// client's packet on broadcast address. Therefore, we need to
|
||||
// correct it here and assign the actual source address.
|
||||
if (pkt->getLocalAddr() == bcast_addr) {
|
||||
const Iface::SocketCollection& sockets = iface.getSockets();
|
||||
for (Iface::SocketCollection::const_iterator it = sockets.begin();
|
||||
it != sockets.end(); ++it) {
|
||||
if (sockfd == it->sockfd_) {
|
||||
pkt->setLocalAddr(it->addr_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IP and UDP header
|
||||
writeIpUdpHeader(pkt, buf);
|
||||
|
||||
|
@@ -577,6 +577,25 @@ TEST_F(IfaceMgrTest, getIface) {
|
||||
EXPECT_EQ(static_cast<void*>(NULL), ifacemgr->getIface("wifi15") );
|
||||
}
|
||||
|
||||
TEST_F(IfaceMgrTest, clearIfaces) {
|
||||
NakedIfaceMgr ifacemgr;
|
||||
// Create a set of fake interfaces. At the same time, remove the actual
|
||||
// interfaces that have been detected by the IfaceMgr.
|
||||
ifacemgr.createIfaces();
|
||||
|
||||
ASSERT_GT(ifacemgr.countIfaces(), 0);
|
||||
|
||||
boost::shared_ptr<TestPktFilter> custom_packet_filter(new TestPktFilter());
|
||||
ASSERT_TRUE(custom_packet_filter);
|
||||
ASSERT_NO_THROW(ifacemgr.setPacketFilter(custom_packet_filter));
|
||||
|
||||
ASSERT_NO_THROW(ifacemgr.openSockets4());
|
||||
|
||||
ifacemgr.clearIfaces();
|
||||
|
||||
EXPECT_EQ(0, ifacemgr.countIfaces());
|
||||
}
|
||||
|
||||
TEST_F(IfaceMgrTest, receiveTimeout6) {
|
||||
using namespace boost::posix_time;
|
||||
std::cout << "Testing DHCPv6 packet reception timeouts."
|
||||
@@ -1328,7 +1347,7 @@ TEST_F(IfaceMgrTest, socket4) {
|
||||
pkt.setIface(LOOPBACK);
|
||||
|
||||
// Expect that we get the socket that we just opened.
|
||||
EXPECT_EQ(socket1, ifacemgr->getSocket(pkt));
|
||||
EXPECT_EQ(socket1, ifacemgr->getSocket(pkt).sockfd_);
|
||||
|
||||
close(socket1);
|
||||
}
|
||||
@@ -1963,7 +1982,7 @@ TEST_F(IfaceMgrTest, socketInfo) {
|
||||
|
||||
// Socket info is set, packet has well defined interface. It should work.
|
||||
pkt4.setIface(LOOPBACK);
|
||||
EXPECT_EQ(7, ifacemgr->getSocket(pkt4));
|
||||
EXPECT_EQ(7, ifacemgr->getSocket(pkt4).sockfd_);
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
ifacemgr->getIface(LOOPBACK)->delSocket(7);
|
||||
|
@@ -798,4 +798,27 @@ TEST_F(Pkt4Test, hwaddrSrcRemote) {
|
||||
remote_addr->hwaddr_.begin()));
|
||||
}
|
||||
|
||||
// This test verifies that the check for a message being relayed is correct.
|
||||
// It also checks that the exception is thrown if the combination of hops and
|
||||
// giaddr is invalid.
|
||||
TEST_F(Pkt4Test, isRelayed) {
|
||||
Pkt4 pkt(DHCPDISCOVER, 1234);
|
||||
// By default, the hops and giaddr should be 0.
|
||||
ASSERT_EQ("0.0.0.0", pkt.getGiaddr().toText());
|
||||
ASSERT_EQ(0, pkt.getHops());
|
||||
// For hops = 0 and giaddr = 0, the message is non-relayed.
|
||||
EXPECT_FALSE(pkt.isRelayed());
|
||||
// Set giaddr but leave hops = 0. This should result in exception.
|
||||
pkt.setGiaddr(IOAddress("10.0.0.1"));
|
||||
EXPECT_THROW(pkt.isRelayed(), isc::BadValue);
|
||||
// Set hops. Now both hops and giaddr is set. The message is relayed.
|
||||
pkt.setHops(10);
|
||||
EXPECT_TRUE(pkt.isRelayed());
|
||||
// Set giaddr to 0. For hops being set to non-zero value the function
|
||||
// should throw an exception.
|
||||
pkt.setGiaddr(IOAddress("0.0.0.0"));
|
||||
EXPECT_THROW(pkt.isRelayed(), isc::BadValue);
|
||||
|
||||
}
|
||||
|
||||
} // end of anonymous namespace
|
||||
|
Reference in New Issue
Block a user