]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[82-improve-kea-test-capabilities] Added remote port to dhcp4/dhcp6/perfdhcp
authorFrancis Dupont <fdupont@isc.org>
Thu, 27 Dec 2018 18:12:10 +0000 (19:12 +0100)
committerTomek Mrugalski <tomasz@isc.org>
Fri, 18 Jan 2019 16:50:10 +0000 (17:50 +0100)
24 files changed:
src/bin/dhcp4/ctrl_dhcp4_srv.cc
src/bin/dhcp4/ctrl_dhcp4_srv.h
src/bin/dhcp4/dhcp4_messages.mes
src/bin/dhcp4/dhcp4_srv.cc
src/bin/dhcp4/dhcp4_srv.h
src/bin/dhcp4/kea-dhcp4.xml
src/bin/dhcp4/main.cc
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
src/bin/dhcp4/tests/dhcp4_test_utils.h
src/bin/dhcp6/ctrl_dhcp6_srv.cc
src/bin/dhcp6/ctrl_dhcp6_srv.h
src/bin/dhcp6/dhcp6_messages.mes
src/bin/dhcp6/dhcp6_srv.cc
src/bin/dhcp6/dhcp6_srv.h
src/bin/dhcp6/dhcp6to4_ipc.cc
src/bin/dhcp6/dhcp6to4_ipc.h
src/bin/dhcp6/main.cc
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
src/bin/dhcp6/tests/dhcp6_test_utils.h
src/bin/dhcp6/tests/dhcp6to4_ipc_unittest.cc
src/bin/perfdhcp/command_options.cc
src/bin/perfdhcp/command_options.h
src/bin/perfdhcp/perfdhcp.xml
src/bin/perfdhcp/test_control.cc

index cacfe35d3d60198e534ccaa4e897eb0c26174ab4..0ab41233b49f5cd8c0d0a861a0c4b7abc79fe7c3 100644 (file)
@@ -716,8 +716,10 @@ ControlledDhcpv4Srv::checkConfig(isc::data::ConstElementPtr config) {
     return (configureDhcp4Server(*srv, config, true));
 }
 
-ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t server_port /*= DHCP4_SERVER_PORT*/)
-    : Dhcpv4Srv(server_port), io_service_(), timer_mgr_(TimerMgr::instance()) {
+ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t server_port /*= DHCP4_SERVER_PORT*/,
+                                         uint16_t client_port /*= 0*/)
+    : Dhcpv4Srv(server_port, client_port), io_service_(),
+      timer_mgr_(TimerMgr::instance()) {
     if (getInstance()) {
         isc_throw(InvalidOperation,
                   "There is another Dhcpv4Srv instance already.");
index 34dda4329e853c8c69e63f5bcdd05963e6165e96..de7a02ef6674cabfeb4503dfe5c157886d97aa75 100644 (file)
@@ -28,7 +28,9 @@ public:
     /// @brief Constructor
     ///
     /// @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.
     ~ControlledDhcpv4Srv();
index c66f7190559f57060816db099d2af64c021a4963..7612e2fa925b54acb9c5926445b8cd4fe2aec6ab 100644 (file)
@@ -739,7 +739,7 @@ This informational message indicates that the DHCPv4 server has
 processed any command-line switches and is starting. The version
 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.
 It lists some information about the parameters with which the server
 is running.
index 4a15e0ffd88239c5f2fd1d0f959cf81ee044f835..79b207c83bb6384d20b435f92442bef9cf455904 100644 (file)
@@ -441,10 +441,11 @@ Dhcpv4Exchange::setReservedMessageFields() {
 
 const std::string Dhcpv4Srv::VENDOR_CLASS_PREFIX("VENDOR_CLASS_");
 
-Dhcpv4Srv::Dhcpv4Srv(uint16_t server_port, const bool use_bcast,
-                     const bool direct_response_desired)
+Dhcpv4Srv::Dhcpv4Srv(uint16_t server_port, uint16_t client_port,
+                     const bool use_bcast, const bool direct_response_desired)
     : io_service_(new IOService()), shutdown_(true), alloc_engine_(),
       server_port_(server_port), use_bcast_(use_bcast),
+      client_port_(client_port),
       network_state_(new NetworkState(NetworkState::DHCPv4)) {
 
     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
     // through relay if message is relayed, and unicast to the client if the
     // 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
     // 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.
-    if (((query->getType() == DHCPINFORM) &&
+    if (client_port_) {
+        response->setRemotePort(client_port_);
+    } else if (((query->getType() == DHCPINFORM) &&
          ((!query->getCiaddr().isV4Zero()) ||
           (!query->isRelayed() && !query->getRemoteAddr().isV4Zero()))) ||
         ((query->getType() != DHCPINFORM) && !query->isRelayed())) {
index c7b838c81317d0547a918ad87dcbc83139b395f5..ccc1ce88a803b79ab2ab7add5196e87f46e3a58d 100644 (file)
@@ -212,7 +212,8 @@ public:
     /// In particular, creates IfaceMgr that will be responsible for
     /// network interaction. Will instantiate lease manager, and load
     /// 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
     /// should be left at default values for normal server operation.
     /// They should be set to 'false' when creating an instance of this
@@ -220,10 +221,12 @@ public:
     /// root privileges.
     ///
     /// @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 direct_response_desired specifies if it is desired to
     /// use direct V4 traffic.
     Dhcpv4Srv(uint16_t server_port = DHCP4_SERVER_PORT,
+              uint16_t client_port = 0,
               const bool use_bcast = true,
               const bool direct_response_desired = true);
 
@@ -772,7 +775,8 @@ protected:
     /// address).
     ///
     /// 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).
     ///
@@ -789,7 +793,7 @@ protected:
     ///
     /// @param ex The exchange holding both the client's message and the
     /// server's response.
-    static void adjustIfaceData(Dhcpv4Exchange& ex);
+    void adjustIfaceData(Dhcpv4Exchange& ex);
 
     /// @brief Sets remote addresses for outgoing packet.
     ///
@@ -962,6 +966,9 @@ private:
 
 protected:
 
+    /// UDP port number to which server sends responses.
+    uint16_t client_port_;
+
     /// @brief Holds information about disabled DHCP service and/or
     /// disabled subnet/network scopes.
     NetworkStatePtr network_state_;
index 03a59336b47353eeef05a5a1cc383315b33b13cd..720ae47bd138223758f57340a938956652cf13ef 100644 (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>-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">client-port-number</replaceable></option></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
 
         </para></listitem>
       </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>
   </refsect1>
 
index 15bc920f25e2e781c9603151c4d589b40f6fa9ed..7e138ac00db62e65069c29cfbf31c0a91ac54291 100644 (file)
@@ -44,7 +44,7 @@ usage() {
     cerr << "Kea DHCPv4 server, version " << VERSION << endl;
     cerr << endl;
     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 extended version 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 << "  -p number: specify non-standard server port number 1-65535 "
          << "(useful for testing only)" << endl;
+    cerr << "  -P number: specify non-standard client port number 1-65535 "
+         << "(useful for testing only)" << endl;
     exit(EXIT_FAILURE);
 }
 } // end of anonymous namespace
@@ -60,15 +62,17 @@ usage() {
 int
 main(int argc, char* argv[]) {
     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;
+    // Not zero values are useful for testing only.
+    int client_port_number = 0;
     bool verbose_mode = false; // Should server be verbose?
     bool check_mode = false;   // Check syntax
 
     // The standard 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) {
         case 'd':
             verbose_mode = true;
@@ -98,12 +102,27 @@ main(int argc, char* argv[]) {
             try {
                 server_port_number = boost::lexical_cast<int>(optarg);
             } 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;
                 usage();
             }
             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;
                 usage();
             }
@@ -187,13 +206,15 @@ main(int argc, char* argv[]) {
         // Initialize logging.  If verbose, we'll use maximum verbosity.
         Daemon::loggerInit(DHCP4_ROOT_LOGGER_NAME, verbose_mode);
         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");
 
         LOG_INFO(dhcp4_logger, DHCP4_STARTING).arg(VERSION);
 
         // Create the server instance.
-        ControlledDhcpv4Srv server(server_port_number);
+        ControlledDhcpv4Srv server(server_port_number, client_port_number);
 
         // Remember verbose-mode
         server.setVerbose(verbose_mode);
index f06dbb949b3b749cdcdd9abd2735cfefaf03b077..412b48b9c4b0bf348cbcd3c93a279f8d063bbd5e 100644 (file)
@@ -176,7 +176,7 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataRelay) {
     resp->setHops(req->getHops());
 
     // 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.
     EXPECT_EQ("192.0.1.1", resp->getRemoteAddr().toText());
@@ -202,10 +202,16 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataRelay) {
     // Clear remote address.
     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.
     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
@@ -260,7 +266,7 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataRelayPort) {
     resp->setRemotePort(67);
 
     // 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.
     EXPECT_EQ("192.0.1.1", resp->getRemoteAddr().toText());
@@ -325,7 +331,7 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataUseRouting) {
     resp->setHops(req->getHops());
 
     // 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.
     EXPECT_EQ("192.0.1.1", resp->getRemoteAddr().toText());
@@ -354,7 +360,7 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataUseRouting) {
     cfg_iface->setOutboundIface(CfgIface::SAME_AS_INBOUND);
     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("eth1", resp->getIface());
@@ -409,7 +415,7 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataRenew) {
     // Copy hops value from the query.
     resp->setHops(req->getHops());
 
-    ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(ex));
+    ASSERT_NO_THROW(srv_.adjustIfaceData(ex));
 
     // Check that server responds to ciaddr
     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
     // carried in yiaddr. In order to send this address to the client,
     // 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
     // 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
     // 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());
 }
@@ -553,7 +559,7 @@ TEST_F(Dhcpv4SrvTest, adjustIfaceDataBroadcast) {
     // Clear the remote address.
     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
     // by setting the broadcast flag in its request.
index cdd3bafe708a39d7dba3934c82e83f49be0d7aae..0ec1171f283485bdfc3683ea8ecf72e977ca15e0 100644 (file)
@@ -229,6 +229,7 @@ public:
     using Dhcpv4Srv::VENDOR_CLASS_PREFIX;
     using Dhcpv4Srv::shutdown_;
     using Dhcpv4Srv::alloc_engine_;
+    using Dhcpv4Srv::client_port_;
 };
 
 // We need to pass one reference to the Dhcp4Client, which is defined in
index 4a85b53032d2474687ce98fa5308847ada9c2381..84ba14ddc61b321f85efa2021f7a1e259223e096 100644 (file)
@@ -738,8 +738,10 @@ ControlledDhcpv6Srv::checkConfig(isc::data::ConstElementPtr config) {
     return (configureDhcp6Server(*srv, config, true));
 }
 
-ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t server_port)
-    : Dhcpv6Srv(server_port), io_service_(), timer_mgr_(TimerMgr::instance()) {
+ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t server_port,
+                                         uint16_t client_port)
+    : Dhcpv6Srv(server_port, client_port), io_service_(),
+      timer_mgr_(TimerMgr::instance()) {
     if (server_) {
         isc_throw(InvalidOperation,
                   "There is another Dhcpv6Srv instance already.");
index f8fd27691e10634bdd21c008eaf3fde305ebbb19..ee81c36a530b6f607142881aecb1b5b4f31a2cd7 100644 (file)
@@ -28,7 +28,9 @@ public:
     /// @brief Constructor
     ///
     /// @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.
     virtual ~ControlledDhcpv6Srv();
index 6fa07ce28fa6fe77a746a98b83cb680245d8686d..9079efb09ef79eeff3678c74c1f0c2822eae9644 100644 (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
 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.
 It lists some information about the parameters with which the server
 is running.
index e333e3be77a7b131a7db2c7b794f11a5164b0f45..36da8047ca1006e385e4ce1cd688a58f53784c5f 100644 (file)
@@ -179,15 +179,18 @@ namespace dhcp {
 
 const std::string Dhcpv6Srv::VENDOR_CLASS_PREFIX("VENDOR_CLASS_");
 
-Dhcpv6Srv::Dhcpv6Srv(uint16_t server_port)
-    : io_service_(new IOService()), server_port_(server_port), serverid_(),
-      shutdown_(true), alloc_engine_(), name_change_reqs_(),
+Dhcpv6Srv::Dhcpv6Srv(uint16_t server_port, uint16_t client_port)
+    : io_service_(new IOService()), server_port_(server_port),
+      client_port_(client_port), serverid_(), shutdown_(true),
+      alloc_engine_(), name_change_reqs_(),
       network_state_(new NetworkState(NetworkState::DHCPv6))
 {
 
     LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET)
         .arg(server_port);
 
+    Dhcp6to4Ipc::instance().client_port = client_port;
+
     // Initialize objects required for DHCP server operation.
     try {
         // 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->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
         rsp->setRemotePort(DHCP6_CLIENT_PORT);
     } else {
index 3c04ac530de85759582bb8183cf03ec47eefa210..36b54ee8c47794d18043b5ced396d2e5150d6364 100644 (file)
@@ -80,8 +80,10 @@ public:
     /// network interaction. Will instantiate lease manager, and load
     /// old or create new DUID.
     ///
-    /// @param server_port port on will all sockets will listen
-    Dhcpv6Srv(uint16_t server_port = DHCP6_SERVER_PORT);
+    /// @param server_port port on which all sockets will listen
+    /// @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.
     virtual ~Dhcpv6Srv();
@@ -943,6 +945,10 @@ private:
     /// UDP port number on which server listens.
     uint16_t server_port_;
 
+protected:
+    /// UDP port number to which server sends all responses.
+    uint16_t client_port_;
+
 public:
     /// @note used by DHCPv4-over-DHCPv6 so must be public and static
 
index 34873daaf188b7556ae9db1b0e10ab76fe16d33c..a9dbfd6ad05ecc83d72c66143bdfe1af1a9f3951 100644 (file)
@@ -27,6 +27,8 @@ using namespace isc::hooks;
 namespace isc {
 namespace dhcp {
 
+uint16_t Dhcp6to4Ipc::client_port = 0;
+
 Dhcp6to4Ipc::Dhcp6to4Ipc() : Dhcp4o6IpcBase() {}
 
 Dhcp6to4Ipc& Dhcp6to4Ipc::instance() {
@@ -93,7 +95,10 @@ void Dhcp6to4Ipc::handler() {
     // want to know if it is a relayed message (vs. internal message type).
     // getType() always returns the type of internal message.
     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);
     } else {
         pkt->setRemotePort(DHCP6_CLIENT_PORT);
index 844033531b0dd5f7173308314c9226cf2a169e6f..fc025ceaeabf9ad88a2e4e3c3acdcc082fe99ca4 100644 (file)
@@ -45,6 +45,10 @@ public:
     ///
     /// The handler sends the DHCPv6 packet back to the remote address
     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
index c934dedbb73407b21b49178872de1d8ad240bcb1..6f44221f5e8946308f13f06265b54e3d8cfa0daa 100644 (file)
@@ -49,7 +49,7 @@ usage() {
     cerr << "Kea DHCPv6 server, version " << VERSION << endl;
     cerr << endl;
     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 extended version 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 << "  -p number: specify non-standard server port number 1-65535 "
          << "(useful for testing only)" << endl;
+    cerr << "  -P number: specify non-standard client port number 1-65535 "
+         << "(useful for testing only)" << endl;
     exit(EXIT_FAILURE);
 }
 } // end of anonymous namespace
@@ -67,13 +69,15 @@ main(int argc, char* argv[]) {
     int ch;
     // The default. Any other values are useful for testing only.
     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 check_mode = false;   // Check syntax
 
     // The standard 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) {
         case 'd':
             verbose_mode = true;
@@ -99,16 +103,31 @@ main(int argc, char* argv[]) {
             config_file = optarg;
             break;
 
-        case 'p': // port number
+        case 'p': // server port number
             try {
                 server_port_number = boost::lexical_cast<int>(optarg);
             } 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;
                 usage();
             }
             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;
                 usage();
             }
@@ -194,13 +213,15 @@ main(int argc, char* argv[]) {
         Daemon::loggerInit(DHCP6_LOGGER_NAME, verbose_mode);
 
         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");
 
         LOG_INFO(dhcp6_logger, DHCP6_STARTING).arg(VERSION);
 
         // Create the server instance.
-        ControlledDhcpv6Srv server(server_port_number);
+        ControlledDhcpv6Srv server(server_port_number, client_port_number);
 
         // Remember verbose-mode
         server.setVerbose(verbose_mode);
index 2a2f505fb68a56d9e20c5f03d69e210780d9a4cd..99d4f5d3c2a8e368499d97df41a93d79b2baddad 100644 (file)
@@ -1609,6 +1609,35 @@ TEST_F(Dhcpv6SrvTest, selectSubnetRelayInterfaceId) {
     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.
 TEST_F(Dhcpv6SrvTest, portsDirectTraffic) {
 
index 71a23d8db26626bd7af43dce539da281386b0d70..a0ba34a96558e2884247a8eeba9daaa57f401caf 100644 (file)
@@ -272,6 +272,7 @@ public:
     using Dhcpv6Srv::name_change_reqs_;
     using Dhcpv6Srv::VENDOR_CLASS_PREFIX;
     using Dhcpv6Srv::initContext;
+    using Dhcpv6Srv::client_port_;
 
     /// @brief packets we pretend to receive
     ///
index 2e1a708e02b6f87ec8932a1a49c05350668b4f06..93f97f092dc63b02af9e256ead767b6868777757 100644 (file)
@@ -139,6 +139,9 @@ TEST_F(Dhcp6to4IpcTest, receive) {
     // 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());
@@ -197,6 +200,9 @@ TEST_F(Dhcp6to4IpcTest, DISABLED_receiveRelayed) {
     // 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());
@@ -249,4 +255,49 @@ TEST_F(Dhcp6to4IpcTest, DISABLED_receiveRelayed) {
     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
index b5b7039701d4983bd363f6d7fa0f8febea891cd2..eb562d97d25f7c7008eb7b58ab96abe02eddcb9c 100644 (file)
@@ -138,6 +138,7 @@ CommandOptions::reset() {
     is_interface_ = false;
     preload_ = 0;
     local_port_ = 0;
+    remote_port_ = 0;
     seeded_ = false;
     seed_ = 0;
     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
     // 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) {
         stream << " -" << static_cast<char>(opt);
         if (optarg) {
@@ -373,8 +374,18 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
                                               " negative integer");
              check(local_port_ >
                    static_cast<int>(std::numeric_limits<uint16_t>::max()),
-                  "local-port must be lower than " +
-                  boost::lexical_cast<std::string>(std::numeric_limits<uint16_t>::max()));
+                   "local-port must be lower than " +
+                   boost::lexical_cast<std::string>(std::numeric_limits<uint16_t>::max()));
+            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':
@@ -992,6 +1003,9 @@ CommandOptions::printCommandLine() const {
     if (getLocalPort() != 0) {
         std::cout << "local-port=" << local_port_ <<  std::endl;
     }
+    if (getRemotePort() != 0) {
+        std::cout << "remote-port=" << remote_port_ <<  std::endl;
+    }
     if (seeded_) {
         std::cout << "seed=" << seed_ << std::endl;
     }
@@ -1056,8 +1070,8 @@ CommandOptions::usage() const {
         "         [-F<release-rate>] [-t<report>] [-R<range>] [-b<base>]\n"
         "         [-n<num-request>] [-p<test-period>] [-d<drop-time>]\n"
         "         [-D<max-drop>] [-l<local-addr|interface>] [-P<preload>]\n"
-        "         [-L<local-port>] [-s<seed>] [-i] [-B] [-g<single/multi>]\n"
-        "         [-W<late-exit-delay>]\n"
+        "         [-L<local-port>] [-N<remote-port>]\n"
+        "         [-s<seed>] [-i] [-B] [-W<late-exit-delay>]\n"
         "         [-c] [-1] [-M<mac-list-file>] [-T<template-file>]\n"
         "         [-X<xid-offset>] [-O<random-offset] [-E<time-offset>]\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"
         "   addresses are used to generate DUID-LLs. This parameter must not be\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"
         "-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"
index f843d2484dcad3284abb5b0784cdbd8827702bac..deb2808267b6f77d66d7cb76df552aad70b6be20 100644 (file)
@@ -243,6 +243,11 @@ public:
     /// \return local port number.
     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.
     ///
     /// @return the time in microseconds to delay the program by.
@@ -577,6 +582,9 @@ private:
     /// Local port number (host endian)
     int local_port_;
 
+    /// Remote port number (host endian)
+    int remote_port_;
+
     /// Randomization seed.
     uint32_t seed_;
 
index 3f85823f80e4d600fe5c70d311818703a1034d3e..2fe4ac5d6ce62e45694a608e406aeb7b28751305 100644 (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>-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">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">code,hexstring</replaceable></option></arg>
             <arg choice="opt" rep="norepeat"><option>-p <replaceable class="parameter">test-period</replaceable></option></arg>
                 </listitem>
             </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>
                 <term><option>-o <replaceable class="parameter">code,hexstring</replaceable></option></term>
                 <listitem>
index 39a61251c5a1d0ca2d5db7f438a7cdfa11a7e1c9..55aaacbc2d2de94068614c3732ae15882277ede3 100644 (file)
@@ -2190,7 +2190,11 @@ TestControl::setDefaults4(const PerfSocket& socket,
     // Local client's port (68)
     pkt->setLocalPort(DHCP4_CLIENT_PORT);
     // Server's port (67)
-    pkt->setRemotePort(DHCP4_SERVER_PORT);
+    if (options.getRemotePort()) {
+        pkt->setRemotePort(options.getRemotePort());
+    } else {
+        pkt->setRemotePort(DHCP4_SERVER_PORT);
+    }
     // The remote server's name or IP.
     pkt->setRemoteAddr(IOAddress(options.getServerName()));
     // Set local address.
@@ -2216,7 +2220,11 @@ TestControl::setDefaults6(const PerfSocket& socket,
     // Local client's port (547)
     pkt->setLocalPort(DHCP6_CLIENT_PORT);
     // Server's port (548)
-    pkt->setRemotePort(DHCP6_SERVER_PORT);
+    if (options.getRemotePort()) {
+        pkt->setRemotePort(options.getRemotePort());
+    } else {
+        pkt->setRemotePort(DHCP6_SERVER_PORT);
+    }
     // Set local address.
     pkt->setLocalAddr(socket.addr_);
     // The remote server's name or IP.