| /* |
| * Copyright (c) 2016, 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 DHCPv6 Client. |
| */ |
| |
| #define WPP_NAME "dhcp6_client.tmh" |
| |
| #include <openthread/config.h> |
| |
| #include "dhcp6_client.hpp" |
| |
| #include <openthread/types.h> |
| #include <openthread/platform/random.h> |
| |
| #include "common/code_utils.hpp" |
| #include "common/encoding.hpp" |
| #include "common/logging.hpp" |
| #include "mac/mac.hpp" |
| #include "net/dhcp6.hpp" |
| #include "thread/thread_netif.hpp" |
| |
| |
| #if OPENTHREAD_ENABLE_DHCP6_CLIENT |
| |
| using ot::Encoding::BigEndian::HostSwap16; |
| using ot::Encoding::BigEndian::HostSwap32; |
| |
| namespace ot { |
| |
| namespace Dhcp6 { |
| |
| Dhcp6Client::Dhcp6Client(ThreadNetif &aThreadNetif) : |
| ThreadNetifLocator(aThreadNetif), |
| mTrickleTimer(aThreadNetif.GetIp6().mTimerScheduler, &Dhcp6Client::HandleTrickleTimer, NULL, this), |
| mSocket(aThreadNetif.GetIp6().mUdp), |
| mStartTime(0), |
| mAddresses(NULL), |
| mNumAddresses(0) |
| { |
| memset(mIdentityAssociations, 0, sizeof(IdentityAssociation)); |
| |
| for (uint8_t i = 0; i < (OPENTHREAD_CONFIG_NUM_DHCP_PREFIXES - 1); i++) |
| { |
| mIdentityAssociations[i].SetNext(&(mIdentityAssociations[i + 1])); |
| } |
| |
| mIdentityAssociationHead = NULL; |
| mIdentityAssociationAvail = &mIdentityAssociations[0]; |
| } |
| |
| void Dhcp6Client::UpdateAddresses(otInstance *aInstance, otDhcpAddress *aAddresses, uint32_t aNumAddresses, |
| void *aContext) |
| { |
| OT_UNUSED_VARIABLE(aContext); |
| bool found = false; |
| bool newAgent = false; |
| otDhcpAddress *address = NULL; |
| otNetworkDataIterator iterator; |
| otBorderRouterConfig config; |
| |
| mAddresses = aAddresses; |
| mNumAddresses = aNumAddresses; |
| |
| // remove addresses directly if prefix not valid in network data |
| for (uint8_t i = 0; i < mNumAddresses; i++) |
| { |
| address = &mAddresses[i]; |
| |
| if (address->mValidLifetime == 0) |
| { |
| continue; |
| } |
| |
| found = false; |
| iterator = OT_NETWORK_DATA_ITERATOR_INIT; |
| |
| while ((otNetDataGetNextOnMeshPrefix(aInstance, &iterator, &config)) == OT_ERROR_NONE) |
| { |
| if (!config.mDhcp) |
| { |
| continue; |
| } |
| |
| if ((otIp6PrefixMatch(&(address->mAddress.mAddress), &(config.mPrefix.mPrefix)) >= |
| address->mAddress.mPrefixLength) && |
| (config.mPrefix.mLength == address->mAddress.mPrefixLength)) |
| { |
| found = true; |
| break; |
| } |
| } |
| |
| if (!found) |
| { |
| otIp6RemoveUnicastAddress(aInstance, &(address->mAddress.mAddress)); |
| RemoveIdentityAssociation(config.mRloc16, config.mPrefix); |
| memset(address, 0, sizeof(*address)); |
| } |
| } |
| |
| // add IdentityAssociation for new configured prefix |
| iterator = OT_NETWORK_DATA_ITERATOR_INIT; |
| |
| while (otNetDataGetNextOnMeshPrefix(aInstance, &iterator, &config) == OT_ERROR_NONE) |
| { |
| if (!config.mDhcp) |
| { |
| continue; |
| } |
| |
| found = false; |
| |
| for (uint8_t i = 0; i < mNumAddresses; i++) |
| { |
| address = &mAddresses[i]; |
| |
| if (address->mAddress.mPrefixLength == 0) |
| { |
| continue; |
| } |
| |
| if ((otIp6PrefixMatch(&(config.mPrefix.mPrefix), &(address->mAddress.mAddress)) >= |
| config.mPrefix.mLength) && |
| (config.mPrefix.mLength == address->mAddress.mPrefixLength)) |
| { |
| found = true; |
| break; |
| } |
| } |
| |
| if (!found) |
| { |
| for (size_t i = 0; i < mNumAddresses; i++) |
| { |
| address = &mAddresses[i]; |
| |
| if (address->mAddress.mPrefixLength != 0) |
| { |
| continue; |
| } |
| |
| memset(address, 0, sizeof(*address)); |
| |
| // suppose all configured prefix are ::/64 |
| memcpy(address->mAddress.mAddress.mFields.m8, config.mPrefix.mPrefix.mFields.m8, 8); |
| address->mAddress.mPrefixLength = config.mPrefix.mLength; |
| |
| AddIdentityAssociation(config.mRloc16, config.mPrefix); |
| newAgent = true; |
| break; |
| } |
| } |
| } |
| |
| if (newAgent) |
| { |
| Start(); |
| } |
| else |
| { |
| Stop(); |
| } |
| } |
| |
| void Dhcp6Client::AddIdentityAssociation(uint16_t aRloc16, otIp6Prefix &aIp6Prefix) |
| { |
| IdentityAssociation *identityAssociation = NULL; |
| IdentityAssociation *identityAssociationCursor = NULL; |
| |
| VerifyOrExit(mIdentityAssociationAvail); |
| |
| identityAssociation = mIdentityAssociationAvail; |
| mIdentityAssociationAvail = mIdentityAssociationAvail->GetNext(); |
| |
| identityAssociation->SetPrefixAgentRloc(aRloc16); |
| identityAssociation->SetPrefix(aIp6Prefix); |
| identityAssociation->SetStatus(IdentityAssociation::kStatusSolicit); |
| |
| identityAssociation->SetNext(NULL); |
| |
| if (mIdentityAssociationHead) |
| { |
| // append the new identityassociation to the tail of used list |
| for (identityAssociationCursor = mIdentityAssociationHead; identityAssociationCursor->GetNext(); |
| identityAssociationCursor = identityAssociationCursor->GetNext()) {} |
| |
| identityAssociationCursor->SetNext(identityAssociation); |
| } |
| else |
| { |
| mIdentityAssociationHead = identityAssociation; |
| } |
| |
| exit: |
| return; |
| } |
| |
| void Dhcp6Client::RemoveIdentityAssociation(uint16_t aRloc16, otIp6Prefix &aIp6Prefix) |
| { |
| IdentityAssociation *prevIdentityAssociation = NULL; |
| IdentityAssociation *identityAssociation = NULL; |
| |
| VerifyOrExit(mIdentityAssociationHead); |
| |
| for (identityAssociation = mIdentityAssociationHead; identityAssociation; |
| prevIdentityAssociation = identityAssociation, identityAssociation = identityAssociation->GetNext()) |
| { |
| if (identityAssociation->GetPrefixAgentRloc() != aRloc16) |
| { |
| continue; |
| } |
| |
| if (otIp6PrefixMatch(&(aIp6Prefix.mPrefix), &(identityAssociation->GetPrefix()->mPrefix)) < aIp6Prefix.mLength) |
| { |
| continue; |
| } |
| |
| // remove from used list |
| if (prevIdentityAssociation) |
| { |
| prevIdentityAssociation->SetNext(identityAssociation->GetNext()); |
| } |
| else |
| { |
| mIdentityAssociationHead = identityAssociation->GetNext(); |
| } |
| |
| // return to available list |
| memset(identityAssociation, 0, sizeof(*identityAssociation)); |
| identityAssociation->SetNext(mIdentityAssociationAvail); |
| mIdentityAssociationAvail = identityAssociation; |
| break; |
| } |
| |
| exit: |
| return; |
| } |
| |
| otError Dhcp6Client::Start(void) |
| { |
| Ip6::SockAddr sockaddr; |
| |
| sockaddr.mPort = kDhcpClientPort; |
| mSocket.Open(&Dhcp6Client::HandleUdpReceive, this); |
| mSocket.Bind(sockaddr); |
| |
| ProcessNextIdentityAssociation(); |
| |
| return OT_ERROR_NONE; |
| } |
| |
| otError Dhcp6Client::Stop(void) |
| { |
| mSocket.Close(); |
| return OT_ERROR_NONE; |
| } |
| |
| bool Dhcp6Client::ProcessNextIdentityAssociation() |
| { |
| bool rval = false; |
| IdentityAssociation *prevIdentityAssociation = NULL; |
| IdentityAssociation *identityAssociation = NULL; |
| |
| VerifyOrExit(mIdentityAssociationHead); |
| // not interrupt in-progress solicit |
| VerifyOrExit((mIdentityAssociationHead->GetStatus() != IdentityAssociation::kStatusSoliciting)); |
| |
| mTrickleTimer.Stop(); |
| |
| for (identityAssociation = mIdentityAssociationHead; identityAssociation; |
| prevIdentityAssociation = identityAssociation, identityAssociation = identityAssociation->GetNext()) |
| { |
| if (identityAssociation->GetStatus() != IdentityAssociation::kStatusSolicit) |
| { |
| continue; |
| } |
| |
| // new transaction id |
| for (uint8_t i = 0; i < kTransactionIdSize; i++) |
| { |
| mTransactionId[i] = static_cast<uint8_t>(otPlatRandomGet()); |
| } |
| |
| // ensure mIdentityAssociationHead is the prefix agent to solicit. |
| if (prevIdentityAssociation) |
| { |
| prevIdentityAssociation->SetNext(identityAssociation->GetNext()); |
| identityAssociation->SetNext(mIdentityAssociationHead); |
| mIdentityAssociationHead = identityAssociation; |
| } |
| |
| mTrickleTimer.Start( |
| Timer::SecToMsec(kTrickleTimerImin), |
| Timer::SecToMsec(kTrickleTimerImax), |
| TrickleTimer::kModeNormal); |
| |
| mTrickleTimer.IndicateInconsistent(); |
| |
| ExitNow(rval = true); |
| } |
| |
| exit: |
| return rval; |
| } |
| |
| bool Dhcp6Client::HandleTrickleTimer(TrickleTimer &aTrickleTimer) |
| { |
| return GetOwner(aTrickleTimer).HandleTrickleTimer(); |
| } |
| |
| bool Dhcp6Client::HandleTrickleTimer(void) |
| { |
| bool rval = true; |
| |
| VerifyOrExit(mIdentityAssociationHead, rval = false); |
| |
| switch (mIdentityAssociationHead->GetStatus()) |
| { |
| case IdentityAssociation::kStatusSolicit: |
| mStartTime = otPlatAlarmGetNow(); |
| mIdentityAssociationHead->SetStatus(IdentityAssociation::kStatusSoliciting); |
| |
| // fall through |
| |
| case IdentityAssociation::kStatusSoliciting: |
| Solicit(mIdentityAssociationHead->GetPrefixAgentRloc()); |
| break; |
| |
| case IdentityAssociation::kStatusSolicitReplied: |
| if (!ProcessNextIdentityAssociation()) |
| { |
| mTrickleTimer.Stop(); |
| Stop(); |
| rval = false; |
| } |
| |
| break; |
| |
| default: |
| break; |
| } |
| |
| exit: |
| return rval; |
| } |
| |
| otError Dhcp6Client::Solicit(uint16_t aRloc16) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| Message *message; |
| Ip6::MessageInfo messageInfo; |
| |
| VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, error = OT_ERROR_NO_BUFS); |
| |
| SuccessOrExit(error = AppendHeader(*message)); |
| SuccessOrExit(error = AppendElapsedTime(*message)); |
| SuccessOrExit(error = AppendClientIdentifier(*message)); |
| SuccessOrExit(error = AppendIaNa(*message, aRloc16)); |
| // specify which prefixes to solicit |
| SuccessOrExit(error = AppendIaAddress(*message, aRloc16)); |
| SuccessOrExit(error = AppendRapidCommit(*message)); |
| |
| memset(&messageInfo, 0, sizeof(messageInfo)); |
| memcpy(messageInfo.GetPeerAddr().mFields.m8, netif.GetMle().GetMeshLocalPrefix(), 8); |
| messageInfo.GetPeerAddr().mFields.m16[4] = HostSwap16(0x0000); |
| messageInfo.GetPeerAddr().mFields.m16[5] = HostSwap16(0x00ff); |
| messageInfo.GetPeerAddr().mFields.m16[6] = HostSwap16(0xfe00); |
| messageInfo.GetPeerAddr().mFields.m16[7] = HostSwap16(aRloc16); |
| messageInfo.SetSockAddr(netif.GetMle().GetMeshLocal16()); |
| messageInfo.mPeerPort = kDhcpServerPort; |
| messageInfo.mInterfaceId = netif.GetInterfaceId(); |
| |
| SuccessOrExit(error = mSocket.SendTo(*message, messageInfo)); |
| otLogInfoIp6(GetInstance(), "solicit"); |
| |
| exit: |
| |
| if (message != NULL && error != OT_ERROR_NONE) |
| { |
| message->Free(); |
| } |
| |
| return error; |
| } |
| |
| otError Dhcp6Client::AppendHeader(Message &aMessage) |
| { |
| Dhcp6Header header; |
| |
| header.Init(); |
| header.SetType(kTypeSolicit); |
| header.SetTransactionId(mTransactionId); |
| return aMessage.Append(&header, sizeof(header)); |
| } |
| |
| otError Dhcp6Client::AppendElapsedTime(Message &aMessage) |
| { |
| ElapsedTime option; |
| |
| option.Init(); |
| option.SetElapsedTime(static_cast<uint16_t>(Timer::MsecToSec(otPlatAlarmGetNow() - mStartTime))); |
| return aMessage.Append(&option, sizeof(option)); |
| } |
| |
| otError Dhcp6Client::AppendClientIdentifier(Message &aMessage) |
| { |
| ClientIdentifier option; |
| |
| option.Init(); |
| option.SetDuidType(kDuidLL); |
| option.SetDuidHardwareType(kHardwareTypeEui64); |
| option.SetDuidLinkLayerAddress(GetNetif().GetMac().GetExtAddress()); |
| return aMessage.Append(&option, sizeof(option)); |
| } |
| |
| otError Dhcp6Client::AppendIaNa(Message &aMessage, uint16_t aRloc16) |
| { |
| otError error = OT_ERROR_NONE; |
| uint8_t count = 0; |
| uint16_t length = 0; |
| IdentityAssociation *identityAssociation = NULL; |
| IaNa option; |
| |
| VerifyOrExit(mIdentityAssociationHead, error = OT_ERROR_DROP); |
| |
| for (identityAssociation = mIdentityAssociationHead; identityAssociation; |
| identityAssociation = identityAssociation->GetNext()) |
| { |
| if (identityAssociation->GetStatus() == IdentityAssociation::kStatusSolicitReplied) |
| { |
| continue; |
| } |
| |
| if (identityAssociation->GetPrefixAgentRloc() == aRloc16) |
| { |
| count++; |
| } |
| } |
| |
| // compute the right length |
| length = sizeof(IaNa) + sizeof(IaAddress) * count - sizeof(Dhcp6Option); |
| |
| option.Init(); |
| option.SetLength(length); |
| option.SetIaid(0); |
| option.SetT1(0); |
| option.SetT2(0); |
| SuccessOrExit(error = aMessage.Append(&option, sizeof(IaNa))); |
| |
| exit: |
| return error; |
| } |
| |
| otError Dhcp6Client::AppendIaAddress(Message &aMessage, uint16_t aRloc16) |
| { |
| otError error = OT_ERROR_NONE; |
| IdentityAssociation *identityAssociation = NULL; |
| IaAddress option; |
| |
| VerifyOrExit(mIdentityAssociationHead, error = OT_ERROR_DROP); |
| |
| option.Init(); |
| |
| for (identityAssociation = mIdentityAssociationHead; identityAssociation; |
| identityAssociation = identityAssociation->GetNext()) |
| { |
| if ((identityAssociation->GetStatus() != IdentityAssociation::kStatusSolicitReplied) && |
| (identityAssociation->GetPrefixAgentRloc() == aRloc16)) |
| { |
| option.SetAddress(identityAssociation->GetPrefix()->mPrefix); |
| option.SetPreferredLifetime(0); |
| option.SetValidLifetime(0); |
| SuccessOrExit(error = aMessage.Append(&option, sizeof(option))); |
| } |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError Dhcp6Client::AppendRapidCommit(Message &aMessage) |
| { |
| RapidCommit option; |
| |
| option.Init(); |
| return aMessage.Append(&option, sizeof(option)); |
| } |
| |
| void Dhcp6Client::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) |
| { |
| Dhcp6Client *obj = static_cast<Dhcp6Client *>(aContext); |
| obj->HandleUdpReceive(*static_cast<Message *>(aMessage), *static_cast<const Ip6::MessageInfo *>(aMessageInfo)); |
| } |
| |
| void Dhcp6Client::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| Dhcp6Header header; |
| OT_UNUSED_VARIABLE(aMessageInfo); |
| |
| VerifyOrExit(aMessage.Read(aMessage.GetOffset(), sizeof(header), &header) == sizeof(header)); |
| aMessage.MoveOffset(sizeof(header)); |
| |
| if ((header.GetType() == kTypeReply) && (!memcmp(header.GetTransactionId(), mTransactionId, kTransactionIdSize))) |
| { |
| ProcessReply(aMessage); |
| } |
| |
| exit: |
| return; |
| } |
| |
| void Dhcp6Client::ProcessReply(Message &aMessage) |
| { |
| uint16_t offset = aMessage.GetOffset(); |
| uint16_t length = aMessage.GetLength() - aMessage.GetOffset(); |
| uint16_t optionOffset; |
| |
| // Server Identifier |
| VerifyOrExit((optionOffset = FindOption(aMessage, offset, length, kOptionServerIdentifier)) > 0); |
| SuccessOrExit(ProcessServerIdentifier(aMessage, optionOffset)); |
| |
| // Client Identifier |
| VerifyOrExit((optionOffset = FindOption(aMessage, offset, length, kOptionClientIdentifier)) > 0); |
| SuccessOrExit(ProcessClientIdentifier(aMessage, optionOffset)); |
| |
| // Rapid Commit |
| VerifyOrExit(FindOption(aMessage, offset, length, kOptionRapidCommit) > 0); |
| |
| // IA_NA |
| VerifyOrExit((optionOffset = FindOption(aMessage, offset, length, kOptionIaNa)) > 0); |
| SuccessOrExit(ProcessIaNa(aMessage, optionOffset)); |
| |
| HandleTrickleTimer(); |
| |
| exit: |
| return; |
| } |
| |
| uint16_t Dhcp6Client::FindOption(Message &aMessage, uint16_t aOffset, uint16_t aLength, Dhcp6::Code aCode) |
| { |
| uint16_t end = aOffset + aLength; |
| |
| while (aOffset <= end) |
| { |
| Dhcp6Option option; |
| VerifyOrExit(aMessage.Read(aOffset, sizeof(option), &option) == sizeof(option)); |
| |
| if (option.GetCode() == (aCode)) |
| { |
| return aOffset; |
| } |
| |
| aOffset += sizeof(option) + option.GetLength(); |
| } |
| |
| exit: |
| return 0; |
| } |
| |
| otError Dhcp6Client::ProcessServerIdentifier(Message &aMessage, uint16_t aOffset) |
| { |
| otError error = OT_ERROR_NONE; |
| ServerIdentifier option; |
| |
| VerifyOrExit(((aMessage.Read(aOffset, sizeof(option), &option) == sizeof(option)) && |
| (option.GetLength() == (sizeof(option) - sizeof(Dhcp6Option))) && |
| (option.GetDuidType() == kDuidLL) && |
| (option.GetDuidHardwareType() == kHardwareTypeEui64)), |
| error = OT_ERROR_PARSE); |
| exit: |
| return error; |
| } |
| |
| otError Dhcp6Client::ProcessClientIdentifier(Message &aMessage, uint16_t aOffset) |
| { |
| otError error = OT_ERROR_NONE; |
| ClientIdentifier option; |
| |
| VerifyOrExit((((aMessage.Read(aOffset, sizeof(option), &option) == sizeof(option)) && |
| (option.GetLength() == (sizeof(option) - sizeof(Dhcp6Option))) && |
| (option.GetDuidType() == kDuidLL) && |
| (option.GetDuidHardwareType() == kHardwareTypeEui64)) && |
| (!memcmp(option.GetDuidLinkLayerAddress(), GetNetif().GetMac().GetExtAddress(), |
| sizeof(Mac::ExtAddress)))), |
| error = OT_ERROR_PARSE); |
| exit: |
| return error; |
| } |
| |
| otError Dhcp6Client::ProcessIaNa(Message &aMessage, uint16_t aOffset) |
| { |
| otError error = OT_ERROR_NONE; |
| IaNa option; |
| uint16_t optionOffset; |
| uint16_t length; |
| |
| VerifyOrExit(aMessage.Read(aOffset, sizeof(option), &option) == sizeof(option), error = OT_ERROR_PARSE); |
| |
| aOffset += sizeof(option); |
| length = option.GetLength() - (sizeof(option) - sizeof(Dhcp6Option)); |
| |
| VerifyOrExit(length <= aMessage.GetLength() - aOffset, error = OT_ERROR_PARSE); |
| |
| if ((optionOffset = FindOption(aMessage, aOffset, length, kOptionStatusCode)) > 0) |
| { |
| SuccessOrExit(error = ProcessStatusCode(aMessage, optionOffset)); |
| } |
| |
| while (length > 0) |
| { |
| if ((optionOffset = FindOption(aMessage, aOffset, length, kOptionIaAddress)) == 0) |
| { |
| ExitNow(); |
| } |
| |
| SuccessOrExit(error = ProcessIaAddress(aMessage, optionOffset)); |
| |
| length -= ((optionOffset - aOffset) + sizeof(IaAddress)); |
| aOffset = optionOffset + sizeof(IaAddress); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError Dhcp6Client::ProcessStatusCode(Message &aMessage, uint16_t aOffset) |
| { |
| otError error = OT_ERROR_NONE; |
| StatusCode option; |
| |
| VerifyOrExit(((aMessage.Read(aOffset, sizeof(option), &option) == sizeof(option)) && |
| (option.GetLength() == (sizeof(option) - sizeof(Dhcp6Option))) && |
| (option.GetStatusCode() == kStatusSuccess)), |
| error = OT_ERROR_PARSE); |
| |
| exit: |
| return error; |
| } |
| |
| otError Dhcp6Client::ProcessIaAddress(Message &aMessage, uint16_t aOffset) |
| { |
| otError error = OT_ERROR_NONE; |
| IdentityAssociation *identityAssociation = NULL; |
| otDhcpAddress *address = NULL; |
| otIp6Prefix *prefix = NULL; |
| |
| IaAddress option; |
| |
| VerifyOrExit(((aMessage.Read(aOffset, sizeof(option), &option) == sizeof(option)) && |
| (option.GetLength() == (sizeof(option) - sizeof(Dhcp6Option)))), |
| error = OT_ERROR_PARSE); |
| |
| for (uint8_t i = 0; i < mNumAddresses; i++) |
| { |
| address = &mAddresses[i]; |
| |
| if (address->mValidLifetime != 0) |
| { |
| continue; |
| } |
| |
| if (otIp6PrefixMatch(&(address->mAddress.mAddress), option.GetAddress()) >= address->mAddress.mPrefixLength) |
| { |
| memcpy(address->mAddress.mAddress.mFields.m8, option.GetAddress()->mFields.m8, sizeof(otIp6Address)); |
| address->mPreferredLifetime = option.GetPreferredLifetime(); |
| address->mValidLifetime = option.GetValidLifetime(); |
| address->mAddress.mPreferred = address->mPreferredLifetime != 0; |
| address->mAddress.mValid = address->mValidLifetime != 0; |
| otIp6AddUnicastAddress(GetNetif().GetInstance(), &address->mAddress); |
| break; |
| } |
| } |
| |
| // mark IdentityAssociation as replied |
| for (identityAssociation = mIdentityAssociationHead; identityAssociation; |
| identityAssociation = identityAssociation->GetNext()) |
| { |
| prefix = identityAssociation->GetPrefix(); |
| |
| if (otIp6PrefixMatch(option.GetAddress(), &(prefix->mPrefix)) >= prefix->mLength) |
| { |
| identityAssociation->SetStatus(IdentityAssociation::kStatusSolicitReplied); |
| break; |
| } |
| } |
| |
| exit: |
| return error; |
| } |
| |
| Dhcp6Client &Dhcp6Client::GetOwner(const Context &aContext) |
| { |
| #if OPENTHREAD_ENABLE_MULTIPLE_INSTANCES |
| Dhcp6Client &client = *static_cast<Dhcp6Client *>(aContext.GetContext()); |
| #else |
| Dhcp6Client &client = otGetThreadNetif().GetDhcp6Client(); |
| OT_UNUSED_VARIABLE(aContext); |
| #endif |
| return client; |
| } |
| |
| } // namespace Dhcp6 |
| } // namespace ot |
| |
| #endif //OPENTHREAD_ENABLE_DHCP6_CLIENT |
| |