-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
return (false);
}
+Pkt4Ptr
+TestControl::createRequestFromAck(const dhcp::Pkt4Ptr& ack) {
+ Pkt4Ptr msg(new Pkt4(DHCPREQUEST, generateTransid()));
+ msg->setCiaddr(ack->getYiaddr());
+ msg->setHWAddr(ack->getHWAddr());
+ return (msg);
+}
+
Pkt6Ptr
TestControl::createMessageFromReply(const uint16_t msg_type,
const dhcp::Pkt6Ptr& reply) {
stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_RA,
options.getDropTime()[1]);
}
+ if (options.getRenewRate() != 0) {
+ stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_RN);
+ }
} else if (options.getIpVersion() == 6) {
stats_mgr6_.reset();
}
}
+uint64_t
+TestControl::sendMultipleRequests(const TestControlSocket& socket,
+ const uint64_t msg_num) {
+ for (uint64_t i = 0; i < msg_num; ++i) {
+ if (!sendRequestFromAck(socket)) {
+ return (i);
+ }
+ }
+ return (msg_num);
+}
+
uint64_t
TestControl::sendMultipleMessages6(const TestControlSocket& socket,
const uint32_t msg_type,
}
}
} else if (pkt4->getType() == DHCPACK) {
- stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_RA, pkt4);
+ // If received message is DHCPACK, we have to check if this is a response
+ // to 4-way exchange. We'll match this packet with a DHCPREQUESTs sent
+ // as part of the 4-way exchages.
+ if (stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_RA, pkt4)) {
+ // The DHCPACK belongs to DHCPREQUEST-DHCPACK exchange type. So, we
+ // may need to keep this DHCPACK in the storage if renews. Note that,
+ // DHCPACK messages hold the information about leases assigned.
+ // We use this information to renew.
+ if (stats_mgr4_->hasExchangeStats(StatsMgr4::XCHG_RN)) {
+ // Renew messages are sent, because StatsMgr has the
+ // specific exchange type specified. Let's append the DHCPACK.
+ // message to a storage
+ ack_storage_.append(pkt4);
+ }
+ // The DHCPACK message is not a server's response to the DHCPREQUEST
+ // message sent within the 4-way exchange. It may be a response to a
+ // renewal. In this case we first check if StatsMgr has exchange type
+ // for renew specified, and if it has, if there is a corresponding
+ // renew message for the received DHCPACK.
+ } else if (stats_mgr4_->hasExchangeStats(StatsMgr4::XCHG_RN)) {
+ stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_RN, pkt4);
+ }
}
}
// If -f<renew-rate> option was specified we have to check how many
// Renew packets should be sent to catch up with a desired rate.
- if ((options.getIpVersion() == 6) && (options.getRenewRate() != 0)) {
+ if (options.getRenewRate() != 0) {
uint64_t renew_packets_due =
renew_rate_control_.getOutboundMessageCount();
checkLateMessages(renew_rate_control_);
- // Send Renew messages.
- sendMultipleMessages6(socket, DHCPV6_RENEW, renew_packets_due);
+
+ // Send multiple renews to satify the desired rate.
+ if (options.getIpVersion() == 4) {
+ sendMultipleRequests(socket, renew_packets_due);
+ } else {
+ sendMultipleMessages6(socket, DHCPV6_RENEW, renew_packets_due);
+ }
}
// If -F<release-rate> option was specified we have to check how many
saveFirstPacket(pkt4);
}
+bool
+TestControl::sendRequestFromAck(const TestControlSocket& socket) {
+ // Update timestamp of last sent renewal.
+ renew_rate_control_.updateSendTime();
+
+ // Get one of the recorded DHCPACK messages.
+ Pkt4Ptr ack = ack_storage_.getRandom();
+ if (!ack) {
+ return (false);
+ }
+
+ // Create message of the specified type.
+ Pkt4Ptr msg = createRequestFromAck(ack);
+ setDefaults4(socket, msg);
+ msg->pack();
+ // And send it.
+ IfaceMgr::instance().send(msg);
+ if (!stats_mgr4_) {
+ isc_throw(Unexpected, "Statistics Manager for DHCPv4 "
+ "hasn't been initialized");
+ }
+ stats_mgr4_->passSentPacket(StatsMgr4::XCHG_RN, msg);
+ return (true);
+}
+
+
bool
TestControl::sendMessageFromReply(const uint16_t msg_type,
const TestControlSocket& socket) {
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
/// has been reached.
void cleanCachedPackets();
+ /// \brief Creates DHCPREQUEST from a DHCPACK message.
+ ///
+ /// \param ack An instance of the DHCPACK message to be used to
+ /// create a new message.
+ ///
+ /// \return Pointer to the created message.
+ dhcp::Pkt4Ptr createRequestFromAck(const dhcp::Pkt4Ptr& ack);
+
/// \brief Creates DHCPv6 message from the Reply packet.
///
/// This function creates DHCPv6 Renew or Release message using the
/// data from the Reply message by copying options from the Reply
/// message.
///
- /// \param msg_type A type of the message to be createad.
+ /// \param msg_type A type of the message to be created.
/// \param reply An instance of the Reply packet which contents should
/// be used to create an instance of the new message.
///
const uint64_t packets_num,
const bool preload = false);
+ /// \brief Send number of DHCPREQUEST (renew) messages to a server.
+ ///
+ /// \param socket An object representing socket to be used to send packets.
+ /// \param msg_num A number of messages to be sent.
+ ///
+ /// \return A number of messages actually sent.
+ uint64_t sendMultipleRequests(const TestControlSocket& socket,
+ const uint64_t msg_num);
+
/// \brief Send number of DHCPv6 Renew or Release messages to the server.
///
/// \param socket An object representing socket to be used to send packets.
const uint32_t msg_type,
const uint64_t msg_num);
+ /// \brief Send DHCPv4 renew (DHCPREQUEST) using specified socket.
+ ///
+ /// \param socket An object encapsulating socket to be used to send
+ /// a packet.
+ ///
+ /// \return true if the message has been sent, false otherwise.
+ bool sendRequestFromAck(const TestControlSocket& socket);
+
/// \brief Send DHCPv6 Renew or Release message using specified socket.
///
/// This method will select an existing lease from the Reply packet cache
StatsMgr4Ptr stats_mgr4_; ///< Statistics Manager 4.
StatsMgr6Ptr stats_mgr6_; ///< Statistics Manager 6.
+ PacketStorage<dhcp::Pkt4> ack_storage_; ///< A storage for DHCPACK messages.
PacketStorage<dhcp::Pkt6> reply_storage_; ///< A storage for reply messages.
NumberGeneratorPtr transid_gen_; ///< Transaction id generator.
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
using TestControl::checkExitConditions;
using TestControl::createMessageFromReply;
+ using TestControl::createRequestFromAck;
using TestControl::factoryElapsedTime6;
using TestControl::factoryGeneric;
using TestControl::factoryIana6;
using TestControl::reset;
using TestControl::sendDiscover4;
using TestControl::sendPackets;
+ using TestControl::sendMultipleRequests;
using TestControl::sendMultipleMessages6;
using TestControl::sendRequest6;
using TestControl::sendSolicit6;
}
}
- /// \brief Test that the DHCPv4 Release or Renew message is created
+ /// \brief Test sending DHCPv4 renews.
+ ///
+ /// This function simulates acquiring 10 leases from the server. Returned
+ /// DHCPACK messages are cached and used to send renew messages.
+ /// The maxmimal number of messages which can be sent is equal to the
+ /// number of leases acquired (10). This function also checks that an
+ /// attempt to send more renew messages than the number of leases acquired
+ /// will fail.
+ void testSendRenew4() {
+ std::string loopback_iface(getLocalLoopback());
+ if (loopback_iface.empty()) {
+ std::cout << "Skipping the test because loopback interface could"
+ " not be detected" << std::endl;
+ return;
+ }
+ // Build a command line. Depending on the message type, we will use
+ // -f<renew-rate> or -F<release-rate> parameter.
+ std::ostringstream s;
+ s << "perfdhcp -4 -l " << loopback_iface << " -r 10 -f";
+ s << " 10 -R 10 -L 10067 -n 10 127.0.0.1";
+ ASSERT_NO_THROW(processCmdLine(s.str()));
+ // Create a test controller class.
+ NakedTestControl tc;
+ tc.initializeStatsMgr();
+ // Set the transaction id generator to sequential to control to
+ // guarantee that transaction ids are predictable.
+ boost::shared_ptr<NakedTestControl::IncrementalGenerator>
+ generator(new NakedTestControl::IncrementalGenerator());
+ tc.setTransidGenerator(generator);
+ // Socket has to be created so as we can actually send packets.
+ int sock_handle = 0;
+ ASSERT_NO_THROW(sock_handle = tc.openSocket());
+ TestControl::TestControlSocket sock(sock_handle);
+
+ // Send a number of DHCPDISCOVER messages. Each generated message will
+ // be assigned a different transaction id, starting from 1 to 10.
+ tc.sendPackets(sock, 10);
+
+ // Simulate DHCPOFFER responses from the server. Each DHCPOFFER is
+ // assigned a transaction id from the range of 1 to 10, so as they
+ // match the transaction ids from the DHCPDISCOVER messages.
+ for (unsigned i = generator->getNext() - 10;
+ i < generator->getNext(); ++i) {
+ Pkt4Ptr offer(createOfferPkt4(i));
+ // If DHCPOFFER is matched with the DHCPDISCOVER the call below
+ // will trigger a corresponding DHCPREQUEST. They will be assigned
+ // transaction ids from the range from 11 to 20 (the range of
+ // 1 to 10 has been used by DHCPDISCOVER-DHCPOFFER).
+ ASSERT_NO_THROW(tc.processReceivedPacket4(sock, offer));
+ }
+
+ // Requests have been sent, so now let's simulate responses from the
+ // server. Generate corresponding DHCPACK messages with the transaction
+ // ids from the range from 11 to 20.
+ for (unsigned i = generator->getNext() - 10;
+ i < generator->getNext(); ++i) {
+ Pkt4Ptr ack(createAckPkt4(i));
+ // Each DHCPACK packet corresponds to the new lease acquired. Since
+ // -f<renew-rate> option has been specified, received Reply
+ // messages are held so as renew messages can be sent for
+ // existing leases.
+ ASSERT_NO_THROW(tc.processReceivedPacket4(sock, ack));
+ }
+
+ uint64_t msg_num;
+ // Try to send 5 messages. It should be successful because 10
+ // DHCPREQUEST messages has been received. For each of them we
+ // should be able to send renewal.
+ ASSERT_NO_THROW(
+ msg_num = tc.sendMultipleRequests(sock, 5)
+ );
+ // Make sure that we have sent 5 messages.
+ EXPECT_EQ(5, msg_num);
+
+ // Try to do it again. We should still have 5 Reply packets for
+ // which renews haven't been sent yet.
+ ASSERT_NO_THROW(
+ msg_num = tc.sendMultipleRequests(sock, 5)
+ );
+ EXPECT_EQ(5, msg_num);
+
+ // We used all the DHCPACK packets (we sent renew or release for each of
+ // them already). Therefore, no further renew messages should be sent
+ // before we acquire new leases.
+ ASSERT_NO_THROW(
+ msg_num = tc.sendMultipleRequests(sock, 5)
+ );
+ // Make sure that no message has been sent.
+ EXPECT_EQ(0, msg_num);
+ }
+
+ /// \brief Test that the DHCPREQUEST message is created correctly and
+ /// comprises expected values.
+ void testCreateRequest() {
+ // This command line specifies that the Release/Renew messages should
+ // be sent with the same rate as the Solicit messages.
+ std::ostringstream s;
+ s << "perfdhcp -4 -l lo -r 10 -f 10";
+ s << " -R 10 -L 10067 -n 10 127.0.0.1";
+ ASSERT_NO_THROW(processCmdLine(s.str()));
+ // Create a test controller class.
+ NakedTestControl tc;
+ // Set the transaction id generator which will be used by the
+ // createRenew or createRelease function to generate transaction id.
+ boost::shared_ptr<NakedTestControl::IncrementalGenerator>
+ generator(new NakedTestControl::IncrementalGenerator());
+ tc.setTransidGenerator(generator);
+
+ Pkt4Ptr ack = createAckPkt4(1);
+
+ // Create DHCPREQUST from DHCPACK.
+ Pkt4Ptr request;
+ ASSERT_NO_THROW(request = tc.createRequestFromAck(ack));
+
+ // Make sure that the DHCPACK has been successfully created and that
+ // it holds expected data.
+ ASSERT_TRUE(request);
+ EXPECT_EQ("127.0.0.1", request->getCiaddr().toText());
+
+ // HW address.
+ HWAddrPtr hwaddr_ack = ack->getHWAddr();
+ ASSERT_TRUE(hwaddr_ack);
+ HWAddrPtr hwaddr_req = request->getHWAddr();
+ ASSERT_TRUE(hwaddr_req);
+ EXPECT_TRUE(hwaddr_ack->hwaddr_ == hwaddr_req->hwaddr_);
+ }
+
+ /// \brief Test that the DHCPv6 Release or Renew message is created
/// correctly and comprises expected options.
///
/// \param msg_type A type of the message to be tested: DHCPV6_RELEASE
CommandOptionsHelper::process(cmdline);
}
+ /// \brief Create DHCPOFFER or DHCPACK packet.
+ ///
+ /// \param pkt_type DHCPOFFER or DHCPACK.
+ /// \param transid Transaction id.
+ ///
+ /// \return Instance of the packet.
+ Pkt4Ptr
+ createResponsePkt4(const uint8_t pkt_type,
+ const uint32_t transid) const {
+ Pkt4Ptr pkt(new Pkt4(pkt_type, transid));
+ OptionPtr opt_serverid = Option::factory(Option::V4,
+ DHO_DHCP_SERVER_IDENTIFIER,
+ OptionBuffer(4, 1));
+ pkt->setYiaddr(asiolink::IOAddress("127.0.0.1"));
+ pkt->addOption(opt_serverid);
+ pkt->updateTimestamp();
+ return (pkt);
+ }
+
/// \brief Create DHCPv4 OFFER packet.
///
/// \param transid transaction id.
/// \return instance of the packet.
- boost::shared_ptr<Pkt4>
+ Pkt4Ptr
createOfferPkt4(uint32_t transid) const {
- boost::shared_ptr<Pkt4> offer(new Pkt4(DHCPOFFER, transid));
- OptionPtr opt_serverid = Option::factory(Option::V4,
- DHO_DHCP_SERVER_IDENTIFIER,
- OptionBuffer(4, 1));
- offer->setYiaddr(asiolink::IOAddress("127.0.0.1"));
- offer->addOption(opt_serverid);
- offer->updateTimestamp();
- return (offer);
+ return (createResponsePkt4(DHCPOFFER, transid));
+ }
+
+ /// \brief Create DHCPACK packet.
+ ///
+ /// \param transid transaction id.
+ /// \return instance of the packet.
+ Pkt4Ptr
+ createAckPkt4(const uint32_t transid) const {
+ return (createResponsePkt4(DHCPACK, transid));
}
/// \brief Create DHCPv6 ADVERTISE packet.
EXPECT_THROW(tc.initPacketTemplates(), isc::BadValue);
}
-TEST_F(TestControlTest, processRenew) {
+// This test verifies that DHCPv4 renew (DHCPREQUEST) messages can be
+// sent for acquired leases.
+TEST_F(TestControlTest, processRenew4) {
+ testSendRenew4();
+}
+
+// This test verifies that DHCPv6 Renew messages can be sent for acquired
+// leases.
+TEST_F(TestControlTest, processRenew6) {
testSendRenewRelease(DHCPV6_RENEW);
}
-TEST_F(TestControlTest, processRelease) {
+// This test verifies that DHCPv6 Release messages can be sent for acquired
+// leases.
+TEST_F(TestControlTest, processRelease6) {
testSendRenewRelease(DHCPV6_RELEASE);
}
+// This test verifies that DHCPREQUEST is created correctly from the
+// DHCPACK message.
+TEST_F(TestControlTest, createRequest) {
+ testCreateRequest();
+}
+
// This test verifies that the DHCPV6 Renew message is created correctly
// and that it comprises all required options.
TEST_F(TestControlTest, createRenew) {