| /* |
| * 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 <arpa/inet.h> |
| #include <assert.h> |
| #include <net/if.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/select.h> |
| #include <unistd.h> |
| |
| #include <openthread/udp.h> |
| #include <openthread/platform/udp.h> |
| |
| #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<void *>(aFd); |
| } |
| |
| int FdFromHandle(void *aHandle) |
| { |
| return static_cast<int>(reinterpret_cast<long>(aHandle)); |
| } |
| |
| bool IsLinkLocal(const struct in6_addr &aAddress) |
| { |
| return aAddress.s6_addr[0] == 0xfe && aAddress.s6_addr[1] == 0x80; |
| } |
| |
| bool IsMulticast(const otIp6Address &aAddress) |
| { |
| return aAddress.mFields.m8[0] == 0xff; |
| } |
| |
| 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; |
| memcpy(&peerAddr.sin6_addr, &aMessageInfo.mPeerAddr, sizeof(peerAddr.sin6_addr)); |
| |
| if (IsLinkLocal(peerAddr.sin6_addr) && !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<decltype(msg.msg_controllen)>(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 (!IsMulticast(aMessageInfo.mSockAddr) && |
| memcmp(&aMessageInfo.mSockAddr, &in6addr_any, sizeof(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; |
| |
| memcpy(&pktinfo.ipi6_addr, &aMessageInfo.mSockAddr, sizeof(pktinfo.ipi6_addr)); |
| memcpy(CMSG_DATA(cmsg), &pktinfo, sizeof(pktinfo)); |
| |
| controlLength += CMSG_SPACE(sizeof(pktinfo)); |
| } |
| |
| #ifdef __APPLE__ |
| msg.msg_controllen = static_cast<socklen_t>(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<uint16_t>(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<uint8_t>(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); |
| memcpy(&aMessageInfo.mSockAddr, &pktinfo.ipi6_addr, sizeof(aMessageInfo.mSockAddr)); |
| } |
| } |
| } |
| |
| aMessageInfo.mPeerPort = ntohs(peerAddr.sin6_port); |
| memcpy(&aMessageInfo.mPeerAddr, &peerAddr.sin6_addr, sizeof(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; |
| memcpy(&sin6.sin6_addr, &aUdpSocket->mSockName.mAddress, sizeof(sin6.sin6_addr)); |
| VerifyOrExit(0 == bind(fd, reinterpret_cast<struct sockaddr *>(&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) |
| { |
| otLogCritPlat("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 __linux__ |
| VerifyOrExit(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, gBackboneNetifName, strlen(gBackboneNetifName)) == 0, |
| error = OT_ERROR_FAILED); |
| #else // __NetBSD__ || __FreeBSD__ || __APPLE__ |
| VerifyOrExit(setsockopt(fd, IPPROTO_IPV6, IPV6_BOUND_IF, &gBackboneNetifIndex, sizeof(gBackboneNetifIndex)) == |
| 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 = memcmp(&aUdpSocket->mPeerName.mAddress, &in6addr_any, sizeof(in6addr_any)) == 0 && |
| 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; |
| memcpy(&sin6.sin6_addr, &aUdpSocket->mPeerName.mAddress, sizeof(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) |
| { |
| otLogWarnPlat("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, { |
| otLogWarnPlat("Failed to bind to device: %s", strerror(errno)); |
| error = OT_ERROR_FAILED; |
| }); |
| } |
| |
| ExitNow(); |
| #endif |
| } |
| |
| if (connect(fd, reinterpret_cast<struct sockaddr *>(&sin6), sizeof(sin6)) != 0) |
| { |
| #ifdef __APPLE__ |
| VerifyOrExit(errno == EAFNOSUPPORT && isDisconnect); |
| #endif |
| otLogWarnPlat("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); |
| |
| memcpy(&mreq.ipv6mr_multiaddr, aAddress->mFields.m8, sizeof(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 = gBackboneNetifIndex; |
| #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) |
| { |
| otLogCritPlat("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); |
| |
| memcpy(&mreq.ipv6mr_multiaddr, aAddress->mFields.m8, sizeof(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 = gBackboneNetifIndex; |
| #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) |
| { |
| otLogCritPlat("IPV6_LEAVE_GROUP failed: %s", strerror(errno)); |
| } |
| return error; |
| } |
| |
| namespace ot { |
| namespace Posix { |
| |
| 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 |