Commit 945a014b authored by Marcin Siodelski's avatar Marcin Siodelski

[3269] Moved common functions from Confirm/Rebind unit tests to new class.

parent c3fab185
......@@ -89,6 +89,7 @@ dhcp6_unittests_SOURCES += sarr_unittest.cc
dhcp6_unittests_SOURCES += ../json_config_parser.cc ../json_config_parser.h
dhcp6_unittests_SOURCES += config_parser_unittest.cc
dhcp6_unittests_SOURCES += confirm_unittest.cc
dhcp6_unittests_SOURCES += dhcp6_message_test.cc dhcp6_message_test.h
if CONFIG_BACKEND_BUNDY
# For Bundy backend, we only need to run the usual tests. There are no
......
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
......@@ -17,8 +17,7 @@
#include <cc/data.h>
#include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcp6/json_config_parser.h>
#include <dhcp6/tests/dhcp6_test_utils.h>
#include <dhcp6/tests/dhcp6_client.h>
#include <dhcp6/tests/dhcp6_message_test.h>
using namespace isc;
using namespace isc::asiolink;
......@@ -82,113 +81,18 @@ const char* CONFIRM_CONFIGS[] = {
};
/// @brief Test fixture class for testing Confirm..
class ConfirmTest : public Dhcpv6SrvTest {
class ConfirmTest : public isc::dhcp::test::Dhcpv6MessageTest {
public:
/// @brief Constructor.
///
/// Sets up fake interfaces.
ConfirmTest()
: Dhcpv6SrvTest(),
iface_mgr_test_config_(true) {
: Dhcpv6MessageTest() {
}
/// @brief Increases last byte of the address.
///
/// This function is helpful to find a different address that is within
/// the same subnet as the input address. It is achieved by increasing
/// the last byte of the input address by one.
///
/// @param input_addr An input address.
///
/// @return New address.
IOAddress bumpAddress(const IOAddress& input_addr);
/// @brief Increases specific byte in the address by one.
///
/// This function is called by @c bumpAddress and @c bumpSubnet.
///
/// @param input_addr An input address.
///
/// @return New address.
IOAddress bumpByteInAddress(const IOAddress& input_addr,
const size_t byte_num);
/// @brief Increases the first byte of the address.
///
/// This function is helpful to find an address which belongs to the
/// different subnet than the input address. It is achived by increasing
/// the first byte of the input address.
///
/// @param input_addr An input addres.
///
/// @return New address.
IOAddress bumpSubnet(const IOAddress& input_addr);
/// @brief Make 4-way exchange to obtain a lease.
///
/// @param config_index Index of the configuration held in @c CONFIRM_CONFIGS
/// to use to configure the server.
/// @param subnets_num Number of subnets being created with the specified
/// configuration.
/// @param client Object representing a test DHCPv6 client to use.
void requestLease(const int config_index, const int subnets_num,
Dhcp6Client& client);
/// @brief Interface Manager's fake configuration control.
IfaceMgrTestConfig iface_mgr_test_config_;
};
void
ConfirmTest::requestLease(const int config_index, const int subnets_num,
Dhcp6Client& client) {
// Check that the index is in the configuration table.
ASSERT_LT(config_index, sizeof(CONFIRM_CONFIGS)/sizeof(CONFIRM_CONFIGS[0]));
// Configure the server.
configure(CONFIRM_CONFIGS[config_index], *client.getServer());
// Make sure we ended-up having expected number of subnets configured.
const Subnet6Collection* subnets = CfgMgr::instance().getSubnets6();
ASSERT_EQ(subnets_num, subnets->size());
// Do the actual 4-way exchange.
ASSERT_NO_THROW(client.doSARR());
// Simulate aging of leases, by moving their cltt_ back by 1000s.
client.fastFwdTime(1000);
// Make sure that we have obtained a lease that belongs to one of the
// subnets.
ASSERT_EQ(1, client.getLeaseNum());
Lease6 lease_client = client.getLease(0);
ASSERT_TRUE(CfgMgr::instance().getSubnet6(lease_client.addr_,
ClientClasses()));
// Check that the client's lease matches the information on the server
// side.
Lease6Ptr lease_server = checkLease(lease_client);
ASSERT_TRUE(lease_server);
// And that status code was OK.
EXPECT_EQ(STATUS_Success, client.getStatusCode(0));
}
IOAddress
ConfirmTest::bumpAddress(const IOAddress& input_addr) {
return (bumpByteInAddress(input_addr, V6ADDRESS_LEN - 1));
}
IOAddress
ConfirmTest::bumpByteInAddress(const IOAddress& input_addr,
const size_t byte_num) {
std::vector<uint8_t> input_addr_buffer = input_addr.toBytes();
if (input_addr_buffer.size() > byte_num) {
++input_addr_buffer[byte_num];
return (IOAddress::fromBytes(AF_INET6, &input_addr_buffer[0]));
}
return (input_addr);
}
IOAddress
ConfirmTest::bumpSubnet(const IOAddress& input_addr) {
return (bumpByteInAddress(input_addr, 0));
}
// Test that directly connected client's Confirm message is processed and Reply
// message is sent back. In this test case, the client sends Confirm for two
......@@ -199,7 +103,7 @@ TEST_F(ConfirmTest, directClientSameIAID) {
// Configure client to request IA_NA.
client.useNA();
// Make 4-way exchange to get the lease.
ASSERT_NO_FATAL_FAILURE(requestLease(0, 2, client));
ASSERT_NO_FATAL_FAILURE(requestLease(CONFIRM_CONFIGS[0], 2, client));
// Keep the client's lease for future reference.
Lease6 lease_client1 = client.getLease(0);
// Clone the lease and modify its address so as it is still in the range
......@@ -241,7 +145,7 @@ TEST_F(ConfirmTest, directClientDifferentIAID) {
// Configure client to request IA_NA.
client.useNA();
// Make 4-way exchange to get the lease.
ASSERT_NO_FATAL_FAILURE(requestLease(0, 2, client));
ASSERT_NO_FATAL_FAILURE(requestLease(CONFIRM_CONFIGS[0], 2, client));
// Keep the client's lease for future reference.
Lease6 lease_client1 = client.getLease(0);
// Clone the lease and modify its address so as it is still in the range
......@@ -288,7 +192,7 @@ TEST_F(ConfirmTest, relayedClient) {
// Configure client to request IA_NA.
client.useNA();
// Make 4-way exchange to get the lease.
ASSERT_NO_FATAL_FAILURE(requestLease(1, 2, client));
ASSERT_NO_FATAL_FAILURE(requestLease(CONFIRM_CONFIGS[1], 2, client));
// Keep the client's lease for future reference.
Lease6 lease_client1 = client.getLease(0);
// Clone the lease and modify its address so as it is still in the range
......@@ -345,7 +249,7 @@ TEST_F(ConfirmTest, relayedUnicast) {
// Configure client to request IA_NA.
client.useNA();
// Make 4-way exchange to get the lease.
ASSERT_NO_FATAL_FAILURE(requestLease(1, 2, client));
ASSERT_NO_FATAL_FAILURE(requestLease(CONFIRM_CONFIGS[1], 2, client));
// Make sure we have got the lease.
ASSERT_GT(client.getLeaseNum(), 0);
client.setDestAddress(IOAddress("2001:db8:1::1"));
......@@ -368,7 +272,7 @@ TEST_F(ConfirmTest, unicast) {
// Configure client to request IA_NA.
client.useNA();
// Make 4-way exchange to get the lease.
ASSERT_NO_FATAL_FAILURE(requestLease(0, 2, client));
ASSERT_NO_FATAL_FAILURE(requestLease(CONFIRM_CONFIGS[0], 2, client));
// Make sure the client has got the lease.
ASSERT_GT(client.getLeaseNum(), 0);
// Send Confirm message to the server to the unicast address.
......
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <dhcp6/tests/dhcp6_message_test.h>
using namespace isc::asiolink;
namespace isc {
namespace dhcp {
namespace test {
Dhcpv6MessageTest::Dhcpv6MessageTest()
: Dhcpv6SrvTest(),
iface_mgr_test_config_(true) {
}
IOAddress
Dhcpv6MessageTest::bumpAddress(const IOAddress& input_addr) {
return (bumpByteInAddress(input_addr, V6ADDRESS_LEN - 1));
}
IOAddress
Dhcpv6MessageTest::bumpByteInAddress(const IOAddress& input_addr,
const size_t byte_num) {
std::vector<uint8_t> input_addr_buffer = input_addr.toBytes();
if (input_addr_buffer.size() > byte_num) {
++input_addr_buffer[byte_num];
return (IOAddress::fromBytes(AF_INET6, &input_addr_buffer[0]));
}
return (input_addr);
}
IOAddress
Dhcpv6MessageTest::bumpSubnet(const IOAddress& input_addr) {
return (bumpByteInAddress(input_addr, 0));
}
void
Dhcpv6MessageTest::requestLease(const std::string& config,
const int subnets_num,
Dhcp6Client& client) {
// Configure the server.
configure(config, *client.getServer());
// Make sure we ended-up having expected number of subnets configured.
const Subnet6Collection* subnets = CfgMgr::instance().getSubnets6();
ASSERT_EQ(subnets_num, subnets->size());
// Do the actual 4-way exchange.
ASSERT_NO_THROW(client.doSARR());
// Simulate aging of leases, by moving their cltt_ back by 1000s.
client.fastFwdTime(1000);
// Make sure that we have obtained a lease that belongs to one of the
// subnets.
ASSERT_EQ(1, client.getLeaseNum());
Lease6 lease_client = client.getLease(0);
ASSERT_TRUE(CfgMgr::instance().getSubnet6(lease_client.addr_,
ClientClasses()));
// Check that the client's lease matches the information on the server
// side.
Lease6Ptr lease_server = checkLease(lease_client);
ASSERT_TRUE(lease_server);
// And that status code was OK.
EXPECT_EQ(STATUS_Success, client.getStatusCode(0));
}
}
}
}
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef DHCP6_MESSAGE_TEST_H
#define DHCP6_MESSAGE_TEST_H
#include <asiolink/io_address.h>
#include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcp6/tests/dhcp6_client.h>
#include <dhcp6/tests/dhcp6_test_utils.h>
namespace isc {
namespace dhcp {
namespace test {
/// @brief Base class for test fixure classes used to validate the DHCPv6
/// message processing by the server.
class Dhcpv6MessageTest : public isc::test::Dhcpv6SrvTest {
public:
/// @brief Constructor.
///
/// Sets up interfaces.
Dhcpv6MessageTest();
/// @brief Increases last byte of the address.
///
/// This function is helpful to find a different address that is within
/// the same subnet as the input address. It is achieved by increasing
/// the last byte of the input address by one.
///
/// @param input_addr An input address.
///
/// @return New address.
asiolink::IOAddress bumpAddress(const asiolink::IOAddress& input_addr);
/// @brief Increases specific byte in the address by one.
///
/// This function is called by @c bumpAddress and @c bumpSubnet.
///
/// @param input_addr An input address.
///
/// @return New address.
asiolink::IOAddress bumpByteInAddress(const asiolink::IOAddress& input_addr,
const size_t byte_num);
/// @brief Increases the first byte of the address.
///
/// This function is helpful to find an address which belongs to the
/// different subnet than the input address. It is achived by increasing
/// the first byte of the input address.
///
/// @param input_addr An input addres.
///
/// @return New address.
asiolink::IOAddress bumpSubnet(const asiolink::IOAddress& input_addr);
/// @brief Make 4-way exchange to obtain a lease.
///
/// @param config Configuration in the JSON format to be applied before the
/// lease is requested..
/// @param subnets_num Number of subnets being created with the specified
/// configuration.
/// @param client Object representing a test DHCPv6 client to use.
void requestLease(const std::string& config, const int subnets_num,
Dhcp6Client& client);
protected:
/// @brief Interface Manager's fake configuration control.
IfaceMgrTestConfig iface_mgr_test_config_;
};
} // isc::dhcp::test
} // isc::dhcp
} // isc
#endif // DHCP6_MESSAGE_TEST_H
......@@ -17,8 +17,7 @@
#include <cc/data.h>
#include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcp6/json_config_parser.h>
#include <dhcp6/tests/dhcp6_test_utils.h>
#include <dhcp6/tests/dhcp6_client.h>
#include <dhcp6/tests/dhcp6_message_test.h>
using namespace isc;
using namespace isc::asiolink;
......@@ -197,60 +196,17 @@ const char* REBIND_CONFIGS[] = {
};
/// @brief Test fixture class for testing Rebind.
class RebindTest : public Dhcpv6SrvTest {
class RebindTest : public Dhcpv6MessageTest {
public:
/// @brief Constructor.
///
/// Sets up fake interfaces.
RebindTest()
: Dhcpv6SrvTest(),
iface_mgr_test_config_(true) {
: Dhcpv6MessageTest() {
}
/// @brief Make 4-way exchange to obtain a lease.
///
/// @param config_index Index of the configuration held in @c REBIND_CONFIGS
/// to use to configure the server.
/// @param subnets_num Number of subnets being created with the specified
/// configuration.
/// @param client Object representing a test DHCPv6 client to use.
void requestLease(const int config_index, const int subnets_num,
Dhcp6Client& client);
/// @brief Interface Manager's fake configuration control.
IfaceMgrTestConfig iface_mgr_test_config_;
};
void
RebindTest::requestLease(const int config_index, const int subnets_num,
Dhcp6Client& client) {
// Check that the index is in the configuration table.
ASSERT_LT(config_index, sizeof(REBIND_CONFIGS)/sizeof(REBIND_CONFIGS[0]));
// Configure the server.
configure(REBIND_CONFIGS[config_index], *client.getServer());
// Make sure we ended-up having expected number of subnets configured.
const Subnet6Collection* subnets = CfgMgr::instance().getSubnets6();
ASSERT_EQ(subnets_num, subnets->size());
// Do the actual 4-way exchange.
ASSERT_NO_THROW(client.doSARR());
// Simulate aging of leases, by moving their cltt_ back by 1000s.
client.fastFwdTime(1000);
// Make sure that we have obtained a lease that belongs to one of the
// subnets.
ASSERT_EQ(1, client.getLeaseNum());
Lease6 lease_client = client.getLease(0);
ASSERT_TRUE(CfgMgr::instance().getSubnet6(lease_client.addr_,
ClientClasses()));
// Check that the client's lease matches the information on the server
// side.
Lease6Ptr lease_server = checkLease(lease_client);
ASSERT_TRUE(lease_server);
// And that status code was OK.
EXPECT_EQ(STATUS_Success, client.getStatusCode(0));
}
// Test that directly connected client's Rebind message is processed and Reply
// message is sent back.
TEST_F(RebindTest, directClient) {
......@@ -258,7 +214,7 @@ TEST_F(RebindTest, directClient) {
// Configure client to request IA_NA.
client.useNA();
// Make 4-way exchange to get the lease.
ASSERT_NO_FATAL_FAILURE(requestLease(0, 2, client));
ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[0], 2, client));
// Keep the client's lease for future reference.
Lease6 lease_client = client.getLease(0);
// Send Rebind message to the server.
......@@ -285,7 +241,7 @@ TEST_F(RebindTest, directClientChangingSubnet) {
// Configure client to request IA_NA.
client.useNA();
// Make 4-way exchange to get the lease.
ASSERT_NO_FATAL_FAILURE(requestLease(0, 2, client));
ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[0], 2, client));
// Keep the client's lease for future reference.
Lease6 lease_client = client.getLease(0);
// Reconfigure the server so as the new subnet is served on the
......@@ -319,7 +275,7 @@ TEST_F(RebindTest, directClientChangingIAID) {
// Configure client to request IA_NA.
client.useNA();
// Make 4-way exchange to get the lease.
ASSERT_NO_FATAL_FAILURE(requestLease(0, 2, client));
ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[0], 2, client));
// Keep the client's lease for future reference.
Lease6 lease_client = client.getLease(0);
// Modify the IAID of the lease record that client stores. By adding
......@@ -346,7 +302,7 @@ TEST_F(RebindTest, directClientLostLease) {
// Configure client to request IA_NA.
client.useNA();
// Make 4-way exchange to get the lease.
ASSERT_NO_FATAL_FAILURE(requestLease(0, 2, client));
ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[0], 2, client));
// Keep the client's lease for future reference.
Lease6 lease_client = client.getLease(0);
// The lease has been acquired. Now, let's explicitly remove it from the
......@@ -372,7 +328,7 @@ TEST_F(RebindTest, relayedClient) {
client.useRelay();
// Make 4-way exchange to get the lease. Pick the configuration #2 as it
// specifies the subnet for the relay agent's link address.
ASSERT_NO_FATAL_FAILURE(requestLease(2, 2, client));
ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[2], 2, client));
// Keep the client's lease for future reference.
Lease6 lease_client = client.getLease(0);
// Send Rebind message to the server.
......@@ -404,7 +360,7 @@ TEST_F(RebindTest, relayedClientChangingSubnet) {
// by the server to pick the suitable subnet.
client.useRelay();
// Make 4-way exchange to get the lease.
ASSERT_NO_FATAL_FAILURE(requestLease(2, 2, client));
ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[2], 2, client));
// Keep the client's lease for future reference.
Lease6 lease_client = client.getLease(0);
// Reconfigure the server so as the new subnet is served on the
......@@ -444,7 +400,7 @@ TEST_F(RebindTest, relayedClientChangingIAID) {
// by the server to pick the suitable subnet.
client.useRelay();
// Make 4-way exchange to get the lease.
ASSERT_NO_FATAL_FAILURE(requestLease(2, 2, client));
ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[2], 2, client));
// Keep the client's lease for future reference.
Lease6 lease_client = client.getLease(0);
// Modify the IAID of the lease record that client stores. By adding
......@@ -475,7 +431,7 @@ TEST_F(RebindTest, relayedClientLostLease) {
// by the server to pick the suitable subnet.
client.useRelay();
// Make 4-way exchange to get the lease.
ASSERT_NO_FATAL_FAILURE(requestLease(2, 2, client));
ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[2], 2, client));
// Keep the client's lease for future reference.
Lease6 lease_client = client.getLease(0);
// The lease has been acquired. Now, let's explicitly remove it from the
......@@ -495,7 +451,7 @@ TEST_F(RebindTest, relayedClientChangingAddress) {
// Configure client to request IA_NA.
client.useNA();
// Make 4-way exchange to get the lease.
ASSERT_NO_FATAL_FAILURE(requestLease(2, 2, client));
ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[2], 2, client));
// Keep the client's lease for future reference.
Lease6 lease_client = client.getLease(0);
// Modify the address of the lease record that client stores. The server
......@@ -533,7 +489,7 @@ TEST_F(RebindTest, directClientPD) {
// Configure client to request IA_PD.
client.usePD();
// Make 4-way exchange to get the lease.
ASSERT_NO_FATAL_FAILURE(requestLease(4, 2, client));
ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[4], 2, client));
// Keep the client's lease for future reference.
Lease6 lease_client = client.getLease(0);
// Send Rebind message to the server.
......@@ -561,7 +517,7 @@ TEST_F(RebindTest, directClientPDChangingSubnet) {
// Configure client to request IA_PD.
client.usePD();
// Make 4-way exchange to get the lease.
ASSERT_NO_FATAL_FAILURE(requestLease(4, 2, client));
ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[4], 2, client));
// Keep the client's lease for future reference.
Lease6 lease_client = client.getLease(0);
// Reconfigure the server so as the new subnet is served on the
......@@ -598,7 +554,7 @@ TEST_F(RebindTest, directClientPDChangingIAID) {
// Configure client to request IA_PD.
client.usePD();
// Make 4-way exchange to get the lease.
ASSERT_NO_FATAL_FAILURE(requestLease(4, 2, client));
ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[4], 2, client));
// Keep the client's lease for future reference.
Lease6 lease_client = client.getLease(0);
// Modify the IAID of the lease record that client stores. By adding
......@@ -628,7 +584,7 @@ TEST_F(RebindTest, directClientPDChangingPrefix) {
// Configure client to request IA_PD.
client.usePD();
// Make 4-way exchange to get the lease.
ASSERT_NO_FATAL_FAILURE(requestLease(4, 2, client));
ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[4], 2, client));
// Keep the client's lease for future reference.
Lease6 lease_client = client.getLease(0);
// Modify the Prefix of the lease record that client stores. The server
......@@ -674,7 +630,7 @@ TEST_F(RebindTest, unicast) {
// Configure client to request IA_NA.
client.useNA();
// Make 4-way exchange to get the lease.
ASSERT_NO_FATAL_FAILURE(requestLease(0, 2, client));
ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[0], 2, client));
// Keep the client's lease for future reference.
Lease6 lease_client = client.getLease(0);
// Set the unicast destination address for the Rebind message.
......@@ -708,7 +664,7 @@ TEST_F(RebindTest, relayedUnicast) {
client.useRelay();
// Make 4-way exchange to get the lease. Pick the configuration #2 as it
// specifies the subnet for the relay agent's link address.
ASSERT_NO_FATAL_FAILURE(requestLease(2, 2, client));
ASSERT_NO_FATAL_FAILURE(requestLease(REBIND_CONFIGS[2], 2, client));
// Keep the client's lease for future reference.
Lease6 lease_client = client.getLease(0);
// Set the unicast destination address.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment