/* * Copyright (c) 2018, 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 * @brief * This file includes the platform UDP driver. */ #ifdef __APPLE__ #define __APPLE_USE_RFC_3542 #endif #include "openthread-posix-config.h" #include "platform-posix.h" #include #include #include #include #include #include #include #include #include #include #include "common/code_utils.hpp" #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE #include "posix/platform/ip6_utils.hpp" #include "posix/platform/mainloop.hpp" #include "posix/platform/udp.hpp" using namespace ot::Posix::Ip6Utils; namespace { constexpr size_t kMaxUdpSize = 1280; void *FdToHandle(int aFd) { return reinterpret_cast(aFd); } int FdFromHandle(void *aHandle) { return static_cast(reinterpret_cast(aHandle)); } otError transmitPacket(int aFd, uint8_t *aPayload, uint16_t aLength, const otMessageInfo &aMessageInfo) { #ifdef __APPLE__ // use fixed value for CMSG_SPACE is not a constant expression on macOS constexpr size_t kBufferSize = 128; #else constexpr size_t kBufferSize = CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int)); #endif struct sockaddr_in6 peerAddr; uint8_t control[kBufferSize]; size_t controlLength = 0; struct iovec iov; struct msghdr msg; struct cmsghdr *cmsg; ssize_t rval; otError error = OT_ERROR_NONE; memset(&peerAddr, 0, sizeof(peerAddr)); peerAddr.sin6_port = htons(aMessageInfo.mPeerPort); peerAddr.sin6_family = AF_INET6; CopyIp6AddressTo(aMessageInfo.mPeerAddr, &peerAddr.sin6_addr); if (IsIp6AddressLinkLocal(aMessageInfo.mPeerAddr) && !aMessageInfo.mIsHostInterface) { // sin6_scope_id only works for link local destinations peerAddr.sin6_scope_id = gNetifIndex; } memset(control, 0, sizeof(control)); iov.iov_base = aPayload; iov.iov_len = aLength; msg.msg_name = &peerAddr; msg.msg_namelen = sizeof(peerAddr); msg.msg_control = control; msg.msg_controllen = static_cast(sizeof(control)); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_flags = 0; { int hopLimit = (aMessageInfo.mHopLimit ? aMessageInfo.mHopLimit : OPENTHREAD_CONFIG_IP6_HOP_LIMIT_DEFAULT); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = IPPROTO_IPV6; cmsg->cmsg_type = IPV6_HOPLIMIT; cmsg->cmsg_len = CMSG_LEN(sizeof(int)); memcpy(CMSG_DATA(cmsg), &hopLimit, sizeof(int)); controlLength += CMSG_SPACE(sizeof(int)); } if (!IsIp6AddressMulticast(aMessageInfo.mSockAddr) && !IsIp6AddressUnspecified(aMessageInfo.mSockAddr)) { struct in6_pktinfo pktinfo; cmsg = CMSG_NXTHDR(&msg, cmsg); cmsg->cmsg_level = IPPROTO_IPV6; cmsg->cmsg_type = IPV6_PKTINFO; cmsg->cmsg_len = CMSG_LEN(sizeof(pktinfo)); pktinfo.ipi6_ifindex = aMessageInfo.mIsHostInterface ? 0 : gNetifIndex; CopyIp6AddressTo(aMessageInfo.mSockAddr, &pktinfo.ipi6_addr); memcpy(CMSG_DATA(cmsg), &pktinfo, sizeof(pktinfo)); controlLength += CMSG_SPACE(sizeof(pktinfo)); } #ifdef __APPLE__ msg.msg_controllen = static_cast(controlLength); #else msg.msg_controllen = controlLength; #endif rval = sendmsg(aFd, &msg, 0); VerifyOrExit(rval > 0, perror("sendmsg")); exit: // EINVAL happens when we shift from child to router and the // interface address changes. Ask callers to try again later. if (rval == -1) { error = (errno == EINVAL) ? OT_ERROR_INVALID_STATE : OT_ERROR_FAILED; } return error; } otError receivePacket(int aFd, uint8_t *aPayload, uint16_t &aLength, otMessageInfo &aMessageInfo) { struct sockaddr_in6 peerAddr; uint8_t control[kMaxUdpSize]; struct iovec iov; struct msghdr msg; ssize_t rval; iov.iov_base = aPayload; iov.iov_len = aLength; msg.msg_name = &peerAddr; msg.msg_namelen = sizeof(peerAddr); msg.msg_control = control; msg.msg_controllen = sizeof(control); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_flags = 0; rval = recvmsg(aFd, &msg, 0); VerifyOrExit(rval > 0, perror("recvmsg")); aLength = static_cast(rval); for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == IPPROTO_IPV6) { if (cmsg->cmsg_type == IPV6_HOPLIMIT) { int hoplimit; memcpy(&hoplimit, CMSG_DATA(cmsg), sizeof(hoplimit)); aMessageInfo.mHopLimit = static_cast(hoplimit); } else if (cmsg->cmsg_type == IPV6_PKTINFO) { struct in6_pktinfo pktinfo; memcpy(&pktinfo, CMSG_DATA(cmsg), sizeof(pktinfo)); aMessageInfo.mIsHostInterface = (pktinfo.ipi6_ifindex != gNetifIndex); ReadIp6AddressFrom(&pktinfo.ipi6_addr, aMessageInfo.mSockAddr); } } } aMessageInfo.mPeerPort = ntohs(peerAddr.sin6_port); ReadIp6AddressFrom(&peerAddr.sin6_addr, aMessageInfo.mPeerAddr); exit: return rval > 0 ? OT_ERROR_NONE : OT_ERROR_FAILED; } } // namespace otError otPlatUdpSocket(otUdpSocket *aUdpSocket) { otError error = OT_ERROR_NONE; int fd; assert(aUdpSocket->mHandle == nullptr); fd = SocketWithCloseExec(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, kSocketNonBlock); VerifyOrExit(fd >= 0, error = OT_ERROR_FAILED); aUdpSocket->mHandle = FdToHandle(fd); exit: return error; } otError otPlatUdpClose(otUdpSocket *aUdpSocket) { otError error = OT_ERROR_NONE; int fd; // Only call `close()` on platform UDP sockets. // Platform UDP sockets always have valid `mHandle` upon creation. VerifyOrExit(aUdpSocket->mHandle != nullptr); fd = FdFromHandle(aUdpSocket->mHandle); VerifyOrExit(0 == close(fd), error = OT_ERROR_FAILED); aUdpSocket->mHandle = nullptr; exit: return error; } otError otPlatUdpBind(otUdpSocket *aUdpSocket) { otError error = OT_ERROR_NONE; int fd; assert(gNetifIndex != 0); assert(aUdpSocket->mHandle != nullptr); VerifyOrExit(aUdpSocket->mSockName.mPort != 0, error = OT_ERROR_INVALID_ARGS); fd = FdFromHandle(aUdpSocket->mHandle); { struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(struct sockaddr_in6)); sin6.sin6_port = htons(aUdpSocket->mSockName.mPort); sin6.sin6_family = AF_INET6; CopyIp6AddressTo(aUdpSocket->mSockName.mAddress, &sin6.sin6_addr); VerifyOrExit(0 == bind(fd, reinterpret_cast(&sin6), sizeof(sin6)), error = OT_ERROR_FAILED); } { int on = 1; VerifyOrExit(0 == setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on)), error = OT_ERROR_FAILED); VerifyOrExit(0 == setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)), error = OT_ERROR_FAILED); } exit: if (error == OT_ERROR_FAILED) { ot::Posix::Udp::LogCrit("Failed to bind UDP socket: %s", strerror(errno)); } return error; } otError otPlatUdpBindToNetif(otUdpSocket *aUdpSocket, otNetifIdentifier aNetifIdentifier) { otError error = OT_ERROR_NONE; int fd = FdFromHandle(aUdpSocket->mHandle); int one = 1; int zero = 0; switch (aNetifIdentifier) { case OT_NETIF_UNSPECIFIED: { #ifdef __linux__ VerifyOrExit(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, nullptr, 0) == 0, error = OT_ERROR_FAILED); #else // __NetBSD__ || __FreeBSD__ || __APPLE__ unsigned int netifIndex = 0; VerifyOrExit(setsockopt(fd, IPPROTO_IPV6, IPV6_BOUND_IF, &netifIndex, sizeof(netifIndex)) == 0, error = OT_ERROR_FAILED); #endif // __linux__ break; } case OT_NETIF_THREAD: { #ifdef __linux__ VerifyOrExit(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &gNetifName, strlen(gNetifName)) == 0, error = OT_ERROR_FAILED); #else // __NetBSD__ || __FreeBSD__ || __APPLE__ VerifyOrExit(setsockopt(fd, IPPROTO_IPV6, IPV6_BOUND_IF, &gNetifIndex, sizeof(gNetifIndex)) == 0, error = OT_ERROR_FAILED); #endif // __linux__ break; } case OT_NETIF_BACKBONE: { #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE if (otSysGetInfraNetifName() == nullptr || otSysGetInfraNetifName()[0] == '\0') { ot::Posix::Udp::LogWarn("No backbone interface given, %s fails.", __func__); ExitNow(error = OT_ERROR_INVALID_ARGS); } #ifdef __linux__ VerifyOrExit(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, otSysGetInfraNetifName(), strlen(otSysGetInfraNetifName())) == 0, error = OT_ERROR_FAILED); #else // __NetBSD__ || __FreeBSD__ || __APPLE__ uint32_t backboneNetifIndex = otSysGetInfraNetifIndex(); VerifyOrExit(setsockopt(fd, IPPROTO_IPV6, IPV6_BOUND_IF, &backboneNetifIndex, sizeof(backboneNetifIndex)) == 0, error = OT_ERROR_FAILED); #endif // __linux__ #else ExitNow(error = OT_ERROR_NOT_IMPLEMENTED); #endif VerifyOrExit(setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&one, sizeof(one)) == 0, error = OT_ERROR_FAILED); break; } } VerifyOrExit(setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &zero, sizeof(zero)) == 0, error = OT_ERROR_FAILED); exit: return error; } otError otPlatUdpConnect(otUdpSocket *aUdpSocket) { otError error = OT_ERROR_NONE; struct sockaddr_in6 sin6; int fd; bool isDisconnect = IsIp6AddressUnspecified(aUdpSocket->mPeerName.mAddress) && (aUdpSocket->mPeerName.mPort == 0); VerifyOrExit(aUdpSocket->mHandle != nullptr, error = OT_ERROR_INVALID_ARGS); fd = FdFromHandle(aUdpSocket->mHandle); memset(&sin6, 0, sizeof(struct sockaddr_in6)); sin6.sin6_port = htons(aUdpSocket->mPeerName.mPort); if (!isDisconnect) { sin6.sin6_family = AF_INET6; CopyIp6AddressTo(aUdpSocket->mPeerName.mAddress, &sin6.sin6_addr); } else { #ifdef __APPLE__ sin6.sin6_family = AF_UNSPEC; #else char netifName[IFNAMSIZ]; socklen_t len = sizeof(netifName); if (getsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &netifName, &len) != 0) { ot::Posix::Udp::LogWarn("Failed to read socket bound device: %s", strerror(errno)); len = 0; } // There is a bug in linux that connecting to AF_UNSPEC does not disconnect. // We create new socket to disconnect. SuccessOrExit(error = otPlatUdpClose(aUdpSocket)); SuccessOrExit(error = otPlatUdpSocket(aUdpSocket)); SuccessOrExit(error = otPlatUdpBind(aUdpSocket)); if (len > 0 && netifName[0] != '\0') { fd = FdFromHandle(aUdpSocket->mHandle); VerifyOrExit(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &netifName, len) == 0, { ot::Posix::Udp::LogWarn("Failed to bind to device: %s", strerror(errno)); error = OT_ERROR_FAILED; }); } ExitNow(); #endif } if (connect(fd, reinterpret_cast(&sin6), sizeof(sin6)) != 0) { #ifdef __APPLE__ VerifyOrExit(errno == EAFNOSUPPORT && isDisconnect); #endif ot::Posix::Udp::LogWarn("Failed to connect to [%s]:%u: %s", Ip6AddressString(&aUdpSocket->mPeerName.mAddress).AsCString(), aUdpSocket->mPeerName.mPort, strerror(errno)); error = OT_ERROR_FAILED; } exit: return error; } otError otPlatUdpSend(otUdpSocket *aUdpSocket, otMessage *aMessage, const otMessageInfo *aMessageInfo) { otError error = OT_ERROR_NONE; int fd; uint16_t len; uint8_t payload[kMaxUdpSize]; VerifyOrExit(aUdpSocket->mHandle != nullptr, error = OT_ERROR_INVALID_ARGS); fd = FdFromHandle(aUdpSocket->mHandle); len = otMessageGetLength(aMessage); VerifyOrExit(len == otMessageRead(aMessage, 0, payload, len), error = OT_ERROR_INVALID_ARGS); if (aMessageInfo->mMulticastLoop) { int value = 1; VerifyOrDie(setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &value, sizeof(value)) == 0, OT_EXIT_ERROR_ERRNO); } error = transmitPacket(fd, payload, len, *aMessageInfo); if (aMessageInfo->mMulticastLoop) { int value = 0; VerifyOrDie(setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &value, sizeof(value)) == 0, OT_EXIT_ERROR_ERRNO); } exit: if (error == OT_ERROR_NONE) { otMessageFree(aMessage); } return error; } otError otPlatUdpJoinMulticastGroup(otUdpSocket *aUdpSocket, otNetifIdentifier aNetifIdentifier, const otIp6Address *aAddress) { otError error = OT_ERROR_NONE; struct ipv6_mreq mreq; int fd; VerifyOrExit(aUdpSocket->mHandle != nullptr, error = OT_ERROR_INVALID_ARGS); fd = FdFromHandle(aUdpSocket->mHandle); CopyIp6AddressTo(*aAddress, &mreq.ipv6mr_multiaddr); switch (aNetifIdentifier) { case OT_NETIF_UNSPECIFIED: break; case OT_NETIF_THREAD: mreq.ipv6mr_interface = gNetifIndex; break; case OT_NETIF_BACKBONE: #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE mreq.ipv6mr_interface = otSysGetInfraNetifIndex(); #else ExitNow(error = OT_ERROR_NOT_IMPLEMENTED); #endif break; } VerifyOrExit(setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == 0 || errno == EADDRINUSE, error = OT_ERROR_FAILED); exit: if (error != OT_ERROR_NONE) { ot::Posix::Udp::LogCrit("IPV6_JOIN_GROUP failed: %s", strerror(errno)); } return error; } otError otPlatUdpLeaveMulticastGroup(otUdpSocket *aUdpSocket, otNetifIdentifier aNetifIdentifier, const otIp6Address *aAddress) { otError error = OT_ERROR_NONE; struct ipv6_mreq mreq; int fd; VerifyOrExit(aUdpSocket->mHandle != nullptr, error = OT_ERROR_INVALID_ARGS); fd = FdFromHandle(aUdpSocket->mHandle); CopyIp6AddressTo(*aAddress, &mreq.ipv6mr_multiaddr); switch (aNetifIdentifier) { case OT_NETIF_UNSPECIFIED: break; case OT_NETIF_THREAD: mreq.ipv6mr_interface = gNetifIndex; break; case OT_NETIF_BACKBONE: #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE mreq.ipv6mr_interface = otSysGetInfraNetifIndex(); #else ExitNow(error = OT_ERROR_NOT_IMPLEMENTED); #endif break; } VerifyOrExit(setsockopt(fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq, sizeof(mreq)) == 0 || errno == EADDRINUSE, error = OT_ERROR_FAILED); exit: if (error != OT_ERROR_NONE) { ot::Posix::Udp::LogCrit("IPV6_LEAVE_GROUP failed: %s", strerror(errno)); } return error; } namespace ot { namespace Posix { const char Udp::kLogModuleName[] = "Udp"; void Udp::Update(otSysMainloopContext &aContext) { VerifyOrExit(gNetifIndex != 0); for (otUdpSocket *socket = otUdpGetSockets(gInstance); socket != nullptr; socket = socket->mNext) { int fd; if (socket->mHandle == nullptr) { continue; } fd = FdFromHandle(socket->mHandle); FD_SET(fd, &aContext.mReadFdSet); if (aContext.mMaxFd < fd) { aContext.mMaxFd = fd; } } exit: return; } void Udp::Init(const char *aIfName) { if (aIfName == nullptr) { DieNow(OT_EXIT_INVALID_ARGUMENTS); } if (aIfName != gNetifName) { VerifyOrDie(strlen(aIfName) < sizeof(gNetifName) - 1, OT_EXIT_INVALID_ARGUMENTS); assert(gNetifIndex == 0); strcpy(gNetifName, aIfName); gNetifIndex = if_nametoindex(gNetifName); VerifyOrDie(gNetifIndex != 0, OT_EXIT_ERROR_ERRNO); } assert(gNetifIndex != 0); } void Udp::SetUp(void) { Mainloop::Manager::Get().Add(*this); } void Udp::TearDown(void) { Mainloop::Manager::Get().Remove(*this); } void Udp::Deinit(void) { // TODO All platform sockets should be closed } Udp &Udp::Get(void) { static Udp sInstance; return sInstance; } void Udp::Process(const otSysMainloopContext &aContext) { otMessageSettings msgSettings = {false, OT_MESSAGE_PRIORITY_NORMAL}; for (otUdpSocket *socket = otUdpGetSockets(gInstance); socket != nullptr; socket = socket->mNext) { int fd = FdFromHandle(socket->mHandle); if (fd > 0 && FD_ISSET(fd, &aContext.mReadFdSet)) { otMessageInfo messageInfo; otMessage *message = nullptr; uint8_t payload[kMaxUdpSize]; uint16_t length = sizeof(payload); memset(&messageInfo, 0, sizeof(messageInfo)); messageInfo.mSockPort = socket->mSockName.mPort; if (OT_ERROR_NONE != receivePacket(fd, payload, length, messageInfo)) { continue; } message = otUdpNewMessage(gInstance, &msgSettings); if (message == nullptr) { continue; } if (otMessageAppend(message, payload, length) != OT_ERROR_NONE) { otMessageFree(message); continue; } socket->mHandler(socket->mContext, message, &messageInfo); otMessageFree(message); // only process one socket a time break; } } return; } } // namespace Posix } // namespace ot #endif // #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE