| /* |
| * 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 Server. |
| */ |
| |
| #include "dhcp6_server.hpp" |
| |
| #if OPENTHREAD_CONFIG_DHCP6_SERVER_ENABLE |
| |
| #include "common/array.hpp" |
| #include "common/as_core_type.hpp" |
| #include "common/code_utils.hpp" |
| #include "common/encoding.hpp" |
| #include "common/instance.hpp" |
| #include "common/locator_getters.hpp" |
| #include "common/log.hpp" |
| #include "thread/mle.hpp" |
| #include "thread/thread_netif.hpp" |
| |
| namespace ot { |
| namespace Dhcp6 { |
| |
| RegisterLogModule("Dhcp6Server"); |
| |
| Server::Server(Instance &aInstance) |
| : InstanceLocator(aInstance) |
| , mSocket(aInstance) |
| , mPrefixAgentsCount(0) |
| , mPrefixAgentsMask(0) |
| { |
| memset(mPrefixAgents, 0, sizeof(mPrefixAgents)); |
| } |
| |
| Error Server::UpdateService(void) |
| { |
| Error error = kErrorNone; |
| uint16_t rloc16 = Get<Mle::MleRouter>().GetRloc16(); |
| NetworkData::Iterator iterator; |
| NetworkData::OnMeshPrefixConfig config; |
| Lowpan::Context lowpanContext; |
| |
| // remove dhcp agent aloc and prefix delegation |
| for (PrefixAgent &prefixAgent : mPrefixAgents) |
| { |
| bool found = false; |
| |
| if (!prefixAgent.IsValid()) |
| { |
| continue; |
| } |
| |
| iterator = NetworkData::kIteratorInit; |
| |
| while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, rloc16, config) == kErrorNone) |
| { |
| if (!(config.mDhcp || config.mConfigure)) |
| { |
| continue; |
| } |
| |
| error = Get<NetworkData::Leader>().GetContext(prefixAgent.GetPrefixAsAddress(), lowpanContext); |
| |
| if ((error == kErrorNone) && (prefixAgent.GetContextId() == lowpanContext.mContextId)) |
| { |
| // still in network data |
| found = true; |
| break; |
| } |
| } |
| |
| if (!found) |
| { |
| Get<ThreadNetif>().RemoveUnicastAddress(prefixAgent.GetAloc()); |
| prefixAgent.Clear(); |
| mPrefixAgentsCount--; |
| } |
| } |
| |
| // add dhcp agent aloc and prefix delegation |
| iterator = NetworkData::kIteratorInit; |
| |
| while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, rloc16, config) == kErrorNone) |
| { |
| if (!(config.mDhcp || config.mConfigure)) |
| { |
| continue; |
| } |
| |
| error = Get<NetworkData::Leader>().GetContext(AsCoreType(&config.mPrefix.mPrefix), lowpanContext); |
| |
| if (error == kErrorNone) |
| { |
| AddPrefixAgent(config.GetPrefix(), lowpanContext); |
| } |
| } |
| |
| if (mPrefixAgentsCount > 0) |
| { |
| Start(); |
| } |
| else |
| { |
| Stop(); |
| } |
| |
| return error; |
| } |
| |
| void Server::Start(void) |
| { |
| VerifyOrExit(!mSocket.IsOpen()); |
| |
| IgnoreError(mSocket.Open(&Server::HandleUdpReceive, this)); |
| IgnoreError(mSocket.Bind(kDhcpServerPort)); |
| |
| exit: |
| return; |
| } |
| |
| void Server::Stop(void) |
| { |
| IgnoreError(mSocket.Close()); |
| } |
| |
| void Server::AddPrefixAgent(const Ip6::Prefix &aIp6Prefix, const Lowpan::Context &aContext) |
| { |
| Error error = kErrorNone; |
| PrefixAgent *newEntry = nullptr; |
| |
| for (PrefixAgent &prefixAgent : mPrefixAgents) |
| { |
| if (!prefixAgent.IsValid()) |
| { |
| newEntry = &prefixAgent; |
| } |
| else if (prefixAgent.GetPrefix() == aIp6Prefix) |
| { |
| // already added |
| ExitNow(); |
| } |
| } |
| |
| VerifyOrExit(newEntry != nullptr, error = kErrorNoBufs); |
| |
| newEntry->Set(aIp6Prefix, Get<Mle::MleRouter>().GetMeshLocalPrefix(), aContext.mContextId); |
| Get<ThreadNetif>().AddUnicastAddress(newEntry->GetAloc()); |
| mPrefixAgentsCount++; |
| |
| exit: |
| |
| if (error != kErrorNone) |
| { |
| LogNote("Failed to add DHCPv6 prefix agent: %s", ErrorToString(error)); |
| } |
| } |
| |
| void Server::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) |
| { |
| static_cast<Server *>(aContext)->HandleUdpReceive(AsCoreType(aMessage), AsCoreType(aMessageInfo)); |
| } |
| |
| void Server::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| Header header; |
| |
| SuccessOrExit(aMessage.Read(aMessage.GetOffset(), header)); |
| aMessage.MoveOffset(sizeof(header)); |
| |
| // discard if not solicit type |
| VerifyOrExit((header.GetType() == kTypeSolicit)); |
| |
| ProcessSolicit(aMessage, aMessageInfo.GetPeerAddr(), header.GetTransactionId()); |
| |
| exit: |
| return; |
| } |
| |
| void Server::ProcessSolicit(Message &aMessage, const Ip6::Address &aDst, const TransactionId &aTransactionId) |
| { |
| IaNa iana; |
| ClientIdentifier clientIdentifier; |
| uint16_t optionOffset; |
| uint16_t offset = aMessage.GetOffset(); |
| uint16_t length = aMessage.GetLength() - aMessage.GetOffset(); |
| |
| // Client Identifier (discard if not present) |
| VerifyOrExit((optionOffset = FindOption(aMessage, offset, length, kOptionClientIdentifier)) > 0); |
| SuccessOrExit(ProcessClientIdentifier(aMessage, optionOffset, clientIdentifier)); |
| |
| // Server Identifier (assuming Rapid Commit, discard if present) |
| VerifyOrExit(FindOption(aMessage, offset, length, kOptionServerIdentifier) == 0); |
| |
| // Rapid Commit (assuming Rapid Commit, discard if not present) |
| VerifyOrExit(FindOption(aMessage, offset, length, kOptionRapidCommit) > 0); |
| |
| // Elapsed Time if present |
| if ((optionOffset = FindOption(aMessage, offset, length, kOptionElapsedTime)) > 0) |
| { |
| SuccessOrExit(ProcessElapsedTime(aMessage, optionOffset)); |
| } |
| |
| // IA_NA (discard if not present) |
| VerifyOrExit((optionOffset = FindOption(aMessage, offset, length, kOptionIaNa)) > 0); |
| SuccessOrExit(ProcessIaNa(aMessage, optionOffset, iana)); |
| |
| SuccessOrExit(SendReply(aDst, aTransactionId, clientIdentifier, iana)); |
| |
| exit: |
| return; |
| } |
| |
| uint16_t Server::FindOption(Message &aMessage, uint16_t aOffset, uint16_t aLength, Code aCode) |
| { |
| uint16_t end = aOffset + aLength; |
| uint16_t rval = 0; |
| |
| while (aOffset <= end) |
| { |
| Option option; |
| |
| SuccessOrExit(aMessage.Read(aOffset, option)); |
| |
| if (option.GetCode() == aCode) |
| { |
| ExitNow(rval = aOffset); |
| } |
| |
| aOffset += sizeof(option) + option.GetLength(); |
| } |
| |
| exit: |
| return rval; |
| } |
| Error Server::ProcessClientIdentifier(Message &aMessage, uint16_t aOffset, ClientIdentifier &aClientId) |
| { |
| Error error = kErrorNone; |
| |
| SuccessOrExit(error = aMessage.Read(aOffset, aClientId)); |
| VerifyOrExit((aClientId.GetLength() == sizeof(aClientId) - sizeof(Option)) && |
| (aClientId.GetDuidType() == kDuidLinkLayerAddress) && |
| (aClientId.GetDuidHardwareType() == kHardwareTypeEui64), |
| error = kErrorParse); |
| exit: |
| return error; |
| } |
| |
| Error Server::ProcessElapsedTime(Message &aMessage, uint16_t aOffset) |
| { |
| Error error = kErrorNone; |
| ElapsedTime option; |
| |
| SuccessOrExit(error = aMessage.Read(aOffset, option)); |
| VerifyOrExit(option.GetLength() == sizeof(option) - sizeof(Option), error = kErrorParse); |
| exit: |
| return error; |
| } |
| |
| Error Server::ProcessIaNa(Message &aMessage, uint16_t aOffset, IaNa &aIaNa) |
| { |
| Error error = kErrorNone; |
| uint16_t optionOffset; |
| uint16_t length; |
| |
| SuccessOrExit(error = aMessage.Read(aOffset, aIaNa)); |
| |
| aOffset += sizeof(aIaNa); |
| length = aIaNa.GetLength() + sizeof(Option) - sizeof(IaNa); |
| |
| VerifyOrExit(length <= aMessage.GetLength() - aOffset, error = kErrorParse); |
| |
| mPrefixAgentsMask = 0; |
| |
| while (length > 0) |
| { |
| VerifyOrExit((optionOffset = FindOption(aMessage, aOffset, length, kOptionIaAddress)) > 0); |
| SuccessOrExit(error = ProcessIaAddress(aMessage, optionOffset)); |
| |
| length -= ((optionOffset - aOffset) + sizeof(IaAddress)); |
| aOffset = optionOffset + sizeof(IaAddress); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| Error Server::ProcessIaAddress(Message &aMessage, uint16_t aOffset) |
| { |
| Error error = kErrorNone; |
| IaAddress 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())) |
| { |
| mPrefixAgentsMask |= (1 << i); |
| break; |
| } |
| } |
| |
| exit: |
| return error; |
| } |
| |
| Error Server::SendReply(const Ip6::Address & aDst, |
| const TransactionId &aTransactionId, |
| ClientIdentifier & aClientId, |
| IaNa & aIaNa) |
| { |
| Error error = kErrorNone; |
| Ip6::MessageInfo messageInfo; |
| Message * message; |
| |
| VerifyOrExit((message = mSocket.NewMessage(0)) != nullptr, error = kErrorNoBufs); |
| SuccessOrExit(error = AppendHeader(*message, aTransactionId)); |
| SuccessOrExit(error = AppendServerIdentifier(*message)); |
| SuccessOrExit(error = AppendClientIdentifier(*message, aClientId)); |
| SuccessOrExit(error = AppendIaNa(*message, aIaNa)); |
| SuccessOrExit(error = AppendStatusCode(*message, kStatusSuccess)); |
| SuccessOrExit(error = AppendIaAddress(*message, aClientId)); |
| SuccessOrExit(error = AppendRapidCommit(*message)); |
| |
| messageInfo.SetPeerAddr(aDst); |
| messageInfo.SetPeerPort(kDhcpClientPort); |
| SuccessOrExit(error = mSocket.SendTo(*message, messageInfo)); |
| |
| exit: |
| FreeMessageOnError(message, error); |
| return error; |
| } |
| |
| Error Server::AppendHeader(Message &aMessage, const TransactionId &aTransactionId) |
| { |
| Header header; |
| |
| header.Clear(); |
| header.SetType(kTypeReply); |
| header.SetTransactionId(aTransactionId); |
| return aMessage.Append(header); |
| } |
| |
| Error Server::AppendClientIdentifier(Message &aMessage, ClientIdentifier &aClientId) |
| { |
| return aMessage.Append(aClientId); |
| } |
| |
| Error Server::AppendServerIdentifier(Message &aMessage) |
| { |
| Error error = kErrorNone; |
| ServerIdentifier option; |
| Mac::ExtAddress eui64; |
| |
| Get<Radio>().GetIeeeEui64(eui64); |
| |
| option.Init(); |
| option.SetDuidType(kDuidLinkLayerAddress); |
| option.SetDuidHardwareType(kHardwareTypeEui64); |
| option.SetDuidLinkLayerAddress(eui64); |
| SuccessOrExit(error = aMessage.Append(option)); |
| |
| exit: |
| return error; |
| } |
| |
| Error Server::AppendIaNa(Message &aMessage, IaNa &aIaNa) |
| { |
| Error error = kErrorNone; |
| uint16_t length = 0; |
| |
| if (mPrefixAgentsMask) |
| { |
| for (uint16_t i = 0; i < GetArrayLength(mPrefixAgents); i++) |
| { |
| if (mPrefixAgentsMask & (1 << i)) |
| { |
| length += sizeof(IaAddress); |
| } |
| } |
| } |
| else |
| { |
| length += sizeof(IaAddress) * mPrefixAgentsCount; |
| } |
| |
| length += sizeof(IaNa) + sizeof(StatusCode) - sizeof(Option); |
| |
| aIaNa.SetLength(length); |
| aIaNa.SetT1(IaNa::kDefaultT1); |
| aIaNa.SetT2(IaNa::kDefaultT2); |
| SuccessOrExit(error = aMessage.Append(aIaNa)); |
| |
| exit: |
| return error; |
| } |
| |
| Error Server::AppendStatusCode(Message &aMessage, Status aStatusCode) |
| { |
| StatusCode option; |
| |
| option.Init(); |
| option.SetStatusCode(aStatusCode); |
| return aMessage.Append(option); |
| } |
| |
| Error Server::AppendIaAddress(Message &aMessage, ClientIdentifier &aClientId) |
| { |
| Error error = kErrorNone; |
| |
| if (mPrefixAgentsMask) |
| { |
| // if specified, only apply specified prefixes |
| for (uint16_t i = 0; i < GetArrayLength(mPrefixAgents); i++) |
| { |
| if (mPrefixAgentsMask & (1 << i)) |
| { |
| SuccessOrExit(error = AddIaAddress(aMessage, mPrefixAgents[i].GetPrefixAsAddress(), aClientId)); |
| } |
| } |
| } |
| else |
| { |
| // if not specified, apply all configured prefixes |
| for (const PrefixAgent &prefixAgent : mPrefixAgents) |
| { |
| if (prefixAgent.IsValid()) |
| { |
| SuccessOrExit(error = AddIaAddress(aMessage, prefixAgent.GetPrefixAsAddress(), aClientId)); |
| } |
| } |
| } |
| |
| exit: |
| return error; |
| } |
| |
| Error Server::AddIaAddress(Message &aMessage, const Ip6::Address &aPrefix, ClientIdentifier &aClientId) |
| { |
| Error error = kErrorNone; |
| IaAddress option; |
| |
| option.Init(); |
| option.GetAddress().SetPrefix(aPrefix.mFields.m8, OT_IP6_PREFIX_BITSIZE); |
| option.GetAddress().GetIid().SetFromExtAddress(aClientId.GetDuidLinkLayerAddress()); |
| option.SetPreferredLifetime(IaAddress::kDefaultPreferredLifetime); |
| option.SetValidLifetime(IaAddress::kDefaultValidLiftetime); |
| SuccessOrExit(error = aMessage.Append(option)); |
| |
| exit: |
| return error; |
| } |
| |
| Error Server::AppendRapidCommit(Message &aMessage) |
| { |
| RapidCommit option; |
| |
| option.Init(); |
| return aMessage.Append(option); |
| } |
| |
| void Server::ApplyMeshLocalPrefix(void) |
| { |
| for (PrefixAgent &prefixAgent : mPrefixAgents) |
| { |
| if (prefixAgent.IsValid()) |
| { |
| PrefixAgent *entry = &prefixAgent; |
| |
| Get<ThreadNetif>().RemoveUnicastAddress(entry->GetAloc()); |
| entry->GetAloc().GetAddress().SetPrefix(Get<Mle::MleRouter>().GetMeshLocalPrefix()); |
| Get<ThreadNetif>().AddUnicastAddress(entry->GetAloc()); |
| } |
| } |
| } |
| |
| } // namespace Dhcp6 |
| } // namespace ot |
| |
| #endif // OPENTHREAD_CONFIG_DHCP6_SERVER_ENABLE |