Files
openthread/tests/unit/test_dhcp6_pd_client.cpp
T
Jonathan Hui dcbd870245 [dhcp6] obsolete DHCPv6 Server Unicast Option per RFC 9915 (#13146)
This commit updates the DHCPv6 Prefix Delegation (PD) client to
comply with RFC 9915, which obsoletes the Server Unicast option
(Option 12) and the UseMulticast status code.

Changes:
- Removed `mServerAddress` and `ProcessServerUnicastOption()` from
  `Dhcp6PdClient`.
- Modified `Dhcp6PdClient::SendMessage` to always transmit via
  multicast to `ff02::1:2`.
- Removed `UseMulticast` status code handling in `HandleReply()`.
- Added `otMessageFree` weak stub in simulation platform's
  `infra_if.c` to resolve linking errors on simulation radio-only
  targets when DHCPv6 PD client is enabled.
- Updated `test_dhcp6_pd_client.cpp` to expect multicast and
  removed the obsolete UseMulticast test case.
2026-05-27 10:31:00 -07:00

2622 lines
91 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(void);
};
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(void)
{
Message *message;
message = sInstance->Get<MessagePool>().Allocate(Message::kTypeOther);
VerifyOrQuit(message != nullptr);
PrepareMessage(*message);
LogMsg("Sending");
otPlatInfraIfDhcp6PdClientHandleReceived(sInstance, message, kInfraIfIndex);
}
//----------------------------------------------------------------------------------------------------------------------
// 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();
}
#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();
printf("All tests passed\n");
#else
printf("DHCP6_PD_CLIENT_ENABLE feature is not enabled\n");
#endif
return 0;
}