/* * 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 * This file implements the platform network on Linux. */ #include "openthread-posix-config.h" #include "platform-posix.h" #if defined(__APPLE__) // NOTE: on mac OS, the utun driver is present on the system and "works" -- // but in a limited way. In particular, the mac OS "utun" driver is marked IFF_POINTTOPOINT, // and you cannot clear that flag with SIOCSIFFLAGS (it's part of the IFF_CANTCHANGE definition // in xnu's net/if.h [but removed from the mac OS SDK net/if.h]). And unfortunately, mac OS's // build of mDNSResponder won't allow for mDNS over an interface marked IFF_POINTTOPOINT // (see comments near definition of MulticastInterface in mDNSMacOSX.c for the bogus reasoning). // // There is an alternative. An open-source tuntap kernel extension is available here: // // // // // and can be installed via homebrew here: // // // // Building from source and installing isn't trivial, and it's // not getting easier (https://forums.developer.apple.com/thread/79590). // // If you want mDNS support, then you can't use Apple utun. I use the non-Apple driver // pretty much exclusively, because mDNS is a requirement. #if !(defined(OPENTHREAD_POSIX_CONFIG_MACOS_TUN_OPTION) && \ ((OPENTHREAD_POSIX_CONFIG_MACOS_TUN_OPTION == OT_POSIX_CONFIG_MACOS_UTUN) || \ (OPENTHREAD_POSIX_CONFIG_MACOS_TUN_OPTION == OT_POSIX_CONFIG_MACOS_TUN))) #error "Unexpected tunnel driver selection" #endif #endif // defined(__APPLE__) #include #include #include #include #include #ifdef __linux__ #include #include #include #endif // __linux__ #include #include #include #include #include #include #include #include #include #include #if defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__) #include #if defined(__APPLE__) || defined(__FreeBSD__) #include #endif // defined(__APPLE__) || defined(__FreeBSD__) #include #include #if defined(__APPLE__) || defined(__FreeBSD__) // the prf_ra structure is defined inside another structure (in6_prflags), and C++ // treats that as out of scope if another structure tries to use it -- this (slightly gross) // workaround makes us dependent on our definition remaining in sync (at least the size of it), // so we add a compile-time check that will fail if the SDK ever changes // // our definition of the struct: struct prf_ra { u_char onlink : 1; u_char autonomous : 1; u_char reserved : 6; } prf_ra; // object that contains the SDK's version of the structure: struct in6_prflags compile_time_check_prflags; // compile time check to make sure they're the same size: extern int compile_time_check_struct_prf_ra[(sizeof(struct prf_ra) == sizeof(compile_time_check_prflags.prf_ra)) ? 1 : -1]; #endif #include // struct sockaddr_dl #include // ND6_INFINITE_LIFETIME #ifdef __APPLE__ #if OPENTHREAD_POSIX_CONFIG_MACOS_TUN_OPTION == OT_POSIX_CONFIG_MACOS_UTUN #include #endif #if OPENTHREAD_POSIX_CONFIG_MACOS_TUN_OPTION == OT_POSIX_CONFIG_MACOS_TUN #include // FIX ME: include the tun_ioctl.h file (challenging, as it's location depends on where the developer puts it) #define TUNSIFHEAD _IOW('t', 96, int) #define TUNGIFHEAD _IOR('t', 97, int) #endif #include #endif // defined(__APPLE__) #if defined(__NetBSD__) || defined(__FreeBSD__) #include #endif // defined(__NetBSD__) || defined(__FreeBSD__) #endif // defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__) #include #include #include #include #include #include #include #include #include "common/code_utils.hpp" #include "common/debug.hpp" #include "net/ip6_address.hpp" unsigned int gNetifIndex = 0; char gNetifName[IFNAMSIZ]; const char *otSysGetThreadNetifName(void) { return gNetifName; } unsigned int otSysGetThreadNetifIndex(void) { return gNetifIndex; } #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE #if OPENTHREAD_POSIX_CONFIG_FIREWALL_ENABLE #include "firewall.hpp" #endif #include "posix/platform/ip6_utils.hpp" using namespace ot::Posix::Ip6Utils; #ifndef OPENTHREAD_POSIX_TUN_DEVICE #ifdef __linux__ #define OPENTHREAD_POSIX_TUN_DEVICE "/dev/net/tun" #elif defined(__NetBSD__) || defined(__FreeBSD__) #define OPENTHREAD_POSIX_TUN_DEVICE "/dev/tun0" #elif defined(__APPLE__) #if OPENTHREAD_POSIX_CONFIG_MACOS_TUN_OPTION == OT_POSIX_CONFIG_MACOS_UTUN #define OPENTHREAD_POSIX_TUN_DEVICE // not used - calculated dynamically #elif OPENTHREAD_POSIX_CONFIG_MACOS_TUN_OPTION == OT_POSIX_CONFIG_MACOS_TUN #define OPENTHREAD_POSIX_TUN_DEVICE "/dev/tun0" #endif #else // good luck -- untested platform... #define OPENTHREAD_POSIX_TUN_DEVICE "/dev/net/tun" #endif #endif // OPENTHREAD_TUN_DEVICE #if defined(__linux__) static uint32_t sNetlinkSequence = 0; ///< Netlink message sequence. #endif #if OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE && __linux__ static constexpr uint32_t kOmrRoutesPriority = OPENTHREAD_POSIX_CONFIG_OMR_ROUTES_PRIORITY; static constexpr uint8_t kMaxOmrRoutesNum = OPENTHREAD_POSIX_CONFIG_MAX_OMR_ROUTES_NUM; static uint8_t sAddedOmrRoutesNum = 0; static otIp6Prefix sAddedOmrRoutes[kMaxOmrRoutesNum]; #endif #if OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE && __linux__ static constexpr uint32_t kExternalRoutePriority = OPENTHREAD_POSIX_CONFIG_EXTERNAL_ROUTE_PRIORITY; static constexpr uint8_t kMaxExternalRoutesNum = OPENTHREAD_POSIX_CONFIG_MAX_EXTERNAL_ROUTE_NUM; static uint8_t sAddedExternalRoutesNum = 0; static otIp6Prefix sAddedExternalRoutes[kMaxExternalRoutesNum]; #endif #if defined(RTM_NEWMADDR) || defined(__NetBSD__) // on some BSDs (mac OS, FreeBSD), we get RTM_NEWMADDR/RTM_DELMADDR messages, so we don't need to monitor using MLD // on NetBSD, MLD monitoring simply doesn't work #define OPENTHREAD_POSIX_USE_MLD_MONITOR 0 #else // on some platforms (Linux, but others might be made to work), we do not get information about multicast // group joining via AF_NETLINK or AF_ROUTE sockets. on those platform, we must listen for IPv6 ICMP // MLDv2 messages to know when mulicast memberships change // https://stackoverflow.com/questions/37346289/using-netlink-is-it-possible-to-listen-whenever-multicast-group-membership-is-ch #define OPENTHREAD_POSIX_USE_MLD_MONITOR 1 #endif // defined(RTM_NEWMADDR) || defined(__NetBSD__) // some platforms (like NetBSD) do not have RTM_NEWMADDR/RTM_DELMADDR messages, and they ALSO lack // working MLDv2 support. for those platforms, we must tell the OpenThread interface to // pass ALL multicast packets up; the kernel's IPv6 will filter and drop those that have no listeners #define OPENTHREAD_POSIX_MULTICAST_PROMISCUOUS_REQUIRED 0 #if !OPENTHREAD_POSIX_USE_MLD_MONITOR #if defined(__NetBSD__) #undef OPENTHREAD_POSIX_MULTICAST_PROMISCUOUS_REQUIRED #define OPENTHREAD_POSIX_MULTICAST_PROMISCUOUS_REQUIRED 1 #endif #endif #if defined(__NetBSD__) || defined(__FreeBSD__) static otError destroyTunnel(void); #endif static int sTunFd = -1; ///< Used to exchange IPv6 packets. static int sIpFd = -1; ///< Used to manage IPv6 stack on Thread interface. static int sNetlinkFd = -1; ///< Used to receive netlink events. #if OPENTHREAD_POSIX_USE_MLD_MONITOR static int sMLDMonitorFd = -1; ///< Used to receive MLD events. #endif #if OPENTHREAD_POSIX_USE_MLD_MONITOR // ff02::16 static const otIp6Address kMLDv2MulticastAddress = { {{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16}}}; OT_TOOL_PACKED_BEGIN struct MLDv2Header { uint8_t mType; uint8_t _rsv0; uint16_t mChecksum; uint16_t _rsv1; uint16_t mNumRecords; } OT_TOOL_PACKED_END; OT_TOOL_PACKED_BEGIN struct MLDv2Record { uint8_t mRecordType; uint8_t mAuxDataLen; uint16_t mNumSources; struct in6_addr mMulticastAddress; } OT_TOOL_PACKED_END; enum { kICMPv6MLDv2Type = 143, kICMPv6MLDv2RecordChangeToExcludeType = 3, kICMPv6MLDv2RecordChangeToIncludeType = 4, }; #endif static constexpr size_t kMaxIp6Size = OPENTHREAD_CONFIG_IP6_MAX_DATAGRAM_LENGTH; #if defined(RTM_NEWLINK) && defined(RTM_DELLINK) static bool sIsSyncingState = false; #endif #define OPENTHREAD_POSIX_LOG_TUN_PACKETS 0 #if !defined(__linux__) static bool UnicastAddressIsSubscribed(otInstance *aInstance, const otNetifAddress *netAddr) { const otNetifAddress *address = otIp6GetUnicastAddresses(aInstance); while (address != nullptr) { if (memcmp(address->mAddress.mFields.m8, netAddr->mAddress.mFields.m8, sizeof(address->mAddress.mFields.m8)) == 0) { return true; } address = address->mNext; } return false; } #endif #if defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__) static const uint8_t allOnes[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; static void InitNetaskWithPrefixLength(struct in6_addr *address, uint8_t prefixLen) { #define MAX_PREFIX_LENGTH (OT_IP6_ADDRESS_SIZE * CHAR_BIT) if (prefixLen > MAX_PREFIX_LENGTH) { prefixLen = MAX_PREFIX_LENGTH; } ot::Ip6::Address addr; addr.Clear(); addr.SetPrefix(allOnes, prefixLen); memcpy(address, addr.mFields.m8, sizeof(addr.mFields.m8)); } static uint8_t NetmaskToPrefixLength(const struct sockaddr_in6 *netmask) { return otIp6PrefixMatch(reinterpret_cast(netmask->sin6_addr.s6_addr), reinterpret_cast(allOnes)); } #endif #if defined(__linux__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" void AddRtAttr(struct nlmsghdr *aHeader, uint32_t aMaxLen, uint8_t aType, const void *aData, uint8_t aLen) { uint8_t len = RTA_LENGTH(aLen); struct rtattr *rta; assert(NLMSG_ALIGN(aHeader->nlmsg_len) + RTA_ALIGN(len) <= aMaxLen); OT_UNUSED_VARIABLE(aMaxLen); rta = (struct rtattr *)((char *)(aHeader) + NLMSG_ALIGN((aHeader)->nlmsg_len)); rta->rta_type = aType; rta->rta_len = len; if (aLen) { memcpy(RTA_DATA(rta), aData, aLen); } aHeader->nlmsg_len = NLMSG_ALIGN(aHeader->nlmsg_len) + RTA_ALIGN(len); } void AddRtAttrUint32(struct nlmsghdr *aHeader, uint32_t aMaxLen, uint8_t aType, uint32_t aData) { AddRtAttr(aHeader, aMaxLen, aType, &aData, sizeof(aData)); } #if OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE static bool IsOmrAddress(otInstance *aInstance, const otIp6AddressInfo &aAddressInfo) { otIp6Prefix addressPrefix{*aAddressInfo.mAddress, aAddressInfo.mPrefixLength}; return otNetDataContainsOmrPrefix(aInstance, &addressPrefix); } #endif static void UpdateUnicastLinux(otInstance *aInstance, const otIp6AddressInfo &aAddressInfo, bool aIsAdded) { OT_UNUSED_VARIABLE(aInstance); struct { struct nlmsghdr nh; struct ifaddrmsg ifa; char buf[512]; } req; memset(&req, 0, sizeof(req)); req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL; req.nh.nlmsg_type = aIsAdded ? RTM_NEWADDR : RTM_DELADDR; req.nh.nlmsg_pid = 0; req.nh.nlmsg_seq = ++sNetlinkSequence; req.ifa.ifa_family = AF_INET6; req.ifa.ifa_prefixlen = aAddressInfo.mPrefixLength; req.ifa.ifa_flags = IFA_F_NODAD; req.ifa.ifa_scope = aAddressInfo.mScope; req.ifa.ifa_index = gNetifIndex; AddRtAttr(&req.nh, sizeof(req), IFA_LOCAL, aAddressInfo.mAddress, sizeof(*aAddressInfo.mAddress)); if (!aAddressInfo.mPreferred) { struct ifa_cacheinfo cacheinfo; memset(&cacheinfo, 0, sizeof(cacheinfo)); cacheinfo.ifa_valid = UINT32_MAX; AddRtAttr(&req.nh, sizeof(req), IFA_CACHEINFO, &cacheinfo, sizeof(cacheinfo)); } #if OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE if (IsOmrAddress(aInstance, aAddressInfo)) { // Remove prefix route for OMR address if `OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE` is enabled to // avoid having two routes. if (aIsAdded) { AddRtAttrUint32(&req.nh, sizeof(req), IFA_FLAGS, IFA_F_NOPREFIXROUTE); } } else #endif { #if OPENTHREAD_POSIX_CONFIG_NETIF_PREFIX_ROUTE_METRIC > 0 if (aAddressInfo.mScope > ot::Ip6::Address::kLinkLocalScope) { AddRtAttrUint32(&req.nh, sizeof(req), IFA_RT_PRIORITY, OPENTHREAD_POSIX_CONFIG_NETIF_PREFIX_ROUTE_METRIC); } #endif } if (send(sNetlinkFd, &req, req.nh.nlmsg_len, 0) != -1) { otLogInfoPlat("[netif] Sent request#%u to %s %s/%u", sNetlinkSequence, (aIsAdded ? "add" : "remove"), Ip6AddressString(aAddressInfo.mAddress).AsCString(), aAddressInfo.mPrefixLength); } else { otLogWarnPlat("[netif] Failed to send request#%u to %s %s/%u", sNetlinkSequence, (aIsAdded ? "add" : "remove"), Ip6AddressString(aAddressInfo.mAddress).AsCString(), aAddressInfo.mPrefixLength); } } #pragma GCC diagnostic pop #endif // defined(__linux__) static void UpdateUnicast(otInstance *aInstance, const otIp6AddressInfo &aAddressInfo, bool aIsAdded) { OT_UNUSED_VARIABLE(aInstance); assert(gInstance == aInstance); assert(sIpFd >= 0); #if defined(__linux__) UpdateUnicastLinux(aInstance, aAddressInfo, aIsAdded); #elif defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__) { int rval; struct in6_aliasreq ifr6; memset(&ifr6, 0, sizeof(ifr6)); strlcpy(ifr6.ifra_name, gNetifName, sizeof(ifr6.ifra_name)); ifr6.ifra_addr.sin6_family = AF_INET6; ifr6.ifra_addr.sin6_len = sizeof(ifr6.ifra_addr); memcpy(&ifr6.ifra_addr.sin6_addr, aAddressInfo.mAddress, sizeof(struct in6_addr)); ifr6.ifra_prefixmask.sin6_family = AF_INET6; ifr6.ifra_prefixmask.sin6_len = sizeof(ifr6.ifra_prefixmask); InitNetaskWithPrefixLength(&ifr6.ifra_prefixmask.sin6_addr, aAddressInfo.mPrefixLength); ifr6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; ifr6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; #if defined(__APPLE__) ifr6.ifra_lifetime.ia6t_expire = ND6_INFINITE_LIFETIME; ifr6.ifra_lifetime.ia6t_preferred = (aAddressInfo.mPreferred ? ND6_INFINITE_LIFETIME : 0); #endif rval = ioctl(sIpFd, aIsAdded ? SIOCAIFADDR_IN6 : SIOCDIFADDR_IN6, &ifr6); if (rval == 0) { otLogInfoPlat("[netif] %s %s/%u", (aIsAdded ? "Added" : "Removed"), Ip6AddressString(aAddressInfo.mAddress).AsCString(), aAddressInfo.mPrefixLength); } else if (errno != EALREADY) { otLogWarnPlat("[netif] Failed to %s %s/%u: %s", (aIsAdded ? "add" : "remove"), Ip6AddressString(aAddressInfo.mAddress).AsCString(), aAddressInfo.mPrefixLength, strerror(errno)); } } #endif } static void UpdateMulticast(otInstance *aInstance, const otIp6Address &aAddress, bool aIsAdded) { OT_UNUSED_VARIABLE(aInstance); struct ipv6_mreq mreq; otError error = OT_ERROR_NONE; int err; assert(gInstance == aInstance); VerifyOrExit(sIpFd >= 0); memcpy(&mreq.ipv6mr_multiaddr, &aAddress, sizeof(mreq.ipv6mr_multiaddr)); mreq.ipv6mr_interface = gNetifIndex; err = setsockopt(sIpFd, IPPROTO_IPV6, (aIsAdded ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP), &mreq, sizeof(mreq)); #if defined(__APPLE__) || defined(__FreeBSD__) if ((err != 0) && (errno == EINVAL) && (IN6_IS_ADDR_MC_LINKLOCAL(&mreq.ipv6mr_multiaddr))) { // FIX ME // on mac OS (and FreeBSD), the first time we run (but not subsequently), we get a failure on this // particular join. do we need to bring up the interface at least once prior to joining? we need to figure // out why so we can get rid of this workaround char addressString[INET6_ADDRSTRLEN + 1]; inet_ntop(AF_INET6, mreq.ipv6mr_multiaddr.s6_addr, addressString, sizeof(addressString)); otLogWarnPlat("[netif] Ignoring %s failure (EINVAL) for MC LINKLOCAL address (%s)", aIsAdded ? "IPV6_JOIN_GROUP" : "IPV6_LEAVE_GROUP", addressString); err = 0; } #endif if (err != 0) { otLogWarnPlat("[netif] %s failure (%d)", aIsAdded ? "IPV6_JOIN_GROUP" : "IPV6_LEAVE_GROUP", errno); error = OT_ERROR_FAILED; ExitNow(); } otLogInfoPlat("[netif] %s multicast address %s", aIsAdded ? "Added" : "Removed", Ip6AddressString(&aAddress).AsCString()); exit: SuccessOrDie(error); } static void UpdateLink(otInstance *aInstance) { otError error = OT_ERROR_NONE; struct ifreq ifr; bool ifState = false; bool otState = false; assert(gInstance == aInstance); VerifyOrExit(sIpFd >= 0); memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, gNetifName, sizeof(ifr.ifr_name)); VerifyOrExit(ioctl(sIpFd, SIOCGIFFLAGS, &ifr) == 0, perror("ioctl"); error = OT_ERROR_FAILED); ifState = ((ifr.ifr_flags & IFF_UP) == IFF_UP) ? true : false; otState = otIp6IsEnabled(aInstance); otLogNotePlat("[netif] Changing interface state to %s%s.", otState ? "up" : "down", (ifState == otState) ? " (already done, ignoring)" : ""); if (ifState != otState) { ifr.ifr_flags = otState ? (ifr.ifr_flags | IFF_UP) : (ifr.ifr_flags & ~IFF_UP); VerifyOrExit(ioctl(sIpFd, SIOCSIFFLAGS, &ifr) == 0, perror("ioctl"); error = OT_ERROR_FAILED); #if defined(RTM_NEWLINK) && defined(RTM_DELLINK) // wait for RTM_NEWLINK event before processing notification from kernel to avoid infinite loop sIsSyncingState = true; #endif } exit: if (error != OT_ERROR_NONE) { otLogWarnPlat("[netif] Failed to update state %s", otThreadErrorToString(error)); } } #if __linux__ && \ (OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE || OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE) static otError AddRoute(const otIp6Prefix &aPrefix, uint32_t aPriority) { constexpr unsigned int kBufSize = 128; struct { struct nlmsghdr header; struct rtmsg msg; char buf[kBufSize]; } req{}; unsigned char data[sizeof(in6_addr)]; char addrBuf[OT_IP6_ADDRESS_STRING_SIZE]; unsigned int netifIdx = otSysGetThreadNetifIndex(); otError error = OT_ERROR_NONE; VerifyOrExit(netifIdx > 0, error = OT_ERROR_INVALID_STATE); VerifyOrExit(sNetlinkFd >= 0, error = OT_ERROR_INVALID_STATE); req.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL; req.header.nlmsg_len = NLMSG_LENGTH(sizeof(rtmsg)); req.header.nlmsg_type = RTM_NEWROUTE; req.header.nlmsg_pid = 0; req.header.nlmsg_seq = ++sNetlinkSequence; req.msg.rtm_family = AF_INET6; req.msg.rtm_src_len = 0; req.msg.rtm_dst_len = aPrefix.mLength; req.msg.rtm_tos = 0; req.msg.rtm_scope = RT_SCOPE_UNIVERSE; req.msg.rtm_type = RTN_UNICAST; req.msg.rtm_table = RT_TABLE_MAIN; req.msg.rtm_protocol = RTPROT_BOOT; req.msg.rtm_flags = 0; otIp6AddressToString(&aPrefix.mPrefix, addrBuf, OT_IP6_ADDRESS_STRING_SIZE); inet_pton(AF_INET6, addrBuf, data); AddRtAttr(reinterpret_cast(&req), sizeof(req), RTA_DST, data, sizeof(data)); AddRtAttrUint32(&req.header, sizeof(req), RTA_PRIORITY, aPriority); AddRtAttrUint32(&req.header, sizeof(req), RTA_OIF, netifIdx); if (send(sNetlinkFd, &req, sizeof(req), 0) < 0) { VerifyOrExit(errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK, error = OT_ERROR_BUSY); DieNow(OT_EXIT_ERROR_ERRNO); } exit: return error; } static otError DeleteRoute(const otIp6Prefix &aPrefix) { constexpr unsigned int kBufSize = 512; struct { struct nlmsghdr header; struct rtmsg msg; char buf[kBufSize]; } req{}; unsigned char data[sizeof(in6_addr)]; char addrBuf[OT_IP6_ADDRESS_STRING_SIZE]; unsigned int netifIdx = otSysGetThreadNetifIndex(); otError error = OT_ERROR_NONE; VerifyOrExit(netifIdx > 0, error = OT_ERROR_INVALID_STATE); VerifyOrExit(sNetlinkFd >= 0, error = OT_ERROR_INVALID_STATE); req.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_NONREC; req.header.nlmsg_len = NLMSG_LENGTH(sizeof(rtmsg)); req.header.nlmsg_type = RTM_DELROUTE; req.header.nlmsg_pid = 0; req.header.nlmsg_seq = ++sNetlinkSequence; req.msg.rtm_family = AF_INET6; req.msg.rtm_src_len = 0; req.msg.rtm_dst_len = aPrefix.mLength; req.msg.rtm_tos = 0; req.msg.rtm_scope = RT_SCOPE_UNIVERSE; req.msg.rtm_type = RTN_UNICAST; req.msg.rtm_table = RT_TABLE_MAIN; req.msg.rtm_protocol = RTPROT_BOOT; req.msg.rtm_flags = 0; otIp6AddressToString(&aPrefix.mPrefix, addrBuf, OT_IP6_ADDRESS_STRING_SIZE); inet_pton(AF_INET6, addrBuf, data); AddRtAttr(reinterpret_cast(&req), sizeof(req), RTA_DST, data, sizeof(data)); AddRtAttrUint32(&req.header, sizeof(req), RTA_OIF, netifIdx); if (send(sNetlinkFd, &req, sizeof(req), 0) < 0) { VerifyOrExit(errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK, error = OT_ERROR_BUSY); DieNow(OT_EXIT_ERROR_ERRNO); } exit: return error; } #endif // __linux__ && (OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE || // OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE) #if OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE && __linux__ static bool HasAddedOmrRoute(const otIp6Prefix &aOmrPrefix) { bool found = false; for (uint8_t i = 0; i < sAddedOmrRoutesNum; ++i) { if (otIp6ArePrefixesEqual(&sAddedOmrRoutes[i], &aOmrPrefix)) { found = true; break; } } return found; } static otError AddOmrRoute(const otIp6Prefix &aPrefix) { otError error; VerifyOrExit(sAddedOmrRoutesNum < kMaxOmrRoutesNum, error = OT_ERROR_NO_BUFS); error = AddRoute(aPrefix, kOmrRoutesPriority); exit: return error; } static void UpdateOmrRoutes(otInstance *aInstance) { otError error; otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT; otBorderRouterConfig config; char prefixString[OT_IP6_PREFIX_STRING_SIZE]; // Remove kernel routes if the OMR prefix is removed for (int i = 0; i < static_cast(sAddedOmrRoutesNum); ++i) { if (otNetDataContainsOmrPrefix(aInstance, &sAddedOmrRoutes[i])) { continue; } otIp6PrefixToString(&sAddedOmrRoutes[i], prefixString, sizeof(prefixString)); if ((error = DeleteRoute(sAddedOmrRoutes[i])) != OT_ERROR_NONE) { otLogWarnPlat("[netif] Failed to delete an OMR route %s in kernel: %s", prefixString, otThreadErrorToString(error)); } else { sAddedOmrRoutes[i] = sAddedOmrRoutes[sAddedOmrRoutesNum - 1]; --sAddedOmrRoutesNum; --i; otLogInfoPlat("[netif] Successfully deleted an OMR route %s in kernel", prefixString); } } // Add kernel routes for OMR prefixes in Network Data while (otNetDataGetNextOnMeshPrefix(aInstance, &iterator, &config) == OT_ERROR_NONE) { if (HasAddedOmrRoute(config.mPrefix)) { continue; } otIp6PrefixToString(&config.mPrefix, prefixString, sizeof(prefixString)); if ((error = AddOmrRoute(config.mPrefix)) != OT_ERROR_NONE) { otLogWarnPlat("[netif] Failed to add an OMR route %s in kernel: %s", prefixString, otThreadErrorToString(error)); } else { sAddedOmrRoutes[sAddedOmrRoutesNum++] = config.mPrefix; otLogInfoPlat("[netif] Successfully added an OMR route %s in kernel: %s", prefixString); } } } #endif // OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE && __linux__ #if OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE && __linux__ static otError AddExternalRoute(const otIp6Prefix &aPrefix) { otError error; VerifyOrExit(sAddedExternalRoutesNum < kMaxExternalRoutesNum, error = OT_ERROR_NO_BUFS); error = AddRoute(aPrefix, kExternalRoutePriority); exit: return error; } bool HasExternalRouteInNetData(otInstance *aInstance, const otIp6Prefix &aExternalRoute) { otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT; otExternalRouteConfig config; bool found = false; while (otNetDataGetNextRoute(aInstance, &iterator, &config) == OT_ERROR_NONE) { if (otIp6ArePrefixesEqual(&config.mPrefix, &aExternalRoute)) { found = true; break; } } return found; } bool HasAddedExternalRoute(const otIp6Prefix &aExternalRoute) { bool found = false; for (uint8_t i = 0; i < sAddedExternalRoutesNum; ++i) { if (otIp6ArePrefixesEqual(&sAddedExternalRoutes[i], &aExternalRoute)) { found = true; break; } } return found; } static void UpdateExternalRoutes(otInstance *aInstance) { otError error; otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT; otExternalRouteConfig config; char prefixString[OT_IP6_PREFIX_STRING_SIZE]; for (int i = 0; i < static_cast(sAddedExternalRoutesNum); ++i) { if (HasExternalRouteInNetData(aInstance, sAddedExternalRoutes[i])) { continue; } otIp6PrefixToString(&sAddedExternalRoutes[i], prefixString, sizeof(prefixString)); if ((error = DeleteRoute(sAddedExternalRoutes[i])) != OT_ERROR_NONE) { otLogWarnPlat("[netif] Failed to delete an external route %s in kernel: %s", prefixString, otThreadErrorToString(error)); } else { sAddedExternalRoutes[i] = sAddedExternalRoutes[sAddedExternalRoutesNum - 1]; --sAddedExternalRoutesNum; --i; otLogWarnPlat("[netif] Successfully deleted an external route %s in kernel", prefixString); } } while (otNetDataGetNextRoute(aInstance, &iterator, &config) == OT_ERROR_NONE) { if (config.mRloc16 == otThreadGetRloc16(aInstance) || HasAddedExternalRoute(config.mPrefix)) { continue; } VerifyOrExit(sAddedExternalRoutesNum < kMaxExternalRoutesNum, otLogWarnPlat("[netif] No buffer to add more external routes in kernel")); otIp6PrefixToString(&config.mPrefix, prefixString, sizeof(prefixString)); if ((error = AddExternalRoute(config.mPrefix)) != OT_ERROR_NONE) { otLogWarnPlat("[netif] Failed to add an external route %s in kernel: %s", prefixString, otThreadErrorToString(error)); } else { sAddedExternalRoutes[sAddedExternalRoutesNum++] = config.mPrefix; otLogWarnPlat("[netif] Successfully added an external route %s in kernel: %s", prefixString); } } exit: return; } #endif // OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE && __linux__ static void processAddressChange(const otIp6AddressInfo *aAddressInfo, bool aIsAdded, void *aContext) { if (aAddressInfo->mAddress->mFields.m8[0] == 0xff) { UpdateMulticast(static_cast(aContext), *aAddressInfo->mAddress, aIsAdded); } else { UpdateUnicast(static_cast(aContext), *aAddressInfo, aIsAdded); } } void platformNetifStateChange(otInstance *aInstance, otChangedFlags aFlags) { if (OT_CHANGED_THREAD_NETIF_STATE & aFlags) { UpdateLink(aInstance); } if (OT_CHANGED_THREAD_NETDATA & aFlags) { #if OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE && __linux__ UpdateOmrRoutes(aInstance); #endif #if OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE && __linux__ UpdateExternalRoutes(aInstance); #endif #if OPENTHREAD_POSIX_CONFIG_FIREWALL_ENABLE ot::Posix::UpdateIpSets(aInstance); #endif } } static void processReceive(otMessage *aMessage, void *aContext) { OT_UNUSED_VARIABLE(aContext); char packet[kMaxIp6Size + 4]; otError error = OT_ERROR_NONE; uint16_t length = otMessageGetLength(aMessage); size_t offset = 0; uint16_t maxLength = sizeof(packet) - 4; #if defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__) // BSD tunnel drivers use (for legacy reasons) a 4-byte header to determine the address family of the packet offset += 4; #endif assert(gInstance == aContext); assert(length <= kMaxIp6Size); VerifyOrExit(sTunFd > 0); VerifyOrExit(otMessageRead(aMessage, 0, &packet[offset], maxLength) == length, error = OT_ERROR_NO_BUFS); #if OPENTHREAD_POSIX_LOG_TUN_PACKETS otLogInfoPlat("[netif] Packet from NCP (%u bytes)", static_cast(length)); otDumpInfoPlat("", &packet[offset], length); #endif #if defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__) packet[0] = 0; packet[1] = 0; packet[2] = (PF_INET6 << 8) & 0xFF; packet[3] = (PF_INET6 << 0) & 0xFF; length += 4; #endif VerifyOrExit(write(sTunFd, packet, length) == length, perror("write"); error = OT_ERROR_FAILED); exit: otMessageFree(aMessage); if (error != OT_ERROR_NONE) { otLogWarnPlat("[netif] Failed to receive, error:%s", otThreadErrorToString(error)); } } static void processTransmit(otInstance *aInstance) { otMessage *message = nullptr; ssize_t rval; char packet[kMaxIp6Size]; otError error = OT_ERROR_NONE; size_t offset = 0; assert(gInstance == aInstance); rval = read(sTunFd, packet, sizeof(packet)); VerifyOrExit(rval > 0, error = OT_ERROR_FAILED); { otMessageSettings settings; settings.mLinkSecurityEnabled = (otThreadGetDeviceRole(aInstance) != OT_DEVICE_ROLE_DISABLED); settings.mPriority = OT_MESSAGE_PRIORITY_LOW; message = otIp6NewMessage(aInstance, &settings); VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS); } #if defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__) // BSD tunnel drivers have (for legacy reasons), may have a 4-byte header on them if ((rval >= 4) && (packet[0] == 0) && (packet[1] == 0)) { rval -= 4; offset = 4; } #endif #if OPENTHREAD_POSIX_LOG_TUN_PACKETS otLogInfoPlat("[netif] Packet to NCP (%hu bytes)", static_cast(rval)); otDumpInfoPlat("", &packet[offset], static_cast(rval)); #endif SuccessOrExit(error = otMessageAppend(message, &packet[offset], static_cast(rval))); error = otIp6Send(aInstance, message); message = nullptr; exit: if (message != nullptr) { otMessageFree(message); } if (error != OT_ERROR_NONE) { if (error == OT_ERROR_DROP) { otLogInfoPlat("[netif] Message dropped by Thread", otThreadErrorToString(error)); } else { otLogWarnPlat("[netif] Failed to transmit, error:%s", otThreadErrorToString(error)); } } } static void logAddrEvent(bool isAdd, const ot::Ip6::Address &aAddress, otError error) { OT_UNUSED_VARIABLE(aAddress); if ((error == OT_ERROR_NONE) || ((isAdd) && (error == OT_ERROR_ALREADY || error == OT_ERROR_REJECTED)) || ((!isAdd) && (error == OT_ERROR_NOT_FOUND || error == OT_ERROR_REJECTED))) { otLogInfoPlat("[netif] %s [%s] %s%s", isAdd ? "ADD" : "DEL", aAddress.IsMulticast() ? "M" : "U", aAddress.ToString().AsCString(), error == OT_ERROR_ALREADY ? " (already subscribed, ignored)" : error == OT_ERROR_REJECTED ? " (rejected)" : error == OT_ERROR_NOT_FOUND ? " (not found, ignored)" : ""); } else { otLogWarnPlat("[netif] %s [%s] %s failed (%s)", isAdd ? "ADD" : "DEL", aAddress.IsMulticast() ? "M" : "U", aAddress.ToString().AsCString(), otThreadErrorToString(error)); } } #if defined(__linux__) static void processNetifAddrEvent(otInstance *aInstance, struct nlmsghdr *aNetlinkMessage) { struct ifaddrmsg * ifaddr = reinterpret_cast(NLMSG_DATA(aNetlinkMessage)); size_t rtaLength; otError error = OT_ERROR_NONE; struct sockaddr_in6 addr6; VerifyOrExit(ifaddr->ifa_index == static_cast(gNetifIndex) && ifaddr->ifa_family == AF_INET6); rtaLength = IFA_PAYLOAD(aNetlinkMessage); for (struct rtattr *rta = reinterpret_cast(IFA_RTA(ifaddr)); RTA_OK(rta, rtaLength); rta = RTA_NEXT(rta, rtaLength)) { switch (rta->rta_type) { case IFA_ADDRESS: case IFA_LOCAL: case IFA_BROADCAST: case IFA_ANYCAST: case IFA_MULTICAST: { ot::Ip6::Address addr; memcpy(&addr, RTA_DATA(rta), sizeof(addr)); memset(&addr6, 0, sizeof(addr6)); addr6.sin6_family = AF_INET6; memcpy(&addr6.sin6_addr, RTA_DATA(rta), sizeof(addr6.sin6_addr)); if (aNetlinkMessage->nlmsg_type == RTM_NEWADDR) { if (!addr.IsMulticast()) { otNetifAddress netAddr; netAddr.mAddress = addr; netAddr.mPrefixLength = ifaddr->ifa_prefixlen; error = otIp6AddUnicastAddress(aInstance, &netAddr); } else { otNetifMulticastAddress netAddr; netAddr.mAddress = addr; error = otIp6SubscribeMulticastAddress(aInstance, &addr); } logAddrEvent(/* isAdd */ true, addr, error); if (error == OT_ERROR_ALREADY || error == OT_ERROR_REJECTED) { error = OT_ERROR_NONE; } SuccessOrExit(error); } else if (aNetlinkMessage->nlmsg_type == RTM_DELADDR) { if (!addr.IsMulticast()) { error = otIp6RemoveUnicastAddress(aInstance, &addr); } else { error = otIp6UnsubscribeMulticastAddress(aInstance, &addr); } logAddrEvent(/* isAdd */ false, addr, error); if (error == OT_ERROR_NOT_FOUND || error == OT_ERROR_REJECTED) { error = OT_ERROR_NONE; } SuccessOrExit(error); } else { continue; } break; } default: otLogDebgPlat("[netif] Unexpected address type (%d).", (int)rta->rta_type); break; } } exit: if (error != OT_ERROR_NONE) { otLogWarnPlat("[netif] Failed to process event, error:%s", otThreadErrorToString(error)); } } static void processNetifLinkEvent(otInstance *aInstance, struct nlmsghdr *aNetlinkMessage) { struct ifinfomsg *ifinfo = reinterpret_cast(NLMSG_DATA(aNetlinkMessage)); otError error = OT_ERROR_NONE; bool isUp; VerifyOrExit(ifinfo->ifi_index == static_cast(gNetifIndex) && (ifinfo->ifi_change & IFF_UP)); isUp = ((ifinfo->ifi_flags & IFF_UP) != 0); otLogInfoPlat("[netif] Host netif is %s", isUp ? "up" : "down"); #if defined(RTM_NEWLINK) && defined(RTM_DELLINK) if (sIsSyncingState) { VerifyOrExit(isUp == otIp6IsEnabled(aInstance), otLogWarnPlat("[netif] Host netif state notification is unexpected (ignore)")); sIsSyncingState = false; } else #endif if (isUp != otIp6IsEnabled(aInstance)) { SuccessOrExit(error = otIp6SetEnabled(aInstance, isUp)); otLogInfoPlat("[netif] Succeeded to sync netif state with host"); } exit: if (error != OT_ERROR_NONE) { otLogWarnPlat("[netif] Failed to sync netif state with host: %s", otThreadErrorToString(error)); } } #endif #if defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__) #if defined(__FreeBSD__) #define ROUNDUP(a) ((a) > 0 ? (1 + (((a)-1) | (sizeof(uint32_t) - 1))) : sizeof(uint32_t)) #endif #if defined(__APPLE__) #define ROUNDUP(a) ((a) > 0 ? (1 + (((a)-1) | (sizeof(uint32_t) - 1))) : sizeof(uint32_t)) #define DARWIN_SA_SIZE(sa) ROUNDUP(sa->sa_len) #define SA_SIZE(sa) DARWIN_SA_SIZE(sa) #endif #if defined(__NetBSD__) #define RT_ROUNDUP2(a, n) ((a) > 0 ? (1 + (((a)-1U) | ((n)-1))) : (n)) #define RT_ROUNDUP(a) RT_ROUNDUP2((a), sizeof(uint64_t)) #define SA_SIZE(sa) RT_ROUNDUP(sa->sa_len) #endif static void processNetifAddrEvent(otInstance *aInstance, struct rt_msghdr *rtm) { otError error; struct ifa_msghdr *ifam; #ifdef RTM_NEWMADDR struct ifma_msghdr *ifmam; #endif struct sockaddr_in6 addr6; struct sockaddr_in6 netmask; uint8_t * addrbuf; unsigned int addrmask = 0; unsigned int i; struct sockaddr * sa; bool is_link_local; addr6.sin6_family = 0; netmask.sin6_family = 0; if ((rtm->rtm_type == RTM_NEWADDR) || (rtm->rtm_type == RTM_DELADDR)) { ifam = reinterpret_cast(rtm); VerifyOrExit(ifam->ifam_index == static_cast(gNetifIndex)); addrbuf = (uint8_t *)&ifam[1]; addrmask = (unsigned int)ifam->ifam_addrs; } #ifdef RTM_NEWMADDR else if ((rtm->rtm_type == RTM_NEWMADDR) || (rtm->rtm_type == RTM_DELMADDR)) { ifmam = reinterpret_cast(rtm); VerifyOrExit(ifmam->ifmam_index == static_cast(gNetifIndex)); addrbuf = (uint8_t *)&ifmam[1]; addrmask = (unsigned int)ifmam->ifmam_addrs; } #endif if (addrmask != 0) { for (i = 0; i < RTAX_MAX; i++) { unsigned int mask = (addrmask & (1 << i)); if (mask) { sa = (struct sockaddr *)addrbuf; if (sa->sa_family == AF_INET6) { if (i == RTAX_IFA) memcpy(&addr6, sa, sizeof(sockaddr_in6)); if (i == RTAX_NETMASK) memcpy(&netmask, sa, sizeof(sockaddr_in6)); } addrbuf += SA_SIZE(sa); } } } if (addr6.sin6_family == AF_INET6) { is_link_local = false; if (IN6_IS_ADDR_LINKLOCAL(&addr6.sin6_addr)) { is_link_local = true; // clear the scope -- Mac OS X sends this to us (bozos!) addr6.sin6_addr.s6_addr[3] = 0; } else if (IN6_IS_ADDR_MC_LINKLOCAL(&addr6.sin6_addr)) { addr6.sin6_addr.s6_addr[3] = 0; } ot::Ip6::Address addr; memcpy(&addr, &addr6.sin6_addr, sizeof(addr)); if (rtm->rtm_type == RTM_NEWADDR #ifdef RTM_NEWMADDR || rtm->rtm_type == RTM_NEWMADDR #endif ) { if (!addr.IsMulticast()) { otNetifAddress netAddr; bool subscribed; netAddr.mAddress = addr; netAddr.mPrefixLength = NetmaskToPrefixLength(&netmask); subscribed = UnicastAddressIsSubscribed(aInstance, &netAddr); if (subscribed) { logAddrEvent(/* isAdd */ true, addr, OT_ERROR_ALREADY); error = OT_ERROR_NONE; } else { if (is_link_local) { // remove the stack-added link-local address int err; struct in6_aliasreq ifr6; char addressString[INET6_ADDRSTRLEN + 1]; OT_UNUSED_VARIABLE(addressString); // if otLog*Plat is disabled, we'll get a warning memset(&ifr6, 0, sizeof(ifr6)); strlcpy(ifr6.ifra_name, gNetifName, sizeof(ifr6.ifra_name)); ifr6.ifra_addr.sin6_family = AF_INET6; ifr6.ifra_addr.sin6_len = sizeof(ifr6.ifra_addr); memcpy(&ifr6.ifra_addr.sin6_addr, &addr6.sin6_addr, sizeof(struct in6_addr)); ifr6.ifra_prefixmask.sin6_family = AF_INET6; ifr6.ifra_prefixmask.sin6_len = sizeof(ifr6.ifra_prefixmask); InitNetaskWithPrefixLength(&ifr6.ifra_prefixmask.sin6_addr, netAddr.mPrefixLength); ifr6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; ifr6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; #if defined(__APPLE__) ifr6.ifra_lifetime.ia6t_expire = ND6_INFINITE_LIFETIME; ifr6.ifra_lifetime.ia6t_preferred = ND6_INFINITE_LIFETIME; #endif err = ioctl(sIpFd, SIOCDIFADDR_IN6, &ifr6); if (err != 0) { otLogWarnPlat( "[netif] Error (%d) removing stack-addded link-local address %s", errno, inet_ntop(AF_INET6, addr6.sin6_addr.s6_addr, addressString, sizeof(addressString))); error = OT_ERROR_FAILED; } else { otLogNotePlat( "[netif] %s (removed stack-added link-local)", inet_ntop(AF_INET6, addr6.sin6_addr.s6_addr, addressString, sizeof(addressString))); error = OT_ERROR_NONE; } } else { error = otIp6AddUnicastAddress(aInstance, &netAddr); logAddrEvent(/* isAdd */ true, addr, error); if (error == OT_ERROR_ALREADY) { error = OT_ERROR_NONE; } } } SuccessOrExit(error); } else { otNetifMulticastAddress netAddr; netAddr.mAddress = addr; error = otIp6SubscribeMulticastAddress(aInstance, &addr); logAddrEvent(/* isAdd */ true, addr, error); if (error == OT_ERROR_ALREADY || error == OT_ERROR_REJECTED) { error = OT_ERROR_NONE; } SuccessOrExit(error); } } else if (rtm->rtm_type == RTM_DELADDR #ifdef RTM_DELMADDR || rtm->rtm_type == RTM_DELMADDR #endif ) { if (!addr.IsMulticast()) { error = otIp6RemoveUnicastAddress(aInstance, &addr); logAddrEvent(/* isAdd */ false, addr, error); if (error == OT_ERROR_NOT_FOUND) { error = OT_ERROR_NONE; } } else { error = otIp6UnsubscribeMulticastAddress(aInstance, &addr); logAddrEvent(/* isAdd */ false, addr, error); if (error == OT_ERROR_NOT_FOUND) { error = OT_ERROR_NONE; } } SuccessOrExit(error); } } exit:; } static void processNetifInfoEvent(otInstance *aInstance, struct rt_msghdr *rtm) { struct if_msghdr *ifm = reinterpret_cast(rtm); otError error = OT_ERROR_NONE; VerifyOrExit(ifm->ifm_index == static_cast(gNetifIndex)); UpdateLink(aInstance); exit: if (error != OT_ERROR_NONE) { otLogWarnPlat("[netif] Failed to process info event: %s", otThreadErrorToString(error)); } } #endif static void processNetlinkEvent(otInstance *aInstance) { const size_t kMaxNetifEvent = 8192; ssize_t length; union { #if defined(__linux__) nlmsghdr nlMsg; #else rt_msghdr rtMsg; #endif char buffer[kMaxNetifEvent]; } msgBuffer; length = recv(sNetlinkFd, msgBuffer.buffer, sizeof(msgBuffer.buffer), 0); VerifyOrExit(length > 0); #if defined(__linux__) for (struct nlmsghdr *msg = &msgBuffer.nlMsg; NLMSG_OK(msg, static_cast(length)); msg = NLMSG_NEXT(msg, length)) { #else { // BSD sends one message per read to routing socket (see route.c, monitor command) struct rt_msghdr *msg; msg = &msgBuffer.rtMsg; #define nlmsg_type rtm_type #endif switch (msg->nlmsg_type) { case RTM_NEWADDR: case RTM_DELADDR: processNetifAddrEvent(aInstance, msg); break; #if defined(RTM_NEWLINK) && defined(RTM_DELLINK) case RTM_NEWLINK: case RTM_DELLINK: processNetifLinkEvent(aInstance, msg); break; #endif #if defined(RTM_NEWMADDR) && defined(RTM_DELMADDR) case RTM_NEWMADDR: case RTM_DELMADDR: processNetifAddrEvent(aInstance, msg); break; #endif #if !defined(__linux__) case RTM_IFINFO: processNetifInfoEvent(aInstance, msg); break; #else case NLMSG_ERROR: { const struct nlmsgerr *err = reinterpret_cast(NLMSG_DATA(msg)); if (err->error == 0) { otLogInfoPlat("[netif] Succeeded to process request#%u", err->msg.nlmsg_seq); } else { otLogWarnPlat("[netif] Failed to process request#%u: %s", err->msg.nlmsg_seq, strerror(err->error)); } break; } #endif #if defined(ROUTE_FILTER) || defined(RO_MSGFILTER) || defined(__linux__) default: otLogWarnPlat("[netif] Unhandled/Unexpected netlink/route message (%d).", (int)msg->nlmsg_type); break; #else // this platform doesn't support filtering, so we expect messages of other types...we just ignore them #endif } } exit: return; } #if OPENTHREAD_POSIX_USE_MLD_MONITOR static void mldListenerInit(void) { struct ipv6_mreq mreq6; sMLDMonitorFd = SocketWithCloseExec(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6, kSocketNonBlock); mreq6.ipv6mr_interface = gNetifIndex; memcpy(&mreq6.ipv6mr_multiaddr, kMLDv2MulticastAddress.mFields.m8, sizeof(kMLDv2MulticastAddress.mFields.m8)); VerifyOrDie(setsockopt(sMLDMonitorFd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6, sizeof(mreq6)) == 0, OT_EXIT_FAILURE); #if defined(__linux__) VerifyOrDie(setsockopt(sMLDMonitorFd, SOL_SOCKET, SO_BINDTODEVICE, gNetifName, static_cast(strnlen(gNetifName, IFNAMSIZ))) == 0, OT_EXIT_FAILURE); #endif } static void processMLDEvent(otInstance *aInstance) { const size_t kMaxMLDEvent = 8192; uint8_t buffer[kMaxMLDEvent]; ssize_t bufferLen = -1; struct sockaddr_in6 srcAddr; socklen_t addrLen = sizeof(srcAddr); bool fromSelf = false; MLDv2Header * hdr = reinterpret_cast(buffer); size_t offset; uint8_t type; struct ifaddrs * ifAddrs = nullptr; char addressString[INET6_ADDRSTRLEN + 1]; bufferLen = recvfrom(sMLDMonitorFd, buffer, sizeof(buffer), 0, reinterpret_cast(&srcAddr), &addrLen); VerifyOrExit(bufferLen > 0); type = buffer[0]; VerifyOrExit(type == kICMPv6MLDv2Type && bufferLen >= static_cast(sizeof(MLDv2Header))); // Check whether it is sent by self VerifyOrExit(getifaddrs(&ifAddrs) == 0); for (struct ifaddrs *ifAddr = ifAddrs; ifAddr != nullptr; ifAddr = ifAddr->ifa_next) { if (ifAddr->ifa_addr != nullptr && ifAddr->ifa_addr->sa_family == AF_INET6 && strncmp(gNetifName, ifAddr->ifa_name, IFNAMSIZ) == 0) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" struct sockaddr_in6 *addr6 = reinterpret_cast(ifAddr->ifa_addr); #pragma GCC diagnostic pop if (memcmp(&addr6->sin6_addr, &srcAddr.sin6_addr, sizeof(in6_addr)) == 0) { fromSelf = true; break; } } } VerifyOrExit(fromSelf); hdr = reinterpret_cast(buffer); offset = sizeof(MLDv2Header); for (size_t i = 0; i < ntohs(hdr->mNumRecords) && offset < static_cast(bufferLen); i++) { if (static_cast(bufferLen) >= (sizeof(MLDv2Record) + offset)) { MLDv2Record *record = reinterpret_cast(&buffer[offset]); otError err; ot::Ip6::Address address; memcpy(&address.mFields.m8, &record->mMulticastAddress, sizeof(address.mFields.m8)); inet_ntop(AF_INET6, &record->mMulticastAddress, addressString, sizeof(addressString)); if (record->mRecordType == kICMPv6MLDv2RecordChangeToIncludeType) { err = otIp6SubscribeMulticastAddress(aInstance, &address); logAddrEvent(/* isAdd */ true, address, err); } else if (record->mRecordType == kICMPv6MLDv2RecordChangeToExcludeType) { err = otIp6UnsubscribeMulticastAddress(aInstance, &address); logAddrEvent(/* isAdd */ false, address, err); } offset += sizeof(MLDv2Record) + sizeof(in6_addr) * ntohs(record->mNumSources); } } exit: if (ifAddrs) { freeifaddrs(ifAddrs); } } #endif #if defined(__linux__) // set up the tun device static void platformConfigureTunDevice(const char *aInterfaceName, char *deviceName, size_t deviceNameLen) { struct ifreq ifr; sTunFd = open(OPENTHREAD_POSIX_TUN_DEVICE, O_RDWR | O_CLOEXEC | O_NONBLOCK); VerifyOrDie(sTunFd >= 0, OT_EXIT_ERROR_ERRNO); memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TUN | IFF_NO_PI | static_cast(IFF_TUN_EXCL); if (aInterfaceName) { VerifyOrDie(strlen(aInterfaceName) < IFNAMSIZ, OT_EXIT_INVALID_ARGUMENTS); strncpy(ifr.ifr_name, aInterfaceName, IFNAMSIZ); } else { strncpy(ifr.ifr_name, "wpan%d", IFNAMSIZ); } VerifyOrDie(ioctl(sTunFd, TUNSETIFF, static_cast(&ifr)) == 0, OT_EXIT_ERROR_ERRNO); VerifyOrDie(ioctl(sTunFd, TUNSETLINK, ARPHRD_VOID) == 0, OT_EXIT_ERROR_ERRNO); strncpy(deviceName, ifr.ifr_name, deviceNameLen); ifr.ifr_mtu = static_cast(kMaxIp6Size); VerifyOrDie(ioctl(sIpFd, SIOCSIFMTU, static_cast(&ifr)) == 0, OT_EXIT_ERROR_ERRNO); } #endif #if defined(__APPLE__) && (OPENTHREAD_POSIX_CONFIG_MACOS_TUN_OPTION == OT_POSIX_CONFIG_MACOS_UTUN) static void platformConfigureTunDevice(const char *aInterfaceName, char *deviceName, size_t deviceNameLen) { (void)aInterfaceName; int err = 0; struct sockaddr_ctl addr; struct ctl_info info; sTunFd = SocketWithCloseExec(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL, kSocketNonBlock); VerifyOrDie(sTunFd >= 0, OT_EXIT_ERROR_ERRNO); memset(&info, 0, sizeof(info)); strncpy(info.ctl_name, UTUN_CONTROL_NAME, strlen(UTUN_CONTROL_NAME)); err = ioctl(sTunFd, CTLIOCGINFO, &info); VerifyOrDie(err == 0, OT_EXIT_ERROR_ERRNO); addr.sc_id = info.ctl_id; addr.sc_len = sizeof(addr); addr.sc_family = AF_SYSTEM; addr.ss_sysaddr = AF_SYS_CONTROL; addr.sc_unit = 0; err = connect(sTunFd, (struct sockaddr *)&addr, sizeof(addr)); VerifyOrDie(err == 0, OT_EXIT_ERROR_ERRNO); socklen_t devNameLen; devNameLen = (socklen_t)deviceNameLen; err = getsockopt(sTunFd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, deviceName, &devNameLen); VerifyOrDie(err == 0, OT_EXIT_ERROR_ERRNO); otLogInfoPlat("[netif] Tunnel device name = '%s'", deviceName); } #endif #if defined(__NetBSD__) || defined(__FreeBSD__) static otError destroyTunnel(void) { otError error; struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, gNetifName, sizeof(ifr.ifr_name)); VerifyOrExit(ioctl(sIpFd, SIOCIFDESTROY, &ifr) == 0, perror("ioctl"); error = OT_ERROR_FAILED); error = OT_ERROR_NONE; exit: return error; } #endif #if defined(__NetBSD__) || \ (defined(__APPLE__) && (OPENTHREAD_POSIX_CONFIG_MACOS_TUN_OPTION == OT_POSIX_CONFIG_MACOS_TUN)) || \ defined(__FreeBSD__) static void platformConfigureTunDevice(const char *aInterfaceName, char *deviceName, size_t deviceNameLen) { int flags = IFF_BROADCAST | IFF_MULTICAST; int err; const char *last_slash; const char *path; (void)aInterfaceName; path = OPENTHREAD_POSIX_TUN_DEVICE; sTunFd = open(path, O_RDWR | O_NONBLOCK); VerifyOrDie(sTunFd >= 0, OT_EXIT_ERROR_ERRNO); #if defined(__NetBSD__) || defined(__FreeBSD__) err = ioctl(sTunFd, TUNSIFMODE, &flags); VerifyOrDie(err == 0, OT_EXIT_ERROR_ERRNO); #endif flags = 1; err = ioctl(sTunFd, TUNSIFHEAD, &flags); VerifyOrDie(err == 0, OT_EXIT_ERROR_ERRNO); last_slash = strrchr(OPENTHREAD_POSIX_TUN_DEVICE, '/'); VerifyOrDie(last_slash != nullptr, OT_EXIT_ERROR_ERRNO); last_slash++; strncpy(deviceName, last_slash, deviceNameLen); } #endif static void platformConfigureNetLink(void) { #if defined(__linux__) sNetlinkFd = SocketWithCloseExec(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE, kSocketNonBlock); #elif defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__) sNetlinkFd = SocketWithCloseExec(PF_ROUTE, SOCK_RAW, 0, kSocketNonBlock); #else #error "!! Unknown platform !!" #endif VerifyOrDie(sNetlinkFd >= 0, OT_EXIT_ERROR_ERRNO); #if defined(__linux__) { struct sockaddr_nl sa; memset(&sa, 0, sizeof(sa)); sa.nl_family = AF_NETLINK; sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV6_IFADDR; VerifyOrDie(bind(sNetlinkFd, reinterpret_cast(&sa), sizeof(sa)) == 0, OT_EXIT_ERROR_ERRNO); } #endif #if defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__) { int status; #ifdef ROUTE_FILTER unsigned int msgfilter = ROUTE_FILTER(RTM_IFINFO) | ROUTE_FILTER(RTM_NEWADDR) | ROUTE_FILTER(RTM_DELADDR) | ROUTE_FILTER(RTM_NEWMADDR) | ROUTE_FILTER(RTM_DELMADDR); #define FILTER_CMD ROUTE_MSGFILTER #define FILTER_ARG msgfilter #define FILTER_ARG_SZ sizeof(msgfilter) #endif #ifdef RO_MSGFILTER uint8_t msgfilter[] = {RTM_IFINFO, RTM_NEWADDR, RTM_DELADDR}; #define FILTER_CMD RO_MSGFILTER #define FILTER_ARG msgfilter #define FILTER_ARG_SZ sizeof(msgfilter) #endif #if defined(ROUTE_FILTER) || defined(RO_MSGFILTER) status = setsockopt(sNetlinkFd, AF_ROUTE, FILTER_CMD, FILTER_ARG, FILTER_ARG_SZ); VerifyOrDie(status == 0, OT_EXIT_ERROR_ERRNO); #endif status = fcntl(sNetlinkFd, F_SETFL, O_NONBLOCK); VerifyOrDie(status == 0, OT_EXIT_ERROR_ERRNO); } #endif // defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__) } void platformNetifInit(const char *aInterfaceName) { sIpFd = SocketWithCloseExec(AF_INET6, SOCK_DGRAM, IPPROTO_IP, kSocketNonBlock); VerifyOrDie(sIpFd >= 0, OT_EXIT_ERROR_ERRNO); platformConfigureNetLink(); platformConfigureTunDevice(aInterfaceName, gNetifName, sizeof(gNetifName)); gNetifIndex = if_nametoindex(gNetifName); VerifyOrDie(gNetifIndex > 0, OT_EXIT_FAILURE); #if OPENTHREAD_POSIX_USE_MLD_MONITOR mldListenerInit(); #endif } void platformNetifSetUp(void) { OT_ASSERT(gInstance != nullptr); otIp6SetReceiveFilterEnabled(gInstance, true); #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE otIcmp6SetEchoMode(gInstance, OT_ICMP6_ECHO_HANDLER_ALL); #else otIcmp6SetEchoMode(gInstance, OT_ICMP6_ECHO_HANDLER_DISABLED); #endif otIp6SetReceiveCallback(gInstance, processReceive, gInstance); otIp6SetAddressCallback(gInstance, processAddressChange, gInstance); #if OPENTHREAD_POSIX_MULTICAST_PROMISCUOUS_REQUIRED otIp6SetMulticastPromiscuousEnabled(aInstance, true); #endif } void platformNetifTearDown(void) { } void platformNetifDeinit(void) { if (sTunFd != -1) { close(sTunFd); sTunFd = -1; #if defined(__NetBSD__) || defined(__FreeBSD__) destroyTunnel(); #endif } if (sIpFd != -1) { close(sIpFd); sIpFd = -1; } if (sNetlinkFd != -1) { close(sNetlinkFd); sNetlinkFd = -1; } #if OPENTHREAD_POSIX_USE_MLD_MONITOR if (sMLDMonitorFd != -1) { close(sMLDMonitorFd); sMLDMonitorFd = -1; } #endif gNetifIndex = 0; } void platformNetifUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, fd_set *aErrorFdSet, int *aMaxFd) { OT_UNUSED_VARIABLE(aWriteFdSet); VerifyOrExit(gNetifIndex > 0); assert(sTunFd >= 0); assert(sNetlinkFd >= 0); assert(sIpFd >= 0); FD_SET(sTunFd, aReadFdSet); FD_SET(sTunFd, aErrorFdSet); FD_SET(sNetlinkFd, aReadFdSet); FD_SET(sNetlinkFd, aErrorFdSet); #if OPENTHREAD_POSIX_USE_MLD_MONITOR FD_SET(sMLDMonitorFd, aReadFdSet); FD_SET(sMLDMonitorFd, aErrorFdSet); #endif if (sTunFd > *aMaxFd) { *aMaxFd = sTunFd; } if (sNetlinkFd > *aMaxFd) { *aMaxFd = sNetlinkFd; } #if OPENTHREAD_POSIX_USE_MLD_MONITOR if (sMLDMonitorFd > *aMaxFd) { *aMaxFd = sMLDMonitorFd; } #endif exit: return; } void platformNetifProcess(const fd_set *aReadFdSet, const fd_set *aWriteFdSet, const fd_set *aErrorFdSet) { OT_UNUSED_VARIABLE(aWriteFdSet); VerifyOrExit(gNetifIndex > 0); if (FD_ISSET(sTunFd, aErrorFdSet)) { close(sTunFd); DieNow(OT_EXIT_FAILURE); } if (FD_ISSET(sNetlinkFd, aErrorFdSet)) { close(sNetlinkFd); DieNow(OT_EXIT_FAILURE); } #if OPENTHREAD_POSIX_USE_MLD_MONITOR if (FD_ISSET(sMLDMonitorFd, aErrorFdSet)) { close(sMLDMonitorFd); DieNow(OT_EXIT_FAILURE); } #endif if (FD_ISSET(sTunFd, aReadFdSet)) { processTransmit(gInstance); } if (FD_ISSET(sNetlinkFd, aReadFdSet)) { processNetlinkEvent(gInstance); } #if OPENTHREAD_POSIX_USE_MLD_MONITOR if (FD_ISSET(sMLDMonitorFd, aReadFdSet)) { processMLDEvent(gInstance); } #endif exit: return; } #endif // OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE