blob: 1b38cc43d0c536a972b50f2e1aa68eaef1cfc5ac [file] [log] [blame]
/*
* 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 <openthread/config.h>
#include "udp6.hpp"
#include <stdio.h>
#include "common/code_utils.hpp"
#include "common/encoding.hpp"
#include "net/ip6.hpp"
using ot::Encoding::BigEndian::HostSwap16;
namespace ot {
namespace Ip6 {
UdpSocket::UdpSocket(Udp &aUdp)
{
mTransport = &aUdp;
}
Message *UdpSocket::NewMessage(uint16_t aReserved)
{
return static_cast<Udp *>(mTransport)->NewMessage(aReserved);
}
otError UdpSocket::Open(otUdpReceive aHandler, void *aContext)
{
memset(&mSockName, 0, sizeof(mSockName));
memset(&mPeerName, 0, sizeof(mPeerName));
mHandler = aHandler;
mContext = aContext;
return static_cast<Udp *>(mTransport)->AddSocket(*this);
}
otError UdpSocket::Bind(const SockAddr &aSockAddr)
{
mSockName = aSockAddr;
if (GetSockName().mPort == 0)
{
mSockName.mPort = static_cast<Udp *>(mTransport)->GetEphemeralPort();
}
return OT_ERROR_NONE;
}
otError UdpSocket::Connect(const SockAddr &aSockAddr)
{
mPeerName = aSockAddr;
return OT_ERROR_NONE;
}
otError UdpSocket::Close(void)
{
otError error = OT_ERROR_NONE;
SuccessOrExit(error = static_cast<Udp *>(mTransport)->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;
UdpHeader udpHeader;
messageInfoLocal = aMessageInfo;
if (messageInfoLocal.GetSockAddr().IsUnspecified())
{
messageInfoLocal.SetSockAddr(GetSockName().GetAddress());
}
if (GetSockName().mPort == 0)
{
GetSockName().mPort = static_cast<Udp *>(mTransport)->GetEphemeralPort();
}
udpHeader.SetSourcePort(GetSockName().mPort);
udpHeader.SetDestinationPort(messageInfoLocal.mPeerPort);
udpHeader.SetLength(sizeof(udpHeader) + aMessage.GetLength());
udpHeader.SetChecksum(0);
SuccessOrExit(error = aMessage.Prepend(&udpHeader, sizeof(udpHeader)));
aMessage.SetOffset(0);
SuccessOrExit(error = static_cast<Udp *>(mTransport)->SendDatagram(aMessage, messageInfoLocal, kProtoUdp));
exit:
return error;
}
Udp::Udp(Ip6 &aIp6):
Ip6Locator(aIp6),
mEphemeralPort(kDynamicPortMin),
mSockets(NULL)
{
}
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)
{
return GetIp6().NewMessage(sizeof(UdpHeader) + aReserved);
}
otError Udp::SendDatagram(Message &aMessage, MessageInfo &aMessageInfo, IpProto aIpProto)
{
return GetIp6().SendDatagram(aMessage, aMessageInfo, aIpProto);
}
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);
VerifyOrExit(checksum == 0xffff, error = OT_ERROR_DROP);
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();
// find socket
for (UdpSocket *socket = mSockets; socket; socket = socket->GetNext())
{
if (socket->GetSockName().mPort != udpHeader.GetDestinationPort())
{
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 != udpHeader.GetSourcePort())
{
continue;
}
if (!socket->GetPeerName().GetAddress().IsUnspecified() &&
socket->GetPeerName().GetAddress() != aMessageInfo.GetPeerAddr())
{
continue;
}
}
socket->HandleUdpReceive(aMessage, aMessageInfo);
}
exit:
return error;
}
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