From b6683948713e3fa69cedab3729bdc9aa2ec47d97 Mon Sep 17 00:00:00 2001 From: Yakun Xu Date: Mon, 12 Oct 2020 23:26:54 +0800 Subject: [PATCH] [posix] map address with rtnetlink API on Linux (#5629) * no DAD for anycast address. * add anycast address as non-preferred address. * update address callback to provide more address info. --- include/openthread/instance.h | 2 +- include/openthread/ip6.h | 22 +++- src/core/net/netif.cpp | 73 ++++++++++-- src/core/net/netif.hpp | 5 - src/posix/platform/netif.cpp | 203 ++++++++++++++++++++++++---------- 5 files changed, 226 insertions(+), 79 deletions(-) diff --git a/include/openthread/instance.h b/include/openthread/instance.h index bee273a16..3e39262e0 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (31) +#define OPENTHREAD_API_VERSION (32) /** * @addtogroup api-instance diff --git a/include/openthread/ip6.h b/include/openthread/ip6.h index ce9772135..2ea857060 100644 --- a/include/openthread/ip6.h +++ b/include/openthread/ip6.h @@ -415,19 +415,29 @@ typedef void (*otIp6ReceiveCallback)(otMessage *aMessage, void *aContext); */ void otIp6SetReceiveCallback(otInstance *aInstance, otIp6ReceiveCallback aCallback, void *aCallbackContext); +/** + * @struct otIp6AddressInfo + * + * This structure represents IPv6 address information. + * + */ +typedef struct otIp6AddressInfo +{ + const otIp6Address *mAddress; ///< A pointer to the IPv6 address. + uint8_t mPrefixLength; ///< The prefix length of mAddress if it is a unicast address. + uint8_t mScope : 4; ///< The scope of this address. + bool mIsAnycast : 1; ///< Whether this is an anycast address. +} otIp6AddressInfo; + /** * This function pointer is called when an internal IPv6 address is added or removed. * - * @param[in] aAddress A pointer to the IPv6 address. - * @param[in] aPrefixLength The prefix length if @p aAddress is unicast address, and 128 for multicast address. + * @param[in] aAddressInfo A pointer to the IPv6 address information. * @param[in] aIsAdded TRUE if the @p aAddress was added, FALSE if @p aAddress was removed. * @param[in] aContext A pointer to application-specific context. * */ -typedef void (*otIp6AddressCallback)(const otIp6Address *aAddress, - uint8_t aPrefixLength, - bool aIsAdded, - void * aContext); +typedef void (*otIp6AddressCallback)(const otIp6AddressInfo *aAddressInfo, bool aIsAdded, void *aContext); /** * This function registers a callback to notify internal IPv6 address changes. diff --git a/src/core/net/netif.cpp b/src/core/net/netif.cpp index 497831915..185e9a382 100644 --- a/src/core/net/netif.cpp +++ b/src/core/net/netif.cpp @@ -42,6 +42,30 @@ namespace ot { namespace Ip6 { +class AddressInfo : public otIp6AddressInfo +{ +public: + AddressInfo(const NetifUnicastAddress &aAddress, Instance &aInstance) + { + mAddress = &aAddress.mAddress; + mPrefixLength = aAddress.mPrefixLength; + mScope = aAddress.GetScope(); + mIsAnycast = aInstance.Get().IsAnycastLocator(aAddress.GetAddress()); + } + + AddressInfo(const NetifMulticastAddress &aAddress) + { + mAddress = &aAddress.GetAddress(); + mPrefixLength = kMulticastPrefixLength; + mScope = aAddress.GetAddress().GetScope(); + mIsAnycast = false; + } + +private: + static constexpr uint8_t kMulticastPrefixLength = + 128; ///< Multicast prefix length used to notify internal address changes. +}; + /* * Certain fixed multicast addresses are defined as a set of chained (linked-list) constant `otNetifMulticastAddress` * entries: @@ -128,7 +152,10 @@ void Netif::SubscribeAllNodesMulticast(void) for (const NetifMulticastAddress *entry = &linkLocalAllNodesAddress; entry; entry = entry->GetNext()) { - mAddressCallback(&entry->GetAddress(), kMulticastPrefixLength, /* IsAdded */ true, mAddressCallbackContext); + AddressInfo addressInfo(*entry); + + mAddressCallback(&addressInfo, + /* IsAdded */ true, mAddressCallbackContext); } exit: @@ -175,7 +202,10 @@ void Netif::UnsubscribeAllNodesMulticast(void) for (const NetifMulticastAddress *entry = &linkLocalAllNodesAddress; entry; entry = entry->GetNext()) { - mAddressCallback(&entry->GetAddress(), kMulticastPrefixLength, /* IsAdded */ false, mAddressCallbackContext); + AddressInfo addressInfo(*entry); + + mAddressCallback(&addressInfo, + /* IsAdded */ false, mAddressCallbackContext); } exit: @@ -233,7 +263,10 @@ void Netif::SubscribeAllRoutersMulticast(void) for (const NetifMulticastAddress *entry = &linkLocalAllRoutersAddress; entry != &linkLocalAllNodesAddress; entry = entry->GetNext()) { - mAddressCallback(&entry->GetAddress(), kMulticastPrefixLength, /* IsAdded */ true, mAddressCallbackContext); + AddressInfo addressInfo(*entry); + + mAddressCallback(&addressInfo, + /* IsAdded */ true, mAddressCallbackContext); } exit: @@ -276,7 +309,10 @@ void Netif::UnsubscribeAllRoutersMulticast(void) for (const NetifMulticastAddress *entry = &linkLocalAllRoutersAddress; entry != &linkLocalAllNodesAddress; entry = entry->GetNext()) { - mAddressCallback(&entry->GetAddress(), kMulticastPrefixLength, /* IsAdded */ false, mAddressCallbackContext); + AddressInfo addressInfo(*entry); + + mAddressCallback(&addressInfo, + /* IsAdded */ false, mAddressCallbackContext); } exit: @@ -295,7 +331,13 @@ void Netif::SubscribeMulticast(NetifMulticastAddress &aAddress) Get().Signal(kEventIp6MulticastSubscribed); VerifyOrExit(mAddressCallback != nullptr, OT_NOOP); - mAddressCallback(&aAddress.mAddress, kMulticastPrefixLength, /* IsAdded */ true, mAddressCallbackContext); + + { + AddressInfo addressInfo(aAddress); + + mAddressCallback(&addressInfo, + /* IsAdded */ true, mAddressCallbackContext); + } exit: return; @@ -308,7 +350,12 @@ void Netif::UnsubscribeMulticast(const NetifMulticastAddress &aAddress) Get().Signal(kEventIp6MulticastUnsubscribed); VerifyOrExit(mAddressCallback != nullptr, OT_NOOP); - mAddressCallback(&aAddress.mAddress, kMulticastPrefixLength, /* IsAdded */ false, mAddressCallbackContext); + { + AddressInfo addressInfo(aAddress); + + mAddressCallback(&addressInfo, + /* IsAdded */ false, mAddressCallbackContext); + } exit: return; @@ -396,7 +443,13 @@ void Netif::AddUnicastAddress(NetifUnicastAddress &aAddress) Get().Signal(aAddress.mRloc ? kEventThreadRlocAdded : kEventIp6AddressAdded); VerifyOrExit(mAddressCallback != nullptr, OT_NOOP); - mAddressCallback(&aAddress.mAddress, aAddress.mPrefixLength, /* IsAdded */ true, mAddressCallbackContext); + + { + AddressInfo addressInfo(aAddress, GetInstance()); + + mAddressCallback(&addressInfo, + /* IsAdded */ true, mAddressCallbackContext); + } exit: return; @@ -409,7 +462,11 @@ void Netif::RemoveUnicastAddress(const NetifUnicastAddress &aAddress) Get().Signal(aAddress.mRloc ? kEventThreadRlocRemoved : kEventIp6AddressRemoved); VerifyOrExit(mAddressCallback != nullptr, OT_NOOP); - mAddressCallback(&aAddress.mAddress, aAddress.mPrefixLength, /* IsAdded */ false, mAddressCallbackContext); + { + AddressInfo addressInfo(aAddress, GetInstance()); + + mAddressCallback(&addressInfo, /* IsAdded */ false, mAddressCallbackContext); + } exit: return; diff --git a/src/core/net/netif.hpp b/src/core/net/netif.hpp index 8c5e13bd3..e73fdfcb9 100644 --- a/src/core/net/netif.hpp +++ b/src/core/net/netif.hpp @@ -683,11 +683,6 @@ protected: void UnsubscribeAllNodesMulticast(void); private: - enum - { - kMulticastPrefixLength = 128, ///< Multicast prefix length used to notify internal address changes. - }; - class ExternalMulticastAddressIteratorBuilder { public: diff --git a/src/posix/platform/netif.cpp b/src/posix/platform/netif.cpp index 6abba510e..0d314f792 100644 --- a/src/posix/platform/netif.cpp +++ b/src/posix/platform/netif.cpp @@ -152,6 +152,40 @@ char gNetifName[IFNAMSIZ]; #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE +namespace { + +/** + * This utility class converts binary IPv6 address to text format. + * + */ +class Ip6AddressString +{ +public: + /** + * The constructor of this converter. + * + * @param[in] aAddress A pointer to a buffer holding an IPv6 address. + * + */ + Ip6AddressString(const void *aAddress) + { + VerifyOrDie(inet_ntop(AF_INET6, aAddress, mBuffer, sizeof(mBuffer)) != nullptr, OT_EXIT_ERROR_ERRNO); + } + + /** + * This method returns the string as a null-terminated C string. + * + * @returns The null-terminated C string. + * + */ + const char *AsCString(void) const { return mBuffer; } + +private: + char mBuffer[INET6_ADDRSTRLEN]; +}; + +} // namespace + #ifndef OPENTHREAD_POSIX_TUN_DEVICE #ifdef __linux__ @@ -171,16 +205,9 @@ char gNetifName[IFNAMSIZ]; #endif // OPENTHREAD_TUN_DEVICE -// Some platforms will include linux/ipv6.h in netinet/in.h -#if defined(__linux__) && !defined(_IPV6_H) && !defined(_UAPI_IPV6_H) -// from linux/ipv6.h -struct in6_ifreq -{ - struct in6_addr ifr6_addr; - __u32 ifr6_prefixlen; - int ifr6_ifindex; -}; -#endif // defined(__linux__) && !defined(_IPV6_H) && !defined(_UAPI_IPV6_H) +#if defined(__linux__) +static uint32_t sNetlinkSequence = 0; ///< Netlink message sequence. +#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 @@ -298,46 +325,91 @@ static uint8_t NetmaskToPrefixLength(const struct sockaddr_in6 *netmask) } #endif -static void UpdateUnicast(otInstance *aInstance, const otIp6Address &aAddress, uint8_t aPrefixLength, bool aIsAdded) +#if defined(__linux__) +static void UpdateUnicastLinux(const otIp6AddressInfo &aAddressInfo, bool aIsAdded) +{ + struct rtattr *rta; + + 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; + + rta = reinterpret_cast((reinterpret_cast(&req)) + NLMSG_ALIGN(req.nh.nlmsg_len)); + rta->rta_type = IFA_LOCAL; + rta->rta_len = RTA_LENGTH(sizeof(*aAddressInfo.mAddress)); + + memcpy(RTA_DATA(rta), aAddressInfo.mAddress, sizeof(*aAddressInfo.mAddress)); + + req.nh.nlmsg_len = NLMSG_ALIGN(req.nh.nlmsg_len) + rta->rta_len; + + if (aAddressInfo.mIsAnycast) + { + struct ifa_cacheinfo cacheinfo; + + rta = reinterpret_cast((reinterpret_cast(rta)) + rta->rta_len); + rta->rta_type = IFA_CACHEINFO; + rta->rta_len = RTA_LENGTH(sizeof(cacheinfo)); + + memset(&cacheinfo, 0, sizeof(cacheinfo)); + cacheinfo.ifa_valid = UINT32_MAX; + + memcpy(RTA_DATA(rta), &cacheinfo, sizeof(cacheinfo)); + + req.nh.nlmsg_len += rta->rta_len; + } + + if (send(sNetlinkFd, &req, req.nh.nlmsg_len, 0) != -1) + { + otLogInfoPlat("Sent request#%u to %s %s/%u", sNetlinkSequence, (aIsAdded ? "add" : "remove"), + Ip6AddressString(aAddressInfo.mAddress).AsCString(), aAddressInfo.mPrefixLength); + } + else + { + otLogInfoPlat("Failed to send request#%u to %s %s/%u", sNetlinkSequence, (aIsAdded ? "add" : "remove"), + Ip6AddressString(aAddressInfo.mAddress).AsCString(), aAddressInfo.mPrefixLength); + } +} +#endif // defined(__linux__) + +static void UpdateUnicast(otInstance *aInstance, const otIp6AddressInfo &aAddressInfo, bool aIsAdded) { OT_UNUSED_VARIABLE(aInstance); - otError error = OT_ERROR_NONE; - assert(sInstance == aInstance); - - VerifyOrExit(sIpFd >= 0, error = OT_ERROR_INVALID_STATE); + assert(sIpFd >= 0); #if defined(__linux__) - { - struct in6_ifreq ifr6; - memcpy(&ifr6.ifr6_addr, &aAddress, sizeof(ifr6.ifr6_addr)); - - ifr6.ifr6_ifindex = static_cast(gNetifIndex); - 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); - } - } + UpdateUnicastLinux(aAddressInfo, aIsAdded); #elif defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__) { - int err; + 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, &aAddress, sizeof(struct in6_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, aPrefixLength); + InitNetaskWithPrefixLength(&ifr6.ifra_prefixmask.sin6_addr, aAddressInfo.mPrefixLength); ifr6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; ifr6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; @@ -346,24 +418,20 @@ static void UpdateUnicast(otInstance *aInstance, const otIp6Address &aAddress, u ifr6.ifra_lifetime.ia6t_preferred = ND6_INFINITE_LIFETIME; #endif - err = ioctl(sIpFd, aIsAdded ? SIOCAIFADDR_IN6 : SIOCDIFADDR_IN6, &ifr6); - if ((err == -1) && (errno == EALREADY)) + rval = ioctl(sIpFd, aIsAdded ? SIOCAIFADDR_IN6 : SIOCDIFADDR_IN6, &ifr6); + if (rval == 0) { - err = 0; + otLogInfoPlat("%s %s/%u", (aIsAdded ? "Added" : "Removed"), + Ip6AddressString(aAddressInfo.mAddress).AsCString(), aAddressInfo.mPrefixLength); + } + else if (errno != EALREADY) + { + otLogWarnPlat("Failed to %s %s/%u: %s", (aIsAdded ? "add" : "remove"), + Ip6AddressString(aAddressInfo.mAddress).AsCString(), aAddressInfo.mPrefixLength, + strerror(errno)); } - VerifyOrExit(err == 0, perror("ioctl"); error = OT_ERROR_FAILED); } #endif - -exit: - if (error != OT_ERROR_NONE) - { - otLogWarnPlat("%s: %s", __func__, otThreadErrorToString(error)); - } - else - { - otLogInfoPlat("%s: %s", __func__, otThreadErrorToString(error)); - } } static void UpdateMulticast(otInstance *aInstance, const otIp6Address &aAddress, bool aIsAdded) @@ -385,9 +453,9 @@ static void UpdateMulticast(otInstance *aInstance, const otIp6Address &aAddress, 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 + // 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)); @@ -445,15 +513,15 @@ exit: } } -static void processAddressChange(const otIp6Address *aAddress, uint8_t aPrefixLength, bool aIsAdded, void *aContext) +static void processAddressChange(const otIp6AddressInfo *aAddressInfo, bool aIsAdded, void *aContext) { - if (aAddress->mFields.m8[0] == 0xff) + if (aAddressInfo->mAddress->mFields.m8[0] == 0xff) { - UpdateMulticast(static_cast(aContext), *aAddress, aIsAdded); + UpdateMulticast(static_cast(aContext), *aAddressInfo->mAddress, aIsAdded); } else { - UpdateUnicast(static_cast(aContext), *aAddress, aPrefixLength, aIsAdded); + UpdateUnicast(static_cast(aContext), *aAddressInfo, aIsAdded); } } @@ -959,7 +1027,7 @@ exit: #endif -static void processNetifEvent(otInstance *aInstance) +static void processNetlinkEvent(otInstance *aInstance) { const size_t kMaxNetifEvent = 8192; ssize_t length; @@ -1008,6 +1076,23 @@ static void processNetifEvent(otInstance *aInstance) 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("Succeeded to process request#%u", err->msg.nlmsg_seq); + } + else + { + otLogWarnPlat("Failed to process request#%u: %s", err->msg.nlmsg_seq, strerror(err->error)); + } + + break; + } #endif #if defined(ROUTE_FILTER) || defined(RO_MSGFILTER) || defined(__linux__) @@ -1015,7 +1100,7 @@ static void processNetifEvent(otInstance *aInstance) 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 + // this platform doesn't support filtering, so we expect messages of other types...we just ignore them #endif } } @@ -1190,7 +1275,7 @@ static void platformConfigureTunDevice(otInstance *aInstance, VerifyOrDie(sTunFd >= 0, OT_EXIT_ERROR_ERRNO); memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_flags = IFF_TUN | IFF_NO_PI; + ifr.ifr_flags = IFF_TUN | IFF_NO_PI | static_cast(IFF_TUN_EXCL); if (aInterfaceName) { @@ -1451,7 +1536,7 @@ void platformNetifProcess(const fd_set *aReadFdSet, const fd_set *aWriteFdSet, c if (FD_ISSET(sNetlinkFd, aReadFdSet)) { - processNetifEvent(sInstance); + processNetlinkEvent(sInstance); } #if OPENTHREAD_POSIX_USE_MLD_MONITOR