| /* |
| * 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 UDP/IPv6 sockets. |
| */ |
| |
| #include "udp6.hpp" |
| |
| #include <stdio.h> |
| |
| #include <openthread/platform/udp.h> |
| |
| #include "common/code_utils.hpp" |
| #include "common/encoding.hpp" |
| #include "common/instance.hpp" |
| #include "common/locator-getters.hpp" |
| #include "net/ip6.hpp" |
| |
| using ot::Encoding::BigEndian::HostSwap16; |
| |
| namespace ot { |
| namespace Ip6 { |
| |
| #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE |
| static bool IsMle(Instance &aInstance, uint16_t aPort) |
| { |
| #if OPENTHREAD_FTD |
| return aPort == ot::Mle::kUdpPort || aPort == aInstance.Get<MeshCoP::JoinerRouter>().GetJoinerUdpPort(); |
| #else |
| OT_UNUSED_VARIABLE(aInstance); |
| return aPort == ot::Mle::kUdpPort; |
| #endif |
| } |
| #endif |
| |
| UdpSocket::UdpSocket(Udp &aUdp) |
| : InstanceLocator(aUdp.GetInstance()) |
| { |
| mHandle = nullptr; |
| } |
| |
| Message *UdpSocket::NewMessage(uint16_t aReserved, const Message::Settings &aSettings) |
| { |
| return Get<Udp>().NewMessage(aReserved, aSettings); |
| } |
| |
| otError UdpSocket::Open(otUdpReceive aHandler, void *aContext) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| GetSockName().Clear(); |
| GetPeerName().Clear(); |
| mHandler = aHandler; |
| mContext = aContext; |
| |
| #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE |
| SuccessOrExit(error = otPlatUdpSocket(this)); |
| #endif |
| |
| Get<Udp>().AddSocket(*this); |
| |
| #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE |
| exit: |
| #endif |
| return error; |
| } |
| |
| otError UdpSocket::Bind(const SockAddr &aSockAddr) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| mSockName = aSockAddr; |
| |
| if (!IsBound()) |
| { |
| do |
| { |
| mSockName.mPort = Get<Udp>().GetEphemeralPort(); |
| #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE |
| error = otPlatUdpBind(this); |
| #endif |
| } while (error != OT_ERROR_NONE); |
| } |
| #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE |
| else if (!IsMle(GetInstance(), mSockName.mPort)) |
| { |
| error = otPlatUdpBind(this); |
| } |
| #endif |
| |
| return error; |
| } |
| |
| otError UdpSocket::Connect(const SockAddr &aSockAddr) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| mPeerName = aSockAddr; |
| |
| #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE |
| if (!IsMle(GetInstance(), mSockName.mPort)) |
| { |
| error = otPlatUdpConnect(this); |
| } |
| #endif |
| return error; |
| } |
| |
| otError UdpSocket::Close(void) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE |
| SuccessOrExit(error = otPlatUdpClose(this)); |
| #endif |
| |
| Get<Udp>().RemoveSocket(*this); |
| GetSockName().Clear(); |
| GetPeerName().Clear(); |
| |
| #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE |
| exit: |
| #endif |
| return error; |
| } |
| |
| otError UdpSocket::SendTo(Message &aMessage, const MessageInfo &aMessageInfo) |
| { |
| otError error = OT_ERROR_NONE; |
| MessageInfo messageInfoLocal; |
| |
| VerifyOrExit((aMessageInfo.GetSockPort() == 0) || (GetSockName().mPort == aMessageInfo.GetSockPort()), |
| error = OT_ERROR_INVALID_ARGS); |
| |
| messageInfoLocal = aMessageInfo; |
| |
| if (messageInfoLocal.GetPeerAddr().IsUnspecified()) |
| { |
| VerifyOrExit(!GetPeerName().GetAddress().IsUnspecified(), error = OT_ERROR_INVALID_ARGS); |
| |
| messageInfoLocal.SetPeerAddr(GetPeerName().GetAddress()); |
| } |
| |
| if (messageInfoLocal.mPeerPort == 0) |
| { |
| VerifyOrExit(GetPeerName().mPort != 0, error = OT_ERROR_INVALID_ARGS); |
| messageInfoLocal.mPeerPort = GetPeerName().mPort; |
| } |
| |
| if (messageInfoLocal.GetSockAddr().IsUnspecified()) |
| { |
| messageInfoLocal.SetSockAddr(GetSockName().GetAddress()); |
| } |
| |
| if (!IsBound()) |
| { |
| SuccessOrExit(error = Bind(GetSockName())); |
| } |
| |
| messageInfoLocal.SetSockPort(GetSockName().mPort); |
| |
| #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE |
| if (!IsMle(GetInstance(), mSockName.mPort) && |
| !(mSockName.mPort == ot::kCoapUdpPort && aMessage.GetSubType() == Message::kSubTypeJoinerEntrust)) |
| { |
| SuccessOrExit(error = otPlatUdpSend(this, &aMessage, &messageInfoLocal)); |
| } |
| else |
| #endif |
| { |
| SuccessOrExit(error = Get<Udp>().SendDatagram(aMessage, messageInfoLocal, kProtoUdp)); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| bool UdpSocket::Matches(const MessageInfo &aMessageInfo) const |
| { |
| bool matches = false; |
| |
| VerifyOrExit(GetSockName().mPort == aMessageInfo.GetSockPort(), OT_NOOP); |
| |
| VerifyOrExit(aMessageInfo.GetSockAddr().IsMulticast() || GetSockName().GetAddress().IsUnspecified() || |
| GetSockName().GetAddress() == aMessageInfo.GetSockAddr(), |
| OT_NOOP); |
| |
| // Verify source if connected socket |
| if (GetPeerName().mPort != 0) |
| { |
| VerifyOrExit(GetPeerName().mPort == aMessageInfo.GetPeerPort(), OT_NOOP); |
| |
| VerifyOrExit(GetPeerName().GetAddress().IsUnspecified() || |
| GetPeerName().GetAddress() == aMessageInfo.GetPeerAddr(), |
| OT_NOOP); |
| } |
| |
| matches = true; |
| |
| exit: |
| return matches; |
| } |
| |
| Udp::Udp(Instance &aInstance) |
| : InstanceLocator(aInstance) |
| , mEphemeralPort(kDynamicPortMin) |
| , mReceivers() |
| , mSockets() |
| #if OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE |
| , mUdpForwarderContext(nullptr) |
| , mUdpForwarder(nullptr) |
| #endif |
| { |
| } |
| |
| otError Udp::AddReceiver(UdpReceiver &aReceiver) |
| { |
| return mReceivers.Add(aReceiver); |
| } |
| |
| otError Udp::RemoveReceiver(UdpReceiver &aReceiver) |
| { |
| otError error; |
| |
| SuccessOrExit(error = mReceivers.Remove(aReceiver)); |
| aReceiver.SetNext(nullptr); |
| |
| exit: |
| return error; |
| } |
| |
| void Udp::AddSocket(UdpSocket &aSocket) |
| { |
| IgnoreError(mSockets.Add(aSocket)); |
| } |
| |
| void Udp::RemoveSocket(UdpSocket &aSocket) |
| { |
| SuccessOrExit(mSockets.Remove(aSocket)); |
| aSocket.SetNext(nullptr); |
| |
| exit: |
| return; |
| } |
| |
| uint16_t Udp::GetEphemeralPort(void) |
| { |
| uint16_t rval = mEphemeralPort; |
| |
| if (mEphemeralPort < kDynamicPortMax) |
| { |
| mEphemeralPort++; |
| } |
| else |
| { |
| mEphemeralPort = kDynamicPortMin; |
| } |
| |
| return rval; |
| } |
| |
| Message *Udp::NewMessage(uint16_t aReserved, const Message::Settings &aSettings) |
| { |
| return Get<Ip6>().NewMessage(sizeof(UdpHeader) + aReserved, aSettings); |
| } |
| |
| otError Udp::SendDatagram(Message &aMessage, MessageInfo &aMessageInfo, uint8_t aIpProto) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| #if OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE |
| if (aMessageInfo.IsHostInterface()) |
| { |
| VerifyOrExit(mUdpForwarder != nullptr, error = OT_ERROR_NO_ROUTE); |
| mUdpForwarder(&aMessage, aMessageInfo.mPeerPort, &aMessageInfo.GetPeerAddr(), aMessageInfo.mSockPort, |
| mUdpForwarderContext); |
| // message is consumed by the callback |
| } |
| else |
| #endif |
| { |
| UdpHeader udpHeader; |
| |
| udpHeader.SetSourcePort(aMessageInfo.mSockPort); |
| udpHeader.SetDestinationPort(aMessageInfo.mPeerPort); |
| udpHeader.SetLength(sizeof(udpHeader) + aMessage.GetLength()); |
| udpHeader.SetChecksum(0); |
| |
| SuccessOrExit(error = aMessage.Prepend(&udpHeader, sizeof(udpHeader))); |
| aMessage.SetOffset(0); |
| |
| error = Get<Ip6>().SendDatagram(aMessage, aMessageInfo, aIpProto); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError Udp::HandleMessage(Message &aMessage, MessageInfo &aMessageInfo) |
| { |
| otError error = OT_ERROR_NONE; |
| UdpHeader udpHeader; |
| uint16_t payloadLength; |
| uint16_t checksum; |
| |
| payloadLength = aMessage.GetLength() - aMessage.GetOffset(); |
| |
| // check length |
| VerifyOrExit(payloadLength >= sizeof(UdpHeader), error = OT_ERROR_PARSE); |
| |
| // verify checksum |
| checksum = Ip6::ComputePseudoheaderChecksum(aMessageInfo.GetPeerAddr(), aMessageInfo.GetSockAddr(), payloadLength, |
| kProtoUdp); |
| checksum = aMessage.UpdateChecksum(checksum, aMessage.GetOffset(), payloadLength); |
| |
| #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
| VerifyOrExit(checksum == 0xffff, error = OT_ERROR_DROP); |
| #endif |
| |
| VerifyOrExit(aMessage.Read(aMessage.GetOffset(), sizeof(udpHeader), &udpHeader) == sizeof(udpHeader), |
| error = OT_ERROR_PARSE); |
| aMessage.MoveOffset(sizeof(udpHeader)); |
| aMessageInfo.mPeerPort = udpHeader.GetSourcePort(); |
| aMessageInfo.mSockPort = udpHeader.GetDestinationPort(); |
| |
| #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE |
| VerifyOrExit(IsMle(GetInstance(), aMessageInfo.mSockPort), OT_NOOP); |
| #endif |
| |
| for (UdpReceiver *receiver = mReceivers.GetHead(); receiver; receiver = receiver->GetNext()) |
| { |
| VerifyOrExit(!receiver->HandleMessage(aMessage, aMessageInfo), OT_NOOP); |
| } |
| |
| HandlePayload(aMessage, aMessageInfo); |
| |
| exit: |
| return error; |
| } |
| |
| void Udp::HandlePayload(Message &aMessage, MessageInfo &aMessageInfo) |
| { |
| UdpSocket *socket; |
| UdpSocket *prev; |
| |
| socket = mSockets.FindMatching(aMessageInfo, prev); |
| VerifyOrExit(socket != nullptr, OT_NOOP); |
| |
| aMessage.RemoveHeader(aMessage.GetOffset()); |
| OT_ASSERT(aMessage.GetOffset() == 0); |
| socket->HandleUdpReceive(aMessage, aMessageInfo); |
| |
| exit: |
| return; |
| } |
| |
| void Udp::UpdateChecksum(Message &aMessage, uint16_t aChecksum) |
| { |
| aChecksum = aMessage.UpdateChecksum(aChecksum, aMessage.GetOffset(), aMessage.GetLength() - aMessage.GetOffset()); |
| |
| if (aChecksum != 0xffff) |
| { |
| aChecksum = ~aChecksum; |
| } |
| |
| aChecksum = HostSwap16(aChecksum); |
| aMessage.Write(aMessage.GetOffset() + UdpHeader::GetChecksumOffset(), sizeof(aChecksum), &aChecksum); |
| } |
| |
| } // namespace Ip6 |
| } // namespace ot |