2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-29 13:07:50 +00:00

[4254] perfdhcp sends DHCPv4 renews when -f option is specified.

This commit is contained in:
Marcin Siodelski 2016-01-13 14:41:00 +01:00
parent 9328124027
commit c6a38e4e7c
3 changed files with 286 additions and 20 deletions

View File

@ -1,4 +1,4 @@
// 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
@ -315,6 +315,14 @@ TestControl::checkExitConditions() const {
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) {
@ -663,6 +671,9 @@ TestControl::initializeStatsMgr() {
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();
@ -831,6 +842,17 @@ TestControl::sendPackets(const TestControlSocket& socket,
}
}
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,
@ -1072,7 +1094,28 @@ TestControl::processReceivedPacket4(const TestControlSocket& socket,
}
}
} 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);
}
}
}
@ -1364,12 +1407,17 @@ TestControl::run() {
// 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
@ -1568,6 +1616,32 @@ TestControl::sendDiscover4(const TestControlSocket& socket,
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) {

View File

@ -1,4 +1,4 @@
// 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
@ -306,13 +306,21 @@ protected:
/// 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.
///
@ -726,6 +734,15 @@ protected:
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.
@ -738,6 +755,14 @@ protected:
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
@ -1077,6 +1102,7 @@ protected:
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.

View File

@ -1,4 +1,4 @@
// 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
@ -95,6 +95,7 @@ public:
using TestControl::checkExitConditions;
using TestControl::createMessageFromReply;
using TestControl::createRequestFromAck;
using TestControl::factoryElapsedTime6;
using TestControl::factoryGeneric;
using TestControl::factoryIana6;
@ -114,6 +115,7 @@ public:
using TestControl::reset;
using TestControl::sendDiscover4;
using TestControl::sendPackets;
using TestControl::sendMultipleRequests;
using TestControl::sendMultipleMessages6;
using TestControl::sendRequest6;
using TestControl::sendSolicit6;
@ -650,7 +652,134 @@ public:
}
}
/// \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
@ -822,20 +951,41 @@ public:
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.
@ -1432,14 +1582,30 @@ TEST_F(TestControlTest, PacketTemplates) {
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) {