diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 60985b9d8..cd586c0cb 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -547,11 +547,12 @@ openthread_core_files = [ "meshcop/timestamp.hpp", "net/checksum.cpp", "net/checksum.hpp", - "net/dhcp6.hpp", "net/dhcp6_client.cpp", "net/dhcp6_client.hpp", "net/dhcp6_server.cpp", "net/dhcp6_server.hpp", + "net/dhcp6_types.cpp", + "net/dhcp6_types.hpp", "net/dns_client.cpp", "net/dns_client.hpp", "net/dns_dso.cpp", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 4509311cc..c01731ee8 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -170,6 +170,7 @@ set(COMMON_SOURCES net/checksum.cpp net/dhcp6_client.cpp net/dhcp6_server.cpp + net/dhcp6_types.cpp net/dns_client.cpp net/dns_dso.cpp net/dns_platform.cpp diff --git a/src/core/instance/instance.hpp b/src/core/instance/instance.hpp index 6a4ce30d8..1976f9c1e 100644 --- a/src/core/instance/instance.hpp +++ b/src/core/instance/instance.hpp @@ -91,9 +91,9 @@ #include "meshcop/joiner_router.hpp" #include "meshcop/meshcop_leader.hpp" #include "meshcop/network_name.hpp" -#include "net/dhcp6.hpp" #include "net/dhcp6_client.hpp" #include "net/dhcp6_server.hpp" +#include "net/dhcp6_types.hpp" #include "net/dns_client.hpp" #include "net/dns_dso.hpp" #include "net/dnssd.hpp" diff --git a/src/core/net/dhcp6_client.cpp b/src/core/net/dhcp6_client.cpp index 0be16e6b1..ec71f7148 100644 --- a/src/core/net/dhcp6_client.cpp +++ b/src/core/net/dhcp6_client.cpp @@ -263,8 +263,6 @@ void Client::Solicit(uint16_t aRloc16) SuccessOrExit(error = AppendElapsedTimeOption(*message)); SuccessOrExit(error = AppendClientIdOption(*message)); SuccessOrExit(error = AppendIaNaOption(*message, aRloc16)); - // specify which prefixes to solicit - SuccessOrExit(error = AppendIaAddressOption(*message, aRloc16)); SuccessOrExit(error = AppendRapidCommitOption(*message)); #if OPENTHREAD_ENABLE_DHCP6_MULTICAST_SOLICIT @@ -322,73 +320,44 @@ Error Client::AppendClientIdOption(Message &aMessage) Error Client::AppendIaNaOption(Message &aMessage, uint16_t aRloc16) { - Error error = kErrorNone; - uint8_t count = 0; - uint16_t length = 0; - IaNaOption option; + Error error = kErrorNone; + uint16_t optionOffset; + IaNaOption option; + IaAddressOption addressOption; VerifyOrExit(mIdentityAssociationCurrent != nullptr, error = kErrorDrop); - for (IdentityAssociation &idAssociation : mIdentityAssociations) - { - if (idAssociation.mStatus == kIaStatusInvalid || idAssociation.mStatus == kIaStatusSolicitReplied) - { - continue; - } - - if (idAssociation.mPrefixAgentRloc == aRloc16) - { - count++; - } - } - - // compute the right length - length = sizeof(IaNaOption) + sizeof(IaAddressOption) * count - sizeof(Option); + optionOffset = aMessage.GetLength(); option.Init(); - option.SetLength(length); option.SetIaid(0); option.SetT1(0); option.SetT2(0); SuccessOrExit(error = aMessage.Append(option)); -exit: - return error; -} + // Append `IaAddressOption`s within the `IaNa` -Error Client::AppendIaAddressOption(Message &aMessage, uint16_t aRloc16) -{ - Error error = kErrorNone; - IaAddressOption option; - - VerifyOrExit(mIdentityAssociationCurrent, error = kErrorDrop); - - option.Init(); + addressOption.Init(); for (IdentityAssociation &idAssociation : mIdentityAssociations) { if ((idAssociation.mStatus == kIaStatusSolicit || idAssociation.mStatus == kIaStatusSoliciting) && (idAssociation.mPrefixAgentRloc == aRloc16)) { - option.SetAddress(idAssociation.mNetifAddress.GetAddress()); - option.SetPreferredLifetime(0); - option.SetValidLifetime(0); - SuccessOrExit(error = aMessage.Append(option)); + addressOption.SetAddress(idAssociation.mNetifAddress.GetAddress()); + addressOption.SetPreferredLifetime(0); + addressOption.SetValidLifetime(0); + SuccessOrExit(error = aMessage.Append(addressOption)); } } + // Update the `IaNaOption` length. + Option::UpdateOptionLengthInMessage(aMessage, optionOffset); + exit: return error; } -Error Client::AppendRapidCommitOption(Message &aMessage) -{ - RapidCommitOption option; - - option.Init(); - return aMessage.Append(option); -} - void Client::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { OT_UNUSED_VARIABLE(aMessageInfo); @@ -407,31 +376,13 @@ exit: return; } -void Client::ProcessReply(Message &aMessage) +void Client::ProcessReply(const Message &aMessage) { - uint16_t offset = aMessage.GetOffset(); - uint16_t length = aMessage.GetLength() - aMessage.GetOffset(); - uint16_t optionOffset; - - if ((optionOffset = FindOption(aMessage, offset, length, Option::kStatusCode)) > 0) - { - SuccessOrExit(ProcessStatusCodeOption(aMessage, optionOffset)); - } - - // Server Identifier - VerifyOrExit((optionOffset = FindOption(aMessage, offset, length, Option::kServerId)) > 0); - SuccessOrExit(ProcessServerIdOption(aMessage, optionOffset)); - - // Client Identifier - VerifyOrExit((optionOffset = FindOption(aMessage, offset, length, Option::kClientId)) > 0); - SuccessOrExit(ProcessClientIdOption(aMessage, optionOffset)); - - // Rapid Commit - VerifyOrExit(FindOption(aMessage, offset, length, Option::kRapidCommit) > 0); - - // IA_NA - VerifyOrExit((optionOffset = FindOption(aMessage, offset, length, Option::kIaNa)) > 0); - SuccessOrExit(ProcessIaNaOption(aMessage, optionOffset)); + VerifyOrExit(StatusCodeOption::ReadStatusFrom(aMessage) == StatusCodeOption::kSuccess); + SuccessOrExit(ProcessServerIdOption(aMessage)); + SuccessOrExit(ProcessClientIdOption(aMessage)); + SuccessOrExit(RapidCommitOption::FindIn(aMessage)); + SuccessOrExit(ProcessIaNaOption(aMessage)); HandleTrickleTimer(); @@ -439,36 +390,15 @@ exit: return; } -uint16_t Client::FindOption(Message &aMessage, uint16_t aOffset, uint16_t aLength, Option::Code aCode) +Error Client::ProcessServerIdOption(const Message &aMessage) { - uint32_t offset = aOffset; - uint16_t end = aOffset + aLength; - uint16_t rval = 0; - - while (offset <= end) - { - Option option; - - SuccessOrExit(aMessage.Read(static_cast(offset), option)); - - if (option.GetCode() == aCode) - { - ExitNow(rval = static_cast(offset)); - } - - offset += sizeof(option) + option.GetLength(); - } - -exit: - return rval; -} - -Error Client::ProcessServerIdOption(Message &aMessage, uint16_t aOffset) -{ - Error error = kErrorNone; + Error error; + OffsetRange offsetRange; ServerIdOption option; - SuccessOrExit(aMessage.Read(aOffset, option)); + SuccessOrExit(error = Option::FindOption(aMessage, Option::kServerId, offsetRange)); + SuccessOrExit(error = aMessage.Read(offsetRange, option)); + VerifyOrExit(((option.GetDuidType() == kDuidLinkLayerAddressPlusTime) && (option.GetDuidHardwareType() == kHardwareTypeEthernet)) || ((option.GetLength() == (sizeof(option) - sizeof(Option))) && @@ -479,80 +409,71 @@ exit: return error; } -Error Client::ProcessClientIdOption(Message &aMessage, uint16_t aOffset) +Error Client::ProcessClientIdOption(const Message &aMessage) { - Error error = kErrorNone; + Error error; + OffsetRange offsetRange; ClientIdOption option; Mac::ExtAddress eui64; Get().GetIeeeEui64(eui64); - SuccessOrExit(error = aMessage.Read(aOffset, option)); - VerifyOrExit( - (option.GetLength() == (sizeof(option) - sizeof(Option))) && (option.GetDuidType() == kDuidLinkLayerAddress) && - (option.GetDuidHardwareType() == kHardwareTypeEui64) && (option.GetDuidLinkLayerAddress() == eui64), - error = kErrorParse); + SuccessOrExit(error = Option::FindOption(aMessage, Option::kClientId, offsetRange)); + SuccessOrExit(error = aMessage.Read(offsetRange, option)); + + VerifyOrExit(option.GetDuidType() == kDuidLinkLayerAddress, error = kErrorParse); + VerifyOrExit(option.GetDuidHardwareType() == kHardwareTypeEui64, error = kErrorParse); + VerifyOrExit(option.GetDuidLinkLayerAddress() == eui64, error = kErrorParse); + exit: return error; } -Error Client::ProcessIaNaOption(Message &aMessage, uint16_t aOffset) +Error Client::ProcessIaNaOption(const Message &aMessage) { - Error error = kErrorNone; - IaNaOption option; - uint16_t optionOffset; - uint16_t length; + Error error = kErrorNone; + OffsetRange offsetRange; + IaNaOption option; - SuccessOrExit(error = aMessage.Read(aOffset, option)); + SuccessOrExit(error = Option::FindOption(aMessage, Option::kIaNa, offsetRange)); + SuccessOrExit(error = aMessage.Read(offsetRange, option)); - aOffset += sizeof(option); - length = option.GetLength() - (sizeof(option) - sizeof(Option)); + offsetRange.AdvanceOffset(sizeof(IaNaOption)); - VerifyOrExit(length <= aMessage.GetLength() - aOffset, error = kErrorParse); + // Iterate over and check the sub-options within `IaNaOption`. - if ((optionOffset = FindOption(aMessage, aOffset, length, Option::kStatusCode)) > 0) + VerifyOrExit(StatusCodeOption::ReadStatusFrom(aMessage, offsetRange) == StatusCodeOption::kSuccess, + error = kErrorFailed); + + while (!offsetRange.IsEmpty()) { - SuccessOrExit(error = ProcessStatusCodeOption(aMessage, optionOffset)); - } + OffsetRange subOffsetRange; + IaAddressOption addressOption; - while (length > 0) - { - if ((optionOffset = FindOption(aMessage, aOffset, length, Option::kIaAddress)) == 0) + error = Option::FindOption(aMessage, offsetRange, Option::kIaAddress, subOffsetRange); + + if (error == kErrorNotFound) { + error = kErrorNone; ExitNow(); } - SuccessOrExit(error = ProcessIaAddressOption(aMessage, optionOffset)); + SuccessOrExit(error); - length -= ((optionOffset - aOffset) + sizeof(IaAddressOption)); - aOffset = optionOffset + sizeof(IaAddressOption); + SuccessOrExit(error = aMessage.Read(subOffsetRange, addressOption)); + SuccessOrExit(error = ProcessIaAddressOption(addressOption)); + + // Update `offsetRange` to after the parsed `IaAddressOption` + offsetRange.InitFromRange(subOffsetRange.GetEndOffset(), offsetRange.GetEndOffset()); } exit: return error; } -Error Client::ProcessStatusCodeOption(Message &aMessage, uint16_t aOffset) +Error Client::ProcessIaAddressOption(const IaAddressOption &aOption) { - Error error = kErrorNone; - StatusCodeOption option; - - SuccessOrExit(error = aMessage.Read(aOffset, option)); - VerifyOrExit((option.GetLength() >= sizeof(option) - sizeof(Option)) && - (option.GetStatusCode() == StatusCodeOption::kSuccess), - error = kErrorParse); - -exit: - return error; -} - -Error Client::ProcessIaAddressOption(Message &aMessage, uint16_t aOffset) -{ - Error error; - IaAddressOption option; - - SuccessOrExit(error = aMessage.Read(aOffset, option)); - VerifyOrExit(option.GetLength() == sizeof(option) - sizeof(Option), error = kErrorParse); + Error error = kErrorNone; for (IdentityAssociation &idAssociation : mIdentityAssociations) { @@ -561,15 +482,15 @@ Error Client::ProcessIaAddressOption(Message &aMessage, uint16_t aOffset) continue; } - if (idAssociation.mNetifAddress.GetAddress().PrefixMatch(option.GetAddress()) >= + if (idAssociation.mNetifAddress.GetAddress().PrefixMatch(aOption.GetAddress()) >= idAssociation.mNetifAddress.mPrefixLength) { - idAssociation.mNetifAddress.mAddress = option.GetAddress(); - idAssociation.mPreferredLifetime = option.GetPreferredLifetime(); - idAssociation.mValidLifetime = option.GetValidLifetime(); + idAssociation.mNetifAddress.mAddress = aOption.GetAddress(); + idAssociation.mPreferredLifetime = aOption.GetPreferredLifetime(); + idAssociation.mValidLifetime = aOption.GetValidLifetime(); idAssociation.mNetifAddress.mAddressOrigin = Ip6::Netif::kOriginDhcp6; - idAssociation.mNetifAddress.mPreferred = option.GetPreferredLifetime() != 0; - idAssociation.mNetifAddress.mValid = option.GetValidLifetime() != 0; + idAssociation.mNetifAddress.mPreferred = aOption.GetPreferredLifetime() != 0; + idAssociation.mNetifAddress.mValid = aOption.GetValidLifetime() != 0; idAssociation.mStatus = kIaStatusSolicitReplied; Get().AddUnicastAddress(idAssociation.mNetifAddress); ExitNow(error = kErrorNone); diff --git a/src/core/net/dhcp6_client.hpp b/src/core/net/dhcp6_client.hpp index 0480184bb..c841c8b9f 100644 --- a/src/core/net/dhcp6_client.hpp +++ b/src/core/net/dhcp6_client.hpp @@ -46,7 +46,7 @@ #include "common/trickle_timer.hpp" #include "mac/mac.hpp" #include "mac/mac_types.hpp" -#include "net/dhcp6.hpp" +#include "net/dhcp6_types.hpp" #include "net/netif.hpp" #include "net/udp6.hpp" @@ -110,19 +110,16 @@ private: Error AppendHeader(Message &aMessage); Error AppendClientIdOption(Message &aMessage); Error AppendIaNaOption(Message &aMessage, uint16_t aRloc16); - Error AppendIaAddressOption(Message &aMessage, uint16_t aRloc16); Error AppendElapsedTimeOption(Message &aMessage); - Error AppendRapidCommitOption(Message &aMessage); + Error AppendRapidCommitOption(Message &aMessage) { return RapidCommitOption::AppendTo(aMessage); } void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - void ProcessReply(Message &aMessage); - uint16_t FindOption(Message &aMessage, uint16_t aOffset, uint16_t aLength, Option::Code aCode); - Error ProcessServerIdOption(Message &aMessage, uint16_t aOffset); - Error ProcessClientIdOption(Message &aMessage, uint16_t aOffset); - Error ProcessIaNaOption(Message &aMessage, uint16_t aOffset); - Error ProcessStatusCodeOption(Message &aMessage, uint16_t aOffset); - Error ProcessIaAddressOption(Message &aMessage, uint16_t aOffset); + void ProcessReply(const Message &aMessage); + Error ProcessServerIdOption(const Message &aMessage); + Error ProcessClientIdOption(const Message &aMessage); + Error ProcessIaNaOption(const Message &aMessage); + Error ProcessIaAddressOption(const IaAddressOption &aOption); void HandleNotifierEvents(Events aEvents); void UpdateAddresses(void); diff --git a/src/core/net/dhcp6_server.cpp b/src/core/net/dhcp6_server.cpp index db3b353d0..069236b79 100644 --- a/src/core/net/dhcp6_server.cpp +++ b/src/core/net/dhcp6_server.cpp @@ -191,138 +191,125 @@ exit: void Server::ProcessSolicit(Message &aMessage, const Ip6::Address &aDst, const TransactionId &aTransactionId) { - IaNaOption iana; - ClientIdOption clientIdentifier; - uint16_t optionOffset; - uint16_t offset = aMessage.GetOffset(); - uint16_t length = aMessage.GetLength() - aMessage.GetOffset(); + uint32_t iaid; + ClientIdOption clientIdOption; + OffsetRange offsetRange; - // Client Identifier (discard if not present) - VerifyOrExit((optionOffset = FindOption(aMessage, offset, length, Option::kClientId)) > 0); - SuccessOrExit(ProcessClientIdOption(aMessage, optionOffset, clientIdentifier)); + SuccessOrExit(ProcessClientIdOption(aMessage, clientIdOption)); // Server Identifier (assuming Rapid Commit, discard if present) - VerifyOrExit(FindOption(aMessage, offset, length, Option::kServerId) == 0); + VerifyOrExit(Option::FindOption(aMessage, Option::kServerId, offsetRange) == kErrorNotFound); // Rapid Commit (assuming Rapid Commit, discard if not present) - VerifyOrExit(FindOption(aMessage, offset, length, Option::kRapidCommit) > 0); + SuccessOrExit(RapidCommitOption::FindIn(aMessage)); // Elapsed Time if present - if ((optionOffset = FindOption(aMessage, offset, length, Option::kElapsedTime)) > 0) - { - SuccessOrExit(ProcessElapsedTimeOption(aMessage, optionOffset)); - } + SuccessOrExit(ProcessElapsedTimeOption(aMessage)); // IA_NA (discard if not present) - VerifyOrExit((optionOffset = FindOption(aMessage, offset, length, Option::kIaNa)) > 0); - SuccessOrExit(ProcessIaNaOption(aMessage, optionOffset, iana)); + SuccessOrExit(ProcessIaNaOption(aMessage, iaid)); - SuccessOrExit(SendReply(aDst, aTransactionId, clientIdentifier, iana)); + SuccessOrExit(SendReply(aDst, aTransactionId, clientIdOption, iaid)); exit: return; } -uint16_t Server::FindOption(Message &aMessage, uint16_t aOffset, uint16_t aLength, Option::Code aCode) +Error Server::ProcessClientIdOption(const Message &aMessage, ClientIdOption &aClientIdOption) { - uint16_t end = aOffset + aLength; - uint16_t rval = 0; + Error error = kErrorNone; + OffsetRange offsetRange; - while (aOffset <= end) + SuccessOrExit(error = Option::FindOption(aMessage, Option::kClientId, offsetRange)); + SuccessOrExit(error = aMessage.Read(offsetRange, aClientIdOption)); + VerifyOrExit(aClientIdOption.GetDuidType() == kDuidLinkLayerAddress, error = kErrorParse); + VerifyOrExit(aClientIdOption.GetDuidHardwareType() == kHardwareTypeEui64, error = kErrorParse); +exit: + return error; +} + +Error Server::ProcessElapsedTimeOption(const Message &aMessage) +{ + Error error = kErrorNone; + OffsetRange offsetRange; + + // Check Elapsed Time to be valid only if present. + + error = Option::FindOption(aMessage, Option::kElapsedTime, offsetRange); + + if (error == kErrorNotFound) { - Option option; - - SuccessOrExit(aMessage.Read(aOffset, option)); - - if (option.GetCode() == aCode) - { - ExitNow(rval = aOffset); - } - - aOffset += sizeof(option) + option.GetLength(); + ExitNow(); } -exit: - return rval; -} -Error Server::ProcessClientIdOption(Message &aMessage, uint16_t aOffset, ClientIdOption &aClientIdOption) -{ - Error error = kErrorNone; + SuccessOrExit(error); + + VerifyOrExit(offsetRange.GetLength() >= sizeof(ElapsedTimeOption), error = kErrorParse); - SuccessOrExit(error = aMessage.Read(aOffset, aClientIdOption)); - VerifyOrExit((aClientIdOption.GetLength() == sizeof(aClientIdOption) - sizeof(Option)) && - (aClientIdOption.GetDuidType() == kDuidLinkLayerAddress) && - (aClientIdOption.GetDuidHardwareType() == kHardwareTypeEui64), - error = kErrorParse); exit: return error; } -Error Server::ProcessElapsedTimeOption(Message &aMessage, uint16_t aOffset) +Error Server::ProcessIaNaOption(const Message &aMessage, uint32_t &aIaid) { - Error error = kErrorNone; - ElapsedTimeOption option; + Error error = kErrorNone; + OffsetRange offsetRange; + IaNaOption iaNaOption; - SuccessOrExit(error = aMessage.Read(aOffset, option)); - VerifyOrExit(option.GetLength() == sizeof(option) - sizeof(Option), error = kErrorParse); -exit: - return error; -} + SuccessOrExit(error = Option::FindOption(aMessage, Option::kIaNa, offsetRange)); + SuccessOrExit(error = aMessage.Read(offsetRange, iaNaOption)); -Error Server::ProcessIaNaOption(Message &aMessage, uint16_t aOffset, IaNaOption &aIaNaOption) -{ - Error error = kErrorNone; - uint16_t optionOffset; - uint16_t length; + aIaid = iaNaOption.GetIaid(); - SuccessOrExit(error = aMessage.Read(aOffset, aIaNaOption)); - - aOffset += sizeof(aIaNaOption); - length = aIaNaOption.GetLength() + sizeof(Option) - sizeof(IaNaOption); - - VerifyOrExit(length <= aMessage.GetLength() - aOffset, error = kErrorParse); + offsetRange.AdvanceOffset(sizeof(IaNaOption)); mPrefixAgentsMask = 0; - while (length > 0) - { - VerifyOrExit((optionOffset = FindOption(aMessage, aOffset, length, Option::kIaAddress)) > 0); - SuccessOrExit(error = ProcessIaAddressOption(aMessage, optionOffset)); + // Iterate and parse sub-options within `IaNaOption`. - length -= ((optionOffset - aOffset) + sizeof(IaAddressOption)); - aOffset = optionOffset + sizeof(IaAddressOption); + while (!offsetRange.IsEmpty()) + { + OffsetRange subOffsetRange; + IaAddressOption addressOption; + + error = Option::FindOption(aMessage, offsetRange, Option::kIaAddress, subOffsetRange); + + if (error == kErrorNotFound) + { + error = kErrorNone; + ExitNow(); + } + + SuccessOrExit(error); + + SuccessOrExit(error = aMessage.Read(subOffsetRange, addressOption)); + ProcessIaAddressOption(addressOption); + + // Update `offsetRange` to after the parsed `IaAddressOption` + offsetRange.InitFromRange(subOffsetRange.GetEndOffset(), offsetRange.GetEndOffset()); } exit: return error; } -Error Server::ProcessIaAddressOption(Message &aMessage, uint16_t aOffset) +void Server::ProcessIaAddressOption(const IaAddressOption &aAddressOption) { - Error error = kErrorNone; - IaAddressOption option; - - SuccessOrExit(error = aMessage.Read(aOffset, option)); - VerifyOrExit(option.GetLength() == sizeof(option) - sizeof(Option), error = kErrorParse); - // mask matching prefix for (uint16_t i = 0; i < GetArrayLength(mPrefixAgents); i++) { - if (mPrefixAgents[i].IsValid() && mPrefixAgents[i].IsPrefixMatch(option.GetAddress())) + if (mPrefixAgents[i].IsValid() && mPrefixAgents[i].IsPrefixMatch(aAddressOption.GetAddress())) { mPrefixAgentsMask |= (1 << i); break; } } - -exit: - return error; } -Error Server::SendReply(const Ip6::Address &aDst, - const TransactionId &aTransactionId, - ClientIdOption &aClientIdOption, - IaNaOption &aIaNaOption) +Error Server::SendReply(const Ip6::Address &aDst, + const TransactionId &aTransactionId, + const ClientIdOption &aClientIdOption, + uint32_t aIaid) { Error error = kErrorNone; Ip6::MessageInfo messageInfo; @@ -332,9 +319,7 @@ Error Server::SendReply(const Ip6::Address &aDst, SuccessOrExit(error = AppendHeader(*message, aTransactionId)); SuccessOrExit(error = AppendServerIdOption(*message)); SuccessOrExit(error = AppendClientIdOption(*message, aClientIdOption)); - SuccessOrExit(error = AppendIaNaOption(*message, aIaNaOption)); - SuccessOrExit(error = AppendStatusCodeOption(*message, StatusCodeOption::kSuccess)); - SuccessOrExit(error = AppendIaAddressOption(*message, aClientIdOption)); + SuccessOrExit(error = AppendIaNaOption(*message, aIaid, aClientIdOption.GetDuidLinkLayerAddress())); SuccessOrExit(error = AppendRapidCommitOption(*message)); messageInfo.SetPeerAddr(aDst); @@ -356,7 +341,7 @@ Error Server::AppendHeader(Message &aMessage, const TransactionId &aTransactionI return aMessage.Append(header); } -Error Server::AppendClientIdOption(Message &aMessage, ClientIdOption &aClientIdOption) +Error Server::AppendClientIdOption(Message &aMessage, const ClientIdOption &aClientIdOption) { return aMessage.Append(aClientIdOption); } @@ -379,32 +364,25 @@ exit: return error; } -Error Server::AppendIaNaOption(Message &aMessage, IaNaOption &aIaNaOption) +Error Server::AppendIaNaOption(Message &aMessage, uint32_t aIaid, const Mac::ExtAddress &aClientAddress) { - Error error = kErrorNone; - uint16_t length = 0; + Error error = kErrorNone; + IaNaOption iaNaOption; + uint16_t optionOffset; - if (mPrefixAgentsMask) - { - for (uint16_t i = 0; i < GetArrayLength(mPrefixAgents); i++) - { - if (mPrefixAgentsMask & (1 << i)) - { - length += sizeof(IaAddressOption); - } - } - } - else - { - length += sizeof(IaAddressOption) * mPrefixAgentsCount; - } + optionOffset = aMessage.GetLength(); - length += sizeof(IaNaOption) + sizeof(StatusCodeOption) - sizeof(Option); + iaNaOption.Init(); + iaNaOption.SetIaid(aIaid); + iaNaOption.SetT1(IaNaOption::kDefaultT1); + iaNaOption.SetT2(IaNaOption::kDefaultT2); + SuccessOrExit(error = aMessage.Append(iaNaOption)); - aIaNaOption.SetLength(length); - aIaNaOption.SetT1(IaNaOption::kDefaultT1); - aIaNaOption.SetT2(IaNaOption::kDefaultT2); - SuccessOrExit(error = aMessage.Append(aIaNaOption)); + SuccessOrExit(error = AppendStatusCodeOption(aMessage, StatusCodeOption::kSuccess)); + SuccessOrExit(error = AppendIaAddressOptions(aMessage, aClientAddress)); + + // Update IaNaOption length + Option::UpdateOptionLengthInMessage(aMessage, optionOffset); exit: return error; @@ -419,7 +397,7 @@ Error Server::AppendStatusCodeOption(Message &aMessage, StatusCodeOption::Status return aMessage.Append(option); } -Error Server::AppendIaAddressOption(Message &aMessage, ClientIdOption &aClientIdOption) +Error Server::AppendIaAddressOptions(Message &aMessage, const Mac::ExtAddress &aClientAddress) { Error error = kErrorNone; @@ -430,8 +408,8 @@ Error Server::AppendIaAddressOption(Message &aMessage, ClientIdOption &aClientId { if (mPrefixAgentsMask & (1 << i)) { - SuccessOrExit(error = - AddIaAddressOption(aMessage, mPrefixAgents[i].GetPrefixAsAddress(), aClientIdOption)); + SuccessOrExit( + error = AppendIaAddressOption(aMessage, mPrefixAgents[i].GetPrefixAsAddress(), aClientAddress)); } } } @@ -442,7 +420,8 @@ Error Server::AppendIaAddressOption(Message &aMessage, ClientIdOption &aClientId { if (prefixAgent.IsValid()) { - SuccessOrExit(error = AddIaAddressOption(aMessage, prefixAgent.GetPrefixAsAddress(), aClientIdOption)); + SuccessOrExit(error = + AppendIaAddressOption(aMessage, prefixAgent.GetPrefixAsAddress(), aClientAddress)); } } } @@ -451,14 +430,16 @@ exit: return error; } -Error Server::AddIaAddressOption(Message &aMessage, const Ip6::Address &aPrefix, ClientIdOption &aClientIdOption) +Error Server::AppendIaAddressOption(Message &aMessage, + const Ip6::Address &aPrefix, + const Mac::ExtAddress &aClientAddress) { Error error = kErrorNone; IaAddressOption option; option.Init(); option.GetAddress().SetPrefix(aPrefix.mFields.m8, OT_IP6_PREFIX_BITSIZE); - option.GetAddress().GetIid().SetFromExtAddress(aClientIdOption.GetDuidLinkLayerAddress()); + option.GetAddress().GetIid().SetFromExtAddress(aClientAddress); option.SetPreferredLifetime(IaAddressOption::kDefaultPreferredLifetime); option.SetValidLifetime(IaAddressOption::kDefaultValidLifetime); SuccessOrExit(error = aMessage.Append(option)); @@ -467,14 +448,6 @@ exit: return error; } -Error Server::AppendRapidCommitOption(Message &aMessage) -{ - RapidCommitOption option; - - option.Init(); - return aMessage.Append(option); -} - } // namespace Dhcp6 } // namespace ot diff --git a/src/core/net/dhcp6_server.hpp b/src/core/net/dhcp6_server.hpp index 42547ed60..6e6ea289e 100644 --- a/src/core/net/dhcp6_server.hpp +++ b/src/core/net/dhcp6_server.hpp @@ -43,7 +43,7 @@ #include "common/notifier.hpp" #include "mac/mac.hpp" #include "mac/mac_types.hpp" -#include "net/dhcp6.hpp" +#include "net/dhcp6_types.hpp" #include "net/udp6.hpp" #include "thread/network_data_leader.hpp" @@ -163,37 +163,30 @@ private: static constexpr uint16_t kNumPrefixes = OPENTHREAD_CONFIG_DHCP6_SERVER_NUM_PREFIXES; - void HandleNotifierEvents(Events aEvents); - void UpdateService(void); - - void Start(void); - void Stop(void); - - void AddPrefixAgent(const Ip6::Prefix &aIp6Prefix, const Lowpan::Context &aContext); - + void HandleNotifierEvents(Events aEvents); + void UpdateService(void); + void Start(void); + void Stop(void); + void AddPrefixAgent(const Ip6::Prefix &aIp6Prefix, const Lowpan::Context &aContext); Error AppendHeader(Message &aMessage, const TransactionId &aTransactionId); - Error AppendClientIdOption(Message &aMessage, ClientIdOption &aClientIdOption); + Error AppendClientIdOption(Message &aMessage, const ClientIdOption &aClientIdOption); Error AppendServerIdOption(Message &aMessage); - Error AppendIaNaOption(Message &aMessage, IaNaOption &aIaNaOption); + Error AppendIaNaOption(Message &aMessage, uint32_t aIaid, const Mac::ExtAddress &aClientAddress); Error AppendStatusCodeOption(Message &aMessage, StatusCodeOption::Status aStatusCode); - Error AppendIaAddressOption(Message &aMessage, ClientIdOption &aClientIdOption); - Error AppendRapidCommitOption(Message &aMessage); + Error AppendIaAddressOptions(Message &aMessage, const Mac::ExtAddress &aClientAddress); + Error AppendRapidCommitOption(Message &aMessage) { return RapidCommitOption::AppendTo(aMessage); } Error AppendVendorSpecificInformation(Message &aMessage); - - Error AddIaAddressOption(Message &aMessage, const Ip6::Address &aPrefix, ClientIdOption &aClientIdOption); + Error AppendIaAddressOption(Message &aMessage, const Ip6::Address &aPrefix, const Mac::ExtAddress &aClientAddress); void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); void ProcessSolicit(Message &aMessage, const Ip6::Address &aDst, const TransactionId &aTransactionId); - - uint16_t FindOption(Message &aMessage, uint16_t aOffset, uint16_t aLength, Option::Code aCode); - Error ProcessClientIdOption(Message &aMessage, uint16_t aOffset, ClientIdOption &aClientIdOption); - Error ProcessIaNaOption(Message &aMessage, uint16_t aOffset, IaNaOption &aIaNaOption); - Error ProcessIaAddressOption(Message &aMessage, uint16_t aOffset); - Error ProcessElapsedTimeOption(Message &aMessage, uint16_t aOffset); - - Error SendReply(const Ip6::Address &aDst, - const TransactionId &aTransactionId, - ClientIdOption &aClientIdOption, - IaNaOption &aIaNaOption); + Error ProcessClientIdOption(const Message &aMessage, ClientIdOption &aClientIdOption); + Error ProcessIaNaOption(const Message &aMessage, uint32_t &aIaid); + void ProcessIaAddressOption(const IaAddressOption &aAddressOption); + Error ProcessElapsedTimeOption(const Message &aMessage); + Error SendReply(const Ip6::Address &aDst, + const TransactionId &aTransactionId, + const ClientIdOption &aClientIdOption, + uint32_t aIaid); using ServerSocket = Ip6::Udp::SocketIn; diff --git a/src/core/net/dhcp6_types.cpp b/src/core/net/dhcp6_types.cpp new file mode 100644 index 000000000..2e5bc23c2 --- /dev/null +++ b/src/core/net/dhcp6_types.cpp @@ -0,0 +1,152 @@ +/* + * 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. + */ + +/** + * @file + * This file implements generating and processing of DHCPv6 options. + */ + +#include "dhcp6_types.hpp" + +#if OPENTHREAD_CONFIG_DHCP6_SERVER_ENABLE || OPENTHREAD_CONFIG_DHCP6_CLIENT_ENABLE + +namespace ot { +namespace Dhcp6 { + +//--------------------------------------------------------------------------------------------------------------------- +// Option + +Error Option::FindOption(const Message &aMessage, Code aCode, OffsetRange &aOptionOffsetRange) +{ + OffsetRange msgOffsetRange; + + msgOffsetRange.InitFromMessageOffsetToEnd(aMessage); + + return FindOption(aMessage, msgOffsetRange, aCode, aOptionOffsetRange); +} + +Error Option::FindOption(const Message &aMessage, + const OffsetRange &aMsgOffsetRange, + Code aCode, + OffsetRange &aOptionOffsetRange) + +{ + Error error; + OffsetRange offsetRange; + + // Restrict `offsetRange to `aMessage.GetLength()`. This way we + // know if option is within `offsetRange` it is also fully + // contained within the `aMessage`. + + offsetRange.InitFromRange(aMsgOffsetRange.GetOffset(), Min(aMessage.GetLength(), aMsgOffsetRange.GetEndOffset())); + + while (!offsetRange.IsEmpty()) + { + Option option; + + SuccessOrExit(error = aMessage.Read(offsetRange, option)); + + VerifyOrExit(offsetRange.Contains(option.GetSize()), error = kErrorParse); + + if (option.GetCode() == aCode) + { + aOptionOffsetRange = offsetRange; + aOptionOffsetRange.ShrinkLength(static_cast(option.GetSize())); + ExitNow(); + } + + offsetRange.AdvanceOffset(option.GetSize()); + } + + error = kErrorNotFound; + +exit: + return error; +} + +void Option::UpdateOptionLengthInMessage(Message &aMessage, uint16_t aOffset) +{ + Option option; + + IgnoreError(aMessage.Read(aOffset, option)); + option.SetLength(aMessage.GetLength() - aOffset - sizeof(Option)); + aMessage.Write(aOffset, option); +} + +//--------------------------------------------------------------------------------------------------------------------- +// StatusCodeOption + +StatusCodeOption::Status StatusCodeOption::ReadStatusFrom(const Message &aMessage) +{ + OffsetRange msgOffsetRange; + + msgOffsetRange.InitFromMessageOffsetToEnd(aMessage); + + return ReadStatusFrom(aMessage, msgOffsetRange); +} + +StatusCodeOption::Status StatusCodeOption::ReadStatusFrom(const Message &aMessage, const OffsetRange &aMsgOffsetRange) +{ + Status status = kSuccess; + StatusCodeOption statusOption; + OffsetRange optionOffsetRange; + + // Per RFC 8415, the absence of a Status Code option implies success + SuccessOrExit(FindOption(aMessage, aMsgOffsetRange, kStatusCode, optionOffsetRange)); + + SuccessOrExit(aMessage.Read(optionOffsetRange, statusOption)); + status = static_cast(statusOption.GetStatusCode()); + +exit: + return status; +} + +//--------------------------------------------------------------------------------------------------------------------- +// RapidCommitOption + +Error RapidCommitOption::FindIn(const Message &aMessage) +{ + OffsetRange optionOffsetRange; + + return Option::FindOption(aMessage, Option::kRapidCommit, optionOffsetRange); +} + +Error RapidCommitOption::AppendTo(Message &aMessage) +{ + Option option; + + option.SetCode(Option::kRapidCommit); + option.SetLength(0); + + return aMessage.Append(option); +} + +} // namespace Dhcp6 +} // namespace ot + +#endif // #if OPENTHREAD_CONFIG_DHCP6_SERVER_ENABLE || OPENTHREAD_CONFIG_DHCP6_CLIENT_ENABLE diff --git a/src/core/net/dhcp6.hpp b/src/core/net/dhcp6_types.hpp similarity index 78% rename from src/core/net/dhcp6.hpp rename to src/core/net/dhcp6_types.hpp index 10c6fe9ad..1d2ca43f3 100644 --- a/src/core/net/dhcp6.hpp +++ b/src/core/net/dhcp6_types.hpp @@ -31,8 +31,8 @@ * This file includes definitions for DHCPv6 Service. */ -#ifndef DHCP6_HPP_ -#define DHCP6_HPP_ +#ifndef DHCP6_TYPES_HPP_ +#define DHCP6_TYPES_HPP_ #include "openthread-core-config.h" @@ -41,6 +41,7 @@ #include "common/clearable.hpp" #include "common/debug.hpp" #include "common/equatable.hpp" +#include "common/message.hpp" #include "common/random.hpp" #include "mac/mac_types.hpp" @@ -205,6 +206,57 @@ public: */ void SetLength(uint16_t aLength) { mLength = BigEndian::HostSwap16(aLength); } + /** + * Returns the total size of DHCPv6 option in bytes. + * + * @returns The size of option in bytes (which includes the Code and Length fields). + */ + uint32_t GetSize(void) const { return GetLength() + sizeof(Option); } + + /** + * Finds the first DHCPv6 option with a given code in a message. + * + * This method searches the message starting from `aMessage.GetOffset()` to the end. + * + * @param[in] aMessage The message to search. + * @param[in] aCode The option code to find. + * @param[out] aOptionOffsetRange On success, is updated to contain the offset range of the found option. + * + * @retval kErrorNone The option was found successfully. + * @retval kErrorNotFound The option was not found in the message. + * @retval kErrorParse The message is malformed and cannot be parsed. + */ + static Error FindOption(const Message &aMessage, Code aCode, OffsetRange &aOptionOffsetRange); + + /** + * Finds the first DHCPv6 option with a given code within a specified range of a message. + * + * @param[in] aMessage The message to search. + * @param[in] aMsgOffsetRange The specific range within `aMessage` to search. + * @param[in] aCode The option code to find. + * @param[out] aOptionOffsetRange On success, is updated to contain the offset range of the found option. + * + * @retval kErrorNone The option was found successfully. + * @retval kErrorNotFound The option was not found in the given message range. + * @retval kErrorParse The message is malformed and cannot be parsed. + */ + static Error FindOption(const Message &aMessage, + const OffsetRange &aMsgOffsetRange, + Code aCode, + OffsetRange &aOptionOffsetRange); + + /** + * Updates the Option length in a message. + * + * This method should be called after all option contents are appended to the message. It uses the current + * message length along with @p aOffset to determine the option length and then updates it within the @p aMessage. + * The @p aOffset should point to to the start of the option in @p aMessage. + * + * @param[in] aMessage The message to update. + * @param[in] aOffset The offset to the start of `Option` in @p aMessage. + */ + static void UpdateOptionLengthInMessage(Message &aMessage, uint16_t aOffset); + private: uint16_t mCode; uint16_t mLength; @@ -560,22 +612,68 @@ public: */ void SetStatusCode(Status aStatus) { mStatus = BigEndian::HostSwap16(aStatus); } + /** + * Reads the status code from a DHCPv6 message. + * + * This method searches the message (starting from `aMessage.GetOffset()` to the end) for a Status Code option. Per + * RFC 8415, the absence of a Status Code option implies success. Therefore, if no Status Code option is found, + * this method returns `kSuccess`. + * + * @param[in] aMessage The message to read the status code from. + * + * @returns The status code from the first found Status Code option, or `kSuccess` if none is found. + */ + static Status ReadStatusFrom(const Message &aMessage); + + /** + * Reads the status code from a specified range within a DHCPv-6 message. + * + * This method searches for a Status Code option only within the given `aMsgOffsetRange`. If no Status Code + * option is found within the range, it is considered a success, and `kSuccess` is returned. + * + * @param[in] aMessage The message to read the status code from. + * @param[in] aMsgOffsetRange The specific range within `aMessage` to search. + * + * @returns The status code from the first found Status Code option within the range, or `kSuccess` if none is + * found. + */ + static Status ReadStatusFrom(const Message &aMessage, const OffsetRange &aMsgOffsetRange); + private: uint16_t mStatus; } OT_TOOL_PACKED_END; /** - * Represents an Rapid Commit DHCPv6 option. + * Implements Rapid Commit DHCPv6 Option generation and parsing. */ -OT_TOOL_PACKED_BEGIN -class RapidCommitOption : public Option +class RapidCommitOption { public: - /** - * Initializes the DHCPv6 Option. + static constexpr uint16_t kCode = Option::kRapidCommit; ///< Rapid Commit Option code. + + /* + * Searches in a given message for Rapid Commit Option. + * + * @param[in] aMessage The message to search in. + * + * @retval kErrorNone The option was found successfully. + * @retval kErrorNotFound Did not find the option in @p aMessage. + * @retval kErrorParse Failed to parse the options in @p aMessage (invalid format). */ - void Init(void) { SetCode(kRapidCommit), SetLength(sizeof(*this) - sizeof(Option)); } -} OT_TOOL_PACKED_END; + static Error FindIn(const Message &aMessage); + + /** + * Append a Rapid Commit Option to a message. + * + * The Rapid Commit Option contains no data fields (zero length). + * + * @param[in,out] aMessage The message to append to. + * + * @retval kErrorNone Successfully appended the option. + * @retval kErrorNoBufs Insufficient available buffers to grow the message. + */ + static Error AppendTo(Message &aMessage); +}; /** * @} @@ -586,4 +684,4 @@ public: #endif // #if OPENTHREAD_CONFIG_DHCP6_SERVER_ENABLE || OPENTHREAD_CONFIG_DHCP6_CLIENT_ENABLE -#endif // DHCP6_HPP_ +#endif // DHCP6_TYPES_HPP_ diff --git a/tests/toranj/cli/test-036-dhcp-prefix-netdata.py b/tests/toranj/cli/test-036-dhcp-prefix-netdata.py new file mode 100755 index 000000000..ea36c5a0a --- /dev/null +++ b/tests/toranj/cli/test-036-dhcp-prefix-netdata.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +# +# 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: DHCPv6 prefix in netdata and DHCPv6 client and server behavior +# +# Network topology +# +# r1 ---- r2 ---- r3 +# + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Node` instances + +speedup = 40 +cli.Node.set_time_speedup_factor(speedup) + +r1 = cli.Node() +r2 = cli.Node() +r3 = cli.Node() + +# ----------------------------------------------------------------------------------------------------------------------- +# Form topology + +r1.allowlist_node(r2) + +r2.allowlist_node(r1) +r2.allowlist_node(r3) + +r3.allowlist_node(r2) + +r1.form('dhcp6') +r2.join(r1) +r3.join(r1) + +verify(r1.get_state() == 'leader') +verify(r2.get_state() == 'router') +verify(r3.get_state() == 'router') + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Add an on-mesh prefix with dhcp6 flag. + +r1.add_prefix('2000:1::/64', 'pdros') +r1.register_netdata() + + +def check_netdata(): + for node in [r1, r2, r3]: + netdata = node.get_netdata() + verify(len(netdata['prefixes']) == 1) + verify(len(netdata['routes']) == 0) + + +verify_within(check_netdata, 5) + + +def check_dhcp_addrs(): + for node in [r1, r2, r3]: + ip_addrs = node.get_ip_addrs() + for addr in ip_addrs: + if addr.startswith('2000:1:0:0:'): + break + else: + verify(False) + + +verify_within(check_dhcp_addrs, 5) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/openthread-core-toranj-config-simulation.h b/tests/toranj/openthread-core-toranj-config-simulation.h index 4071198f2..9fd554e63 100644 --- a/tests/toranj/openthread-core-toranj-config-simulation.h +++ b/tests/toranj/openthread-core-toranj-config-simulation.h @@ -85,6 +85,10 @@ #define OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE 1 +#define OPENTHREAD_CONFIG_DHCP6_SERVER_ENABLE 1 + +#define OPENTHREAD_CONFIG_DHCP6_CLIENT_ENABLE 1 + #define OPENTHREAD_CONFIG_RADIO_STATS_ENABLE 0 #define OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE 0 diff --git a/tests/toranj/start.sh b/tests/toranj/start.sh index 75d8062ae..792bba4f9 100755 --- a/tests/toranj/start.sh +++ b/tests/toranj/start.sh @@ -201,6 +201,7 @@ if [ "$TORANJ_CLI" = 1 ]; then run cli/test-033-alt-short-addr-role-transition.py run cli/test-034-fed-parent-search.py run cli/test-035-context-id-change-addr-reg.py + run cli/test-036-dhcp-prefix-netdata.py run cli/test-400-srp-client-server.py run cli/test-401-srp-server-address-cache-snoop.py run cli/test-500-two-brs-two-networks.py