2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-09-01 14:35:29 +00:00

[82-improve-kea-test-capabilities] Added remote port to dhcp4/dhcp6/perfdhcp

This commit is contained in:
Francis Dupont
2018-12-27 19:12:10 +01:00
committed by Tomek Mrugalski
parent 0487f6fc96
commit c25bbf2616
24 changed files with 276 additions and 51 deletions

View File

@@ -716,8 +716,10 @@ ControlledDhcpv4Srv::checkConfig(isc::data::ConstElementPtr config) {
return (configureDhcp4Server(*srv, config, true)); return (configureDhcp4Server(*srv, config, true));
} }
ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t server_port /*= DHCP4_SERVER_PORT*/) ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t server_port /*= DHCP4_SERVER_PORT*/,
: Dhcpv4Srv(server_port), io_service_(), timer_mgr_(TimerMgr::instance()) { uint16_t client_port /*= 0*/)
: Dhcpv4Srv(server_port, client_port), io_service_(),
timer_mgr_(TimerMgr::instance()) {
if (getInstance()) { if (getInstance()) {
isc_throw(InvalidOperation, isc_throw(InvalidOperation,
"There is another Dhcpv4Srv instance already."); "There is another Dhcpv4Srv instance already.");

View File

@@ -28,7 +28,9 @@ public:
/// @brief Constructor /// @brief Constructor
/// ///
/// @param server_port UDP port to be opened for DHCP traffic /// @param server_port UDP port to be opened for DHCP traffic
ControlledDhcpv4Srv(uint16_t server_port = DHCP4_SERVER_PORT); /// @param client_port UDP port where all responses are sent to.
ControlledDhcpv4Srv(uint16_t server_port = DHCP4_SERVER_PORT,
uint16_t client_port = 0);
/// @brief Destructor. /// @brief Destructor.
~ControlledDhcpv4Srv(); ~ControlledDhcpv4Srv();

View File

@@ -739,7 +739,7 @@ This informational message indicates that the DHCPv4 server has
processed any command-line switches and is starting. The version processed any command-line switches and is starting. The version
is also printed. is also printed.
% DHCP4_START_INFO pid: %1, server port: %2, verbose: %3 % DHCP4_START_INFO pid: %1, server port: %2, client port: %3, verbose: %4
This is a debug message issued during the DHCPv4 server startup. This is a debug message issued during the DHCPv4 server startup.
It lists some information about the parameters with which the server It lists some information about the parameters with which the server
is running. is running.

View File

@@ -441,10 +441,11 @@ Dhcpv4Exchange::setReservedMessageFields() {
const std::string Dhcpv4Srv::VENDOR_CLASS_PREFIX("VENDOR_CLASS_"); const std::string Dhcpv4Srv::VENDOR_CLASS_PREFIX("VENDOR_CLASS_");
Dhcpv4Srv::Dhcpv4Srv(uint16_t server_port, const bool use_bcast, Dhcpv4Srv::Dhcpv4Srv(uint16_t server_port, uint16_t client_port,
const bool direct_response_desired) const bool use_bcast, const bool direct_response_desired)
: io_service_(new IOService()), shutdown_(true), alloc_engine_(), : io_service_(new IOService()), shutdown_(true), alloc_engine_(),
server_port_(server_port), use_bcast_(use_bcast), server_port_(server_port), use_bcast_(use_bcast),
client_port_(client_port),
network_state_(new NetworkState(NetworkState::DHCPv4)) { network_state_(new NetworkState(NetworkState::DHCPv4)) {
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET) LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET)
@@ -2296,11 +2297,15 @@ Dhcpv4Srv::adjustIfaceData(Dhcpv4Exchange& ex) {
// server has to reply via relay agent. For other messages we send back // server has to reply via relay agent. For other messages we send back
// through relay if message is relayed, and unicast to the client if the // through relay if message is relayed, and unicast to the client if the
// message is not relayed. // message is not relayed.
// If client port was set from the command line enforce all responses
// to it. Of course it is only for testing purposes.
// Note that the call to this function may throw if invalid combination // 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 // 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 // giaddr != 0). The exception will propagate down and eventually cause the
// packet to be discarded. // packet to be discarded.
if (((query->getType() == DHCPINFORM) && if (client_port_) {
response->setRemotePort(client_port_);
} else if (((query->getType() == DHCPINFORM) &&
((!query->getCiaddr().isV4Zero()) || ((!query->getCiaddr().isV4Zero()) ||
(!query->isRelayed() && !query->getRemoteAddr().isV4Zero()))) || (!query->isRelayed() && !query->getRemoteAddr().isV4Zero()))) ||
((query->getType() != DHCPINFORM) && !query->isRelayed())) { ((query->getType() != DHCPINFORM) && !query->isRelayed())) {

View File

@@ -212,7 +212,8 @@ public:
/// In particular, creates IfaceMgr that will be responsible for /// In particular, creates IfaceMgr that will be responsible for
/// network interaction. Will instantiate lease manager, and load /// network interaction. Will instantiate lease manager, and load
/// old or create new DUID. It is possible to specify alternate /// old or create new DUID. It is possible to specify alternate
/// port on which DHCPv4 server will listen on. That is mostly useful /// port on which DHCPv4 server will listen on and alternate port
/// where DHCPv4 server sends all responses to. Those are mostly useful
/// for testing purposes. The Last two arguments of the constructor /// for testing purposes. The Last two arguments of the constructor
/// should be left at default values for normal server operation. /// should be left at default values for normal server operation.
/// They should be set to 'false' when creating an instance of this /// They should be set to 'false' when creating an instance of this
@@ -220,10 +221,12 @@ public:
/// root privileges. /// root privileges.
/// ///
/// @param server_port specifies port number to listen on /// @param server_port specifies port number to listen on
/// @param client_port specifies port number to send to
/// @param use_bcast configure sockets to support broadcast messages. /// @param use_bcast configure sockets to support broadcast messages.
/// @param direct_response_desired specifies if it is desired to /// @param direct_response_desired specifies if it is desired to
/// use direct V4 traffic. /// use direct V4 traffic.
Dhcpv4Srv(uint16_t server_port = DHCP4_SERVER_PORT, Dhcpv4Srv(uint16_t server_port = DHCP4_SERVER_PORT,
uint16_t client_port = 0,
const bool use_bcast = true, const bool use_bcast = true,
const bool direct_response_desired = true); const bool direct_response_desired = true);
@@ -772,7 +775,8 @@ protected:
/// address). /// address).
/// ///
/// The destination port is always DHCPv4 client (68) or relay (67) port, /// The destination port is always DHCPv4 client (68) or relay (67) port,
/// depending if the response will be sent directly to a client. /// depending if the response will be sent directly to a client, unless
/// a client port was enforced from the command line.
/// ///
/// The source port is always set to DHCPv4 server port (67). /// The source port is always set to DHCPv4 server port (67).
/// ///
@@ -789,7 +793,7 @@ protected:
/// ///
/// @param ex The exchange holding both the client's message and the /// @param ex The exchange holding both the client's message and the
/// server's response. /// server's response.
static void adjustIfaceData(Dhcpv4Exchange& ex); void adjustIfaceData(Dhcpv4Exchange& ex);
/// @brief Sets remote addresses for outgoing packet. /// @brief Sets remote addresses for outgoing packet.
/// ///
@@ -962,6 +966,9 @@ private:
protected: protected:
/// UDP port number to which server sends responses.
uint16_t client_port_;
/// @brief Holds information about disabled DHCP service and/or /// @brief Holds information about disabled DHCP service and/or
/// disabled subnet/network scopes. /// disabled subnet/network scopes.
NetworkStatePtr network_state_; NetworkStatePtr network_state_;

View File

@@ -50,6 +50,7 @@
<arg choice="opt" rep="norepeat"><option>-c <replaceable class="parameter">config-file</replaceable></option></arg> <arg choice="opt" rep="norepeat"><option>-c <replaceable class="parameter">config-file</replaceable></option></arg>
<arg choice="opt" rep="norepeat"><option>-t <replaceable class="parameter">config-file</replaceable></option></arg> <arg choice="opt" rep="norepeat"><option>-t <replaceable class="parameter">config-file</replaceable></option></arg>
<arg choice="opt" rep="norepeat"><option>-p <replaceable class="parameter">server-port-number</replaceable></option></arg> <arg choice="opt" rep="norepeat"><option>-p <replaceable class="parameter">server-port-number</replaceable></option></arg>
<arg choice="opt" rep="norepeat"><option>-P <replaceable class="parameter">client-port-number</replaceable></option></arg>
</cmdsynopsis> </cmdsynopsis>
</refsynopsisdiv> </refsynopsisdiv>
@@ -123,6 +124,14 @@
</para></listitem> </para></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>-P</option></term>
<listitem><para>
Client port number (1-65535) to which the server responds.
This is useful for testing purposes only.
</para></listitem>
</varlistentry>
</variablelist> </variablelist>
</refsect1> </refsect1>

View File

@@ -44,7 +44,7 @@ usage() {
cerr << "Kea DHCPv4 server, version " << VERSION << endl; cerr << "Kea DHCPv4 server, version " << VERSION << endl;
cerr << endl; cerr << endl;
cerr << "Usage: " << DHCP4_NAME cerr << "Usage: " << DHCP4_NAME
<< " -[v|V|W] [-d] [-{c|t} cfgfile] [-p number]" << endl; << " -[v|V|W] [-d] [-{c|t} cfgfile] [-p number] [-P number]" << endl;
cerr << " -v: print version number and exit" << endl; cerr << " -v: print version number and exit" << endl;
cerr << " -V: print extended version and exit" << endl; cerr << " -V: print extended version and exit" << endl;
cerr << " -W: display the configuration report and exit" << endl; cerr << " -W: display the configuration report and exit" << endl;
@@ -53,6 +53,8 @@ usage() {
cerr << " -t file: check the configuration file syntax and exit" << endl; cerr << " -t file: check the configuration file syntax and exit" << endl;
cerr << " -p number: specify non-standard server port number 1-65535 " cerr << " -p number: specify non-standard server port number 1-65535 "
<< "(useful for testing only)" << endl; << "(useful for testing only)" << endl;
cerr << " -P number: specify non-standard client port number 1-65535 "
<< "(useful for testing only)" << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} // end of anonymous namespace } // end of anonymous namespace
@@ -60,15 +62,17 @@ usage() {
int int
main(int argc, char* argv[]) { main(int argc, char* argv[]) {
int ch; int ch;
// The default. any other values are useful for testing only. // The default. Any other values are useful for testing only.
int server_port_number = DHCP4_SERVER_PORT; int server_port_number = DHCP4_SERVER_PORT;
// Not zero values are useful for testing only.
int client_port_number = 0;
bool verbose_mode = false; // Should server be verbose? bool verbose_mode = false; // Should server be verbose?
bool check_mode = false; // Check syntax bool check_mode = false; // Check syntax
// The standard config file // The standard config file
std::string config_file(""); std::string config_file("");
while ((ch = getopt(argc, argv, "dvVWc:p:t:")) != -1) { while ((ch = getopt(argc, argv, "dvVWc:p:P:t:")) != -1) {
switch (ch) { switch (ch) {
case 'd': case 'd':
verbose_mode = true; verbose_mode = true;
@@ -98,12 +102,27 @@ main(int argc, char* argv[]) {
try { try {
server_port_number = boost::lexical_cast<int>(optarg); server_port_number = boost::lexical_cast<int>(optarg);
} catch (const boost::bad_lexical_cast &) { } catch (const boost::bad_lexical_cast &) {
cerr << "Failed to parse port number: [" << optarg cerr << "Failed to parse server port number: [" << optarg
<< "], 1-65535 allowed." << endl; << "], 1-65535 allowed." << endl;
usage(); usage();
} }
if (server_port_number <= 0 || server_port_number > 65535) { if (server_port_number <= 0 || server_port_number > 65535) {
cerr << "Failed to parse port number: [" << optarg cerr << "Failed to parse server port number: [" << optarg
<< "], 1-65535 allowed." << endl;
usage();
}
break;
case 'P':
try {
client_port_number = boost::lexical_cast<int>(optarg);
} catch (const boost::bad_lexical_cast &) {
cerr << "Failed to parse client port number: [" << optarg
<< "], 1-65535 allowed." << endl;
usage();
}
if (client_port_number <= 0 || client_port_number > 65535) {
cerr << "Failed to parse client port number: [" << optarg
<< "], 1-65535 allowed." << endl; << "], 1-65535 allowed." << endl;
usage(); usage();
} }
@@ -187,13 +206,15 @@ main(int argc, char* argv[]) {
// Initialize logging. If verbose, we'll use maximum verbosity. // Initialize logging. If verbose, we'll use maximum verbosity.
Daemon::loggerInit(DHCP4_ROOT_LOGGER_NAME, verbose_mode); Daemon::loggerInit(DHCP4_ROOT_LOGGER_NAME, verbose_mode);
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_START_INFO) LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_START_INFO)
.arg(getpid()).arg(server_port_number) .arg(getpid())
.arg(server_port_number)
.arg(client_port_number)
.arg(verbose_mode ? "yes" : "no"); .arg(verbose_mode ? "yes" : "no");
LOG_INFO(dhcp4_logger, DHCP4_STARTING).arg(VERSION); LOG_INFO(dhcp4_logger, DHCP4_STARTING).arg(VERSION);
// Create the server instance. // Create the server instance.
ControlledDhcpv4Srv server(server_port_number); ControlledDhcpv4Srv server(server_port_number, client_port_number);
// Remember verbose-mode // Remember verbose-mode
server.setVerbose(verbose_mode); server.setVerbose(verbose_mode);

View File

@@ -176,7 +176,7 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataRelay) {
resp->setHops(req->getHops()); resp->setHops(req->getHops());
// This function never throws. // This function never throws.
ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex)); ASSERT_NO_THROW(srv_.adjustIfaceData(ex));
// Now the destination address should be relay's address. // Now the destination address should be relay's address.
EXPECT_EQ("192.0.1.1", resp->getRemoteAddr().toText()); EXPECT_EQ("192.0.1.1", resp->getRemoteAddr().toText());
@@ -202,10 +202,16 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataRelay) {
// Clear remote address. // Clear remote address.
resp->setRemoteAddr(IOAddress("0.0.0.0")); resp->setRemoteAddr(IOAddress("0.0.0.0"));
ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex)); // Set the client port.
srv_.client_port_ = 1234;
ASSERT_NO_THROW(srv_.adjustIfaceData(ex));
// Response should be sent back to the relay address. // Response should be sent back to the relay address.
EXPECT_EQ("192.0.1.50", resp->getRemoteAddr().toText()); EXPECT_EQ("192.0.1.50", resp->getRemoteAddr().toText());
// Remote port was enforced to the client port.
EXPECT_EQ(srv_.client_port_, resp->getRemotePort());
} }
// This test verifies that the remote port is adjusted when // This test verifies that the remote port is adjusted when
@@ -260,7 +266,7 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataRelayPort) {
resp->setRemotePort(67); resp->setRemotePort(67);
// This function never throws. // This function never throws.
ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex)); ASSERT_NO_THROW(srv_.adjustIfaceData(ex));
// Now the destination address should be relay's address. // Now the destination address should be relay's address.
EXPECT_EQ("192.0.1.1", resp->getRemoteAddr().toText()); EXPECT_EQ("192.0.1.1", resp->getRemoteAddr().toText());
@@ -325,7 +331,7 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataUseRouting) {
resp->setHops(req->getHops()); resp->setHops(req->getHops());
// This function never throws. // This function never throws.
ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex)); ASSERT_NO_THROW(srv_.adjustIfaceData(ex));
// Now the destination address should be relay's address. // Now the destination address should be relay's address.
EXPECT_EQ("192.0.1.1", resp->getRemoteAddr().toText()); EXPECT_EQ("192.0.1.1", resp->getRemoteAddr().toText());
@@ -354,7 +360,7 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataUseRouting) {
cfg_iface->setOutboundIface(CfgIface::SAME_AS_INBOUND); cfg_iface->setOutboundIface(CfgIface::SAME_AS_INBOUND);
CfgMgr::instance().commit(); CfgMgr::instance().commit();
ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex)); ASSERT_NO_THROW(srv_.adjustIfaceData(ex));
EXPECT_EQ("192.0.2.5", resp->getLocalAddr().toText()); EXPECT_EQ("192.0.2.5", resp->getLocalAddr().toText());
EXPECT_EQ("eth1", resp->getIface()); EXPECT_EQ("eth1", resp->getIface());
@@ -409,7 +415,7 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataRenew) {
// Copy hops value from the query. // Copy hops value from the query.
resp->setHops(req->getHops()); resp->setHops(req->getHops());
ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex)); ASSERT_NO_THROW(srv_.adjustIfaceData(ex));
// Check that server responds to ciaddr // Check that server responds to ciaddr
EXPECT_EQ("192.0.1.15", resp->getRemoteAddr().toText()); EXPECT_EQ("192.0.1.15", resp->getRemoteAddr().toText());
@@ -482,7 +488,7 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataSelect) {
// are zero and client has just got new lease, the assigned address is // are zero and client has just got new lease, the assigned address is
// carried in yiaddr. In order to send this address to the client, // carried in yiaddr. In order to send this address to the client,
// server must broadcast its response. // server must broadcast its response.
ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex)); ASSERT_NO_THROW(srv_.adjustIfaceData(ex));
// Check that the response is sent to broadcast address as the // Check that the response is sent to broadcast address as the
// server doesn't have capability to respond directly. // server doesn't have capability to respond directly.
@@ -511,7 +517,7 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataSelect) {
// Now we expect that the server will send its response to the // Now we expect that the server will send its response to the
// address assigned for the client. // address assigned for the client.
ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex)); ASSERT_NO_THROW(srv_.adjustIfaceData(ex));
EXPECT_EQ("192.0.1.13", resp->getRemoteAddr().toText()); EXPECT_EQ("192.0.1.13", resp->getRemoteAddr().toText());
} }
@@ -553,7 +559,7 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataBroadcast) {
// Clear the remote address. // Clear the remote address.
resp->setRemoteAddr(IOAddress("0.0.0.0")); resp->setRemoteAddr(IOAddress("0.0.0.0"));
ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex)); ASSERT_NO_THROW(srv_.adjustIfaceData(ex));
// Server must respond to broadcast address when client desired that // Server must respond to broadcast address when client desired that
// by setting the broadcast flag in its request. // by setting the broadcast flag in its request.

View File

@@ -229,6 +229,7 @@ public:
using Dhcpv4Srv::VENDOR_CLASS_PREFIX; using Dhcpv4Srv::VENDOR_CLASS_PREFIX;
using Dhcpv4Srv::shutdown_; using Dhcpv4Srv::shutdown_;
using Dhcpv4Srv::alloc_engine_; using Dhcpv4Srv::alloc_engine_;
using Dhcpv4Srv::client_port_;
}; };
// We need to pass one reference to the Dhcp4Client, which is defined in // We need to pass one reference to the Dhcp4Client, which is defined in

View File

@@ -738,8 +738,10 @@ ControlledDhcpv6Srv::checkConfig(isc::data::ConstElementPtr config) {
return (configureDhcp6Server(*srv, config, true)); return (configureDhcp6Server(*srv, config, true));
} }
ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t server_port) ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t server_port,
: Dhcpv6Srv(server_port), io_service_(), timer_mgr_(TimerMgr::instance()) { uint16_t client_port)
: Dhcpv6Srv(server_port, client_port), io_service_(),
timer_mgr_(TimerMgr::instance()) {
if (server_) { if (server_) {
isc_throw(InvalidOperation, isc_throw(InvalidOperation,
"There is another Dhcpv6Srv instance already."); "There is another Dhcpv6Srv instance already.");

View File

@@ -28,7 +28,9 @@ public:
/// @brief Constructor /// @brief Constructor
/// ///
/// @param server_port UDP port to be opened for DHCP traffic /// @param server_port UDP port to be opened for DHCP traffic
ControlledDhcpv6Srv(uint16_t server_port = DHCP6_SERVER_PORT); /// @param client_port UDP port where all responses are sent to.
ControlledDhcpv6Srv(uint16_t server_port = DHCP6_SERVER_PORT,
uint16_t client_port = 0);
/// @brief Destructor. /// @brief Destructor.
virtual ~ControlledDhcpv6Srv(); virtual ~ControlledDhcpv6Srv();

View File

@@ -782,7 +782,7 @@ This informational message indicates that the IPv6 DHCP server has
processed any command-line switches and is starting. The version processed any command-line switches and is starting. The version
is also printed. is also printed.
% DHCP6_START_INFO pid: %1, server port: %2, verbose: %3 % DHCP6_START_INFO pid: %1, server port: %2, client port: %3, verbose: %4
This is a debug message issued during the IPv6 DHCP server startup. This is a debug message issued during the IPv6 DHCP server startup.
It lists some information about the parameters with which the server It lists some information about the parameters with which the server
is running. is running.

View File

@@ -179,15 +179,18 @@ namespace dhcp {
const std::string Dhcpv6Srv::VENDOR_CLASS_PREFIX("VENDOR_CLASS_"); const std::string Dhcpv6Srv::VENDOR_CLASS_PREFIX("VENDOR_CLASS_");
Dhcpv6Srv::Dhcpv6Srv(uint16_t server_port) Dhcpv6Srv::Dhcpv6Srv(uint16_t server_port, uint16_t client_port)
: io_service_(new IOService()), server_port_(server_port), serverid_(), : io_service_(new IOService()), server_port_(server_port),
shutdown_(true), alloc_engine_(), name_change_reqs_(), client_port_(client_port), serverid_(), shutdown_(true),
alloc_engine_(), name_change_reqs_(),
network_state_(new NetworkState(NetworkState::DHCPv6)) network_state_(new NetworkState(NetworkState::DHCPv6))
{ {
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET) LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET)
.arg(server_port); .arg(server_port);
Dhcp6to4Ipc::instance().client_port = client_port;
// Initialize objects required for DHCP server operation. // Initialize objects required for DHCP server operation.
try { try {
// Port 0 is used for testing purposes where in most cases we don't // Port 0 is used for testing purposes where in most cases we don't
@@ -775,7 +778,10 @@ Dhcpv6Srv::processPacket(Pkt6Ptr& query, Pkt6Ptr& rsp) {
rsp->setRemoteAddr(query->getRemoteAddr()); rsp->setRemoteAddr(query->getRemoteAddr());
rsp->setLocalAddr(query->getLocalAddr()); rsp->setLocalAddr(query->getLocalAddr());
if (rsp->relay_info_.empty()) { if (client_port_) {
// A command line option enforces a specific client port
rsp->setRemotePort(client_port_);
} else if (rsp->relay_info_.empty()) {
// Direct traffic, send back to the client directly // Direct traffic, send back to the client directly
rsp->setRemotePort(DHCP6_CLIENT_PORT); rsp->setRemotePort(DHCP6_CLIENT_PORT);
} else { } else {

View File

@@ -80,8 +80,10 @@ public:
/// network interaction. Will instantiate lease manager, and load /// network interaction. Will instantiate lease manager, and load
/// old or create new DUID. /// old or create new DUID.
/// ///
/// @param server_port port on will all sockets will listen /// @param server_port port on which all sockets will listen
Dhcpv6Srv(uint16_t server_port = DHCP6_SERVER_PORT); /// @param client_port port to which all responses will be sent
Dhcpv6Srv(uint16_t server_port = DHCP6_SERVER_PORT,
uint16_t client_port = 0);
/// @brief Destructor. Used during DHCPv6 service shutdown. /// @brief Destructor. Used during DHCPv6 service shutdown.
virtual ~Dhcpv6Srv(); virtual ~Dhcpv6Srv();
@@ -943,6 +945,10 @@ private:
/// UDP port number on which server listens. /// UDP port number on which server listens.
uint16_t server_port_; uint16_t server_port_;
protected:
/// UDP port number to which server sends all responses.
uint16_t client_port_;
public: public:
/// @note used by DHCPv4-over-DHCPv6 so must be public and static /// @note used by DHCPv4-over-DHCPv6 so must be public and static

View File

@@ -27,6 +27,8 @@ using namespace isc::hooks;
namespace isc { namespace isc {
namespace dhcp { namespace dhcp {
uint16_t Dhcp6to4Ipc::client_port = 0;
Dhcp6to4Ipc::Dhcp6to4Ipc() : Dhcp4o6IpcBase() {} Dhcp6to4Ipc::Dhcp6to4Ipc() : Dhcp4o6IpcBase() {}
Dhcp6to4Ipc& Dhcp6to4Ipc::instance() { Dhcp6to4Ipc& Dhcp6to4Ipc::instance() {
@@ -93,7 +95,10 @@ void Dhcp6to4Ipc::handler() {
// want to know if it is a relayed message (vs. internal message type). // want to know if it is a relayed message (vs. internal message type).
// getType() always returns the type of internal message. // getType() always returns the type of internal message.
uint8_t msg_type = buf[0]; uint8_t msg_type = buf[0];
if ((msg_type == DHCPV6_RELAY_FORW) || (msg_type == DHCPV6_RELAY_REPL)) { if (client_port) {
pkt->setRemotePort(client_port);
} else if ((msg_type == DHCPV6_RELAY_FORW) ||
(msg_type == DHCPV6_RELAY_REPL)) {
pkt->setRemotePort(relay_port ? relay_port : DHCP6_SERVER_PORT); pkt->setRemotePort(relay_port ? relay_port : DHCP6_SERVER_PORT);
} else { } else {
pkt->setRemotePort(DHCP6_CLIENT_PORT); pkt->setRemotePort(DHCP6_CLIENT_PORT);

View File

@@ -45,6 +45,10 @@ public:
/// ///
/// The handler sends the DHCPv6 packet back to the remote address /// The handler sends the DHCPv6 packet back to the remote address
static void handler(); static void handler();
/// @param client_port UDP port where all responses are sent to.
/// Not zero is mostly useful for testing purposes.
static uint16_t client_port;
}; };
} // namespace isc } // namespace isc

View File

@@ -49,7 +49,7 @@ usage() {
cerr << "Kea DHCPv6 server, version " << VERSION << endl; cerr << "Kea DHCPv6 server, version " << VERSION << endl;
cerr << endl; cerr << endl;
cerr << "Usage: " << DHCP6_NAME cerr << "Usage: " << DHCP6_NAME
<< " -[v|V|W] [-d] [-{c|t} cfgfile] [-p server_port_number]" << endl; << " -[v|V|W] [-d] [-{c|t} cfgfile] [-p number] [-P number]" << endl;
cerr << " -v: print version number and exit." << endl; cerr << " -v: print version number and exit." << endl;
cerr << " -V: print extended version and exit" << endl; cerr << " -V: print extended version and exit" << endl;
cerr << " -W: display the configuration report and exit" << endl; cerr << " -W: display the configuration report and exit" << endl;
@@ -58,6 +58,8 @@ usage() {
cerr << " -t file: check the configuration file syntax and exit" << endl; cerr << " -t file: check the configuration file syntax and exit" << endl;
cerr << " -p number: specify non-standard server port number 1-65535 " cerr << " -p number: specify non-standard server port number 1-65535 "
<< "(useful for testing only)" << endl; << "(useful for testing only)" << endl;
cerr << " -P number: specify non-standard client port number 1-65535 "
<< "(useful for testing only)" << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} // end of anonymous namespace } // end of anonymous namespace
@@ -67,13 +69,15 @@ main(int argc, char* argv[]) {
int ch; int ch;
// The default. Any other values are useful for testing only. // The default. Any other values are useful for testing only.
int server_port_number = DHCP6_SERVER_PORT; int server_port_number = DHCP6_SERVER_PORT;
// Not zero values are useful for testing only.
int client_port_number = 0;
bool verbose_mode = false; // Should server be verbose? bool verbose_mode = false; // Should server be verbose?
bool check_mode = false; // Check syntax bool check_mode = false; // Check syntax
// The standard config file // The standard config file
std::string config_file(""); std::string config_file("");
while ((ch = getopt(argc, argv, "dvVWc:p:t:")) != -1) { while ((ch = getopt(argc, argv, "dvVWc:p:P:t:")) != -1) {
switch (ch) { switch (ch) {
case 'd': case 'd':
verbose_mode = true; verbose_mode = true;
@@ -99,16 +103,31 @@ main(int argc, char* argv[]) {
config_file = optarg; config_file = optarg;
break; break;
case 'p': // port number case 'p': // server port number
try { try {
server_port_number = boost::lexical_cast<int>(optarg); server_port_number = boost::lexical_cast<int>(optarg);
} catch (const boost::bad_lexical_cast &) { } catch (const boost::bad_lexical_cast &) {
cerr << "Failed to parse port number: [" << optarg cerr << "Failed to parse server port number: [" << optarg
<< "], 1-65535 allowed." << endl; << "], 1-65535 allowed." << endl;
usage(); usage();
} }
if (server_port_number <= 0 || server_port_number > 65535) { if (server_port_number <= 0 || server_port_number > 65535) {
cerr << "Failed to parse port number: [" << optarg cerr << "Failed to parse server port number: [" << optarg
<< "], 1-65535 allowed." << endl;
usage();
}
break;
case 'P': // client port number
try {
client_port_number = boost::lexical_cast<int>(optarg);
} catch (const boost::bad_lexical_cast &) {
cerr << "Failed to parse client port number: [" << optarg
<< "], 1-65535 allowed." << endl;
usage();
}
if (client_port_number <= 0 || client_port_number > 65535) {
cerr << "Failed to parse client port number: [" << optarg
<< "], 1-65535 allowed." << endl; << "], 1-65535 allowed." << endl;
usage(); usage();
} }
@@ -194,13 +213,15 @@ main(int argc, char* argv[]) {
Daemon::loggerInit(DHCP6_LOGGER_NAME, verbose_mode); Daemon::loggerInit(DHCP6_LOGGER_NAME, verbose_mode);
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_START_INFO) LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_START_INFO)
.arg(getpid()).arg(server_port_number) .arg(getpid())
.arg(server_port_number)
.arg(client_port_number)
.arg(verbose_mode ? "yes" : "no"); .arg(verbose_mode ? "yes" : "no");
LOG_INFO(dhcp6_logger, DHCP6_STARTING).arg(VERSION); LOG_INFO(dhcp6_logger, DHCP6_STARTING).arg(VERSION);
// Create the server instance. // Create the server instance.
ControlledDhcpv6Srv server(server_port_number); ControlledDhcpv6Srv server(server_port_number, client_port_number);
// Remember verbose-mode // Remember verbose-mode
server.setVerbose(verbose_mode); server.setVerbose(verbose_mode);

View File

@@ -1609,6 +1609,35 @@ TEST_F(Dhcpv6SrvTest, selectSubnetRelayInterfaceId) {
EXPECT_FALSE(drop); EXPECT_FALSE(drop);
} }
// Checks if server responses are sent to the proper port.
TEST_F(Dhcpv6SrvTest, portsClientPort) {
NakedDhcpv6Srv srv(0);
// Enforce a specific client port value.
EXPECT_EQ(0, srv.client_port_);
srv.client_port_ = 1234;
// Let's create a simple SOLICIT
Pkt6Ptr sol = PktCaptures::captureSimpleSolicit();
// Simulate that we have received that traffic
srv.fakeReceive(sol);
// Server will now process to run its normal loop, but instead of calling
// IfaceMgr::receive6(), it will read all packets from the list set by
// fakeReceive()
srv.run();
// Get Advertise...
ASSERT_FALSE(srv.fake_sent_.empty());
Pkt6Ptr adv = srv.fake_sent_.front();
ASSERT_TRUE(adv);
// This is sent back to client directly, should be port 546
EXPECT_EQ(srv.client_port_, adv->getRemotePort());
}
// Checks if server responses are sent to the proper port. // Checks if server responses are sent to the proper port.
TEST_F(Dhcpv6SrvTest, portsDirectTraffic) { TEST_F(Dhcpv6SrvTest, portsDirectTraffic) {

View File

@@ -272,6 +272,7 @@ public:
using Dhcpv6Srv::name_change_reqs_; using Dhcpv6Srv::name_change_reqs_;
using Dhcpv6Srv::VENDOR_CLASS_PREFIX; using Dhcpv6Srv::VENDOR_CLASS_PREFIX;
using Dhcpv6Srv::initContext; using Dhcpv6Srv::initContext;
using Dhcpv6Srv::client_port_;
/// @brief packets we pretend to receive /// @brief packets we pretend to receive
/// ///

View File

@@ -139,6 +139,9 @@ TEST_F(Dhcp6to4IpcTest, receive) {
// Create instance of the IPC endpoint being used as a source of messages. // Create instance of the IPC endpoint being used as a source of messages.
TestIpc src_ipc(TEST_PORT, TestIpc::ENDPOINT_TYPE_V4); TestIpc src_ipc(TEST_PORT, TestIpc::ENDPOINT_TYPE_V4);
// Reset the IPC.
ASSERT_NO_THROW(ipc.close());
// Open both endpoints. // Open both endpoints.
ASSERT_NO_THROW(ipc.open()); ASSERT_NO_THROW(ipc.open());
ASSERT_NO_THROW(src_ipc.open()); ASSERT_NO_THROW(src_ipc.open());
@@ -197,6 +200,9 @@ TEST_F(Dhcp6to4IpcTest, DISABLED_receiveRelayed) {
// Create instance of the IPC endpoint being used as a source of messages. // Create instance of the IPC endpoint being used as a source of messages.
TestIpc src_ipc(TEST_PORT, TestIpc::ENDPOINT_TYPE_V4); TestIpc src_ipc(TEST_PORT, TestIpc::ENDPOINT_TYPE_V4);
// Reset the IPC.
ASSERT_NO_THROW(ipc.close());
// Open both endpoints. // Open both endpoints.
ASSERT_NO_THROW(ipc.open()); ASSERT_NO_THROW(ipc.open());
ASSERT_NO_THROW(src_ipc.open()); ASSERT_NO_THROW(src_ipc.open());
@@ -249,4 +255,49 @@ TEST_F(Dhcp6to4IpcTest, DISABLED_receiveRelayed) {
EXPECT_EQ(1, d4_resp->getInteger().first); EXPECT_EQ(1, d4_resp->getInteger().first);
} }
// This test verifies the client port is enforced also with DHCP4o6.
TEST_F(Dhcp6to4IpcTest, clientPort) {
// Create instance of the IPC endpoint under test.
Dhcp6to4Ipc& ipc = Dhcp6to4Ipc::instance();
// Set the client port.
ipc.client_port = 1234;
// Create instance of the IPC endpoint being used as a source of messages.
TestIpc src_ipc(TEST_PORT, TestIpc::ENDPOINT_TYPE_V4);
// Reset the IPC.
ASSERT_NO_THROW(ipc.close());
// Open both endpoints.
ASSERT_NO_THROW(ipc.open());
ASSERT_NO_THROW(src_ipc.open());
// Create message to be sent over IPC.
Pkt6Ptr pkt(new Pkt6(DHCPV6_DHCPV4_RESPONSE, 1234));
pkt->addOption(createDHCPv4MsgOption());
pkt->setIface("eth0");
pkt->setRemoteAddr(IOAddress("2001:db8:1::123"));
ASSERT_NO_THROW(pkt->pack());
// Reset the callout cached packet
Dhcp6to4IpcTest::callback_pkt_.reset();
// Send and wait up to 1 second to receive it.
ASSERT_NO_THROW(src_ipc.send(pkt));
ASSERT_NO_THROW(IfaceMgr::instance().receive6(1, 0));
// Make sure that the received packet was configured to return copy of
// retrieved options within a callout.
EXPECT_TRUE(callback_pkt_options_copy_);
// Get the forwarded packet from the callout
Pkt6Ptr forwarded = Dhcp6to4IpcTest::callback_pkt_;
ASSERT_TRUE(forwarded);
// Verify the packet received.
EXPECT_EQ(ipc.client_port, forwarded->getRemotePort());
// Reset the value in case tests are not in order.
ipc.client_port = 0;
}
} // end of anonymous namespace } // end of anonymous namespace

View File

@@ -138,6 +138,7 @@ CommandOptions::reset() {
is_interface_ = false; is_interface_ = false;
preload_ = 0; preload_ = 0;
local_port_ = 0; local_port_ = 0;
remote_port_ = 0;
seeded_ = false; seeded_ = false;
seed_ = 0; seed_ = 0;
broadcast_ = false; broadcast_ = false;
@@ -226,7 +227,7 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
// In this section we collect argument values from command line // In this section we collect argument values from command line
// they will be tuned and validated elsewhere // they will be tuned and validated elsewhere
while((opt = getopt(argc, argv, "hv46A:r:t:R:b:n:p:d:D:l:P:L:M:" while((opt = getopt(argc, argv, "hv46A:r:t:R:b:n:p:d:D:l:P:a:L:N:M:"
"s:iBc1T:X:O:o:E:S:I:x:W:w:e:f:F:g:")) != -1) { "s:iBc1T:X:O:o:E:S:I:x:W:w:e:f:F:g:")) != -1) {
stream << " -" << static_cast<char>(opt); stream << " -" << static_cast<char>(opt);
if (optarg) { if (optarg) {
@@ -377,6 +378,16 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
boost::lexical_cast<std::string>(std::numeric_limits<uint16_t>::max())); boost::lexical_cast<std::string>(std::numeric_limits<uint16_t>::max()));
break; break;
case 'N':
remote_port_ = nonNegativeInteger("value of remote port:"
" -L<value> must not be a"
" negative integer");
check(remote_port_ >
static_cast<int>(std::numeric_limits<uint16_t>::max()),
"remote-port must be lower than " +
boost::lexical_cast<std::string>(std::numeric_limits<uint16_t>::max()));
break;
case 'M': case 'M':
check(num_mac_list_files >= 1, "only -M option can be specified"); check(num_mac_list_files >= 1, "only -M option can be specified");
num_mac_list_files++; num_mac_list_files++;
@@ -992,6 +1003,9 @@ CommandOptions::printCommandLine() const {
if (getLocalPort() != 0) { if (getLocalPort() != 0) {
std::cout << "local-port=" << local_port_ << std::endl; std::cout << "local-port=" << local_port_ << std::endl;
} }
if (getRemotePort() != 0) {
std::cout << "remote-port=" << remote_port_ << std::endl;
}
if (seeded_) { if (seeded_) {
std::cout << "seed=" << seed_ << std::endl; std::cout << "seed=" << seed_ << std::endl;
} }
@@ -1056,8 +1070,8 @@ CommandOptions::usage() const {
" [-F<release-rate>] [-t<report>] [-R<range>] [-b<base>]\n" " [-F<release-rate>] [-t<report>] [-R<range>] [-b<base>]\n"
" [-n<num-request>] [-p<test-period>] [-d<drop-time>]\n" " [-n<num-request>] [-p<test-period>] [-d<drop-time>]\n"
" [-D<max-drop>] [-l<local-addr|interface>] [-P<preload>]\n" " [-D<max-drop>] [-l<local-addr|interface>] [-P<preload>]\n"
" [-L<local-port>] [-s<seed>] [-i] [-B] [-g<single/multi>]\n" " [-L<local-port>] [-N<remote-port>]\n"
" [-W<late-exit-delay>]\n" " [-s<seed>] [-i] [-B] [-W<late-exit-delay>]\n"
" [-c] [-1] [-M<mac-list-file>] [-T<template-file>]\n" " [-c] [-1] [-M<mac-list-file>] [-T<template-file>]\n"
" [-X<xid-offset>] [-O<random-offset] [-E<time-offset>]\n" " [-X<xid-offset>] [-O<random-offset] [-E<time-offset>]\n"
" [-S<srvid-offset>] [-I<ip-offset>] [-x<diagnostic-selector>]\n" " [-S<srvid-offset>] [-I<ip-offset>] [-x<diagnostic-selector>]\n"
@@ -1129,6 +1143,8 @@ CommandOptions::usage() const {
" from this list for every new exchange. In the DHCPv6 case, MAC\n" " from this list for every new exchange. In the DHCPv6 case, MAC\n"
" addresses are used to generate DUID-LLs. This parameter must not be\n" " addresses are used to generate DUID-LLs. This parameter must not be\n"
" used in conjunction with the -b parameter.\n" " used in conjunction with the -b parameter.\n"
"-N<remote-port>: Specify the remote port to use\n"
" (the value 0 means to use the default).\n"
"-O<random-offset>: Offset of the last octet to randomize in the template.\n" "-O<random-offset>: Offset of the last octet to randomize in the template.\n"
"-P<preload>: Initiate first <preload> exchanges back to back at startup.\n" "-P<preload>: Initiate first <preload> exchanges back to back at startup.\n"
"-r<rate>: Initiate <rate> DORA/SARR (or if -i is given, DO/SA)\n" "-r<rate>: Initiate <rate> DORA/SARR (or if -i is given, DO/SA)\n"

View File

@@ -243,6 +243,11 @@ public:
/// \return local port number. /// \return local port number.
int getLocalPort() const { return local_port_; } int getLocalPort() const { return local_port_; }
/// \brief Returns remote port number.
///
/// \return remote port number.
int getRemotePort() const { return remote_port_; }
/// @brief Returns the time in microseconds to delay the program by. /// @brief Returns the time in microseconds to delay the program by.
/// ///
/// @return the time in microseconds to delay the program by. /// @return the time in microseconds to delay the program by.
@@ -577,6 +582,9 @@ private:
/// Local port number (host endian) /// Local port number (host endian)
int local_port_; int local_port_;
/// Remote port number (host endian)
int remote_port_;
/// Randomization seed. /// Randomization seed.
uint32_t seed_; uint32_t seed_;

View File

@@ -64,6 +64,7 @@
<arg choice="opt" rep="norepeat"><option>-L <replaceable class="parameter">local-port</replaceable></option></arg> <arg choice="opt" rep="norepeat"><option>-L <replaceable class="parameter">local-port</replaceable></option></arg>
<arg choice="opt" rep="norepeat"><option>-M <replaceable class="parameter">mac-list-file</replaceable></option></arg> <arg choice="opt" rep="norepeat"><option>-M <replaceable class="parameter">mac-list-file</replaceable></option></arg>
<arg choice="opt" rep="norepeat"><option>-n <replaceable class="parameter">num-request</replaceable></option></arg> <arg choice="opt" rep="norepeat"><option>-n <replaceable class="parameter">num-request</replaceable></option></arg>
<arg choice="opt" rep="norepeat"><option>-N <replaceable class="parameter">remote-port</replaceable></option></arg>
<arg choice="opt" rep="norepeat"><option>-O <replaceable class="parameter">random-offset</replaceable></option></arg> <arg choice="opt" rep="norepeat"><option>-O <replaceable class="parameter">random-offset</replaceable></option></arg>
<arg choice="opt" rep="norepeat"><option>-o <replaceable class="parameter">code,hexstring</replaceable></option></arg> <arg choice="opt" rep="norepeat"><option>-o <replaceable class="parameter">code,hexstring</replaceable></option></arg>
<arg choice="opt" rep="norepeat"><option>-p <replaceable class="parameter">test-period</replaceable></option></arg> <arg choice="opt" rep="norepeat"><option>-p <replaceable class="parameter">test-period</replaceable></option></arg>
@@ -416,6 +417,18 @@
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>-N <replaceable class="parameter">remote-port</replaceable></option></term>
<listitem>
<para>
Specify the remote port to use. This must be zero
or a positive integer up to 65535. A value of 0
(the default) allows <command>perfdhcp</command>
to choose the standard service port.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>-o <replaceable class="parameter">code,hexstring</replaceable></option></term> <term><option>-o <replaceable class="parameter">code,hexstring</replaceable></option></term>
<listitem> <listitem>

View File

@@ -2190,7 +2190,11 @@ TestControl::setDefaults4(const PerfSocket& socket,
// Local client's port (68) // Local client's port (68)
pkt->setLocalPort(DHCP4_CLIENT_PORT); pkt->setLocalPort(DHCP4_CLIENT_PORT);
// Server's port (67) // Server's port (67)
if (options.getRemotePort()) {
pkt->setRemotePort(options.getRemotePort());
} else {
pkt->setRemotePort(DHCP4_SERVER_PORT); pkt->setRemotePort(DHCP4_SERVER_PORT);
}
// The remote server's name or IP. // The remote server's name or IP.
pkt->setRemoteAddr(IOAddress(options.getServerName())); pkt->setRemoteAddr(IOAddress(options.getServerName()));
// Set local address. // Set local address.
@@ -2216,7 +2220,11 @@ TestControl::setDefaults6(const PerfSocket& socket,
// Local client's port (547) // Local client's port (547)
pkt->setLocalPort(DHCP6_CLIENT_PORT); pkt->setLocalPort(DHCP6_CLIENT_PORT);
// Server's port (548) // Server's port (548)
if (options.getRemotePort()) {
pkt->setRemotePort(options.getRemotePort());
} else {
pkt->setRemotePort(DHCP6_SERVER_PORT); pkt->setRemotePort(DHCP6_SERVER_PORT);
}
// Set local address. // Set local address.
pkt->setLocalAddr(socket.addr_); pkt->setLocalAddr(socket.addr_);
// The remote server's name or IP. // The remote server's name or IP.