/* * 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 #include "test_platform.h" #include "test_util.hpp" #include #include #include #include #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 "::." #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 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 { 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 InfoString; struct IaPrefix : public Clearable { Ip6::Prefix mPrefix; uint32_t mPreferredLifetime; uint32_t mValidLifetime; }; using IaPrefixArray = Array; struct IaPd : Clearable { 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; 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; 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(&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(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(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(mMsgType)); header.SetTransactionId(mTransactionId); SuccessOrQuit(aMessage.Append(header)); if (mHasStatus) { statusOption.Init(); statusOption.SetStatusCode(static_cast(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(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().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().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 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(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().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().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().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().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().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().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().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().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().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().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().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().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().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().GetDelegatedPrefix() == nullptr); Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); Log("Stop the client and make sure there are no more transmissions"); sDhcp6RxMsgs.Clear(); sInstance->Get().Stop(); VerifyOrQuit(!sDhcp6ListeningEnabled); AdvanceTime(200 * 1000); VerifyOrQuit(sDhcp6RxMsgs.IsEmpty()); Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); Log("Start again and check the Solicit message tx"); sInstance->Get().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().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().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().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().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().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().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().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().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().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().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().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().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().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().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().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().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().GetDelegatedPrefix(); VerifyOrQuit(delegatedPrefix == nullptr); Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); sInstance->Get().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().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().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().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().GetDelegatedPrefix(); VerifyOrQuit(delegatedPrefix == nullptr); AdvanceTime(5 * 1000); VerifyOrQuit(!sDhcp6RxMsgs.IsEmpty()); sDhcp6RxMsgs[0].ValidateAsSolicit(); VerifyOrQuit(sDhcp6RxMsgs[0].mElapsedTime == 0); Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); sInstance->Get().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().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().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().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().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().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().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().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; }