| /* |
| * 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 "net/ip6.hpp" |
| |
| using ot::Encoding::BigEndian::HostSwap16; |
| |
| namespace ot { |
| namespace Ip6 { |
| |
| #if OPENTHREAD_ENABLE_PLATFORM_UDP |
| static bool IsMle(Instance &aInstance, uint16_t aPort) |
| { |
| #if OPENTHREAD_FTD |
| return aPort == ot::Mle::kUdpPort || aPort == aInstance.Get<MeshCoP::JoinerRouter>().GetJoinerUdpPort(); |
| #else |
| return aPort == ot::Mle::kUdpPort; |
| #endif |
| } |
| #endif |
| |
| UdpSocket::UdpSocket(Udp &aUdp) |
| : InstanceLocator(aUdp.GetInstance()) |
| { |
| mHandle = NULL; |
| } |
| |
| Udp &UdpSocket::GetUdp(void) |
| { |
| return GetInstance().GetIp6().GetUdp(); |
| } |
| |
| Message *UdpSocket::NewMessage(uint16_t aReserved, uint8_t aPriority) |
| { |
| return GetUdp().NewMessage(aReserved, aPriority); |
| } |
| |
| otError UdpSocket::Open(otUdpReceive aHandler, void *aContext) |
| { |
| otError error; |
| |
| memset(&mSockName, 0, sizeof(mSockName)); |
| memset(&mPeerName, 0, sizeof(mPeerName)); |
| mHandler = aHandler; |
| mContext = aContext; |
| |
| #if OPENTHREAD_ENABLE_PLATFORM_UDP |
| SuccessOrExit(error = otPlatUdpSocket(this)); |
| #endif |
| SuccessOrExit(error = GetUdp().AddSocket(*this)); |
| |
| exit: |
| return error; |
| } |
| |
| otError UdpSocket::Bind(const SockAddr &aSockAddr) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| mSockName = aSockAddr; |
| |
| if (mSockName.mPort == 0) |
| { |
| do |
| { |
| mSockName.mPort = GetUdp().GetEphemeralPort(); |
| #if OPENTHREAD_ENABLE_PLATFORM_UDP |
| error = otPlatUdpBind(this); |
| #endif |
| } while (error != OT_ERROR_NONE); |
| } |
| #if OPENTHREAD_ENABLE_PLATFORM_UDP |
| 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_ENABLE_PLATFORM_UDP |
| if (!IsMle(GetInstance(), mSockName.mPort)) |
| { |
| error = otPlatUdpConnect(this); |
| } |
| #endif |
| return error; |
| } |
| |
| otError UdpSocket::Close(void) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| #if OPENTHREAD_ENABLE_PLATFORM_UDP |
| SuccessOrExit(error = otPlatUdpClose(this)); |
| #endif |
| SuccessOrExit(error = GetUdp().RemoveSocket(*this)); |
| memset(&mSockName, 0, sizeof(mSockName)); |
| memset(&mPeerName, 0, sizeof(mPeerName)); |
| |
| exit: |
| return error; |
| } |
| |
| otError UdpSocket::SendTo(Message &aMessage, const MessageInfo &aMessageInfo) |
| { |
| otError error = OT_ERROR_NONE; |
| MessageInfo messageInfoLocal; |
| |
| 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 (GetSockName().mPort == 0) |
| { |
| SuccessOrExit(error = Bind(GetSockName())); |
| } |
| messageInfoLocal.SetSockPort(GetSockName().mPort); |
| |
| #if OPENTHREAD_ENABLE_PLATFORM_UDP |
| if (!IsMle(GetInstance(), mSockName.mPort) && |
| !(mSockName.mPort == ot::kCoapUdpPort && aMessage.GetSubType() == Message::kSubTypeJoinerEntrust)) |
| { |
| SuccessOrExit(error = otPlatUdpSend(this, &aMessage, &messageInfoLocal)); |
| } |
| else |
| #endif |
| { |
| SuccessOrExit(error = GetUdp().SendDatagram(aMessage, messageInfoLocal, kProtoUdp)); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| Udp::Udp(Instance &aInstance) |
| : InstanceLocator(aInstance) |
| , mEphemeralPort(kDynamicPortMin) |
| , mReceivers(NULL) |
| , mSockets(NULL) |
| { |
| } |
| |
| otError Udp::AddReceiver(UdpReceiver &aReceiver) |
| { |
| for (UdpReceiver *cur = mReceivers; cur; cur = cur->GetNext()) |
| { |
| if (cur == &aReceiver) |
| { |
| ExitNow(); |
| } |
| } |
| |
| aReceiver.SetNext(mReceivers); |
| mReceivers = &aReceiver; |
| |
| exit: |
| return OT_ERROR_NONE; |
| } |
| |
| otError Udp::RemoveReceiver(UdpReceiver &aReceiver) |
| { |
| if (mReceivers == &aReceiver) |
| { |
| mReceivers = mReceivers->GetNext(); |
| } |
| else |
| { |
| for (UdpReceiver *handler = mReceivers; handler; handler = handler->GetNext()) |
| { |
| if (handler->GetNext() == &aReceiver) |
| { |
| handler->SetNext(aReceiver.GetNext()); |
| break; |
| } |
| } |
| } |
| |
| aReceiver.SetNext(NULL); |
| |
| return OT_ERROR_NONE; |
| } |
| |
| otError Udp::AddSocket(UdpSocket &aSocket) |
| { |
| for (UdpSocket *cur = mSockets; cur; cur = cur->GetNext()) |
| { |
| if (cur == &aSocket) |
| { |
| ExitNow(); |
| } |
| } |
| |
| aSocket.SetNext(mSockets); |
| mSockets = &aSocket; |
| |
| exit: |
| return OT_ERROR_NONE; |
| } |
| |
| otError Udp::RemoveSocket(UdpSocket &aSocket) |
| { |
| if (mSockets == &aSocket) |
| { |
| mSockets = mSockets->GetNext(); |
| } |
| else |
| { |
| for (UdpSocket *socket = mSockets; socket; socket = socket->GetNext()) |
| { |
| if (socket->GetNext() == &aSocket) |
| { |
| socket->SetNext(aSocket.GetNext()); |
| break; |
| } |
| } |
| } |
| |
| aSocket.SetNext(NULL); |
| |
| return OT_ERROR_NONE; |
| } |
| |
| uint16_t Udp::GetEphemeralPort(void) |
| { |
| uint16_t rval = mEphemeralPort; |
| |
| if (mEphemeralPort < kDynamicPortMax) |
| { |
| mEphemeralPort++; |
| } |
| else |
| { |
| mEphemeralPort = kDynamicPortMin; |
| } |
| |
| return rval; |
| } |
| |
| Message *Udp::NewMessage(uint16_t aReserved, uint8_t aPriority) |
| { |
| return GetIp6().NewMessage(sizeof(UdpHeader) + aReserved, aPriority); |
| } |
| |
| otError Udp::SendDatagram(Message &aMessage, MessageInfo &aMessageInfo, IpProto aIpProto) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| #if OPENTHREAD_ENABLE_UDP_PROXY |
| if (aMessageInfo.GetInterfaceId() == OT_NETIF_INTERFACE_ID_HOST) |
| { |
| VerifyOrExit(mProxySender != NULL, error = OT_ERROR_NO_ROUTE); |
| mProxySender(&aMessage, aMessageInfo.mPeerPort, &aMessageInfo.GetPeerAddr(), aMessageInfo.mSockPort, |
| mProxySenderContext); |
| // 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 = GetIp6().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_ENABLE_PLATFORM_UDP |
| VerifyOrExit(IsMle(GetInstance(), aMessageInfo.mSockPort)); |
| #endif |
| |
| for (UdpReceiver *receiver = mReceivers; receiver; receiver = receiver->GetNext()) |
| { |
| VerifyOrExit(!receiver->HandleMessage(aMessage, aMessageInfo)); |
| } |
| |
| HandlePayload(aMessage, aMessageInfo); |
| |
| exit: |
| return error; |
| } |
| |
| void Udp::HandlePayload(Message &aMessage, MessageInfo &aMessageInfo) |
| { |
| // find socket |
| for (UdpSocket *socket = mSockets; socket; socket = socket->GetNext()) |
| { |
| if (socket->GetSockName().mPort != aMessageInfo.GetSockPort()) |
| { |
| continue; |
| } |
| |
| if (socket->GetSockName().mScopeId != 0 && socket->GetSockName().mScopeId != aMessageInfo.mInterfaceId) |
| { |
| continue; |
| } |
| |
| if (!aMessageInfo.GetSockAddr().IsMulticast() && !socket->GetSockName().GetAddress().IsUnspecified() && |
| socket->GetSockName().GetAddress() != aMessageInfo.GetSockAddr()) |
| { |
| continue; |
| } |
| |
| // verify source if connected socket |
| if (socket->GetPeerName().mPort != 0) |
| { |
| if (socket->GetPeerName().mPort != aMessageInfo.GetPeerPort()) |
| { |
| continue; |
| } |
| |
| if (!socket->GetPeerName().GetAddress().IsUnspecified() && |
| socket->GetPeerName().GetAddress() != aMessageInfo.GetPeerAddr()) |
| { |
| continue; |
| } |
| } |
| |
| socket->HandleUdpReceive(aMessage, aMessageInfo); |
| } |
| } |
| |
| otError 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); |
| return OT_ERROR_NONE; |
| } |
| |
| } // namespace Ip6 |
| } // namespace ot |