Files
openthread/tests/unit/test_dhcp6_pd_client.cpp
Jonathan HuiandGitHub cf24e583f5 [infra-if] free DHCPv6 message on error in HandleDhcp6Received (#13536)
This commit fixes a message leak in `InfraIf::HandleDhcp6Received()`
when incoming DHCPv6 messages are rejected.

`otPlatInfraIfDhcp6PdClientHandleReceived()` transfers ownership of the
received `aMessage` to OpenThread. Previously, early validation guards
in `InfraIf::HandleDhcp6Received()` (when the interface is not running
or when the interface index does not match) jumped to `exit:` without
freeing `aMessage`. Because DHCPv6 listening remains enabled while a
lease is held, bringing down the infrastructure interface caused any
subsequent incoming DHCPv6 packets delivered by the socket to be
dropped without being freed, resulting in leaked message buffers.

This change ensures `aMessage.Free()` is called whenever an error
occurs in `InfraIf::HandleDhcp6Received()`. It also updates unit tests
to verify that message buffers are not leaked when packets are
received while the infra interface is down or has a mismatched
interface index.
2026-08-22 15:43:31 -07:00

2730 lines
95 KiB
C++

/*
* Copyright (c) 2025, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <openthread/config.h>
#include "test_platform.h"
#include "test_util.hpp"
#include <openthread/dataset_ftd.h>
#include <openthread/thread.h>
#include <openthread/platform/border_routing.h>
#include <openthread/platform/infra_if.h>
#include "border_router/dhcp6_pd_client.hpp"
#include "common/arg_macros.hpp"
#include "common/array.hpp"
#include "common/num_utils.hpp"
#include "instance/instance.hpp"
namespace ot {
#if OT_CONFIG_DHCP6_PD_CLIENT_ENABLE
// Logs a message and adds current time (sNow) as "<hours>:<min>:<secs>.<msec>"
#define Log(...) \
printf("%02u:%02u:%02u.%03u " OT_FIRST_ARG(__VA_ARGS__) "\n", (sNow / 3600000), (sNow / 60000) % 60, \
(sNow / 1000) % 60, sNow % 1000 OT_REST_ARGS(__VA_ARGS__))
static constexpr uint32_t kInfraIfIndex = 1;
static Instance *sInstance;
static uint32_t sNow = 0;
static uint32_t sAlarmTime;
static bool sAlarmOn = false;
//----------------------------------------------------------------------------------------------------------------------
// Function prototypes
void ProcessTasklets(void);
void AdvanceTime(uint32_t aDuration);
void AdvanceNowTo(uint32_t aNewNow);
extern "C" {
#if OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED
#if OPENTHREAD_CONFIG_LOG_INSTANCE_AWARE_API_ENABLE
void otPlatLogOutput(otInstance *, otLogLevel, const char *aLogLine) { printf(" %s\n", aLogLine); }
#else
void otPlatLog(otLogLevel, otLogRegion, const char *aFormat, ...)
{
va_list args;
printf(" ");
va_start(args, aFormat);
vprintf(aFormat, args);
va_end(args);
printf("\n");
}
#endif
#endif
//----------------------------------------------------------------------------------------------------------------------
// otPlatAlarams
void otPlatAlarmMilliStop(otInstance *) { sAlarmOn = false; }
void otPlatAlarmMilliStartAt(otInstance *, uint32_t aT0, uint32_t aDt)
{
sAlarmOn = true;
sAlarmTime = aT0 + aDt;
}
uint32_t otPlatAlarmMilliGetNow(void) { return sNow; }
//----------------------------------------------------------------------------------------------------------------------
// Heap
Array<void *, 500> sHeapAllocatedPtrs;
#if OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE
void *otPlatCAlloc(size_t aNum, size_t aSize)
{
void *ptr = calloc(aNum, aSize);
SuccessOrQuit(sHeapAllocatedPtrs.PushBack(ptr));
return ptr;
}
void otPlatFree(void *aPtr)
{
if (aPtr != nullptr)
{
void **entry = sHeapAllocatedPtrs.Find(aPtr);
VerifyOrQuit(entry != nullptr, "A heap allocated item is freed twice");
sHeapAllocatedPtrs.Remove(*entry);
}
free(aPtr);
}
#endif
} // extern "C"
//---------------------------------------------------------------------------------------------------------------------
// Dhcp6Msg
struct Dhcp6Msg : public Clearable<Dhcp6Msg>
{
static constexpr uint16_t kMaxIaPds = 2;
static constexpr uint16_t kMaxIaPrefixes = 3;
static constexpr uint16_t kMaxReqOptions = 3;
static constexpr uint16_t kInfoStringSize = 100;
typedef String<kInfoStringSize> InfoString;
struct IaPrefix : public Clearable<IaPrefix>
{
Ip6::Prefix mPrefix;
uint32_t mPreferredLifetime;
uint32_t mValidLifetime;
};
using IaPrefixArray = Array<IaPrefix, kMaxIaPrefixes>;
struct IaPd : Clearable<IaPd>
{
bool Matches(uint32_t aIaid) const { return mIaid == aIaid; }
uint32_t mIaid;
uint32_t mT1;
uint32_t mT2;
bool mHasStatus;
uint16_t mStatusCode;
IaPrefixArray mIaPrefixes;
};
using IaPdArray = Array<IaPd, kMaxIaPds>;
struct Duid
{
InfoString ToString(void) const
{
InfoString string;
string.AppendHexBytes(mShared.mBytes, mLength);
return string;
}
union
{
uint8_t mBytes[Dhcp6::Duid::kMaxSize];
Dhcp6::Eui64Duid mEui64;
} mShared;
uint16_t mLength;
};
using ReqOptionArray = Array<uint16_t, kMaxReqOptions>;
void LogMsg(const char *aAction) const;
void ParseFrom(const Message &aMessage);
void PrepareMessage(Message &aMessage);
uint8_t mMsgType;
Dhcp6::TransactionId mTransactionId;
bool mHasStatus : 1;
bool mHasElapsedTime : 1;
bool mHasClientId : 1;
bool mHasServerId : 1;
bool mHasOptionRequest : 1;
bool mHasPreference : 1;
bool mHasSolMaxRt : 1;
uint16_t mStatusCode;
uint16_t mElapsedTime;
Duid mClientDuid;
Duid mServerDuid;
ReqOptionArray mRequestedOptions;
uint8_t mPreference;
uint16_t mSolMaxRt;
IaPdArray mIaPds;
};
static const char *Dhcp6MsgTypeToString(uint8_t aMsgType)
{
const char *str = "Unknown";
switch (aMsgType)
{
case Dhcp6::kMsgTypeSolicit:
str = "Solicit";
break;
case Dhcp6::kMsgTypeAdvertise:
str = "Advertise";
break;
case Dhcp6::kMsgTypeRequest:
str = "Request";
break;
case Dhcp6::kMsgTypeRenew:
str = "Renew";
break;
case Dhcp6::kMsgTypeRebind:
str = "Rebind";
break;
case Dhcp6::kMsgTypeReply:
str = "Reply";
break;
case Dhcp6::kMsgTypeRelease:
str = "Release";
break;
case Dhcp6::kMsgTypeReconfigure:
str = "Reconfigure";
break;
}
return str;
}
void Dhcp6Msg::LogMsg(const char *aAction) const
{
const uint8_t *txnId = reinterpret_cast<const uint8_t *>(&mTransactionId);
Log("%s %s message", aAction, Dhcp6MsgTypeToString(mMsgType));
Log(" %-13s : %02x%02x%02x", "TransactionId", txnId[0], txnId[1], txnId[2]);
if (mHasStatus)
{
Log(" %-13s : %u", "StatusCode", mStatusCode);
}
if (mHasElapsedTime)
{
Log(" %-13s : %u", "ElapsedTime", mElapsedTime);
}
if (mHasClientId)
{
Log(" %-13s : %s", "ClientId", mClientDuid.ToString().AsCString());
}
if (mHasServerId)
{
Log(" %-13s : %s", "ServerId", mServerDuid.ToString().AsCString());
}
if (mHasOptionRequest)
{
InfoString string;
string.Append("[ ");
for (uint16_t optionCode : mRequestedOptions)
{
string.Append("%u ", optionCode);
}
string.Append("]");
Log(" %-13s : %s", "ReqOptions", string.AsCString());
}
if (mHasPreference)
{
Log(" %-13s : %u", "Preference", mPreference);
}
if (mHasSolMaxRt)
{
Log(" %-13s : %lu", "SolMaxRt", ToUlong(mSolMaxRt));
}
for (const IaPd &iaPd : mIaPds)
{
Log(" %-13s : Iaid:%lu, T1:%lu, T2:%lu", "IaPd", ToUlong(iaPd.mIaid), ToUlong(iaPd.mT1), ToUlong(iaPd.mT2));
if (iaPd.mHasStatus)
{
Log(" %-11s : %u", "StatusCode", iaPd.mStatusCode);
}
for (const IaPrefix &iaPrefix : iaPd.mIaPrefixes)
{
Log(" %-11s : %s, preferred:%lu, valid:%lu", "Prefix", iaPrefix.mPrefix.ToString().AsCString(),
ToUlong(iaPrefix.mPreferredLifetime), ToUlong(iaPrefix.mValidLifetime));
}
}
}
void Dhcp6Msg::ParseFrom(const Message &aMessage)
{
OffsetRange offsetRange;
OffsetRange optionOffsetRange;
OffsetRange subOptionOffsetRange;
Dhcp6::Header header;
IaPd *iaPd;
IaPrefix *iaPrefix;
union
{
Dhcp6::Option option;
Dhcp6::StatusCodeOption statusOption;
Dhcp6::ElapsedTimeOption elapsedTimeOption;
Dhcp6::PreferenceOption preferenceOption;
Dhcp6::SolMaxRtOption solMaxRtOption;
Dhcp6::IaPdOption iaPdOption;
Dhcp6::IaPrefixOption iaPrefixOption;
};
Clear();
offsetRange.InitFromMessageFullLength(aMessage);
SuccessOrQuit(aMessage.ReadAndAdvance(offsetRange, header));
mMsgType = header.GetMsgType();
mTransactionId = header.GetTransactionId();
while (!offsetRange.IsEmpty())
{
SuccessOrQuit(aMessage.Read(offsetRange, option));
VerifyOrQuit(offsetRange.Contains(option.GetSize()));
optionOffsetRange = offsetRange;
optionOffsetRange.ShrinkLength(static_cast<uint16_t>(option.GetSize()));
offsetRange.AdvanceOffset(option.GetSize());
switch (option.GetCode())
{
case Dhcp6::Option::kStatusCode:
VerifyOrQuit(!mHasStatus);
mHasStatus = true;
SuccessOrQuit(aMessage.Read(optionOffsetRange, statusOption));
mStatusCode = statusOption.GetStatusCode();
break;
case Dhcp6::Option::kElapsedTime:
VerifyOrQuit(!mHasElapsedTime);
mHasElapsedTime = true;
SuccessOrQuit(aMessage.Read(optionOffsetRange, elapsedTimeOption));
mElapsedTime = elapsedTimeOption.GetElapsedTime();
break;
case Dhcp6::Option::kClientId:
VerifyOrQuit(!mHasClientId);
mHasClientId = true;
optionOffsetRange.AdvanceOffset(sizeof(Dhcp6::Option));
VerifyOrQuit(optionOffsetRange.GetLength() >= Dhcp6::Duid::kMinSize);
VerifyOrQuit(optionOffsetRange.GetLength() <= Dhcp6::Duid::kMaxSize);
mClientDuid.mLength = optionOffsetRange.GetLength();
aMessage.ReadBytes(optionOffsetRange, mClientDuid.mShared.mBytes);
break;
case Dhcp6::Option::kServerId:
VerifyOrQuit(!mHasServerId);
mHasServerId = true;
optionOffsetRange.AdvanceOffset(sizeof(Dhcp6::Option));
VerifyOrQuit(optionOffsetRange.GetLength() >= Dhcp6::Duid::kMinSize);
VerifyOrQuit(optionOffsetRange.GetLength() <= Dhcp6::Duid::kMaxSize);
mServerDuid.mLength = optionOffsetRange.GetLength();
aMessage.ReadBytes(optionOffsetRange, mServerDuid.mShared.mBytes);
break;
case Dhcp6::Option::kOptionRequest:
VerifyOrQuit(!mHasOptionRequest);
mHasOptionRequest = true;
optionOffsetRange.AdvanceOffset(sizeof(Dhcp6::Option));
VerifyOrQuit(optionOffsetRange.GetLength() > 0);
while (!optionOffsetRange.IsEmpty())
{
uint16_t reqOption;
SuccessOrQuit(aMessage.Read(optionOffsetRange, reqOption));
reqOption = BigEndian::HostSwap16(reqOption);
SuccessOrQuit(mRequestedOptions.PushBack(reqOption));
optionOffsetRange.AdvanceOffset(sizeof(uint16_t));
}
break;
case Dhcp6::Option::kPreference:
VerifyOrQuit(!mHasPreference);
mHasPreference = true;
SuccessOrQuit(aMessage.Read(optionOffsetRange, preferenceOption));
mPreference = preferenceOption.GetPreference();
break;
case Dhcp6::Option::kSolMaxRt:
VerifyOrQuit(!mHasSolMaxRt);
mHasSolMaxRt = true;
SuccessOrQuit(aMessage.Read(optionOffsetRange, solMaxRtOption));
mSolMaxRt = solMaxRtOption.GetSolMaxRt();
break;
case Dhcp6::Option::kIaPd:
SuccessOrQuit(aMessage.ReadAndAdvance(optionOffsetRange, iaPdOption));
VerifyOrQuit(!mIaPds.ContainsMatching(iaPdOption.GetIaid()));
iaPd = mIaPds.PushBack();
VerifyOrQuit(iaPd != nullptr);
iaPd->Clear();
iaPd->mIaid = iaPdOption.GetIaid();
iaPd->mT1 = iaPdOption.GetT1();
iaPd->mT2 = iaPdOption.GetT2();
while (!optionOffsetRange.IsEmpty())
{
SuccessOrQuit(aMessage.Read(optionOffsetRange, option));
VerifyOrQuit(optionOffsetRange.Contains(option.GetSize()));
subOptionOffsetRange = optionOffsetRange;
subOptionOffsetRange.ShrinkLength(static_cast<uint16_t>(option.GetSize()));
optionOffsetRange.AdvanceOffset(option.GetSize());
switch (option.GetCode())
{
case Dhcp6::Option::kStatusCode:
VerifyOrQuit(!iaPd->mHasStatus);
iaPd->mHasStatus = true;
SuccessOrQuit(aMessage.Read(subOptionOffsetRange, statusOption));
iaPd->mStatusCode = statusOption.GetStatusCode();
break;
case Dhcp6::Option::kIaPrefix:
SuccessOrQuit(aMessage.Read(subOptionOffsetRange, iaPrefixOption));
iaPrefix = iaPd->mIaPrefixes.PushBack();
VerifyOrQuit(iaPrefix != nullptr);
iaPrefix->Clear();
iaPrefixOption.GetPrefix(iaPrefix->mPrefix);
iaPrefix->mPreferredLifetime = iaPrefixOption.GetPreferredLifetime();
iaPrefix->mValidLifetime = iaPrefixOption.GetValidLifetime();
break;
default:
// Unexpected sub-option
VerifyOrQuit(false);
break;
}
}
break;
default:
// Unexpected option
VerifyOrQuit(false);
break;
}
}
}
void Dhcp6Msg::PrepareMessage(Message &aMessage)
{
Dhcp6::Header header;
union
{
Dhcp6::Option option;
Dhcp6::StatusCodeOption statusOption;
Dhcp6::ElapsedTimeOption elapsedTimeOption;
Dhcp6::PreferenceOption preferenceOption;
Dhcp6::SolMaxRtOption solMaxRtOption;
Dhcp6::IaPdOption iaPdOption;
Dhcp6::IaPrefixOption iaPrefixOption;
};
header.SetMsgType(static_cast<Dhcp6::MsgType>(mMsgType));
header.SetTransactionId(mTransactionId);
SuccessOrQuit(aMessage.Append(header));
if (mHasStatus)
{
statusOption.Init();
statusOption.SetStatusCode(static_cast<Dhcp6::StatusCodeOption::Status>(mStatusCode));
SuccessOrQuit(aMessage.Append(statusOption));
}
if (mHasElapsedTime)
{
elapsedTimeOption.Init();
elapsedTimeOption.SetElapsedTime(mElapsedTime);
SuccessOrQuit(aMessage.Append(elapsedTimeOption));
}
if (mHasClientId)
{
SuccessOrQuit(Dhcp6::Option::AppendOption(aMessage, Dhcp6::Option::kClientId, mClientDuid.mShared.mBytes,
mClientDuid.mLength));
}
if (mHasServerId)
{
SuccessOrQuit(Dhcp6::Option::AppendOption(aMessage, Dhcp6::Option::kServerId, mServerDuid.mShared.mBytes,
mServerDuid.mLength));
}
if (mHasOptionRequest)
{
option.SetCode(Dhcp6::Option::kOptionRequest);
option.SetLength(mRequestedOptions.GetLength() * sizeof(uint16_t));
SuccessOrQuit(aMessage.Append(option));
for (uint16_t reqOption : mRequestedOptions)
{
uint16_t swapedOption = BigEndian::HostSwap16(reqOption);
SuccessOrQuit(aMessage.Append(swapedOption));
}
}
if (mHasPreference)
{
preferenceOption.Init();
preferenceOption.SetPreference(mPreference);
SuccessOrQuit(aMessage.Append(preferenceOption));
}
if (mHasSolMaxRt)
{
solMaxRtOption.Init();
solMaxRtOption.SetSolMaxRt(mSolMaxRt);
SuccessOrQuit(aMessage.Append(solMaxRtOption));
}
for (const IaPd &iaPd : mIaPds)
{
uint16_t length;
length = sizeof(Dhcp6::IaPdOption) - sizeof(Dhcp6::Option);
if (mHasStatus)
{
length += sizeof(Dhcp6::StatusCodeOption);
}
length += sizeof(Dhcp6::IaPrefixOption) * iaPd.mIaPrefixes.GetLength();
iaPdOption.Init();
iaPdOption.SetLength(length);
iaPdOption.SetIaid(iaPd.mIaid);
iaPdOption.SetT1(iaPd.mT1);
iaPdOption.SetT2(iaPd.mT2);
SuccessOrQuit(aMessage.Append(iaPdOption));
if (mHasStatus)
{
statusOption.Init();
statusOption.SetStatusCode(static_cast<Dhcp6::StatusCodeOption::Status>(iaPd.mStatusCode));
SuccessOrQuit(aMessage.Append(statusOption));
}
for (const IaPrefix &iaPrefix : iaPd.mIaPrefixes)
{
iaPrefixOption.Init();
iaPrefixOption.SetPreferredLifetime(iaPrefix.mPreferredLifetime);
iaPrefixOption.SetValidLifetime(iaPrefix.mValidLifetime);
iaPrefixOption.SetPrefix(iaPrefix.mPrefix);
SuccessOrQuit(aMessage.Append(iaPrefixOption));
}
}
}
//----------------------------------------------------------------------------------------------------------------------
// Dhcp6RxMsg
static const uint32_t kExpectedIaid = 0;
static const uint8_t kExpectedPrefixLength = 64;
static const uint16_t kMaxDhcp6RxMsgs = 32;
class Dhcp6RxMsg : public Dhcp6Msg
{
public:
void ValidateAsSolicit(void) const;
void ValidateAsRequest(const Ip6::Prefix &aPrefix, const Mac::ExtAddress &aServerMacAddr) const;
void ValidateAsRenew(const Ip6::Prefix &aPrefix, const Mac::ExtAddress &aServerMacAddr) const;
void ValidateAsRebind(const Ip6::Prefix &aPrefix) const;
void ValidateAsRelease(const Ip6::Prefix &aPrefix, const Mac::ExtAddress &aServerMacAddr) const;
uint32_t mRxTime;
Ip6::Address mDstAddr;
private:
void Validate(Dhcp6::MsgType aMsgType,
const Ip6::Prefix &aPrefix,
const Mac::ExtAddress *aServerMacAddr = nullptr) const;
};
Ip6::Prefix PrefixFromString(const char *aString, uint8_t aPrefixLength)
{
Ip6::Prefix prefix;
SuccessOrQuit(AsCoreType(&prefix.mPrefix).FromString(aString));
prefix.mLength = aPrefixLength;
return prefix;
}
Ip6::Address AddressFromString(const char *aString)
{
Ip6::Address address;
SuccessOrQuit(address.FromString(aString));
return address;
}
void Dhcp6RxMsg::ValidateAsSolicit(void) const
{
Ip6::Prefix prefix;
VerifyOrQuit(mMsgType == Dhcp6::kMsgTypeSolicit);
VerifyOrQuit(mDstAddr == AddressFromString("ff02::1:2"));
prefix.Clear();
prefix.SetLength(kExpectedPrefixLength);
Validate(Dhcp6::kMsgTypeSolicit, prefix);
}
void Dhcp6RxMsg::ValidateAsRequest(const Ip6::Prefix &aPrefix, const Mac::ExtAddress &aServerMacAddr) const
{
Validate(Dhcp6::kMsgTypeRequest, aPrefix, &aServerMacAddr);
}
void Dhcp6RxMsg::ValidateAsRenew(const Ip6::Prefix &aPrefix, const Mac::ExtAddress &aServerMacAddr) const
{
Validate(Dhcp6::kMsgTypeRenew, aPrefix, &aServerMacAddr);
}
void Dhcp6RxMsg::ValidateAsRebind(const Ip6::Prefix &aPrefix) const { Validate(Dhcp6::kMsgTypeRebind, aPrefix); }
void Dhcp6RxMsg::ValidateAsRelease(const Ip6::Prefix &aPrefix, const Mac::ExtAddress &aServerMacAddr) const
{
Validate(Dhcp6::kMsgTypeRelease, aPrefix, &aServerMacAddr);
}
void Dhcp6RxMsg::Validate(Dhcp6::MsgType aMsgType,
const Ip6::Prefix &aPrefix,
const Mac::ExtAddress *aServerMacAddr) const
{
VerifyOrQuit(mMsgType == aMsgType);
VerifyOrQuit(mDstAddr == AddressFromString("ff02::1:2"));
VerifyOrQuit(!mHasStatus);
VerifyOrQuit(!mHasPreference);
VerifyOrQuit(!mHasSolMaxRt);
VerifyOrQuit(mHasElapsedTime);
VerifyOrQuit(mHasOptionRequest);
VerifyOrQuit(mRequestedOptions.GetLength() == 1);
VerifyOrQuit(mRequestedOptions[0] == Dhcp6::Option::kSolMaxRt);
VerifyOrQuit(mHasClientId);
VerifyOrQuit(mClientDuid.mShared.mEui64.IsValid());
VerifyOrQuit(mClientDuid.mShared.mEui64.GetLinkLayerAddress() == sInstance->Get<Mac::Mac>().GetExtAddress());
if (aServerMacAddr != nullptr)
{
VerifyOrQuit(mHasServerId);
VerifyOrQuit(mServerDuid.mShared.mEui64.IsValid());
VerifyOrQuit(mServerDuid.mShared.mEui64.GetLinkLayerAddress() == *aServerMacAddr);
}
else
{
VerifyOrQuit(!mHasServerId);
}
VerifyOrQuit(mIaPds.GetLength() == 1);
VerifyOrQuit(mIaPds[0].mIaid == kExpectedIaid);
VerifyOrQuit(mIaPds[0].mT1 == 0);
VerifyOrQuit(mIaPds[0].mT2 == 0);
VerifyOrQuit(!mIaPds[0].mHasStatus);
VerifyOrQuit(mIaPds[0].mIaPrefixes.GetLength() == 1);
VerifyOrQuit(mIaPds[0].mIaPrefixes[0].mPrefix == aPrefix);
VerifyOrQuit(mIaPds[0].mIaPrefixes[0].mPreferredLifetime == 0);
VerifyOrQuit(mIaPds[0].mIaPrefixes[0].mValidLifetime == 0);
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
struct Dhcp6TxMsg : public Dhcp6Msg
{
struct PrefixInfo
{
uint16_t mIaid;
uint32_t mT1;
uint32_t mT2;
uint32_t mPreferredLifetime;
uint32_t mValidLifetime;
Ip6::Prefix mPrefix;
};
void PrepareAdvertise(const Dhcp6RxMsg &aClientMsg, const Mac::ExtAddress &aServerMacAddr);
void PrepareReply(const Dhcp6RxMsg &aClientMsg, const Mac::ExtAddress &aServerMacAddr);
void AddIaPrefix(const PrefixInfo &aInfo);
void Send(uint32_t aInfraIfIndex = kInfraIfIndex);
};
void Dhcp6TxMsg::PrepareAdvertise(const Dhcp6RxMsg &aClientMsg, const Mac::ExtAddress &aServerMacAddr)
{
Clear();
mMsgType = Dhcp6::kMsgTypeAdvertise;
mTransactionId = aClientMsg.mTransactionId;
mHasClientId = true;
mHasServerId = true;
mClientDuid = aClientMsg.mClientDuid;
mServerDuid.mShared.mEui64.Init(aServerMacAddr);
mServerDuid.mLength = sizeof(Dhcp6::Eui64Duid);
}
void Dhcp6TxMsg::PrepareReply(const Dhcp6RxMsg &aClientMsg, const Mac::ExtAddress &aServerMacAddr)
{
Clear();
mMsgType = Dhcp6::kMsgTypeReply;
mTransactionId = aClientMsg.mTransactionId;
mHasClientId = true;
mHasServerId = true;
mClientDuid = aClientMsg.mClientDuid;
mServerDuid.mShared.mEui64.Init(aServerMacAddr);
mServerDuid.mLength = sizeof(Dhcp6::Eui64Duid);
}
void Dhcp6TxMsg::AddIaPrefix(const PrefixInfo &aInfo)
{
IaPd *iaPd = nullptr;
IaPrefix *iaPrefix;
for (IaPd &ia : mIaPds)
{
if (ia.mIaid == aInfo.mIaid)
{
iaPd = &ia;
break;
}
}
if (iaPd == nullptr)
{
iaPd = mIaPds.PushBack();
VerifyOrQuit(iaPd != nullptr);
}
iaPd->mIaid = aInfo.mIaid;
iaPd->mT1 = aInfo.mT1;
iaPd->mT2 = aInfo.mT2;
iaPrefix = iaPd->mIaPrefixes.PushBack();
VerifyOrQuit(iaPrefix != nullptr);
iaPrefix->mPrefix = aInfo.mPrefix;
iaPrefix->mPreferredLifetime = aInfo.mPreferredLifetime;
iaPrefix->mValidLifetime = aInfo.mValidLifetime;
}
void Dhcp6TxMsg::Send(uint32_t aInfraIfIndex)
{
Message *message;
message = sInstance->Get<MessagePool>().Allocate(Message::kTypeOther);
VerifyOrQuit(message != nullptr);
PrepareMessage(*message);
LogMsg("Sending");
otPlatInfraIfDhcp6PdClientHandleReceived(sInstance, message, aInfraIfIndex);
}
//----------------------------------------------------------------------------------------------------------------------
// otPlatInfraIf
typedef BorderRouter::Dhcp6PdClient::DelegatedPrefix DelegatedPrefix;
static bool sDhcp6ListeningEnabled = false;
static Array<Dhcp6RxMsg, kMaxDhcp6RxMsgs> sDhcp6RxMsgs;
extern "C" {
void otPlatInfraIfDhcp6PdClientSetListeningEnabled(otInstance *aInstance, bool aEnable, uint32_t aInfraIfIndex)
{
Log("otPlatInfraIfDhcp6PdClientSetListeningEnabled(aEnable:%u)", aEnable);
VerifyOrQuit(aInstance == sInstance);
VerifyOrQuit(aInfraIfIndex == kInfraIfIndex);
sDhcp6ListeningEnabled = aEnable;
}
void otPlatInfraIfDhcp6PdClientSend(otInstance *aInstance,
otMessage *aMessage,
otIp6Address *aDestAddress,
uint32_t aInfraIfIndex)
{
Message *message = AsCoreTypePtr(aMessage);
Dhcp6RxMsg *rxMsg;
VerifyOrQuit(aInstance == sInstance);
VerifyOrQuit(aInfraIfIndex == kInfraIfIndex);
VerifyOrQuit(message != nullptr);
VerifyOrQuit(aDestAddress != nullptr);
Log("otPlatInfraIfDhcp6PdClientSend(%s)", AsCoreType(aDestAddress).ToString().AsCString());
rxMsg = sDhcp6RxMsgs.PushBack();
VerifyOrQuit(rxMsg != nullptr);
rxMsg->ParseFrom(*message);
rxMsg->LogMsg("Received");
rxMsg->mRxTime = sNow;
rxMsg->mDstAddr = AsCoreType(aDestAddress);
message->Free();
}
} // extern "C"
//---------------------------------------------------------------------------------------------------------------------
void ProcessTasklets(void)
{
do
{
otTaskletsProcess(sInstance);
} while (otTaskletsArePending(sInstance));
}
void AdvanceTime(uint32_t aDuration)
{
uint32_t time = sNow + aDuration;
Log("AdvanceTime for %u.%03u", aDuration / 1000, aDuration % 1000);
while (TimeMilli(sAlarmTime) <= TimeMilli(time))
{
ProcessTasklets();
sNow = sAlarmTime;
otPlatAlarmMilliFired(sInstance);
}
ProcessTasklets();
sNow = time;
}
void AdvanceNowTo(uint32_t aNewNow)
{
VerifyOrQuit(aNewNow >= sNow);
AdvanceTime(aNewNow - sNow);
}
void InitTest(void)
{
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Initialize OT instance.
sNow = 0;
sAlarmOn = false;
sInstance = static_cast<Instance *>(testInitInstance());
sDhcp6ListeningEnabled = false;
sDhcp6RxMsgs.Clear();
SuccessOrQuit(otBorderRoutingInit(sInstance, kInfraIfIndex, /* aInfraIfIsRunning */ true));
AdvanceTime(100);
}
void FinalizeTest(void) { testFreeInstance(sInstance); }
//---------------------------------------------------------------------------------------------------------------------
void TestDhcp6PdPrefixDelegation(bool aShortPrefix)
{
uint16_t heapAllocations;
Dhcp6TxMsg txMsg;
Dhcp6TxMsg::PrefixInfo prefixInfo;
Ip6::Prefix prefix;
Ip6::Prefix adjustedPrefix;
Mac::ExtAddress serverMacAddr;
const DelegatedPrefix *delegatedPrefix;
Log("--------------------------------------------------------------------------------------------");
Log("TestDhcp6PdPrefixDelegation(aShortPrefix:%u)", aShortPrefix);
InitTest();
heapAllocations = sHeapAllocatedPtrs.GetLength();
serverMacAddr.GenerateRandom();
if (aShortPrefix)
{
prefix = PrefixFromString("2001:1111::", 48);
adjustedPrefix = PrefixFromString("2001:1111::", 64);
}
else
{
prefix = PrefixFromString("2001:2222::", 64);
adjustedPrefix = prefix;
}
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start the client and wait for the first two Solicit messages");
VerifyOrQuit(!sDhcp6ListeningEnabled);
sInstance->Get<BorderRouter::Dhcp6PdClient>().Start();
VerifyOrQuit(sDhcp6ListeningEnabled);
AdvanceTime(2200);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 2);
sDhcp6RxMsgs[0].ValidateAsSolicit();
sDhcp6RxMsgs[1].ValidateAsSolicit();
VerifyOrQuit(sDhcp6RxMsgs[0].mTransactionId == sDhcp6RxMsgs[1].mTransactionId);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send Advertisement");
txMsg.PrepareAdvertise(sDhcp6RxMsgs[0], serverMacAddr);
prefixInfo.mIaid = sDhcp6RxMsgs[0].mIaPds[0].mIaid;
prefixInfo.mT1 = 2000;
prefixInfo.mT2 = 3200;
prefixInfo.mPreferredLifetime = 3600;
prefixInfo.mValidLifetime = 4000;
prefixInfo.mPrefix = prefix;
txMsg.AddIaPrefix(prefixInfo);
sDhcp6RxMsgs.Clear();
txMsg.Send();
AdvanceTime(1);
VerifyOrQuit(sInstance->Get<BorderRouter::Dhcp6PdClient>().GetDelegatedPrefix() == nullptr);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Validate Request message is received");
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
sDhcp6RxMsgs[0].ValidateAsRequest(prefix, serverMacAddr);
for (uint16_t iter = 0; iter < 3; iter++)
{
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send Reply message to %s", iter == 0 ? "Request" : "Renew");
sDhcp6RxMsgs.Clear();
txMsg.PrepareReply(sDhcp6RxMsgs[0], serverMacAddr);
txMsg.AddIaPrefix(prefixInfo);
txMsg.Send();
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Validate the delegated prefix on the client");
delegatedPrefix = sInstance->Get<BorderRouter::Dhcp6PdClient>().GetDelegatedPrefix();
VerifyOrQuit(delegatedPrefix != nullptr);
VerifyOrQuit(delegatedPrefix->mPrefix == prefix);
VerifyOrQuit(delegatedPrefix->mAdjustedPrefix == adjustedPrefix);
VerifyOrQuit(delegatedPrefix->mT1 == prefixInfo.mT1);
VerifyOrQuit(delegatedPrefix->mT2 == prefixInfo.mT2);
VerifyOrQuit(delegatedPrefix->mPreferredLifetime == prefixInfo.mPreferredLifetime);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Validate that no messages is received until renew time T1");
AdvanceTime(prefixInfo.mT1 * 1000 - 1);
VerifyOrQuit(sDhcp6RxMsgs.IsEmpty());
delegatedPrefix = sInstance->Get<BorderRouter::Dhcp6PdClient>().GetDelegatedPrefix();
VerifyOrQuit(delegatedPrefix != nullptr);
VerifyOrQuit(delegatedPrefix->mPrefix == prefix);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Validate that at T1 time Renew message is received");
AdvanceTime(5);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
sDhcp6RxMsgs[0].ValidateAsRenew(prefix, serverMacAddr);
VerifyOrQuit(sDhcp6RxMsgs[0].mElapsedTime == 0);
}
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Do not send a Reply to the Renew. Wait till T2 time and check the Renew message retries");
AdvanceTime((prefixInfo.mT2 - prefixInfo.mT1) * 1000 - 6);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() > 1);
for (uint16_t index = 2; index < sDhcp6RxMsgs.GetLength(); index++)
{
sDhcp6RxMsgs[index].ValidateAsRenew(prefix, serverMacAddr);
VerifyOrQuit(sDhcp6RxMsgs[index].mTransactionId == sDhcp6RxMsgs[0].mTransactionId);
VerifyOrQuit(sDhcp6RxMsgs[index].mElapsedTime > 0);
}
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Validate that the delegated prefix on the client remains unchanged");
delegatedPrefix = sInstance->Get<BorderRouter::Dhcp6PdClient>().GetDelegatedPrefix();
VerifyOrQuit(delegatedPrefix != nullptr);
VerifyOrQuit(delegatedPrefix->mPrefix == prefix);
VerifyOrQuit(delegatedPrefix->mAdjustedPrefix == adjustedPrefix);
VerifyOrQuit(delegatedPrefix->mT1 == prefixInfo.mT1);
VerifyOrQuit(delegatedPrefix->mT2 == prefixInfo.mT2);
VerifyOrQuit(delegatedPrefix->mPreferredLifetime == prefixInfo.mPreferredLifetime);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Validate that Rebind message is received at T2 time");
sDhcp6RxMsgs.Clear();
AdvanceTime(10);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
sDhcp6RxMsgs[0].ValidateAsRebind(prefix);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send Reply to Rebind");
txMsg.PrepareReply(sDhcp6RxMsgs[0], serverMacAddr);
txMsg.AddIaPrefix(prefixInfo);
sDhcp6RxMsgs.Clear();
txMsg.Send();
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Validate that the delegated prefix on the client is renewed");
delegatedPrefix = sInstance->Get<BorderRouter::Dhcp6PdClient>().GetDelegatedPrefix();
VerifyOrQuit(delegatedPrefix != nullptr);
VerifyOrQuit(delegatedPrefix->mPrefix == prefix);
VerifyOrQuit(delegatedPrefix->mAdjustedPrefix == adjustedPrefix);
VerifyOrQuit(delegatedPrefix->mT1 == prefixInfo.mT1);
VerifyOrQuit(delegatedPrefix->mT2 == prefixInfo.mT2);
VerifyOrQuit(delegatedPrefix->mPreferredLifetime == prefixInfo.mPreferredLifetime);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Validate that no message is received until renew time T1");
AdvanceTime(prefixInfo.mT1 * 1000 - 1);
VerifyOrQuit(sDhcp6RxMsgs.IsEmpty());
delegatedPrefix = sInstance->Get<BorderRouter::Dhcp6PdClient>().GetDelegatedPrefix();
VerifyOrQuit(delegatedPrefix != nullptr);
VerifyOrQuit(delegatedPrefix->mPrefix == prefix);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Validate Renew message is received");
AdvanceTime(5);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
sDhcp6RxMsgs[0].ValidateAsRenew(prefix, serverMacAddr);
VerifyOrQuit(sDhcp6RxMsgs[0].mElapsedTime == 0);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Wait till T2 time and check that Renew message is retried");
AdvanceTime((prefixInfo.mT2 - prefixInfo.mT1) * 1000 - 6);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() > 1);
for (uint16_t index = 2; index < sDhcp6RxMsgs.GetLength(); index++)
{
sDhcp6RxMsgs[index].ValidateAsRenew(prefix, serverMacAddr);
VerifyOrQuit(sDhcp6RxMsgs[index].mTransactionId == sDhcp6RxMsgs[0].mTransactionId);
VerifyOrQuit(sDhcp6RxMsgs[index].mElapsedTime > 0);
}
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Validate that Rebind message is received after T2 time");
sDhcp6RxMsgs.Clear();
AdvanceTime(5);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
sDhcp6RxMsgs[0].ValidateAsRebind(prefix);
VerifyOrQuit(sDhcp6RxMsgs[0].mElapsedTime == 0);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Wait till preferred time and check that Rebind message is now retried");
AdvanceTime((prefixInfo.mPreferredLifetime - prefixInfo.mT2) * 1000 - 6);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() > 1);
for (uint16_t index = 2; index < sDhcp6RxMsgs.GetLength(); index++)
{
sDhcp6RxMsgs[index].ValidateAsRebind(prefix);
VerifyOrQuit(sDhcp6RxMsgs[index].mTransactionId == sDhcp6RxMsgs[0].mTransactionId);
VerifyOrQuit(sDhcp6RxMsgs[index].mElapsedTime > 0);
}
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Validate that the delegated prefix is still present on the client right before its expire time");
delegatedPrefix = sInstance->Get<BorderRouter::Dhcp6PdClient>().GetDelegatedPrefix();
VerifyOrQuit(delegatedPrefix != nullptr);
VerifyOrQuit(delegatedPrefix->mPrefix == prefix);
VerifyOrQuit(delegatedPrefix->mAdjustedPrefix == adjustedPrefix);
VerifyOrQuit(delegatedPrefix->mT1 == prefixInfo.mT1);
VerifyOrQuit(delegatedPrefix->mT2 == prefixInfo.mT2);
VerifyOrQuit(delegatedPrefix->mPreferredLifetime == prefixInfo.mPreferredLifetime);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Validate that the delegated prefix is removed after its expire time");
sDhcp6RxMsgs.Clear();
AdvanceTime(5);
delegatedPrefix = sInstance->Get<BorderRouter::Dhcp6PdClient>().GetDelegatedPrefix();
VerifyOrQuit(delegatedPrefix == nullptr);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Validate that Solicit messages are sent again");
AdvanceTime(10 * 1000);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() > 1);
sDhcp6RxMsgs[0].ValidateAsSolicit();
VerifyOrQuit(sDhcp6RxMsgs[0].mElapsedTime == 0);
for (uint16_t index = 2; index < sDhcp6RxMsgs.GetLength(); index++)
{
sDhcp6RxMsgs[index].ValidateAsSolicit();
VerifyOrQuit(sDhcp6RxMsgs[index].mTransactionId == sDhcp6RxMsgs[0].mTransactionId);
VerifyOrQuit(sDhcp6RxMsgs[index].mElapsedTime > 0);
}
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send Advertisement, check Request message and respond with Reply");
txMsg.PrepareAdvertise(sDhcp6RxMsgs[0], serverMacAddr);
txMsg.AddIaPrefix(prefixInfo);
sDhcp6RxMsgs.Clear();
txMsg.Send();
AdvanceTime(1);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
sDhcp6RxMsgs[0].ValidateAsRequest(prefix, serverMacAddr);
txMsg.PrepareReply(sDhcp6RxMsgs[0], serverMacAddr);
txMsg.AddIaPrefix(prefixInfo);
sDhcp6RxMsgs.Clear();
txMsg.Send();
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Validate the delegated prefix on the client");
delegatedPrefix = sInstance->Get<BorderRouter::Dhcp6PdClient>().GetDelegatedPrefix();
VerifyOrQuit(delegatedPrefix != nullptr);
VerifyOrQuit(delegatedPrefix->mPrefix == prefix);
VerifyOrQuit(delegatedPrefix->mAdjustedPrefix == adjustedPrefix);
VerifyOrQuit(delegatedPrefix->mT1 == prefixInfo.mT1);
VerifyOrQuit(delegatedPrefix->mT2 == prefixInfo.mT2);
VerifyOrQuit(delegatedPrefix->mPreferredLifetime == prefixInfo.mPreferredLifetime);
AdvanceTime(5 * 1000);
VerifyOrQuit(sDhcp6RxMsgs.IsEmpty());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Stop the client, and validate that Release message is received");
sInstance->Get<BorderRouter::Dhcp6PdClient>().Stop();
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
sDhcp6RxMsgs[0].ValidateAsRelease(prefix, serverMacAddr);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send Reply to Release message and check that no more messages is received");
txMsg.PrepareReply(sDhcp6RxMsgs[0], serverMacAddr);
txMsg.AddIaPrefix(prefixInfo);
sDhcp6RxMsgs.Clear();
txMsg.Send();
AdvanceTime(20 * 1000);
VerifyOrQuit(sDhcp6RxMsgs.IsEmpty());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
Log("End of TestDhcp6PdPrefixDelegation(aShortPrefix:%u)", aShortPrefix);
FinalizeTest();
}
//---------------------------------------------------------------------------------------------------------------------
void TestDhcp6PdSolicitRetries(void)
{
static uint32_t kMaxTimeout = 3600 * 1000;
uint16_t heapAllocations;
uint32_t firstRxTime;
uint32_t timeout;
Log("--------------------------------------------------------------------------------------------");
Log("TestDhcp6PdSolicitRetries");
InitTest();
heapAllocations = sHeapAllocatedPtrs.GetLength();
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start the client and check initial delay for first Solicit");
VerifyOrQuit(!sDhcp6ListeningEnabled);
sInstance->Get<BorderRouter::Dhcp6PdClient>().Start();
VerifyOrQuit(sDhcp6ListeningEnabled);
// Initial random delay of [0, 1000] msec to send first solicit
AdvanceTime(1000);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
sDhcp6RxMsgs[0].ValidateAsSolicit();
VerifyOrQuit(sDhcp6RxMsgs[0].mElapsedTime == 0);
firstRxTime = sDhcp6RxMsgs[0].mRxTime;
VerifyOrQuit(sInstance->Get<BorderRouter::Dhcp6PdClient>().GetDelegatedPrefix() == nullptr);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Wait for more than 30 minutes and collect all Solicit messages");
AdvanceTime(20000 * 1000);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() >= 14);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Validate the retx timing of Solicit messages");
sDhcp6RxMsgs[1].ValidateAsSolicit();
VerifyOrQuit(sDhcp6RxMsgs[1].mTransactionId == sDhcp6RxMsgs[0].mTransactionId);
VerifyOrQuit(sDhcp6RxMsgs[1].mElapsedTime == (sDhcp6RxMsgs[1].mRxTime - firstRxTime) / 10);
timeout = sDhcp6RxMsgs[1].mRxTime - firstRxTime;
// The initial time should be randomly picked from [1, 1.1] sec.
VerifyOrQuit(timeout >= 1000);
VerifyOrQuit(timeout <= 1100);
for (uint16_t index = 2; index < sDhcp6RxMsgs.GetLength(); index++)
{
uint32_t newTimeout;
uint32_t minTimeout;
uint32_t maxTimeout;
uint32_t elapsedTime;
sDhcp6RxMsgs[index].ValidateAsSolicit();
VerifyOrQuit(sDhcp6RxMsgs[index].mTransactionId == sDhcp6RxMsgs[0].mTransactionId);
newTimeout = sDhcp6RxMsgs[index].mRxTime - sDhcp6RxMsgs[index - 1].mRxTime;
minTimeout = 2 * timeout - timeout / 10;
maxTimeout = 2 * timeout + timeout / 10;
if (maxTimeout > kMaxTimeout)
{
minTimeout = Min(minTimeout, kMaxTimeout - kMaxTimeout / 10);
maxTimeout = kMaxTimeout + kMaxTimeout / 10;
}
Log("Solicit %2u -> timeout:%lu, min:%lu, max:%lu", index, ToUlong(newTimeout), ToUlong(minTimeout),
ToUlong(maxTimeout));
elapsedTime = (sDhcp6RxMsgs[index].mRxTime - firstRxTime) / 10;
VerifyOrQuit(sDhcp6RxMsgs[index].mElapsedTime == ClampToUint16(elapsedTime));
VerifyOrQuit(newTimeout >= minTimeout);
VerifyOrQuit(newTimeout <= maxTimeout);
timeout = newTimeout;
}
VerifyOrQuit(sInstance->Get<BorderRouter::Dhcp6PdClient>().GetDelegatedPrefix() == nullptr);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Stop the client and make sure there are no more transmissions");
sDhcp6RxMsgs.Clear();
sInstance->Get<BorderRouter::Dhcp6PdClient>().Stop();
VerifyOrQuit(!sDhcp6ListeningEnabled);
AdvanceTime(200 * 1000);
VerifyOrQuit(sDhcp6RxMsgs.IsEmpty());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start again and check the Solicit message tx");
sInstance->Get<BorderRouter::Dhcp6PdClient>().Start();
VerifyOrQuit(sDhcp6ListeningEnabled);
// Initial random delay of [0, 1000] msec to send first solicit
AdvanceTime(1000);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
sDhcp6RxMsgs[0].ValidateAsSolicit();
VerifyOrQuit(sDhcp6RxMsgs[0].mElapsedTime == 0);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Stop the client");
sInstance->Get<BorderRouter::Dhcp6PdClient>().Stop();
AdvanceTime(15 * 1000);
VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
Log("End of TestDhcp6PdSolicitRetries");
FinalizeTest();
}
//---------------------------------------------------------------------------------------------------------------------
void TestDhcp6PdRequestRetries(void)
{
static constexpr uint32_t kInitialRequestTimeout = 1000;
static constexpr uint32_t kMaxRequestTimeout = 30 * 1000;
static constexpr uint16_t kMaxRequestRetxCount = 10;
uint16_t heapAllocations;
Dhcp6TxMsg txMsg;
Dhcp6TxMsg::PrefixInfo prefixInfo;
Ip6::Prefix prefix;
Mac::ExtAddress serverMacAddr;
uint32_t firstRxTime;
uint32_t timeout;
uint32_t newTimeout;
uint32_t minTimeout;
uint32_t maxTimeout;
uint32_t elapsedTime;
uint16_t index;
Log("--------------------------------------------------------------------------------------------");
Log("TestDhcp6PdRequestRetries");
InitTest();
heapAllocations = sHeapAllocatedPtrs.GetLength();
serverMacAddr.GenerateRandom();
prefix = PrefixFromString("2001:aa::", 64);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start the client and wait for the first two Solicit messages");
VerifyOrQuit(!sDhcp6ListeningEnabled);
sInstance->Get<BorderRouter::Dhcp6PdClient>().Start();
VerifyOrQuit(sDhcp6ListeningEnabled);
AdvanceTime(2200);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 2);
sDhcp6RxMsgs[0].ValidateAsSolicit();
sDhcp6RxMsgs[1].ValidateAsSolicit();
VerifyOrQuit(sDhcp6RxMsgs[0].mTransactionId == sDhcp6RxMsgs[1].mTransactionId);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send Advertisement");
txMsg.PrepareAdvertise(sDhcp6RxMsgs[0], serverMacAddr);
prefixInfo.mIaid = sDhcp6RxMsgs[0].mIaPds[0].mIaid;
prefixInfo.mT1 = 2000;
prefixInfo.mT2 = 3200;
prefixInfo.mPreferredLifetime = 3600;
prefixInfo.mValidLifetime = 4000;
prefixInfo.mPrefix = prefix;
txMsg.AddIaPrefix(prefixInfo);
sDhcp6RxMsgs.Clear();
txMsg.Send();
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Validate Request message is received");
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
sDhcp6RxMsgs[0].ValidateAsRequest(prefix, serverMacAddr);
VerifyOrQuit(sDhcp6RxMsgs[0].mElapsedTime == 0);
firstRxTime = sDhcp6RxMsgs[0].mRxTime;
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Wait for 5 minutes and collect all messages");
AdvanceTime(300 * 1000);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() > kMaxRequestRetxCount);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Validate the retx timing of Request messages");
sDhcp6RxMsgs[1].ValidateAsRequest(prefix, serverMacAddr);
VerifyOrQuit(sDhcp6RxMsgs[1].mTransactionId == sDhcp6RxMsgs[0].mTransactionId);
VerifyOrQuit(sDhcp6RxMsgs[1].mElapsedTime == (sDhcp6RxMsgs[1].mRxTime - firstRxTime) / 10);
timeout = sDhcp6RxMsgs[1].mRxTime - firstRxTime;
// Validate the initial timeout
minTimeout = kInitialRequestTimeout - kInitialRequestTimeout / 10;
maxTimeout = kInitialRequestTimeout + kInitialRequestTimeout / 10;
Log("Request %2u -> timeout:%lu, min:%lu, max:%lu", 1, ToUlong(timeout), ToUlong(minTimeout), ToUlong(maxTimeout));
VerifyOrQuit(timeout >= minTimeout);
VerifyOrQuit(timeout <= maxTimeout);
for (index = 2; index <= kMaxRequestRetxCount; index++)
{
sDhcp6RxMsgs[index].ValidateAsRequest(prefix, serverMacAddr);
VerifyOrQuit(sDhcp6RxMsgs[index].mTransactionId == sDhcp6RxMsgs[0].mTransactionId);
newTimeout = sDhcp6RxMsgs[index].mRxTime - sDhcp6RxMsgs[index - 1].mRxTime;
minTimeout = 2 * timeout - timeout / 10;
maxTimeout = 2 * timeout + timeout / 10;
if (maxTimeout > kMaxRequestTimeout)
{
minTimeout = Min(minTimeout, kMaxRequestTimeout - kMaxRequestTimeout / 10);
maxTimeout = kMaxRequestTimeout + kMaxRequestTimeout / 10;
}
Log("Request %2u -> timeout:%lu, min:%lu, max:%lu", index, ToUlong(newTimeout), ToUlong(minTimeout),
ToUlong(maxTimeout));
elapsedTime = (sDhcp6RxMsgs[index].mRxTime - firstRxTime) / 10;
VerifyOrQuit(sDhcp6RxMsgs[index].mElapsedTime == ClampToUint16(elapsedTime));
VerifyOrQuit(newTimeout >= minTimeout);
VerifyOrQuit(newTimeout <= maxTimeout);
timeout = newTimeout;
}
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Validate that after the Request Retries are finished, client restarts sending Solicit");
VerifyOrQuit(sDhcp6RxMsgs.GetLength() > kMaxRequestRetxCount + 1);
index = kMaxRequestRetxCount + 1;
sDhcp6RxMsgs[index].ValidateAsSolicit();
VerifyOrQuit(sDhcp6RxMsgs[index].mTransactionId != sDhcp6RxMsgs[0].mTransactionId);
VerifyOrQuit(sDhcp6RxMsgs[index].mElapsedTime == 0);
// Check the timeout of the last Request message
timeout = sDhcp6RxMsgs[index].mRxTime - sDhcp6RxMsgs[index - 1].mRxTime;
VerifyOrQuit(timeout >= minTimeout);
VerifyOrQuit(timeout <= maxTimeout);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Stop the client");
sInstance->Get<BorderRouter::Dhcp6PdClient>().Stop();
AdvanceTime(15 * 1000);
VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
Log("End of TestDhcp6PdRequestRetries");
FinalizeTest();
}
//---------------------------------------------------------------------------------------------------------------------
void TestDhcp6PdSelectBetweenMultipleServers(void)
{
uint16_t heapAllocations;
Dhcp6TxMsg txMsg;
Dhcp6TxMsg::PrefixInfo prefixInfo;
Ip6::Prefix prefix1;
Ip6::Prefix prefix2;
Ip6::Prefix prefix3;
Mac::ExtAddress serverMacAddr1;
Mac::ExtAddress serverMacAddr2;
Mac::ExtAddress serverMacAddr3;
Log("--------------------------------------------------------------------------------------------");
Log("TestDhcp6PdSelectBetweenMultipleServers()");
InitTest();
heapAllocations = sHeapAllocatedPtrs.GetLength();
serverMacAddr1.GenerateRandom();
serverMacAddr2.GenerateRandom();
serverMacAddr3.GenerateRandom();
prefix1 = PrefixFromString("2001:ff::", 64);
prefix2 = PrefixFromString("2001:0::", 48);
prefix3 = PrefixFromString("2001:dad0::", 40);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start the client and wait for the first Solicit message");
VerifyOrQuit(!sDhcp6ListeningEnabled);
sInstance->Get<BorderRouter::Dhcp6PdClient>().Start();
VerifyOrQuit(sDhcp6ListeningEnabled);
AdvanceTime(1000);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
sDhcp6RxMsgs[0].ValidateAsSolicit();
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send multiple Advertisements from different servers providing different prefixes");
txMsg.PrepareAdvertise(sDhcp6RxMsgs[0], serverMacAddr1);
prefixInfo.mIaid = sDhcp6RxMsgs[0].mIaPds[0].mIaid;
prefixInfo.mT1 = 2000;
prefixInfo.mT2 = 3200;
prefixInfo.mPreferredLifetime = 3600;
prefixInfo.mValidLifetime = 4000;
prefixInfo.mPrefix = prefix1;
txMsg.AddIaPrefix(prefixInfo);
txMsg.Send();
txMsg.PrepareAdvertise(sDhcp6RxMsgs[0], serverMacAddr2);
prefixInfo.mIaid = sDhcp6RxMsgs[0].mIaPds[0].mIaid;
prefixInfo.mT1 = 2000;
prefixInfo.mT2 = 3200;
prefixInfo.mPreferredLifetime = 3600;
prefixInfo.mValidLifetime = 4000;
prefixInfo.mPrefix = prefix2;
txMsg.AddIaPrefix(prefixInfo);
txMsg.Send();
txMsg.PrepareAdvertise(sDhcp6RxMsgs[0], serverMacAddr3);
prefixInfo.mIaid = sDhcp6RxMsgs[0].mIaPds[0].mIaid;
prefixInfo.mT1 = 2000;
prefixInfo.mT2 = 3200;
prefixInfo.mPreferredLifetime = 3600;
prefixInfo.mValidLifetime = 4000;
prefixInfo.mPrefix = prefix3;
txMsg.AddIaPrefix(prefixInfo);
txMsg.Send();
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Validate that the client does wait for the full timeout (on first Solicit) ");
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
// First timeout is at least 1000 msec
AdvanceNowTo(sDhcp6RxMsgs[0].mRxTime + 1000 - 1);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Validate Request message is received and its for the preferred prefix from server2");
AdvanceTime(200);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 2);
sDhcp6RxMsgs[1].ValidateAsRequest(prefix2, serverMacAddr2);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Stop the client");
sInstance->Get<BorderRouter::Dhcp6PdClient>().Stop();
AdvanceTime(5 * 1000);
VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
Log("End of TestDhcp6PdSelectBetweenMultipleServers()");
FinalizeTest();
}
void TestDhcp6PdServerWithMaxPreferrence(void)
{
uint16_t heapAllocations;
Dhcp6TxMsg txMsg;
Dhcp6TxMsg::PrefixInfo prefixInfo;
Ip6::Prefix prefix;
Mac::ExtAddress serverMacAddr;
Log("--------------------------------------------------------------------------------------------");
Log("TestDhcp6PdServerWithMaxPreferrence()");
InitTest();
heapAllocations = sHeapAllocatedPtrs.GetLength();
serverMacAddr.GenerateRandom();
prefix = PrefixFromString("2001:7::", 64);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start the client and wait for the first Solicit message");
VerifyOrQuit(!sDhcp6ListeningEnabled);
sInstance->Get<BorderRouter::Dhcp6PdClient>().Start();
VerifyOrQuit(sDhcp6ListeningEnabled);
AdvanceTime(1000);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
sDhcp6RxMsgs[0].ValidateAsSolicit();
// On the first Solicit, the client must wait for the full timeout
// unless preference is in Advertisement is set to max (255).
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send Advertisement with Preference Option string preference to max to the first Solicit");
txMsg.PrepareAdvertise(sDhcp6RxMsgs[0], serverMacAddr);
txMsg.mHasPreference = true;
txMsg.mPreference = 255;
prefixInfo.mIaid = sDhcp6RxMsgs[0].mIaPds[0].mIaid;
prefixInfo.mT1 = 2000;
prefixInfo.mT2 = 3200;
prefixInfo.mPreferredLifetime = 3600;
prefixInfo.mValidLifetime = 4000;
prefixInfo.mPrefix = prefix;
txMsg.AddIaPrefix(prefixInfo);
txMsg.Send();
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Validate that the client does not wait any longer and sends Request immediately");
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 2);
sDhcp6RxMsgs[1].ValidateAsRequest(prefix, serverMacAddr);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Stop the client");
sInstance->Get<BorderRouter::Dhcp6PdClient>().Stop();
AdvanceTime(5 * 1000);
VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
Log("End of TestDhcp6PdServerWithMaxPreferrence()");
FinalizeTest();
}
//---------------------------------------------------------------------------------------------------------------------
void TestDhcp6PdServerOfferingMultiplePrefixes(void)
{
uint16_t heapAllocations;
Dhcp6TxMsg txMsg;
Dhcp6TxMsg::PrefixInfo prefixInfo;
Ip6::Prefix prefix1;
Ip6::Prefix prefix2;
Ip6::Prefix prefix3;
Mac::ExtAddress serverMacAddr;
Log("--------------------------------------------------------------------------------------------");
Log("TestDhcp6PdServerOfferingMultiplePrefixes()");
InitTest();
heapAllocations = sHeapAllocatedPtrs.GetLength();
serverMacAddr.GenerateRandom();
prefix1 = PrefixFromString("2001:5:baba:beef::", 64);
prefix2 = PrefixFromString("2001:4::", 48);
prefix3 = PrefixFromString("2001:ef::", 40);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start the client and wait for the first Solicit message");
VerifyOrQuit(!sDhcp6ListeningEnabled);
sInstance->Get<BorderRouter::Dhcp6PdClient>().Start();
VerifyOrQuit(sDhcp6ListeningEnabled);
AdvanceTime(1000);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
sDhcp6RxMsgs[0].ValidateAsSolicit();
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send Advertisements from server offering three prefixes");
txMsg.PrepareAdvertise(sDhcp6RxMsgs[0], serverMacAddr);
prefixInfo.mIaid = sDhcp6RxMsgs[0].mIaPds[0].mIaid;
prefixInfo.mT1 = 2000;
prefixInfo.mT2 = 3200;
prefixInfo.mPreferredLifetime = 3600;
prefixInfo.mValidLifetime = 4000;
prefixInfo.mPrefix = prefix1;
txMsg.AddIaPrefix(prefixInfo);
prefixInfo.mPrefix = prefix2;
txMsg.AddIaPrefix(prefixInfo);
prefixInfo.mPrefix = prefix3;
txMsg.AddIaPrefix(prefixInfo);
txMsg.Send();
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Validate that the client does wait for the full timeout (on first Solicit) ");
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
AdvanceNowTo(sDhcp6RxMsgs[0].mRxTime + 1000 - 1);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Validate Request message is received and its for the preferred prefix");
AdvanceTime(200);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 2);
sDhcp6RxMsgs[1].ValidateAsRequest(prefix2, serverMacAddr);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Stop the client");
sInstance->Get<BorderRouter::Dhcp6PdClient>().Stop();
AdvanceTime(5 * 1000);
VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
Log("End of TestDhcp6PdServerOfferingMultiplePrefixes()");
FinalizeTest();
}
void TestDhcp6PdInvalidOrUnusablePrefix(void)
{
uint16_t heapAllocations;
Dhcp6TxMsg txMsg;
Dhcp6TxMsg::PrefixInfo prefixInfo;
Ip6::Prefix prefix;
Mac::ExtAddress serverMacAddr;
Dhcp6RxMsg modifiedSolicitRxMsg;
Mac::ExtAddress modifiedClientMacAddr;
Log("--------------------------------------------------------------------------------------------");
Log("TestDhcp6PdInvalidOrUnusablePrefix");
InitTest();
heapAllocations = sHeapAllocatedPtrs.GetLength();
serverMacAddr.GenerateRandom();
prefix = PrefixFromString("2001:9::", 64);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start the client and wait for the first two Solicit messages");
VerifyOrQuit(!sDhcp6ListeningEnabled);
sInstance->Get<BorderRouter::Dhcp6PdClient>().Start();
VerifyOrQuit(sDhcp6ListeningEnabled);
AdvanceTime(2200);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 2);
sDhcp6RxMsgs[0].ValidateAsSolicit();
sDhcp6RxMsgs[1].ValidateAsSolicit();
VerifyOrQuit(sDhcp6RxMsgs[0].mTransactionId == sDhcp6RxMsgs[1].mTransactionId);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send Advertisement with wrong TransactionId and ensure it is not accepted");
modifiedSolicitRxMsg = sDhcp6RxMsgs[0];
do
{
modifiedSolicitRxMsg.mTransactionId.GenerateRandom();
} while (modifiedSolicitRxMsg.mTransactionId == sDhcp6RxMsgs[0].mTransactionId);
txMsg.PrepareAdvertise(modifiedSolicitRxMsg, serverMacAddr);
prefixInfo.mIaid = modifiedSolicitRxMsg.mIaPds[0].mIaid;
prefixInfo.mT1 = 2000;
prefixInfo.mT2 = 3200;
prefixInfo.mPreferredLifetime = 3600;
prefixInfo.mValidLifetime = 4000;
prefixInfo.mPrefix = prefix;
txMsg.AddIaPrefix(prefixInfo);
txMsg.Send();
AdvanceTime(1);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 2);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send Advertisement with wrong Client ID and ensure it is not accepted");
modifiedSolicitRxMsg = sDhcp6RxMsgs[0];
do
{
modifiedClientMacAddr.GenerateRandom();
} while (modifiedClientMacAddr == sDhcp6RxMsgs[0].mClientDuid.mShared.mEui64.GetLinkLayerAddress());
modifiedSolicitRxMsg.mClientDuid.mShared.mEui64.Init(modifiedClientMacAddr);
txMsg.PrepareAdvertise(modifiedSolicitRxMsg, serverMacAddr);
prefixInfo.mIaid = modifiedSolicitRxMsg.mIaPds[0].mIaid;
prefixInfo.mT1 = 2000;
prefixInfo.mT2 = 3200;
prefixInfo.mPreferredLifetime = 3600;
prefixInfo.mValidLifetime = 4000;
prefixInfo.mPrefix = prefix;
txMsg.AddIaPrefix(prefixInfo);
txMsg.Send();
AdvanceTime(1);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 2);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send Advertisement with no Client ID and ensure it is not accepted");
txMsg.PrepareAdvertise(sDhcp6RxMsgs[0], serverMacAddr);
txMsg.mHasClientId = false;
prefixInfo.mIaid = sDhcp6RxMsgs[0].mIaPds[0].mIaid;
prefixInfo.mT1 = 2000;
prefixInfo.mT2 = 3200;
prefixInfo.mPreferredLifetime = 3600;
prefixInfo.mValidLifetime = 4000;
prefixInfo.mPrefix = prefix;
txMsg.AddIaPrefix(prefixInfo);
txMsg.Send();
AdvanceTime(1);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 2);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send Advertisement with no Server ID and ensure it is not accepted");
txMsg.PrepareAdvertise(sDhcp6RxMsgs[0], serverMacAddr);
txMsg.mHasServerId = false;
prefixInfo.mIaid = sDhcp6RxMsgs[0].mIaPds[0].mIaid;
prefixInfo.mT1 = 2000;
prefixInfo.mT2 = 3200;
prefixInfo.mPreferredLifetime = 3600;
prefixInfo.mValidLifetime = 4000;
prefixInfo.mPrefix = prefix;
txMsg.AddIaPrefix(prefixInfo);
txMsg.Send();
AdvanceTime(1);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 2);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send Advertisement with a wrong `Iaid` and ensure it is not accepted");
txMsg.PrepareAdvertise(sDhcp6RxMsgs[0], serverMacAddr);
prefixInfo.mIaid = sDhcp6RxMsgs[0].mIaPds[0].mIaid + 1;
prefixInfo.mT1 = 2000;
prefixInfo.mT2 = 3200;
prefixInfo.mPreferredLifetime = 3600;
prefixInfo.mValidLifetime = 4000;
prefixInfo.mPrefix = prefix;
txMsg.AddIaPrefix(prefixInfo);
txMsg.Send();
AdvanceTime(1);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 2);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send Advertisement with a long prefix and ensure it is not accepted");
txMsg.PrepareAdvertise(sDhcp6RxMsgs[0], serverMacAddr);
prefixInfo.mIaid = sDhcp6RxMsgs[0].mIaPds[0].mIaid;
prefixInfo.mT1 = 2000;
prefixInfo.mT2 = 3200;
prefixInfo.mPreferredLifetime = 3600;
prefixInfo.mValidLifetime = 4000;
prefixInfo.mPrefix = PrefixFromString("2001:dead:beef:cafe::", 65);
txMsg.AddIaPrefix(prefixInfo);
txMsg.Send();
AdvanceTime(1);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 2);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send Advertisement T1 longer than T2 and ensure it is not accepted");
txMsg.PrepareAdvertise(sDhcp6RxMsgs[0], serverMacAddr);
prefixInfo.mIaid = sDhcp6RxMsgs[0].mIaPds[0].mIaid;
prefixInfo.mT1 = 3000;
prefixInfo.mT2 = 2000;
prefixInfo.mPreferredLifetime = 3600;
prefixInfo.mValidLifetime = 4000;
prefixInfo.mPrefix = prefix;
txMsg.AddIaPrefix(prefixInfo);
txMsg.Send();
AdvanceTime(1);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 2);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send Advertisement preferred lifetime longer than valid lifetime and ensure it is not accepted");
txMsg.PrepareAdvertise(sDhcp6RxMsgs[0], serverMacAddr);
prefixInfo.mIaid = sDhcp6RxMsgs[0].mIaPds[0].mIaid;
prefixInfo.mT1 = 2000;
prefixInfo.mT2 = 3200;
prefixInfo.mPreferredLifetime = 4000;
prefixInfo.mValidLifetime = 3600;
prefixInfo.mPrefix = prefix;
txMsg.AddIaPrefix(prefixInfo);
txMsg.Send();
AdvanceTime(1);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 2);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send Advertisement with preferred lifetime below the min required threshold of 30 min");
txMsg.PrepareAdvertise(sDhcp6RxMsgs[0], serverMacAddr);
prefixInfo.mIaid = sDhcp6RxMsgs[0].mIaPds[0].mIaid;
prefixInfo.mT1 = 900;
prefixInfo.mT2 = 1000;
prefixInfo.mPreferredLifetime = 1799;
prefixInfo.mValidLifetime = 4000;
prefixInfo.mPrefix = prefix;
txMsg.AddIaPrefix(prefixInfo);
txMsg.Send();
AdvanceTime(1);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 2);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Finally send Advertisement with everything valid and ensure it is accepted");
txMsg.PrepareAdvertise(sDhcp6RxMsgs[0], serverMacAddr);
prefixInfo.mIaid = sDhcp6RxMsgs[0].mIaPds[0].mIaid;
prefixInfo.mT1 = 0;
prefixInfo.mT2 = 0;
prefixInfo.mPreferredLifetime = 1800;
prefixInfo.mValidLifetime = 4000;
prefixInfo.mPrefix = prefix;
txMsg.AddIaPrefix(prefixInfo);
txMsg.Send();
AdvanceTime(1);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 3);
sDhcp6RxMsgs[2].ValidateAsRequest(prefix, serverMacAddr);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Stop the client");
sInstance->Get<BorderRouter::Dhcp6PdClient>().Stop();
AdvanceTime(15 * 1000);
VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
Log("End of TestDhcp6PdInvalidOrUnusablePrefix");
FinalizeTest();
}
void TestDhcp6PdLifetimeT1AndT2Adjustments(void)
{
struct TestCase
{
uint32_t mT1;
uint32_t mT2;
uint32_t mPreferredLifetime;
uint32_t mExpectedT1;
uint32_t mExpectedT2;
uint32_t mExpectedPreferredLifetime;
};
// Validate client will adjust the T1 and T2 and lifetime to reasonable range.
static const TestCase kTestCases[] = {
// T1 and T2 are zero. Client must pick 0.5 and 0.8 times lifetime for T1 and T2.
{0, 0, 1800, 900, 1440, 1800},
// Only T1 is zero.
{0, 1300, 1800, 900, 1300, 1800},
// Only T2 is zero.
{800, 0, 1800, 800, 1440, 1800},
// T1 is zero, but default T1 (half of preferred lifetime) will be larger than given T2.
{0, 800, 1800, 900, 900, 1800},
// T1 and T2 are given but way too short. Client enforces min of 300s (5 min).
{1, 5, 1800, 300, 300, 1800},
// T1 and T2 zero with preferred lifetime of 7200 (2 hours).
{0, 0, 7200, 3600, 5760, 7200},
// T1 and T2 longer than lifetime.
{2000, 2500, 1800, 900, 1440, 1800},
// Given T1 and T2 (shorter than 0.5 and 0.8) with preferred lifetime of 7200 (2 hours).
{1000, 1200, 7200, 1000, 1200, 7200},
// Given T1 and T2 are too close to the preferred lifetime of 7200 (2 hours).
{7100, 7150, 7200, 6300, 6840, 7200},
// Very long preferred lifetime. Client limit it to 4 hours (14400)
{0, 0, 14500, 7200, 11520, 14400},
// Very long preferred lifetime. Client limit it to 4 hours (14400)
{2000, 2500, 14500, 2000, 2500, 14400},
// Infinite lifetime and T1 and T2. Client limit to 4 hours.
{0xffffffff, 0xffffffff, 0xffffffff, 13500, 14040, 14400}};
uint16_t heapAllocations;
Dhcp6TxMsg txMsg;
Dhcp6TxMsg::PrefixInfo prefixInfo;
Ip6::Prefix prefix;
Ip6::Prefix adjustedPrefix;
Mac::ExtAddress serverMacAddr;
const DelegatedPrefix *delegatedPrefix;
Log("--------------------------------------------------------------------------------------------");
Log("TestDhcp6PdLifetimeT1AndT2Adjustments()");
InitTest();
heapAllocations = sHeapAllocatedPtrs.GetLength();
serverMacAddr.GenerateRandom();
prefix = PrefixFromString("2001:13::", 48);
adjustedPrefix = PrefixFromString("2001:13::", 64);
for (const TestCase &testCase : kTestCases)
{
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Test case: {T1:%lu T2:%lu prf:%lu } --> Expected {T1:%lu T2:%lu prf:%lu}", ToUlong(testCase.mT1),
ToUlong(testCase.mT2), ToUlong(testCase.mPreferredLifetime), ToUlong(testCase.mExpectedT1),
ToUlong(testCase.mExpectedT2), ToUlong(testCase.mExpectedPreferredLifetime));
sDhcp6RxMsgs.Clear();
sInstance->Get<BorderRouter::Dhcp6PdClient>().Start();
AdvanceTime(2200);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 2);
sDhcp6RxMsgs[0].ValidateAsSolicit();
sDhcp6RxMsgs[1].ValidateAsSolicit();
VerifyOrQuit(sDhcp6RxMsgs[0].mTransactionId == sDhcp6RxMsgs[1].mTransactionId);
txMsg.PrepareAdvertise(sDhcp6RxMsgs[0], serverMacAddr);
prefixInfo.mIaid = sDhcp6RxMsgs[0].mIaPds[0].mIaid;
prefixInfo.mT1 = testCase.mT1;
prefixInfo.mT2 = testCase.mT2;
prefixInfo.mPreferredLifetime = testCase.mPreferredLifetime;
prefixInfo.mValidLifetime = testCase.mPreferredLifetime;
prefixInfo.mPrefix = prefix;
txMsg.AddIaPrefix(prefixInfo);
sDhcp6RxMsgs.Clear();
txMsg.Send();
AdvanceTime(1);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
sDhcp6RxMsgs[0].ValidateAsRequest(prefix, serverMacAddr);
sDhcp6RxMsgs.Clear();
txMsg.PrepareReply(sDhcp6RxMsgs[0], serverMacAddr);
txMsg.AddIaPrefix(prefixInfo);
txMsg.Send();
delegatedPrefix = sInstance->Get<BorderRouter::Dhcp6PdClient>().GetDelegatedPrefix();
VerifyOrQuit(delegatedPrefix != nullptr);
Log("Delegated Prefix -> {T1:%lu T2:%lu prf:%lu}", ToUlong(delegatedPrefix->mT1), ToUlong(delegatedPrefix->mT2),
ToUlong(delegatedPrefix->mPreferredLifetime));
VerifyOrQuit(delegatedPrefix->mPrefix == prefix);
VerifyOrQuit(delegatedPrefix->mAdjustedPrefix == adjustedPrefix);
VerifyOrQuit(delegatedPrefix->mT1 == testCase.mExpectedT1);
VerifyOrQuit(delegatedPrefix->mT2 == testCase.mExpectedT2);
VerifyOrQuit(delegatedPrefix->mPreferredLifetime == testCase.mExpectedPreferredLifetime);
sInstance->Get<BorderRouter::Dhcp6PdClient>().Stop();
AdvanceTime(100);
}
VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
Log("End of TestDhcp6PdLifetimeT1AndT2Adjustments");
FinalizeTest();
}
void TestDhcp6PdServerVoidingLeaseDuringRenew(void)
{
uint16_t heapAllocations;
Dhcp6TxMsg txMsg;
Dhcp6TxMsg::PrefixInfo prefixInfo;
Ip6::Prefix prefix;
Ip6::Prefix adjustedPrefix;
Mac::ExtAddress serverMacAddr;
const DelegatedPrefix *delegatedPrefix;
Log("--------------------------------------------------------------------------------------------");
Log("TestDhcp6PdServerVoidingLeaseDuringRenew()");
InitTest();
heapAllocations = sHeapAllocatedPtrs.GetLength();
serverMacAddr.GenerateRandom();
prefix = PrefixFromString("2001:cafe:5555::", 48);
adjustedPrefix = PrefixFromString("2001:cafe:5555::", 64);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start client, interact with it to successfully delegate a prefix");
sInstance->Get<BorderRouter::Dhcp6PdClient>().Start();
AdvanceTime(2200);
// Listen for Solicit and send Advertise
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 2);
sDhcp6RxMsgs[0].ValidateAsSolicit();
sDhcp6RxMsgs[1].ValidateAsSolicit();
VerifyOrQuit(sDhcp6RxMsgs[0].mTransactionId == sDhcp6RxMsgs[1].mTransactionId);
txMsg.PrepareAdvertise(sDhcp6RxMsgs[0], serverMacAddr);
prefixInfo.mIaid = sDhcp6RxMsgs[0].mIaPds[0].mIaid;
prefixInfo.mT1 = 0;
prefixInfo.mT2 = 0;
prefixInfo.mPreferredLifetime = 1800;
prefixInfo.mValidLifetime = 1800;
prefixInfo.mPrefix = prefix;
txMsg.AddIaPrefix(prefixInfo);
sDhcp6RxMsgs.Clear();
txMsg.Send();
AdvanceTime(1);
// Listen for Request and send Reply
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
sDhcp6RxMsgs[0].ValidateAsRequest(prefix, serverMacAddr);
sDhcp6RxMsgs.Clear();
txMsg.PrepareReply(sDhcp6RxMsgs[0], serverMacAddr);
txMsg.AddIaPrefix(prefixInfo);
txMsg.Send();
delegatedPrefix = sInstance->Get<BorderRouter::Dhcp6PdClient>().GetDelegatedPrefix();
VerifyOrQuit(delegatedPrefix != nullptr);
VerifyOrQuit(delegatedPrefix->mPrefix == prefix);
VerifyOrQuit(delegatedPrefix->mAdjustedPrefix == adjustedPrefix);
VerifyOrQuit(delegatedPrefix->mT1 == 900);
VerifyOrQuit(delegatedPrefix->mT2 == 1440);
VerifyOrQuit(delegatedPrefix->mPreferredLifetime == 1800);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Wait till T1 time for client to renew");
AdvanceTime(900 * 1000 + 10);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
sDhcp6RxMsgs[0].ValidateAsRenew(prefix, serverMacAddr);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send a Reply invalidating the previously delegated prefix");
txMsg.PrepareReply(sDhcp6RxMsgs[0], serverMacAddr);
prefixInfo.mIaid = sDhcp6RxMsgs[0].mIaPds[0].mIaid;
prefixInfo.mT1 = 0;
prefixInfo.mT2 = 0;
prefixInfo.mPreferredLifetime = 0;
prefixInfo.mValidLifetime = 0;
prefixInfo.mPrefix = prefix;
txMsg.AddIaPrefix(prefixInfo);
sDhcp6RxMsgs.Clear();
txMsg.Send();
AdvanceTime(1);
delegatedPrefix = sInstance->Get<BorderRouter::Dhcp6PdClient>().GetDelegatedPrefix();
VerifyOrQuit(delegatedPrefix == nullptr);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
sInstance->Get<BorderRouter::Dhcp6PdClient>().Stop();
AdvanceTime(5 * 1000);
VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
Log("End of TestDhcp6PdServerVoidingLeaseDuringRenew");
FinalizeTest();
}
void TestDhcp6PdServerNotExtendingLeaseDuringRenew(void)
{
uint16_t heapAllocations;
Dhcp6TxMsg txMsg;
Dhcp6TxMsg::PrefixInfo prefixInfo;
Ip6::Prefix prefix;
Ip6::Prefix adjustedPrefix;
Mac::ExtAddress serverMacAddr;
const DelegatedPrefix *delegatedPrefix;
Log("--------------------------------------------------------------------------------------------");
Log("TestDhcp6PdServerNotExtendingLeaseDuringRenew()");
InitTest();
heapAllocations = sHeapAllocatedPtrs.GetLength();
serverMacAddr.GenerateRandom();
prefix = PrefixFromString("2001:4567::", 48);
adjustedPrefix = PrefixFromString("2001:4567::", 64);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start client, interact with it to successfully delegate a prefix");
sInstance->Get<BorderRouter::Dhcp6PdClient>().Start();
AdvanceTime(2200);
// Listen for Solicit and send Advertise
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 2);
sDhcp6RxMsgs[0].ValidateAsSolicit();
sDhcp6RxMsgs[1].ValidateAsSolicit();
VerifyOrQuit(sDhcp6RxMsgs[0].mTransactionId == sDhcp6RxMsgs[1].mTransactionId);
txMsg.PrepareAdvertise(sDhcp6RxMsgs[0], serverMacAddr);
prefixInfo.mIaid = sDhcp6RxMsgs[0].mIaPds[0].mIaid;
prefixInfo.mT1 = 0;
prefixInfo.mT2 = 0;
prefixInfo.mPreferredLifetime = 1800;
prefixInfo.mValidLifetime = 1800;
prefixInfo.mPrefix = prefix;
txMsg.AddIaPrefix(prefixInfo);
sDhcp6RxMsgs.Clear();
txMsg.Send();
AdvanceTime(1);
// Listen for Request and send Reply
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
sDhcp6RxMsgs[0].ValidateAsRequest(prefix, serverMacAddr);
sDhcp6RxMsgs.Clear();
txMsg.PrepareReply(sDhcp6RxMsgs[0], serverMacAddr);
txMsg.AddIaPrefix(prefixInfo);
txMsg.Send();
delegatedPrefix = sInstance->Get<BorderRouter::Dhcp6PdClient>().GetDelegatedPrefix();
VerifyOrQuit(delegatedPrefix != nullptr);
VerifyOrQuit(delegatedPrefix->mPrefix == prefix);
VerifyOrQuit(delegatedPrefix->mAdjustedPrefix == adjustedPrefix);
VerifyOrQuit(delegatedPrefix->mT1 == 900);
VerifyOrQuit(delegatedPrefix->mT2 == 1440);
VerifyOrQuit(delegatedPrefix->mPreferredLifetime == 1800);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Wait till T1 time for client to renew");
AdvanceTime(900 * 1000 + 10);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
sDhcp6RxMsgs[0].ValidateAsRenew(prefix, serverMacAddr);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send a Reply including the previously delegated prefix but with short lifetime");
txMsg.PrepareReply(sDhcp6RxMsgs[0], serverMacAddr);
prefixInfo.mIaid = sDhcp6RxMsgs[0].mIaPds[0].mIaid;
prefixInfo.mT1 = 0;
prefixInfo.mT2 = 0;
prefixInfo.mPreferredLifetime = 100;
prefixInfo.mValidLifetime = 100;
prefixInfo.mPrefix = prefix;
txMsg.AddIaPrefix(prefixInfo);
sDhcp6RxMsgs.Clear();
txMsg.Send();
AdvanceTime(1);
delegatedPrefix = sInstance->Get<BorderRouter::Dhcp6PdClient>().GetDelegatedPrefix();
VerifyOrQuit(delegatedPrefix != nullptr);
VerifyOrQuit(delegatedPrefix->mPrefix == prefix);
VerifyOrQuit(delegatedPrefix->mAdjustedPrefix == adjustedPrefix);
VerifyOrQuit(delegatedPrefix->mT1 == 100);
VerifyOrQuit(delegatedPrefix->mT2 == 100);
VerifyOrQuit(delegatedPrefix->mPreferredLifetime == 100);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Wait for the short lifetime to expire and validate that the delegated prefix is removed");
AdvanceTime(100 * 1000 + 1);
delegatedPrefix = sInstance->Get<BorderRouter::Dhcp6PdClient>().GetDelegatedPrefix();
VerifyOrQuit(delegatedPrefix == nullptr);
AdvanceTime(5 * 1000);
VerifyOrQuit(!sDhcp6RxMsgs.IsEmpty());
sDhcp6RxMsgs[0].ValidateAsSolicit();
VerifyOrQuit(sDhcp6RxMsgs[0].mElapsedTime == 0);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
sInstance->Get<BorderRouter::Dhcp6PdClient>().Stop();
AdvanceTime(5 * 1000);
VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
Log("End of TestDhcp6PdServerNotExtendingLeaseDuringRenew");
FinalizeTest();
}
void TestDhcp6PdServerReplacingPrefix(void)
{
uint16_t heapAllocations;
Dhcp6TxMsg txMsg;
Dhcp6TxMsg::PrefixInfo prefixInfo;
Ip6::Prefix prefix1;
Ip6::Prefix prefix2;
Mac::ExtAddress serverMacAddr;
const DelegatedPrefix *delegatedPrefix;
Log("--------------------------------------------------------------------------------------------");
Log("TestDhcp6PdServerReplacingPrefix()");
InitTest();
heapAllocations = sHeapAllocatedPtrs.GetLength();
serverMacAddr.GenerateRandom();
prefix1 = PrefixFromString("2001:b2d4:1111::", 64);
prefix1 = PrefixFromString("2001:b2d4:2222::", 64);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start client, interact with it to successfully delegate a prefix");
sInstance->Get<BorderRouter::Dhcp6PdClient>().Start();
AdvanceTime(2200);
// Listen for Solicit and send Advertise
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 2);
sDhcp6RxMsgs[0].ValidateAsSolicit();
sDhcp6RxMsgs[1].ValidateAsSolicit();
VerifyOrQuit(sDhcp6RxMsgs[0].mTransactionId == sDhcp6RxMsgs[1].mTransactionId);
txMsg.PrepareAdvertise(sDhcp6RxMsgs[0], serverMacAddr);
prefixInfo.mIaid = sDhcp6RxMsgs[0].mIaPds[0].mIaid;
prefixInfo.mT1 = 0;
prefixInfo.mT2 = 0;
prefixInfo.mPreferredLifetime = 1800;
prefixInfo.mValidLifetime = 1800;
prefixInfo.mPrefix = prefix1;
txMsg.AddIaPrefix(prefixInfo);
sDhcp6RxMsgs.Clear();
txMsg.Send();
AdvanceTime(1);
// Listen for Request and send Reply
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
sDhcp6RxMsgs[0].ValidateAsRequest(prefix1, serverMacAddr);
sDhcp6RxMsgs.Clear();
txMsg.PrepareReply(sDhcp6RxMsgs[0], serverMacAddr);
txMsg.AddIaPrefix(prefixInfo);
txMsg.Send();
delegatedPrefix = sInstance->Get<BorderRouter::Dhcp6PdClient>().GetDelegatedPrefix();
VerifyOrQuit(delegatedPrefix != nullptr);
VerifyOrQuit(delegatedPrefix->mPrefix == prefix1);
VerifyOrQuit(delegatedPrefix->mAdjustedPrefix == prefix1);
VerifyOrQuit(delegatedPrefix->mT1 == 900);
VerifyOrQuit(delegatedPrefix->mT2 == 1440);
VerifyOrQuit(delegatedPrefix->mPreferredLifetime == 1800);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Wait till T1 time for client to renew");
AdvanceTime(900 * 1000 + 10);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
sDhcp6RxMsgs[0].ValidateAsRenew(prefix1, serverMacAddr);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send a Reply including a new prefix with old one with short lifetime");
txMsg.PrepareReply(sDhcp6RxMsgs[0], serverMacAddr);
prefixInfo.mIaid = sDhcp6RxMsgs[0].mIaPds[0].mIaid;
prefixInfo.mT1 = 0;
prefixInfo.mT2 = 0;
prefixInfo.mPreferredLifetime = 120;
prefixInfo.mValidLifetime = 120;
prefixInfo.mPrefix = prefix1;
txMsg.AddIaPrefix(prefixInfo);
prefixInfo.mPreferredLifetime = 1800;
prefixInfo.mValidLifetime = 1800;
prefixInfo.mPrefix = prefix2;
txMsg.AddIaPrefix(prefixInfo);
sDhcp6RxMsgs.Clear();
txMsg.Send();
AdvanceTime(1);
delegatedPrefix = sInstance->Get<BorderRouter::Dhcp6PdClient>().GetDelegatedPrefix();
VerifyOrQuit(delegatedPrefix != nullptr);
VerifyOrQuit(delegatedPrefix->mPrefix == prefix2);
VerifyOrQuit(delegatedPrefix->mAdjustedPrefix == prefix2);
VerifyOrQuit(delegatedPrefix->mT1 == 900);
VerifyOrQuit(delegatedPrefix->mT2 == 1440);
VerifyOrQuit(delegatedPrefix->mPreferredLifetime == 1800);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
sInstance->Get<BorderRouter::Dhcp6PdClient>().Stop();
AdvanceTime(5 * 1000);
VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
Log("End of TestDhcp6PdServerReplacingPrefix");
FinalizeTest();
}
//---------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------
void TestDhcp6PdServerReplyWithNoBindingToRelease(void)
{
uint16_t heapAllocations;
Dhcp6TxMsg txMsg;
Dhcp6TxMsg::PrefixInfo prefixInfo;
Ip6::Prefix prefix;
Mac::ExtAddress serverMacAddr;
const DelegatedPrefix *delegatedPrefix;
Log("--------------------------------------------------------------------------------------------");
Log("TestDhcp6PdServerReplyWithNoBindingToRelease()");
InitTest();
heapAllocations = sHeapAllocatedPtrs.GetLength();
serverMacAddr.GenerateRandom();
prefix = PrefixFromString("2001:8765::", 64);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start the client and wait for the first two Solicit messages");
VerifyOrQuit(!sDhcp6ListeningEnabled);
sInstance->Get<BorderRouter::Dhcp6PdClient>().Start();
VerifyOrQuit(sDhcp6ListeningEnabled);
AdvanceTime(2200);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 2);
sDhcp6RxMsgs[0].ValidateAsSolicit();
sDhcp6RxMsgs[1].ValidateAsSolicit();
VerifyOrQuit(sDhcp6RxMsgs[0].mTransactionId == sDhcp6RxMsgs[1].mTransactionId);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send Advertisements from server offering three prefixes");
txMsg.PrepareAdvertise(sDhcp6RxMsgs[0], serverMacAddr);
prefixInfo.mIaid = sDhcp6RxMsgs[0].mIaPds[0].mIaid;
prefixInfo.mT1 = 0;
prefixInfo.mT2 = 0;
prefixInfo.mPreferredLifetime = 5000;
prefixInfo.mValidLifetime = 5000;
prefixInfo.mPrefix = prefix;
txMsg.AddIaPrefix(prefixInfo);
sDhcp6RxMsgs.Clear();
txMsg.Send();
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Validate Request message is received");
AdvanceTime(1);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
sDhcp6RxMsgs[0].ValidateAsRequest(prefix, serverMacAddr);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send Reply message");
txMsg.PrepareReply(sDhcp6RxMsgs[0], serverMacAddr);
prefixInfo.mIaid = sDhcp6RxMsgs[0].mIaPds[0].mIaid;
txMsg.AddIaPrefix(prefixInfo);
sDhcp6RxMsgs.Clear();
txMsg.Send();
delegatedPrefix = sInstance->Get<BorderRouter::Dhcp6PdClient>().GetDelegatedPrefix();
VerifyOrQuit(delegatedPrefix != nullptr);
VerifyOrQuit(delegatedPrefix->mPrefix == prefix);
VerifyOrQuit(delegatedPrefix->mAdjustedPrefix == prefix);
VerifyOrQuit(delegatedPrefix->mPreferredLifetime == prefixInfo.mPreferredLifetime);
sDhcp6RxMsgs.Clear();
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Stop the client to release the prefix");
sInstance->Get<BorderRouter::Dhcp6PdClient>().Stop();
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
sDhcp6RxMsgs[0].ValidateAsRelease(prefix, serverMacAddr);
VerifyOrQuit(sDhcp6RxMsgs[0].mElapsedTime == 0);
AdvanceTime(1200);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 2);
sDhcp6RxMsgs[1].ValidateAsRelease(prefix, serverMacAddr);
VerifyOrQuit(sDhcp6RxMsgs[1].mTransactionId == sDhcp6RxMsgs[0].mTransactionId);
VerifyOrQuit(sDhcp6RxMsgs[1].mElapsedTime > 0);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send a Reply to the Release with status code NoBinding, validate that it is accepted");
txMsg.PrepareReply(sDhcp6RxMsgs[0], serverMacAddr);
prefixInfo.mIaid = sDhcp6RxMsgs[0].mIaPds[0].mIaid;
txMsg.AddIaPrefix(prefixInfo);
txMsg.mIaPds[0].mHasStatus = true;
txMsg.mIaPds[0].mStatusCode = Dhcp6::StatusCodeOption::kNoBinding;
sDhcp6RxMsgs.Clear();
txMsg.Send();
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Validate that the client accepts the Reply and there are no more retries of Release");
AdvanceTime(30 * 1000);
VerifyOrQuit(sDhcp6RxMsgs.IsEmpty());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
Log("End of TestDhcp6PdServerReplyWithNoBindingToRelease()");
FinalizeTest();
}
void TestDhcp6PdReceiveWhenInfraIfDownOrWrongIndex(void)
{
uint16_t heapAllocations;
Dhcp6TxMsg txMsg;
Dhcp6TxMsg::PrefixInfo prefixInfo;
Ip6::Prefix prefix;
Mac::ExtAddress serverMacAddr;
const DelegatedPrefix *delegatedPrefix;
Log("--------------------------------------------------------------------------------------------");
Log("TestDhcp6PdReceiveWhenInfraIfDownOrWrongIndex()");
InitTest();
heapAllocations = sHeapAllocatedPtrs.GetLength();
serverMacAddr.GenerateRandom();
prefix = PrefixFromString("2001:2222::", 64);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Start the client and get a delegated prefix");
VerifyOrQuit(!sDhcp6ListeningEnabled);
sInstance->Get<BorderRouter::Dhcp6PdClient>().Start();
AdvanceTime(2200);
VerifyOrQuit(sDhcp6ListeningEnabled);
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 2);
sDhcp6RxMsgs[0].ValidateAsSolicit();
sDhcp6RxMsgs[1].ValidateAsSolicit();
txMsg.PrepareAdvertise(sDhcp6RxMsgs[0], serverMacAddr);
prefixInfo.mIaid = 0;
prefixInfo.mT1 = 0;
prefixInfo.mT2 = 0;
prefixInfo.mPreferredLifetime = 1800;
prefixInfo.mValidLifetime = 1800;
prefixInfo.mPrefix = prefix;
txMsg.AddIaPrefix(prefixInfo);
txMsg.Send();
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 3);
sDhcp6RxMsgs[2].ValidateAsRequest(prefix, serverMacAddr);
AdvanceTime(1);
txMsg.PrepareReply(sDhcp6RxMsgs[2], serverMacAddr);
txMsg.AddIaPrefix(prefixInfo);
txMsg.Send();
delegatedPrefix = sInstance->Get<BorderRouter::Dhcp6PdClient>().GetDelegatedPrefix();
VerifyOrQuit(delegatedPrefix != nullptr);
VerifyOrQuit(delegatedPrefix->mPrefix == prefix);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Signal infra interface is down while lease is held (listening stays enabled)");
SuccessOrQuit(otPlatInfraIfStateChanged(sInstance, kInfraIfIndex, /* aIsRunning */ false));
VerifyOrQuit(sDhcp6ListeningEnabled);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send DHCPv6 message while infra if is down - should be rejected and freed");
txMsg.Send();
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Signal infra interface is up again");
SuccessOrQuit(otPlatInfraIfStateChanged(sInstance, kInfraIfIndex, /* aIsRunning */ true));
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send DHCPv6 message with wrong interface index - should be rejected and freed");
txMsg.Send(kInfraIfIndex + 1);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Stop the client, and validate that Release message is received");
sDhcp6RxMsgs.Clear();
sInstance->Get<BorderRouter::Dhcp6PdClient>().Stop();
VerifyOrQuit(sDhcp6RxMsgs.GetLength() == 1);
sDhcp6RxMsgs[0].ValidateAsRelease(prefix, serverMacAddr);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
Log("Send Reply to Release message and check that no more messages is received");
txMsg.PrepareReply(sDhcp6RxMsgs[0], serverMacAddr);
txMsg.AddIaPrefix(prefixInfo);
sDhcp6RxMsgs.Clear();
txMsg.Send();
AdvanceTime(20 * 1000);
VerifyOrQuit(sDhcp6RxMsgs.IsEmpty());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
Log("End of TestDhcp6PdReceiveWhenInfraIfDownOrWrongIndex()");
FinalizeTest();
}
#endif // OT_CONFIG_DHCP6_PD_CLIENT_ENABLE
} // namespace ot
int main(void)
{
#if OT_CONFIG_DHCP6_PD_CLIENT_ENABLE
ot::TestDhcp6PdPrefixDelegation(/* aShortPrefix */ false);
ot::TestDhcp6PdPrefixDelegation(/* aShortPrefix */ true);
ot::TestDhcp6PdSolicitRetries();
ot::TestDhcp6PdRequestRetries();
ot::TestDhcp6PdSelectBetweenMultipleServers();
ot::TestDhcp6PdServerWithMaxPreferrence();
ot::TestDhcp6PdServerOfferingMultiplePrefixes();
ot::TestDhcp6PdInvalidOrUnusablePrefix();
ot::TestDhcp6PdLifetimeT1AndT2Adjustments();
ot::TestDhcp6PdServerVoidingLeaseDuringRenew();
ot::TestDhcp6PdServerNotExtendingLeaseDuringRenew();
ot::TestDhcp6PdServerReplacingPrefix();
ot::TestDhcp6PdServerReplyWithNoBindingToRelease();
ot::TestDhcp6PdReceiveWhenInfraIfDownOrWrongIndex();
printf("All tests passed\n");
#else
printf("DHCP6_PD_CLIENT_ENABLE feature is not enabled\n");
#endif
return 0;
}