diff --git a/src/posix/platform/netif.cpp b/src/posix/platform/netif.cpp index 8032ad3f9..17e3a33b6 100644 --- a/src/posix/platform/netif.cpp +++ b/src/posix/platform/netif.cpp @@ -34,18 +34,53 @@ #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 unfortuntately, 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. + +#define USE_APPLE_UTUN 0 +#define USE_TUN 1 + +#if USE_TUN && USE_APPLE_UTUN +#error "You may not use both tun and utun." +#endif // USE_TUN && USE_APPLE_UTUN + +#endif // defined(__APPLE__) + #include #include +#include #include #include #ifdef __linux__ #include #include #include -#endif +#endif // __linux__ #include #include #include +#include #include #include #include @@ -53,6 +88,48 @@ #include #include +#if defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__) +#include +#if defined(__APPLE__) || defined(__FreeBSD__) +#include +#endif // defined(__APPLE__) || defined(__FreeBSD__) +#include +#include +// +// we need the definition of ND6_INFINITE_LIFETIME, but on +// on mac OS, you can't include nd6.h, at least not from C++, because +// prf_ra isn't defined. But it actually IS defined -- just inside +// another struct in , which was just included. +// +#if !(defined(__APPLE__) || defined(__FreeBSD__)) +#include // ND6_INFINITE_LIFETIME +#else +#define ND6_INFINITE_LIFETIME 0xffffffff +#endif // !(defined(__APPLE__) || defined(__FreeBSD__)) + +#include // struct sockaddr_dl + +#ifdef __APPLE__ +#if USE_APPLE_UTUN +#include +#endif // USE_APPLE_UTUN + +#if USE_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 // USE_TUN + +#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 @@ -65,11 +142,26 @@ #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE #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 USE_APPLE_UTUN +#define OPENTHREAD_POSIX_TUN_DEVICE // not used - calculated dynamically +#elif USE_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 // Some platforms will include linux/ipv6.h in netinet/in.h -#if !defined(_IPV6_H) && !defined(_UAPI_IPV6_H) +#if defined(__linux__) && !defined(_IPV6_H) && !defined(_UAPI_IPV6_H) // from linux/ipv6.h struct in6_ifreq { @@ -77,15 +169,45 @@ struct in6_ifreq __u32 ifr6_prefixlen; int ifr6_ifindex; }; +#endif // defined(__linux__) && !defined(_IPV6_H) && !defined(_UAPI_IPV6_H) + +#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 -static otInstance * sInstance = NULL; -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. -static int sMLDMonitorFd = -1; ///< Used to receive MLD events. -static unsigned int sTunIndex = 0; +#if defined(__NetBSD__) || defined(__FreeBSD__) +static otError destroyTunnel(void); +#endif + +static otInstance *sInstance = NULL; +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 +static unsigned int sTunIndex = 0; static char sTunName[IFNAMSIZ]; +#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}}}; @@ -115,31 +237,113 @@ enum kICMPv6MLDv2RecordChangeToExcludeType = 3, kICMPv6MLDv2RecordChangeToIncludeType = 4, }; +#endif static const size_t kMaxIp6Size = 1536; +#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 != NULL) + { + 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 ot::Ip6::Address::PrefixMatch(netmask->sin6_addr.s6_addr, allOnes, 128); +} +#endif + static void UpdateUnicast(otInstance *aInstance, const otIp6Address &aAddress, uint8_t aPrefixLength, bool aIsAdded) { - struct in6_ifreq ifr6; - otError error = OT_ERROR_NONE; + otError error = OT_ERROR_NONE; assert(sInstance == aInstance); - VerifyOrExit(sIpFd > 0, error = OT_ERROR_INVALID_STATE); + VerifyOrExit(sIpFd >= 0, error = OT_ERROR_INVALID_STATE); - memcpy(&ifr6.ifr6_addr, &aAddress, sizeof(ifr6.ifr6_addr)); - - ifr6.ifr6_ifindex = static_cast(sTunIndex); - ifr6.ifr6_prefixlen = aPrefixLength; - - if (aIsAdded) +#if defined(__linux__) { - VerifyOrDie(ioctl(sIpFd, SIOCSIFADDR, &ifr6) == 0, OT_EXIT_ERROR_ERRNO); + struct in6_ifreq ifr6; + memcpy(&ifr6.ifr6_addr, &aAddress, sizeof(ifr6.ifr6_addr)); + + ifr6.ifr6_ifindex = static_cast(sTunIndex); + ifr6.ifr6_prefixlen = aPrefixLength; + + if (aIsAdded) + { + VerifyOrDie(ioctl(sIpFd, SIOCSIFADDR, &ifr6) == 0, OT_EXIT_ERROR_ERRNO); + } + else + { + VerifyOrExit(ioctl(sIpFd, SIOCDIFADDR, &ifr6) == 0, perror("ioctl"); error = OT_ERROR_FAILED); + } } - else +#elif defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__) { - VerifyOrExit(ioctl(sIpFd, SIOCDIFADDR, &ifr6) == 0, perror("ioctl"); error = OT_ERROR_FAILED); + int err; + struct in6_aliasreq ifr6; + + memset(&ifr6, 0, sizeof(ifr6)); + strlcpy(ifr6.ifra_name, sTunName, 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, &aAddress, 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, aPrefixLength); + 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; + + ifr6.ifra_flags |= IN6_IFF_NODAD; +#endif + + err = ioctl(sIpFd, aIsAdded ? SIOCAIFADDR_IN6 : SIOCDIFADDR_IN6, &ifr6); + if ((err == -1) && (errno == EALREADY)) + { + err = 0; + } + VerifyOrExit(err == 0, perror("ioctl"); error = OT_ERROR_FAILED); } +#endif exit: if (error != OT_ERROR_NONE) @@ -159,14 +363,34 @@ static void UpdateMulticast(otInstance *aInstance, const otIp6Address &aAddress, assert(sInstance == aInstance); - VerifyOrExit(sIpFd > 0, OT_NOOP); + VerifyOrExit(sIpFd >= 0, OT_NOOP); memcpy(&mreq.ipv6mr_multiaddr, &aAddress, sizeof(mreq.ipv6mr_multiaddr)); mreq.ipv6mr_interface = sTunIndex; - VerifyOrExit( - setsockopt(sIpFd, IPPROTO_IPV6, (aIsAdded ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP), &mreq, sizeof(mreq)) == 0, - perror("setsockopt"); - error = OT_ERROR_FAILED); + int err; + 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("ignoring %s failure (EINVAL) for MC LINKLOCAL address (%s)", + aIsAdded ? "IPV6_JOIN_GROUP" : "IPV6_LEAVE_GROUP", addressString); + err = 0; + } +#endif + + if (err != 0) + { + otLogWarnPlat("%s failure (%d)", aIsAdded ? "IPV6_JOIN_GROUP" : "IPV6_LEAVE_GROUP", errno); + } + + VerifyOrExit(err == 0, perror("setsockopt"); error = OT_ERROR_FAILED); exit: SuccessOrDie(error); @@ -177,24 +401,26 @@ static void UpdateLink(otInstance *aInstance) { otError error = OT_ERROR_NONE; struct ifreq ifr; + bool ifState = false; + bool otState = false; assert(sInstance == aInstance); - VerifyOrExit(sIpFd > 0, OT_NOOP); + VerifyOrExit(sIpFd >= 0, OT_NOOP); memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, sTunName, sizeof(ifr.ifr_name)); VerifyOrExit(ioctl(sIpFd, SIOCGIFFLAGS, &ifr) == 0, perror("ioctl"); error = OT_ERROR_FAILED); - if (otIp6IsEnabled(aInstance)) - { - ifr.ifr_flags |= IFF_UP; - } - else - { - ifr.ifr_flags &= ~IFF_UP; - } + ifState = ((ifr.ifr_flags & IFF_UP) == IFF_UP) ? true : false; + otState = otIp6IsEnabled(aInstance); - VerifyOrExit(ioctl(sIpFd, SIOCSIFFLAGS, &ifr) == 0, perror("ioctl"); error = OT_ERROR_FAILED); + otLogNotePlat("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); + } exit: if (error == OT_ERROR_NONE) @@ -229,15 +455,34 @@ static void processStateChange(otChangedFlags aFlags, void *aContext) static void processReceive(otMessage *aMessage, void *aContext) { - char packet[kMaxIp6Size]; - otError error = OT_ERROR_NONE; - uint16_t length = otMessageGetLength(aMessage); + 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(sInstance == aContext); VerifyOrExit(sTunFd > 0, OT_NOOP); - VerifyOrExit(otMessageRead(aMessage, 0, packet, sizeof(packet)) == length, error = OT_ERROR_NO_BUFS); + VerifyOrExit(otMessageRead(aMessage, 0, &packet[offset], maxLength) == length, error = OT_ERROR_NO_BUFS); + +#if OPENTHREAD_POSIX_LOG_TUN_PACKETS + otLogInfoPlat("Packet from NCP (%hu bytes)", static_cast(length)); + otDumpInfo(OT_LOG_REGION_PLATFORM, "", &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); @@ -259,7 +504,8 @@ static void processTransmit(otInstance *aInstance) otMessage *message = NULL; ssize_t rval; char packet[kMaxIp6Size]; - otError error = OT_ERROR_NONE; + otError error = OT_ERROR_NONE; + size_t offset = 0; assert(sInstance == aInstance); @@ -269,7 +515,21 @@ static void processTransmit(otInstance *aInstance) message = otIp6NewMessage(aInstance, NULL); VerifyOrExit(message != NULL, error = OT_ERROR_NO_BUFS); - SuccessOrExit(error = otMessageAppend(message, packet, static_cast(rval))); +#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("Packet to NCP (%hu bytes)", static_cast(rval)); + otDumpInfo(OT_LOG_REGION_PLATFORM, "", &packet[offset], static_cast(rval)); +#endif + + SuccessOrExit(error = otMessageAppend(message, &packet[offset], static_cast(rval))); error = otIp6Send(aInstance, message); message = NULL; @@ -290,11 +550,43 @@ exit: } } +#define kAddAddress true +#define kRemoveAddress false +#define kUnicastAddress true +#define kMulticastAddress false +static void logAddrEvent(bool isAdd, bool isUnicast, struct sockaddr_in6 &addr6, otError error) +{ + char addressString[INET6_ADDRSTRLEN + 1]; + + // these parameters may not be used if logging is disabled at compile time + OT_UNUSED_VARIABLE(isUnicast); + OT_UNUSED_VARIABLE(addr6); + OT_UNUSED_VARIABLE(addressString); + + if ((error == OT_ERROR_NONE) || ((isAdd) && (error == OT_ERROR_ALREADY)) || + ((!isAdd) && (error == OT_ERROR_NOT_FOUND))) + { + otLogNotePlat("%s [%s] %s%s", isAdd ? "ADD" : "DEL", isUnicast ? "U" : "M", + inet_ntop(AF_INET6, addr6.sin6_addr.s6_addr, addressString, sizeof(addressString)), + error == OT_ERROR_ALREADY ? " (already subscribed, ignored)" + : error == OT_ERROR_NOT_FOUND ? " (not found, ignored)" : ""); + } + else + { + otLogWarnPlat("%s [%s] %s failed (%s)", isAdd ? "ADD" : "DEL", isUnicast ? "U" : "M", + inet_ntop(AF_INET6, addr6.sin6_addr.s6_addr, addressString, sizeof(addressString)), + 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 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(sTunIndex) && ifaddr->ifa_family == AF_INET6, OT_NOOP); @@ -309,10 +601,15 @@ static void processNetifAddrEvent(otInstance *aInstance, struct nlmsghdr *aNetli 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()) @@ -321,23 +618,44 @@ static void processNetifAddrEvent(otInstance *aInstance, struct nlmsghdr *aNetli netAddr.mAddress = addr; netAddr.mPrefixLength = ifaddr->ifa_prefixlen; - SuccessOrExit(error = otIp6AddUnicastAddress(aInstance, &netAddr)); + + error = otIp6AddUnicastAddress(aInstance, &netAddr); } else { - SuccessOrExit(error = otIp6SubscribeMulticastAddress(aInstance, &addr)); + otNetifMulticastAddress netAddr; + + netAddr.mAddress = addr; + + error = otIp6SubscribeMulticastAddress(aInstance, &addr); } + + logAddrEvent(kAddAddress, !addr.IsMulticast(), addr6, error); + if (error == OT_ERROR_ALREADY) + { + error = OT_ERROR_NONE; + } + + SuccessOrExit(error); } else if (aNetlinkMessage->nlmsg_type == RTM_DELADDR) { if (!addr.IsMulticast()) { - otIp6RemoveUnicastAddress(aInstance, &addr); + error = otIp6RemoveUnicastAddress(aInstance, &addr); } else { - otIp6UnsubscribeMulticastAddress(aInstance, &addr); + error = otIp6UnsubscribeMulticastAddress(aInstance, &addr); } + + logAddrEvent(kRemoveAddress, !addr.IsMulticast(), addr6, error); + if (error == OT_ERROR_NOT_FOUND) + { + error = OT_ERROR_NONE; + } + + SuccessOrExit(error); } else { @@ -345,7 +663,9 @@ static void processNetifAddrEvent(otInstance *aInstance, struct nlmsghdr *aNetli } break; } + default: + otLogWarnPlat("unexpected address type (%d).", (int)rta->rta_type); break; } } @@ -379,6 +699,252 @@ exit: otLogWarnPlat("%s: %s", __func__, 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 addrlen; + unsigned int addrmask = 0; + unsigned int i; + struct sockaddr * sa; + bool is_link_local; + size_t buffer_len = rtm->rtm_msglen; + + 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(sTunIndex), OT_NOOP); + + 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(sTunIndex), OT_NOOP); + + addrbuf = (uint8_t *)&ifmam[1]; + addrmask = (unsigned int)ifmam->ifmam_addrs; + } +#endif + addrlen = (unsigned int)buffer_len; + + 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)); + } + addrlen -= SA_SIZE(sa); + 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(kAddAddress, kUnicastAddress, addr6, 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, sTunName, 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; + + ifr6.ifra_flags |= IN6_IFF_NODAD; +#endif + + err = ioctl(sIpFd, SIOCDIFADDR_IN6, &ifr6); + if (err != 0) + { + otLogWarnPlat( + "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( + " %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(kAddAddress, kUnicastAddress, addr6, error); + if (error == OT_ERROR_ALREADY) + { + error = OT_ERROR_NONE; + } + } + } + SuccessOrExit(error); + } + else + { + otNetifMulticastAddress netAddr; + netAddr.mAddress = addr; + + error = otIp6SubscribeMulticastAddress(aInstance, &addr); + logAddrEvent(kAddAddress, kMulticastAddress, addr6, error); + if (error == OT_ERROR_ALREADY) + { + 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(kRemoveAddress, kUnicastAddress, addr6, error); + if (error == OT_ERROR_NOT_FOUND) + { + error = OT_ERROR_NONE; + } + } + else + { + error = otIp6UnsubscribeMulticastAddress(aInstance, &addr); + logAddrEvent(kRemoveAddress, kMulticastAddress, addr6, 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(sTunIndex), OT_NOOP); + + UpdateLink(aInstance); + +exit: + if (error == OT_ERROR_NONE) + { + otLogInfoPlat("%s: %s", __func__, otThreadErrorToString(error)); + } + else + { + otLogWarnPlat("%s: %s", __func__, otThreadErrorToString(error)); + } +} + +#endif static void processNetifEvent(otInstance *aInstance) { @@ -390,9 +956,20 @@ static void processNetifEvent(otInstance *aInstance) VerifyOrExit(length > 0, OT_NOOP); +#if defined(__linux__) for (struct nlmsghdr *msg = reinterpret_cast(buffer); NLMSG_OK(msg, 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 = (struct rt_msghdr *)buffer; + +#define nlmsg_type rtm_type + +#endif switch (msg->nlmsg_type) { case RTM_NEWADDR: @@ -400,13 +977,33 @@ static void processNetifEvent(otInstance *aInstance) processNetifAddrEvent(aInstance, msg); break; +#if defined(RTM_NEWLINK) && defined(RTM_DELLINK) case RTM_NEWLINK: case RTM_DELLINK: processNetifLinkEvent(aInstance, msg); break; +#endif - default: +#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; +#endif + +#if defined(ROUTE_FILTER) || defined(RO_MSGFILTER) || defined(__linux__) + default: + otLogWarnPlat("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 } } @@ -420,6 +1017,10 @@ void platformNetifDeinit(void) { close(sTunFd); sTunFd = -1; + +#if defined(__NetBSD__) || defined(__FreeBSD__) + destroyTunnel(); +#endif } if (sIpFd != -1) @@ -434,15 +1035,18 @@ void platformNetifDeinit(void) sNetlinkFd = -1; } +#if OPENTHREAD_POSIX_USE_MLD_MONITOR if (sMLDMonitorFd != -1) { close(sMLDMonitorFd); sMLDMonitorFd = -1; } +#endif sTunIndex = 0; } +#if OPENTHREAD_POSIX_USE_MLD_MONITOR static void mldListenerInit(void) { struct ipv6_mreq mreq6; @@ -452,9 +1056,11 @@ static void mldListenerInit(void) 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, sTunName, static_cast(strnlen(sTunName, IFNAMSIZ))) == 0, OT_EXIT_FAILURE); +#endif } static void processMLDEvent(otInstance *aInstance) @@ -469,6 +1075,7 @@ static void processMLDEvent(otInstance *aInstance) size_t offset; uint8_t type; struct ifaddrs * ifAddrs = NULL; + char addressString[INET6_ADDRSTRLEN + 1]; bufferLen = recvfrom(sMLDMonitorFd, buffer, sizeof(buffer), 0, reinterpret_cast(&srcAddr), &addrLen); VerifyOrExit(bufferLen > 0, OT_NOOP); @@ -505,7 +1112,6 @@ static void processMLDEvent(otInstance *aInstance) otError err; otIp6Address address; - char addressString[INET6_ADDRSTRLEN + 1]; memcpy(&address.mFields.m8, &record->mMulticastAddress, sizeof(address.mFields.m8)); inet_ntop(AF_INET6, &record->mMulticastAddress, addressString, sizeof(addressString)); @@ -554,30 +1160,21 @@ exit: return; } +#endif -void platformNetifInit(otInstance *aInstance, const char *aInterfaceName) +#if defined(__linux__) +// set up the tun device +static void platformConfigureTunDevice(otInstance *aInstance, + const char *aInterfaceName, + char * deviceName, + size_t deviceNameLen) { struct ifreq ifr; - sIpFd = SocketWithCloseExec(AF_INET6, SOCK_DGRAM, IPPROTO_IP, kSocketNonBlock); - VerifyOrDie(sIpFd >= 0, OT_EXIT_ERROR_ERRNO); - - sNetlinkFd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); - VerifyOrDie(sNetlinkFd > 0, OT_EXIT_ERROR_ERRNO); - - otIcmp6SetEchoMode(aInstance, OT_ICMP6_ECHO_HANDLER_DISABLED); - - { - 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); - } + (void)aInstance; sTunFd = open(OPENTHREAD_POSIX_TUN_DEVICE, O_RDWR | O_CLOEXEC | O_NONBLOCK); - VerifyOrDie(sTunFd > 0, OT_EXIT_ERROR_ERRNO); + VerifyOrDie(sTunFd >= 0, OT_EXIT_ERROR_ERRNO); memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TUN | IFF_NO_PI; @@ -596,18 +1193,175 @@ void platformNetifInit(otInstance *aInstance, const char *aInterfaceName) VerifyOrDie(ioctl(sTunFd, TUNSETIFF, static_cast(&ifr)) == 0, OT_EXIT_ERROR_ERRNO); VerifyOrDie(ioctl(sTunFd, TUNSETLINK, ARPHRD_VOID) == 0, OT_EXIT_ERROR_ERRNO); - sTunIndex = if_nametoindex(ifr.ifr_name); + strncpy(deviceName, ifr.ifr_name, deviceNameLen); +} +#endif + +#if defined(__APPLE__) && USE_APPLE_UTUN +static void platformConfigureTunDevice(otInstance *aInstance, + const char *aInterfaceName, + char * deviceName, + size_t deviceNameLen) +{ + (void)aInterfaceName; + int err = 0; + struct sockaddr_ctl addr; + struct ctl_info info; + + (void)aInstance; + + sTunFd = NonBlockSocketWithCloseExec(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); + 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(fd, 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("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, sTunName, 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__) && USE_TUN) || defined(__FreeBSD__) +static void platformConfigureTunDevice(otInstance *aInstance, + 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; + (void)aInstance; + + 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 != NULL, OT_EXIT_ERROR_ERRNO); + last_slash++; + + strncpy(deviceName, last_slash, deviceNameLen); +} +#endif + +static void platformConfigureNetLink(void) +{ +#if defined(__linux__) + sNetlinkFd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); +#elif defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__) + sNetlinkFd = socket(PF_ROUTE, SOCK_RAW, 0); +#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(otInstance *aInstance, const char *aInterfaceName) +{ + sIpFd = SocketWithCloseExec(AF_INET6, SOCK_DGRAM, IPPROTO_IP, kSocketNonBlock); + VerifyOrDie(sIpFd >= 0, OT_EXIT_ERROR_ERRNO); + + platformConfigureNetLink(); + platformConfigureTunDevice(aInstance, aInterfaceName, sTunName, sizeof(sTunName)); + + sTunIndex = if_nametoindex(sTunName); VerifyOrDie(sTunIndex > 0, OT_EXIT_FAILURE); - strncpy(sTunName, ifr.ifr_name, sizeof(sTunName)); #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE platformUdpInit(sTunName); #endif +#if OPENTHREAD_POSIX_USE_MLD_MONITOR mldListenerInit(); +#endif + otIcmp6SetEchoMode(aInstance, OT_ICMP6_ECHO_HANDLER_DISABLED); otIp6SetReceiveCallback(aInstance, processReceive, aInstance); otIp6SetAddressCallback(aInstance, processAddressChange, aInstance); otSetStateChangedCallback(aInstance, processStateChange, aInstance); +#if OPENTHREAD_POSIX_MULTICAST_PROMISCUOUS_REQUIRED + otIp6SetMulticastPromiscuousEnabled(aInstance, true); +#endif + sInstance = aInstance; } @@ -617,16 +1371,18 @@ void platformNetifUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, fd_set *a VerifyOrExit(sTunIndex > 0, OT_NOOP); - assert(sTunFd > 0); - assert(sNetlinkFd > 0); - assert(sIpFd > 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) { @@ -638,10 +1394,12 @@ void platformNetifUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, fd_set *a *aMaxFd = sNetlinkFd; } +#if OPENTHREAD_POSIX_USE_MLD_MONITOR if (sMLDMonitorFd > *aMaxFd) { *aMaxFd = sMLDMonitorFd; } +#endif exit: return; } @@ -663,11 +1421,13 @@ void platformNetifProcess(const fd_set *aReadFdSet, const fd_set *aWriteFdSet, c DieNow(OT_EXIT_FAILURE); } +#if OPENTHREAD_POSIX_USE_MLD_MONITOR if (FD_ISSET(sMLDMonitorFd, aErrorFdSet)) { - close(sNetlinkFd); + close(sMLDMonitorFd); DieNow(OT_EXIT_FAILURE); } +#endif if (FD_ISSET(sTunFd, aReadFdSet)) { @@ -679,10 +1439,12 @@ void platformNetifProcess(const fd_set *aReadFdSet, const fd_set *aWriteFdSet, c processNetifEvent(sInstance); } +#if OPENTHREAD_POSIX_USE_MLD_MONITOR if (FD_ISSET(sMLDMonitorFd, aReadFdSet)) { processMLDEvent(sInstance); } +#endif exit: return;