diff --git a/Android.mk b/Android.mk index 113884274..40834bd93 100644 --- a/Android.mk +++ b/Android.mk @@ -145,6 +145,7 @@ LOCAL_EXPORT_C_INCLUDE_DIRS := \ LOCAL_CPPFLAGS := \ -std=c++11 \ + -Wno-error=non-virtual-dtor \ -pedantic-errors \ $(NULL) @@ -355,6 +356,7 @@ LOCAL_SRC_FILES := \ src/posix/platform/hdlc_interface.cpp \ src/posix/platform/infra_if.cpp \ src/posix/platform/logging.cpp \ + src/posix/platform/mainloop.cpp \ src/posix/platform/memory.cpp \ src/posix/platform/misc.cpp \ src/posix/platform/multicast_routing.cpp \ diff --git a/src/posix/platform/CMakeLists.txt b/src/posix/platform/CMakeLists.txt index 0c9deb401..d4b6a66c0 100644 --- a/src/posix/platform/CMakeLists.txt +++ b/src/posix/platform/CMakeLists.txt @@ -76,6 +76,7 @@ add_library(openthread-posix hdlc_interface.cpp infra_if.cpp logging.cpp + mainloop.cpp memory.cpp misc.cpp multicast_routing.cpp diff --git a/src/posix/platform/Makefile.am b/src/posix/platform/Makefile.am index 24c39a644..7a2c72339 100644 --- a/src/posix/platform/Makefile.am +++ b/src/posix/platform/Makefile.am @@ -51,6 +51,7 @@ libopenthread_posix_a_SOURCES = \ hdlc_interface.cpp \ infra_if.cpp \ logging.cpp \ + mainloop.cpp \ memory.cpp \ misc.cpp \ multicast_routing.cpp \ @@ -72,6 +73,7 @@ libopenthread_posix_a_LIBADD noinst_HEADERS = \ hdlc_interface.hpp \ + mainloop.hpp \ multicast_routing.hpp \ openthread-posix-config.h \ platform-posix.h \ diff --git a/src/posix/platform/backbone.cpp b/src/posix/platform/backbone.cpp index b9f9e1180..dea590848 100644 --- a/src/posix/platform/backbone.cpp +++ b/src/posix/platform/backbone.cpp @@ -38,6 +38,7 @@ #include "multicast_routing.hpp" #include "platform-posix.h" #include "common/code_utils.hpp" +#include "posix/platform/mainloop.hpp" char gBackboneNetifName[IFNAMSIZ] = ""; unsigned int gBackboneNetifIndex = 0; @@ -49,10 +50,10 @@ void platformBackboneInit(otInstance *aInstance, const char *aInterfaceName) { OT_UNUSED_VARIABLE(aInstance); - VerifyOrDie(aInterfaceName != nullptr, OT_EXIT_INVALID_ARGUMENTS); + VerifyOrExit(aInterfaceName != nullptr); VerifyOrDie(strnlen(aInterfaceName, IFNAMSIZ) <= IFNAMSIZ - 1, OT_EXIT_INVALID_ARGUMENTS); - strcpy(gBackboneNetifName, aInterfaceName); + strncpy(gBackboneNetifName, aInterfaceName, sizeof(gBackboneNetifName)); gBackboneNetifIndex = if_nametoindex(gBackboneNetifName); VerifyOrDie(gBackboneNetifIndex > 0, OT_EXIT_FAILURE); @@ -62,25 +63,9 @@ void platformBackboneInit(otInstance *aInstance, const char *aInterfaceName) #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE sMulticastRoutingManager.Init(aInstance); #endif -} -void platformBackboneUpdateFdSet(fd_set &aReadFdSet, int &aMaxFd) -{ - OT_UNUSED_VARIABLE(aReadFdSet); - OT_UNUSED_VARIABLE(aMaxFd); - -#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE - sMulticastRoutingManager.UpdateFdSet(aReadFdSet, aMaxFd); -#endif -} - -void platformBackboneProcess(const fd_set &aReadSet) -{ - OT_UNUSED_VARIABLE(aReadSet); - -#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE - sMulticastRoutingManager.Process(aReadSet); -#endif +exit: + return; } void platformBackboneStateChange(otInstance *aInstance, otChangedFlags aFlags) diff --git a/src/posix/platform/infra_if.cpp b/src/posix/platform/infra_if.cpp index a17110857..91ab6d5d7 100644 --- a/src/posix/platform/infra_if.cpp +++ b/src/posix/platform/infra_if.cpp @@ -58,16 +58,7 @@ #include "common/code_utils.hpp" #include "common/debug.hpp" #include "lib/platform/exit_code.h" - -static char sInfraIfName[IFNAMSIZ]; -static uint32_t sInfraIfIndex = 0; -static int sInfraIfIcmp6Socket = -1; -static int sNetLinkSocket = -1; - -static int CreateIcmp6Socket(void); -static int CreateNetLinkSocket(void); -static void ReceiveNetLinkMessage(otInstance *aInstance); -static void ReceiveIcmp6Message(otInstance *aInstance); +#include "posix/platform/infra_if.hpp" bool otPlatInfraIfHasAddress(uint32_t aInfraIfIndex, const otIp6Address *aAddress) { @@ -102,95 +93,19 @@ otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex, const uint8_t * aBuffer, uint16_t aBufferLength) { - otError error = OT_ERROR_NONE; - - struct iovec iov; - struct in6_pktinfo *packetInfo; - - int hopLimit = 255; - uint8_t cmsgBuffer[CMSG_SPACE(sizeof(*packetInfo)) + CMSG_SPACE(sizeof(hopLimit))]; - struct msghdr msgHeader; - struct cmsghdr * cmsgPointer; - ssize_t rval; - struct sockaddr_in6 dest; - - VerifyOrExit(sInfraIfIcmp6Socket >= 0, error = OT_ERROR_FAILED); - VerifyOrExit(aInfraIfIndex == sInfraIfIndex, error = OT_ERROR_DROP); - - memset(cmsgBuffer, 0, sizeof(cmsgBuffer)); - - // Send the message - memset(&dest, 0, sizeof(dest)); - dest.sin6_family = AF_INET6; - memcpy(&dest.sin6_addr, aDestAddress, sizeof(*aDestAddress)); - if (IN6_IS_ADDR_LINKLOCAL(&dest.sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&dest.sin6_addr)) - { - dest.sin6_scope_id = sInfraIfIndex; - } - - iov.iov_base = const_cast(aBuffer); - iov.iov_len = aBufferLength; - - msgHeader.msg_namelen = sizeof(dest); - msgHeader.msg_name = &dest; - msgHeader.msg_iov = &iov; - msgHeader.msg_iovlen = 1; - msgHeader.msg_control = cmsgBuffer; - msgHeader.msg_controllen = sizeof(cmsgBuffer); - - // Specify the interface. - cmsgPointer = CMSG_FIRSTHDR(&msgHeader); - cmsgPointer->cmsg_level = IPPROTO_IPV6; - cmsgPointer->cmsg_type = IPV6_PKTINFO; - cmsgPointer->cmsg_len = CMSG_LEN(sizeof(*packetInfo)); - packetInfo = (struct in6_pktinfo *)CMSG_DATA(cmsgPointer); - memset(packetInfo, 0, sizeof(*packetInfo)); - packetInfo->ipi6_ifindex = sInfraIfIndex; - - // Per section 6.1.2 of RFC 4861, we need to send the ICMPv6 message with IP Hop Limit 255. - cmsgPointer = CMSG_NXTHDR(&msgHeader, cmsgPointer); - cmsgPointer->cmsg_level = IPPROTO_IPV6; - cmsgPointer->cmsg_type = IPV6_HOPLIMIT; - cmsgPointer->cmsg_len = CMSG_LEN(sizeof(hopLimit)); - memcpy(CMSG_DATA(cmsgPointer), &hopLimit, sizeof(hopLimit)); - - rval = sendmsg(sInfraIfIcmp6Socket, &msgHeader, 0); - if (rval < 0) - { - otLogWarnPlat("failed to send ICMPv6 message: %s", strerror(errno)); - ExitNow(error = OT_ERROR_FAILED); - } - - if (static_cast(rval) != iov.iov_len) - { - otLogWarnPlat("failed to send ICMPv6 message: partially sent"); - ExitNow(error = OT_ERROR_FAILED); - } - -exit: - return error; + return ot::Posix::InfraNetif::Get().SendIcmp6Nd(aInfraIfIndex, *aDestAddress, aBuffer, aBufferLength); } bool platformInfraIfIsRunning(void) { - int sock; - struct ifreq ifReq; - - OT_ASSERT(sInfraIfIndex != 0); - - sock = SocketWithCloseExec(AF_INET6, SOCK_DGRAM, IPPROTO_IP, kSocketBlock); - VerifyOrDie(sock != -1, OT_EXIT_ERROR_ERRNO); - - memset(&ifReq, 0, sizeof(ifReq)); - strncpy(ifReq.ifr_name, sInfraIfName, sizeof(ifReq.ifr_name)); - VerifyOrDie(ioctl(sock, SIOCGIFFLAGS, &ifReq) != -1, OT_EXIT_ERROR_ERRNO); - - close(sock); - - return (ifReq.ifr_flags & IFF_RUNNING); + return ot::Posix::InfraNetif::Get().IsRunning(); } -static int CreateIcmp6Socket(void) +namespace ot { +namespace Posix { +namespace { + +int CreateIcmp6Socket(void) { int sock; int rval; @@ -235,76 +150,8 @@ static int CreateIcmp6Socket(void) return sock; } -uint32_t platformInfraIfInit(otInstance *aInstance, const char *aIfName) -{ - OT_UNUSED_VARIABLE(aInstance); - - ssize_t rval; - uint32_t ifIndex = 0; - - if (strlen(aIfName) >= sizeof(sInfraIfName)) - { - otLogCritPlat("infra interface name '%s' is too long", aIfName); - DieNow(OT_EXIT_INVALID_ARGUMENTS); - } - strcpy(sInfraIfName, aIfName); - - // Initializes the infra interface. - ifIndex = if_nametoindex(aIfName); - if (ifIndex == 0) - { - otLogCritPlat("failed to get the index for infra interface %s", aIfName); - DieNow(OT_EXIT_INVALID_ARGUMENTS); - } - sInfraIfIndex = ifIndex; - - sInfraIfIcmp6Socket = CreateIcmp6Socket(); -#ifdef __linux__ - rval = setsockopt(sInfraIfIcmp6Socket, SOL_SOCKET, SO_BINDTODEVICE, sInfraIfName, strlen(sInfraIfName)); -#else // __NetBSD__ || __FreeBSD__ || __APPLE__ - rval = setsockopt(sInfraIfIcmp6Socket, IPPROTO_IP, IP_BOUND_IF, &sInfraIfIndex, sizeof(sInfraIfIndex)); -#endif // __linux__ - VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO); - - sNetLinkSocket = CreateNetLinkSocket(); - - return sInfraIfIndex; -} - -void platformInfraIfDeinit(void) -{ - if (sInfraIfIcmp6Socket != -1) - { - close(sInfraIfIcmp6Socket); - sInfraIfIcmp6Socket = -1; - } - - if (sNetLinkSocket != -1) - { - close(sNetLinkSocket); - sNetLinkSocket = -1; - } - - sInfraIfIndex = 0; -} - -void platformInfraIfUpdateFdSet(fd_set &aReadFdSet, int &aMaxFd) -{ - VerifyOrExit(sInfraIfIcmp6Socket != -1); - VerifyOrExit(sNetLinkSocket != -1); - - FD_SET(sInfraIfIcmp6Socket, &aReadFdSet); - aMaxFd = OT_MAX(aMaxFd, sInfraIfIcmp6Socket); - - FD_SET(sNetLinkSocket, &aReadFdSet); - aMaxFd = OT_MAX(aMaxFd, sNetLinkSocket); - -exit: - return; -} - // Create a net-link socket that subscribes to link & addresses events. -static int CreateNetLinkSocket(void) +int CreateNetLinkSocket(void) { int sock; int rval; @@ -324,7 +171,175 @@ static int CreateNetLinkSocket(void) return sock; } -static void ReceiveNetLinkMessage(otInstance *aInstance) +} // namespace + +otError InfraNetif::SendIcmp6Nd(uint32_t aInfraIfIndex, + const otIp6Address &aDestAddress, + const uint8_t * aBuffer, + uint16_t aBufferLength) +{ + otError error = OT_ERROR_NONE; + + struct iovec iov; + struct in6_pktinfo *packetInfo; + + int hopLimit = 255; + uint8_t cmsgBuffer[CMSG_SPACE(sizeof(*packetInfo)) + CMSG_SPACE(sizeof(hopLimit))]; + struct msghdr msgHeader; + struct cmsghdr * cmsgPointer; + ssize_t rval; + struct sockaddr_in6 dest; + + VerifyOrExit(mInfraIfIcmp6Socket >= 0, error = OT_ERROR_FAILED); + VerifyOrExit(aInfraIfIndex == mInfraIfIndex, error = OT_ERROR_DROP); + + memset(cmsgBuffer, 0, sizeof(cmsgBuffer)); + + // Send the message + memset(&dest, 0, sizeof(dest)); + dest.sin6_family = AF_INET6; + memcpy(&dest.sin6_addr, &aDestAddress, sizeof(aDestAddress)); + if (IN6_IS_ADDR_LINKLOCAL(&dest.sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&dest.sin6_addr)) + { + dest.sin6_scope_id = mInfraIfIndex; + } + + iov.iov_base = const_cast(aBuffer); + iov.iov_len = aBufferLength; + + msgHeader.msg_namelen = sizeof(dest); + msgHeader.msg_name = &dest; + msgHeader.msg_iov = &iov; + msgHeader.msg_iovlen = 1; + msgHeader.msg_control = cmsgBuffer; + msgHeader.msg_controllen = sizeof(cmsgBuffer); + + // Specify the interface. + cmsgPointer = CMSG_FIRSTHDR(&msgHeader); + cmsgPointer->cmsg_level = IPPROTO_IPV6; + cmsgPointer->cmsg_type = IPV6_PKTINFO; + cmsgPointer->cmsg_len = CMSG_LEN(sizeof(*packetInfo)); + packetInfo = (struct in6_pktinfo *)CMSG_DATA(cmsgPointer); + memset(packetInfo, 0, sizeof(*packetInfo)); + packetInfo->ipi6_ifindex = mInfraIfIndex; + + // Per section 6.1.2 of RFC 4861, we need to send the ICMPv6 message with IP Hop Limit 255. + cmsgPointer = CMSG_NXTHDR(&msgHeader, cmsgPointer); + cmsgPointer->cmsg_level = IPPROTO_IPV6; + cmsgPointer->cmsg_type = IPV6_HOPLIMIT; + cmsgPointer->cmsg_len = CMSG_LEN(sizeof(hopLimit)); + memcpy(CMSG_DATA(cmsgPointer), &hopLimit, sizeof(hopLimit)); + + rval = sendmsg(mInfraIfIcmp6Socket, &msgHeader, 0); + if (rval < 0) + { + otLogWarnPlat("failed to send ICMPv6 message: %s", strerror(errno)); + ExitNow(error = OT_ERROR_FAILED); + } + + if (static_cast(rval) != iov.iov_len) + { + otLogWarnPlat("failed to send ICMPv6 message: partially sent"); + ExitNow(error = OT_ERROR_FAILED); + } + +exit: + return error; +} + +bool InfraNetif::IsRunning(void) const +{ + int sock; + struct ifreq ifReq; + + OT_ASSERT(mInfraIfIndex != 0); + + sock = SocketWithCloseExec(AF_INET6, SOCK_DGRAM, IPPROTO_IP, kSocketBlock); + VerifyOrDie(sock != -1, OT_EXIT_ERROR_ERRNO); + + memset(&ifReq, 0, sizeof(ifReq)); + strncpy(ifReq.ifr_name, mInfraIfName, sizeof(ifReq.ifr_name)); + VerifyOrDie(ioctl(sock, SIOCGIFFLAGS, &ifReq) != -1, OT_EXIT_ERROR_ERRNO); + + close(sock); + + return (ifReq.ifr_flags & IFF_RUNNING); +} + +void InfraNetif::Init(otInstance *aInstance, const char *aIfName) +{ + ssize_t rval; + uint32_t ifIndex = 0; + + VerifyOrExit(aIfName != nullptr); + + VerifyOrDie(strnlen(aIfName, sizeof(mInfraIfName)) <= sizeof(mInfraIfName) - 1, OT_EXIT_INVALID_ARGUMENTS); + strncpy(mInfraIfName, aIfName, sizeof(mInfraIfName)); + + // Initializes the infra interface. + ifIndex = if_nametoindex(aIfName); + if (ifIndex == 0) + { + otLogCritPlat("failed to get the index for infra interface %s", aIfName); + DieNow(OT_EXIT_INVALID_ARGUMENTS); + } + mInfraIfIndex = ifIndex; + + mInfraIfIcmp6Socket = CreateIcmp6Socket(); +#ifdef __linux__ + rval = setsockopt(mInfraIfIcmp6Socket, SOL_SOCKET, SO_BINDTODEVICE, mInfraIfName, strlen(mInfraIfName)); +#else // __NetBSD__ || __FreeBSD__ || __APPLE__ + rval = setsockopt(mInfraIfIcmp6Socket, IPPROTO_IP, IP_BOUND_IF, &mInfraIfIndex, sizeof(mInfraIfIndex)); +#endif // __linux__ + VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO); + + mNetLinkSocket = CreateNetLinkSocket(); + + SuccessOrDie(otBorderRoutingInit(aInstance, ifIndex, platformInfraIfIsRunning())); + SuccessOrDie(otBorderRoutingSetEnabled(aInstance, /* aEnabled */ true)); + + mInstance = aInstance; + Mainloop::Manager::Get().Add(*this); + +exit: + return; +} + +void InfraNetif::Deinit(void) +{ + Mainloop::Manager::Get().Remove(*this); + + if (mInfraIfIcmp6Socket != -1) + { + close(mInfraIfIcmp6Socket); + mInfraIfIcmp6Socket = -1; + } + + if (mNetLinkSocket != -1) + { + close(mNetLinkSocket); + mNetLinkSocket = -1; + } + + mInfraIfIndex = 0; +} + +void InfraNetif::Update(otSysMainloopContext &aContext) +{ + VerifyOrExit(mInfraIfIcmp6Socket != -1); + VerifyOrExit(mNetLinkSocket != -1); + + FD_SET(mInfraIfIcmp6Socket, &aContext.mReadFdSet); + aContext.mMaxFd = OT_MAX(aContext.mMaxFd, mInfraIfIcmp6Socket); + + FD_SET(mNetLinkSocket, &aContext.mReadFdSet); + aContext.mMaxFd = OT_MAX(aContext.mMaxFd, mNetLinkSocket); + +exit: + return; +} + +void InfraNetif::ReceiveNetLinkMessage(void) { const size_t kMaxNetLinkBufSize = 8192; ssize_t len; @@ -334,7 +349,7 @@ static void ReceiveNetLinkMessage(otInstance *aInstance) uint8_t mBuffer[kMaxNetLinkBufSize]; } msgBuffer; - len = recv(sNetLinkSocket, msgBuffer.mBuffer, sizeof(msgBuffer.mBuffer), 0); + len = recv(mNetLinkSocket, msgBuffer.mBuffer, sizeof(msgBuffer.mBuffer), 0); if (len < 0) { otLogCritPlat("failed to receive netlink message: %s", strerror(errno)); @@ -353,7 +368,7 @@ static void ReceiveNetLinkMessage(otInstance *aInstance) case RTM_DELADDR: case RTM_NEWLINK: case RTM_DELLINK: - SuccessOrDie(otPlatInfraIfStateChanged(aInstance, sInfraIfIndex, platformInfraIfIsRunning())); + SuccessOrDie(otPlatInfraIfStateChanged(mInstance, mInfraIfIndex, platformInfraIfIsRunning())); break; case NLMSG_ERROR: { @@ -371,7 +386,7 @@ exit: return; } -static void ReceiveIcmp6Message(otInstance *aInstance) +void InfraNetif::ReceiveIcmp6Message(void) { otError error = OT_ERROR_NONE; uint8_t buffer[1500]; @@ -400,7 +415,7 @@ static void ReceiveIcmp6Message(otInstance *aInstance) msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); - rval = recvmsg(sInfraIfIcmp6Socket, &msg, 0); + rval = recvmsg(mInfraIfIcmp6Socket, &msg, 0); if (rval < 0) { otLogWarnPlat("failed to receive ICMPv6 message: %s", strerror(errno)); @@ -427,13 +442,13 @@ static void ReceiveIcmp6Message(otInstance *aInstance) } } - VerifyOrExit(ifIndex == sInfraIfIndex, error = OT_ERROR_DROP); + VerifyOrExit(ifIndex == mInfraIfIndex, error = OT_ERROR_DROP); // We currently accept only RA & RS messages for the Border Router and it requires that // the hoplimit must be 255 and the source address must be a link-local address. VerifyOrExit(hopLimit == 255 && IN6_IS_ADDR_LINKLOCAL(&srcAddr.sin6_addr), error = OT_ERROR_DROP); - otPlatInfraIfRecvIcmp6Nd(aInstance, ifIndex, reinterpret_cast(&srcAddr.sin6_addr), buffer, + otPlatInfraIfRecvIcmp6Nd(mInstance, ifIndex, reinterpret_cast(&srcAddr.sin6_addr), buffer, bufferLength); exit: @@ -443,23 +458,32 @@ exit: } } -void platformInfraIfProcess(otInstance *aInstance, const fd_set &aReadFdSet) +void InfraNetif::Process(const otSysMainloopContext &aContext) { - VerifyOrExit(sInfraIfIcmp6Socket != -1); - VerifyOrExit(sNetLinkSocket != -1); + VerifyOrExit(mInfraIfIcmp6Socket != -1); + VerifyOrExit(mNetLinkSocket != -1); - if (FD_ISSET(sInfraIfIcmp6Socket, &aReadFdSet)) + if (FD_ISSET(mInfraIfIcmp6Socket, &aContext.mReadFdSet)) { - ReceiveIcmp6Message(aInstance); + ReceiveIcmp6Message(); } - if (FD_ISSET(sNetLinkSocket, &aReadFdSet)) + if (FD_ISSET(mNetLinkSocket, &aContext.mReadFdSet)) { - ReceiveNetLinkMessage(aInstance); + ReceiveNetLinkMessage(); } exit: return; } +InfraNetif &InfraNetif::Get(void) +{ + static InfraNetif sInstance; + + return sInstance; +} + +} // namespace Posix +} // namespace ot #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE diff --git a/src/posix/platform/infra_if.hpp b/src/posix/platform/infra_if.hpp new file mode 100644 index 000000000..6cd2eda53 --- /dev/null +++ b/src/posix/platform/infra_if.hpp @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2021, 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 the infrastructure interface for posix. + */ + +#include "openthread-posix-config.h" + +#include + +#include "core/common/non_copyable.hpp" +#include "posix/platform/mainloop.hpp" + +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + +namespace ot { +namespace Posix { + +/** + * This class manages infrastructure network interface. + * + */ +class InfraNetif : public Mainloop::Source, private NonCopyable +{ +public: + /** + * This method updates the fd_set and timeout for mainloop. + * + * @param[inout] aContext A reference to the mainloop context. + * + */ + void Update(otSysMainloopContext &aContext) override; + + /** + * This method performs infrastructure network interface processing. + * + * @param[in] aContext A reference to the mainloop context. + * + */ + void Process(const otSysMainloopContext &aContext) override; + + /** + * This method initializes the infrastructure network interface. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aIfName A pointer to infrastructure network interface name. + * + */ + void Init(otInstance *aInstance, const char *aIfName); + + /** + * This method deinitializes the infrastructure network interface. + * + */ + void Deinit(void); + + /** + * This method checks whether the infrastructure network interface is running. + * + */ + bool IsRunning(void) const; + + /** + * This method sends an ICMPv6 Neighbor Discovery message on given infrastructure interface. + * + * See RFC 4861: https://tools.ietf.org/html/rfc4861. + * + * @param[in] aInfraIfIndex The index of the infrastructure interface this message is sent to. + * @param[in] aDestAddress The destination address this message is sent to. + * @param[in] aBuffer The ICMPv6 message buffer. The ICMPv6 checksum is left zero and the + * platform should do the checksum calculate. + * @param[in] aBufferLength The length of the message buffer. + * + * @note Per RFC 4861, the implementation should send the message with IPv6 link-local source address + * of interface @p aInfraIfIndex and IP Hop Limit 255. + * + * @retval OT_ERROR_NONE Successfully sent the ICMPv6 message. + * @retval OT_ERROR_FAILED Failed to send the ICMPv6 message. + * + */ + otError SendIcmp6Nd(uint32_t aInfraIfIndex, + const otIp6Address &aDestAddress, + const uint8_t * aBuffer, + uint16_t aBufferLength); + /** + * This function gets the infrastructure network interface singleton. + * + * @returns The singleton object. + */ + static InfraNetif &Get(void); + +private: + otInstance *mInstance; + char mInfraIfName[IFNAMSIZ]; + uint32_t mInfraIfIndex = 0; + int mInfraIfIcmp6Socket = -1; + int mNetLinkSocket = -1; + + void ReceiveNetLinkMessage(void); + void ReceiveIcmp6Message(void); +}; + +} // namespace Posix +} // namespace ot +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE diff --git a/src/posix/platform/mainloop.cpp b/src/posix/platform/mainloop.cpp new file mode 100644 index 000000000..36b0d262c --- /dev/null +++ b/src/posix/platform/mainloop.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2021, 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. + */ + +#include "posix/platform/mainloop.hpp" + +#include + +#include "core/common/code_utils.hpp" + +namespace ot { +namespace Posix { +namespace Mainloop { + +void Manager::Add(Source &aSource) +{ + assert(aSource.mNext == nullptr); + + aSource.mNext = mSources; + mSources = &aSource; +} + +void Manager::Remove(Source &aSource) +{ + for (Source **pnext = &mSources; *pnext != nullptr; pnext = &(*pnext)->mNext) + { + if (*pnext == &aSource) + { + *pnext = aSource.mNext; + break; + } + } + + aSource.mNext = nullptr; +} + +void Manager::Update(otSysMainloopContext &aContext) +{ + for (Source *source = mSources; source != nullptr; source = source->mNext) + { + source->Update(aContext); + } +} + +void Manager::Process(const otSysMainloopContext &aContext) +{ + for (Source *source = mSources; source != nullptr; source = source->mNext) + { + source->Process(aContext); + } +} + +Manager &Manager::Get(void) +{ + static Manager sInstance; + + return sInstance; +} + +} // namespace Mainloop +} // namespace Posix +} // namespace ot diff --git a/src/posix/platform/mainloop.hpp b/src/posix/platform/mainloop.hpp new file mode 100644 index 000000000..e6e7e25f5 --- /dev/null +++ b/src/posix/platform/mainloop.hpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2021, 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 includes definitions for the SPI interface to radio (RCP). + */ + +#ifndef OT_POSIX_PLATFORM_MAINLOOP_HPP_ +#define OT_POSIX_PLATFORM_MAINLOOP_HPP_ + +#include + +namespace ot { +namespace Posix { +namespace Mainloop { + +/** + * This class is the base for all mainloop event sources. + * + */ +class Source +{ + friend class Manager; + +public: + /** + * This method registers events in the mainloop. + * + * @param[inout] aContext A reference to the mainloop context. + * + */ + virtual void Update(otSysMainloopContext &aContext) = 0; + + /** + * This method processes the mainloop events. + * + * @param[in] aContext A reference to the mainloop context. + * + */ + virtual void Process(const otSysMainloopContext &aContext) = 0; + +private: + Source *mNext = nullptr; +}; + +/** + * This class manages mainloop. + * + */ +class Manager +{ +public: + /** + * This method updates event polls in the mainloop context. + * + * @param[inout] aContext A reference to the mainloop context. + * + */ + void Update(otSysMainloopContext &aContext); + + /** + * This method processes events in the mainloop context. + * + * @param[in] aContext A reference to the mainloop context. + * + */ + void Process(const otSysMainloopContext &aContext); + + /** + * This method adds a new event source into the mainloop. + * + * @param[in] aSource A reference to the event source. + * + */ + void Add(Source &aSource); + + /** + * This method removes an event source from the mainloop. + * + * @param[in] aSource A reference to the event source. + * + */ + void Remove(Source &aSource); + + /** + * This function returns the Mainloop singleton. + * + * @returns A refernce to the Mainloop singleton. + * + */ + static Manager &Get(void); + +private: + Source *mSources = nullptr; +}; + +} // namespace Mainloop +} // namespace Posix +} // namespace ot +#endif // OT_POSIX_PLATFORM_MAINLOOP_HPP_ diff --git a/src/posix/platform/multicast_routing.cpp b/src/posix/platform/multicast_routing.cpp index ff9b79fd8..d3c8b15d6 100644 --- a/src/posix/platform/multicast_routing.cpp +++ b/src/posix/platform/multicast_routing.cpp @@ -57,6 +57,7 @@ void MulticastRoutingManager::Init(otInstance *aInstance) otBackboneRouterSetMulticastListenerCallback(aInstance, &MulticastRoutingManager::HandleBackboneMulticastListenerEvent, this); + Mainloop::Manager::Get().Add(*this); } void MulticastRoutingManager::HandleBackboneMulticastListenerEvent(void * aContext, @@ -138,24 +139,24 @@ exit: return found; } -void MulticastRoutingManager::UpdateFdSet(fd_set &aReadFdSet, int &aMaxFd) const +void MulticastRoutingManager::Update(otSysMainloopContext &aContext) { VerifyOrExit(IsEnabled()); - FD_SET(mMulticastRouterSock, &aReadFdSet); - aMaxFd = OT_MAX(aMaxFd, mMulticastRouterSock); + FD_SET(mMulticastRouterSock, &aContext.mReadFdSet); + aContext.mMaxFd = OT_MAX(aContext.mMaxFd, mMulticastRouterSock); exit: return; } -void MulticastRoutingManager::Process(const fd_set &aReadFdSet) +void MulticastRoutingManager::Process(const otSysMainloopContext &aContext) { VerifyOrExit(IsEnabled()); ExpireMulticastForwardingCache(); - if (FD_ISSET(mMulticastRouterSock, &aReadFdSet)) + if (FD_ISSET(mMulticastRouterSock, &aContext.mReadFdSet)) { ProcessMulticastRouterMessages(); } diff --git a/src/posix/platform/multicast_routing.hpp b/src/posix/platform/multicast_routing.hpp index f2eba627c..1407bd433 100644 --- a/src/posix/platform/multicast_routing.hpp +++ b/src/posix/platform/multicast_routing.hpp @@ -43,6 +43,7 @@ #include "core/common/non_copyable.hpp" #include "core/net/ip6_address.hpp" #include "lib/url/url.hpp" +#include "posix/platform/mainloop.hpp" namespace ot { namespace Posix { @@ -51,7 +52,7 @@ namespace Posix { * This class implements Multicast Routing management. * */ -class MulticastRoutingManager : private NonCopyable +class MulticastRoutingManager : public Mainloop::Source, private NonCopyable { public: /** @@ -59,9 +60,9 @@ public: * */ explicit MulticastRoutingManager() + : mLastExpireTime(0) , mMulticastRouterSock(-1) - , mInstance(nullptr) { } @@ -76,19 +77,18 @@ public: /** * This method updates the fd_set and timeout for mainloop. * - * @param[inout] aReadFdSet A reference to fd_set for polling read. - * @param[inout] aMaxFd A reference to the current max fd in fd_sets. + * @param[inout] aContext A reference to the mainloop context. * */ - void UpdateFdSet(fd_set &aReadFdSet, int &aMaxFd) const; + void Update(otSysMainloopContext &aContext) override; /** * This method performs Multicast Routing processing. * - * @param[in] aReadFdSet A reference to read file descriptors. + * @param[in] aContext A reference to the mainloop context. * */ - void Process(const fd_set &aReadFdSet); + void Process(const otSysMainloopContext &aContext) override; /** * This method handles Thread state changes. diff --git a/src/posix/platform/platform-posix.h b/src/posix/platform/platform-posix.h index 57b5fa577..69b944ebb 100644 --- a/src/posix/platform/platform-posix.h +++ b/src/posix/platform/platform-posix.h @@ -462,23 +462,6 @@ extern unsigned int gNetifIndex; */ void platformBackboneInit(otInstance *aInstance, const char *aInterfaceName); -/** - * This function updates the file descriptor sets with file descriptors used by the platform Backbone network. - * - * @param[inout] aReadFdSet A reference to the read file descriptors. - * @param[inout] aMaxFd A reference to the max file descriptor. - * - */ -void platformBackboneUpdateFdSet(fd_set &aReadFdSet, int &aMaxFd); - -/** - * This function performs platform Backbone network processing. - * - * @param[in] aReadFdSet A reference to the read file descriptors. - * - */ -void platformBackboneProcess(const fd_set &aReadSet); - /** * This function performs notifies state changes to platform Backbone network. * @@ -500,23 +483,6 @@ extern char gBackboneNetifName[IFNAMSIZ]; */ extern unsigned int gBackboneNetifIndex; -/** - * This function initializes the infrastructure interface. - * - * @param[in] aInstance The OpenThread instance. - * @param[in] aIfName The name of the infrastructure interface. - * - * @returns The index of the infrastructure interface. - * - */ -uint32_t platformInfraIfInit(otInstance *aInstance, const char *aIfName); - -/** - * This function deinitializes the infrastructure interface. - * - */ -void platformInfraIfDeinit(void); - /** * This function tells if the infrastructure interface is running. * @@ -525,24 +491,6 @@ void platformInfraIfDeinit(void); */ bool platformInfraIfIsRunning(void); -/** - * This function updates the read fd set. - * - * @param[out] aReadFdSet The fd set to be updated. - * @param[out] aMaxFd The maximum fd to be updated. - * - */ -void platformInfraIfUpdateFdSet(fd_set &aReadFdSet, int &aMaxFd); - -/** - * This function processes possible events on the infrastructure interface. - * - * @param[in] aInstance The OpenThread instance. - * @param[in] aReadFdSet The fd set which may contain read vents. - * - */ -void platformInfraIfProcess(otInstance *aInstance, const fd_set &aReadFdSet); - /** * This function enables daemon. * diff --git a/src/posix/platform/system.cpp b/src/posix/platform/system.cpp index deec53b1f..58f34b6a4 100644 --- a/src/posix/platform/system.cpp +++ b/src/posix/platform/system.cpp @@ -47,6 +47,8 @@ #include #include "common/code_utils.hpp" +#include "posix/platform/infra_if.hpp" +#include "posix/platform/mainloop.hpp" #include "posix/platform/radio_url.hpp" #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE || OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE @@ -62,7 +64,10 @@ static void processStateChange(otChangedFlags aFlags, void *aContext) #endif #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE - platformBackboneStateChange(instance, aFlags); + if (gBackboneNetifIndex != 0) + { + platformBackboneStateChange(instance, aFlags); + } #endif } #endif @@ -127,20 +132,7 @@ otInstance *otSysInit(otPlatformConfig *aPlatformConfig) #endif #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE - { - uint32_t infraIfIndex; - - // Reuse the backbone interface name. - if (aPlatformConfig->mBackboneInterfaceName == nullptr || strlen(aPlatformConfig->mBackboneInterfaceName) == 0) - { - DieNowWithMessage("no infra interface is specified", OT_EXIT_INVALID_ARGUMENTS); - } - - infraIfIndex = platformInfraIfInit(instance, aPlatformConfig->mBackboneInterfaceName); - - SuccessOrDie(otBorderRoutingInit(instance, infraIfIndex, platformInfraIfIsRunning())); - SuccessOrDie(otBorderRoutingSetEnabled(instance, /* aEnabled */ true)); - } + ot::Posix::InfraNetif::Get().Init(instance, aPlatformConfig->mBackboneInterfaceName); #endif #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE @@ -178,7 +170,7 @@ void otSysDeinit(void) #endif #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE - platformInfraIfDeinit(); + ot::Posix::InfraNetif::Get().Deinit(); #endif } @@ -217,6 +209,8 @@ static int trySelect(fd_set *aReadFdSet, fd_set *aWriteFdSet, fd_set *aErrorFdSe void otSysMainloopUpdate(otInstance *aInstance, otSysMainloopContext *aMainloop) { + ot::Posix::Mainloop::Manager::Get().Update(*aMainloop); + platformAlarmUpdateTimeout(&aMainloop->mTimeout); #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE platformUdpUpdateFdSet(aInstance, &aMainloop->mReadFdSet, &aMainloop->mMaxFd); @@ -225,12 +219,6 @@ void otSysMainloopUpdate(otInstance *aInstance, otSysMainloopContext *aMainloop) platformNetifUpdateFdSet(&aMainloop->mReadFdSet, &aMainloop->mWriteFdSet, &aMainloop->mErrorFdSet, &aMainloop->mMaxFd); #endif -#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE - platformBackboneUpdateFdSet(aMainloop->mReadFdSet, aMainloop->mMaxFd); -#endif -#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE - platformInfraIfUpdateFdSet(aMainloop->mReadFdSet, aMainloop->mMaxFd); -#endif #if OPENTHREAD_POSIX_VIRTUAL_TIME virtualTimeUpdateFdSet(&aMainloop->mReadFdSet, &aMainloop->mWriteFdSet, &aMainloop->mErrorFdSet, &aMainloop->mMaxFd, &aMainloop->mTimeout); @@ -297,6 +285,8 @@ int otSysMainloopPoll(otSysMainloopContext *aMainloop) void otSysMainloopProcess(otInstance *aInstance, const otSysMainloopContext *aMainloop) { + ot::Posix::Mainloop::Manager::Get().Process(*aMainloop); + #if OPENTHREAD_POSIX_VIRTUAL_TIME virtualTimeProcess(aInstance, &aMainloop->mReadFdSet, &aMainloop->mWriteFdSet, &aMainloop->mErrorFdSet); #else @@ -312,12 +302,6 @@ void otSysMainloopProcess(otInstance *aInstance, const otSysMainloopContext *aMa #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE platformUdpProcess(aInstance, &aMainloop->mReadFdSet); #endif -#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE - platformBackboneProcess(aMainloop->mReadFdSet); -#endif -#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE - platformInfraIfProcess(aInstance, aMainloop->mReadFdSet); -#endif #if OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE platformDaemonProcess(aMainloop); #endif