mirror of
https://github.com/espressif/openthread.git
synced 2026-06-06 05:24:51 +00:00
[dhcp6] add helper methods for option parsing and generation (#11551)
This commit introduces new helper methods for DHCPv6 Option parsing and generation. - It adds `Option::FindOption()` to search and parse the first DHCPv6 option with a given code within a specified range of a `Message`. - It also adds `Option::UpdateOptionLengthInMessage()`, which updates the Option length in a message based on the number of bytes appended, simplifying the appending of variable-length options (e.g., `IaNaOption`, which can contain multiple sub-options). - Additionally, `StatusCodeOption::ReadStatusFrom()` is added to read the status code from a Status Code Option in a specified range within a `Message`. The absence of a Status Code option implies success. - Helper methods to search for and append `RapidCommitOption` are also included. These new helper methods are then used by `Dhcp6::Server` and `Dhcp6::Client`, simplifying the code, particularly for processing options in received DHCPv6 messages. This commit also adds a new `test-036-dhcp-prefix-netdata.py` test to validate the publishing of prefixes with the DHCP flag in Network Data and the behavior of the DHCPv6 client and server.
This commit is contained in:
committed by
GitHub
parent
c2de9f646a
commit
02b8b2cda6
+2
-1
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
+68
-147
@@ -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<uint16_t>(offset), option));
|
||||
|
||||
if (option.GetCode() == aCode)
|
||||
{
|
||||
ExitNow(rval = static_cast<uint16_t>(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<Radio>().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<ThreadNetif>().AddUnicastAddress(idAssociation.mNetifAddress);
|
||||
ExitNow(error = kErrorNone);
|
||||
|
||||
@@ -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);
|
||||
|
||||
+95
-122
@@ -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
|
||||
|
||||
|
||||
@@ -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<Server, &Server::HandleUdpReceive>;
|
||||
|
||||
|
||||
@@ -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<uint16_t>(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<Status>(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
|
||||
@@ -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_
|
||||
+111
@@ -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))
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user