From b214f739f0b8d4969ed8f40606438d0c327422a7 Mon Sep 17 00:00:00 2001 From: Li Cao Date: Tue, 3 Sep 2024 16:28:33 +0800 Subject: [PATCH] [netif] add wpan multicast address subscribe/unsubscribe for NCP --- src/ncp/ncp_spinel.cpp | 8 ++ src/ncp/ncp_spinel.hpp | 2 + src/ncp/posix/netif.cpp | 201 +++++++++++++++++++++++++++++++++++++ src/ncp/posix/netif.hpp | 24 ++++- tests/gtest/test_netif.cpp | 117 ++++++++++++++++++++- 5 files changed, 344 insertions(+), 8 deletions(-) diff --git a/src/ncp/ncp_spinel.cpp b/src/ncp/ncp_spinel.cpp index 57295ec9abf..5394d56c54c 100644 --- a/src/ncp/ncp_spinel.cpp +++ b/src/ncp/ncp_spinel.cpp @@ -473,6 +473,14 @@ otbrError NcpSpinel::HandleResponseForPropSet(spinel_tid_t aTid, return error; } +otbrError NcpSpinel::Ip6MulAddrUpdateSubscription(const otIp6Address &aAddress, bool aIsAdded) +{ + OTBR_UNUSED_VARIABLE(aAddress); + OTBR_UNUSED_VARIABLE(aIsAdded); + + return OTBR_ERROR_NOT_IMPLEMENTED; +} + spinel_tid_t NcpSpinel::GetNextTid(void) { spinel_tid_t tid = mCmdNextTid; diff --git a/src/ncp/ncp_spinel.hpp b/src/ncp/ncp_spinel.hpp index 24312b78073..e53294bad98 100644 --- a/src/ncp/ncp_spinel.hpp +++ b/src/ncp/ncp_spinel.hpp @@ -287,6 +287,8 @@ class NcpSpinel : public Netif::Dependencies const uint8_t *aData, uint16_t aLength); + otbrError Ip6MulAddrUpdateSubscription(const otIp6Address &aAddress, bool aIsAdded) override; + spinel_tid_t GetNextTid(void); void FreeTidTableItem(spinel_tid_t aTid); diff --git a/src/ncp/posix/netif.cpp b/src/ncp/posix/netif.cpp index 2023d6e6418..0b9239f98cc 100644 --- a/src/ncp/posix/netif.cpp +++ b/src/ncp/posix/netif.cpp @@ -30,8 +30,10 @@ #include "netif.hpp" +#include #include #include +#include #include #include #include @@ -54,13 +56,55 @@ otbrError Netif::Dependencies::Ip6Send(const uint8_t *aData, uint16_t aLength) { OTBR_UNUSED_VARIABLE(aData); OTBR_UNUSED_VARIABLE(aLength); + return OTBR_ERROR_NONE; } +otbrError Netif::Dependencies::Ip6MulAddrUpdateSubscription(const otIp6Address &aAddress, bool aIsAdd) +{ + OTBR_UNUSED_VARIABLE(aAddress); + OTBR_UNUSED_VARIABLE(aIsAdd); + + return OTBR_ERROR_NONE; +} + +#if OTBR_NETIF_USE_MLD_MONITOR +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, + kIcmpv6Mldv2ModeIsIncludeType = 1, + kIcmpv6Mldv2ModeIsExcludeType = 2, + kIcmpv6Mldv2RecordChangeToIncludeType = 3, + kIcmpv6Mldv2RecordChangeToExcludeType = 4, +}; +#endif // OTBR_NETIF_USE_MLD_MONITOR + Netif::Netif(Dependencies &aDependencies) : mTunFd(-1) , mIpFd(-1) , mNetlinkFd(-1) +#if OTBR_NETIF_USE_MLD_MONITOR + , mMldFd(-1) +#endif , mNetlinkSequence(0) , mNetifIndex(0) , mDeps(aDependencies) @@ -80,6 +124,8 @@ otbrError Netif::Init(const std::string &aInterfaceName) mNetifIndex = if_nametoindex(mNetifName.c_str()); VerifyOrExit(mNetifIndex > 0, error = OTBR_ERROR_INVALID_STATE); + SuccessOrExit(error = InitMldListener()); + PlatformSpecificInit(); exit: @@ -103,10 +149,25 @@ void Netif::Process(const MainloopContext *aContext) DieNow("Error on Tun Fd!"); } +#if OTBR_NETIF_USE_MLD_MONITOR + if (FD_ISSET(mMldFd, &aContext->mErrorFdSet)) + { + close(mMldFd); + DieNow("Error on MLD Fd!"); + } +#endif + if (FD_ISSET(mTunFd, &aContext->mReadFdSet)) { ProcessIp6Send(); } + +#if OTBR_NETIF_USE_MLD_MONITOR + if (FD_ISSET(mMldFd, &aContext->mReadFdSet)) + { + ProcessMldEvent(); + } +#endif } void Netif::UpdateFdSet(MainloopContext *aContext) @@ -114,8 +175,14 @@ void Netif::UpdateFdSet(MainloopContext *aContext) assert(aContext != nullptr); assert(mTunFd >= 0); assert(mIpFd >= 0); +#if OTBR_NETIF_USE_MLD_MONITOR + assert(mMldFd >= 0); +#endif aContext->AddFdToSet(mTunFd, MainloopContext::kErrorFdSet | MainloopContext::kReadFdSet); +#if OTBR_NETIF_USE_MLD_MONITOR + aContext->AddFdToSet(mMldFd, MainloopContext::kErrorFdSet | MainloopContext::kReadFdSet); +#endif } void Netif::UpdateIp6UnicastAddresses(const std::vector &aAddrInfos) @@ -289,9 +356,143 @@ void Netif::Clear(void) mNetlinkFd = -1; } +#if OTBR_NETIF_USE_MLD_MONITOR + if (mMldFd != -1) + { + close(mMldFd); + mMldFd = -1; + } +#endif + mNetifIndex = 0; mIp6UnicastAddresses.clear(); mIp6MulticastAddresses.clear(); } +#if OTBR_NETIF_USE_MLD_MONITOR +static const otIp6Address kMldv2MulticastAddress = { + {{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16}}}; +static const otIp6Address kAllRouterLocalMulticastAddress = { + {{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}}}; + +static bool IsMulAddrFiltered(const otIp6Address &aAddr) +{ + return otIp6IsAddressEqual(&aAddr, &kMldv2MulticastAddress) || + otIp6IsAddressEqual(&aAddr, &kAllRouterLocalMulticastAddress); +} + +otbrError Netif::InitMldListener(void) +{ + otbrError error = OTBR_ERROR_NONE; + struct ipv6_mreq mreq6; + + mMldFd = SocketWithCloseExec(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6, kSocketNonBlock); + VerifyOrExit(mMldFd != -1, error = OTBR_ERROR_ERRNO); + + mreq6.ipv6mr_interface = mNetifIndex; + memcpy(&mreq6.ipv6mr_multiaddr, kMldv2MulticastAddress.mFields.m8, sizeof(kMldv2MulticastAddress.mFields.m8)); + + VerifyOrExit(setsockopt(mMldFd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6, sizeof(mreq6)) == 0, + error = OTBR_ERROR_ERRNO); +#ifdef __linux__ + VerifyOrExit(setsockopt(mMldFd, SOL_SOCKET, SO_BINDTODEVICE, mNetifName.c_str(), + static_cast(mNetifName.length())) == 0, + error = OTBR_ERROR_ERRNO); +#endif + +exit: + return error; +} + +void Netif::ProcessMldEvent(void) +{ + 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(mMldFd, 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(mNetifName.c_str(), ifAddr->ifa_name, IFNAMSIZ) == 0) + { + struct sockaddr_in6 *addr6 = reinterpret_cast(ifAddr->ifa_addr); + + 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]); + + otbrError error = OTBR_ERROR_DROPPED; + otIp6Address address; + + memcpy(&address, &record->mMulticastAddress, sizeof(address)); + if (IsMulAddrFiltered(address)) + { + continue; + } + + inet_ntop(AF_INET6, &record->mMulticastAddress, addressString, sizeof(addressString)); + + switch (record->mRecordType) + { + case kIcmpv6Mldv2ModeIsIncludeType: + case kIcmpv6Mldv2ModeIsExcludeType: + error = OTBR_ERROR_NONE; + break; + case kIcmpv6Mldv2RecordChangeToIncludeType: + error = (record->mNumSources == 0) ? mDeps.Ip6MulAddrUpdateSubscription(address, /* isAdd */ false) + : OTBR_ERROR_NONE; + break; + case kIcmpv6Mldv2RecordChangeToExcludeType: + error = mDeps.Ip6MulAddrUpdateSubscription(address, /* isAdd */ true); + break; + } + + offset += sizeof(Mldv2Record) + sizeof(in6_addr) * ntohs(record->mNumSources); + + if (error != OTBR_ERROR_NONE) + { + otbrLogWarning("Failed to Update multicast subscription: %s", otbrErrorString(error)); + } + } + } + +exit: + if (ifAddrs) + { + freeifaddrs(ifAddrs); + } +} + +#endif // OTBR_NETIF_USE_MLD_MONITOR + } // namespace otbr diff --git a/src/ncp/posix/netif.hpp b/src/ncp/posix/netif.hpp index 8da21c03589..08faf65406c 100644 --- a/src/ncp/posix/netif.hpp +++ b/src/ncp/posix/netif.hpp @@ -46,6 +46,12 @@ namespace otbr { +#if defined(RTM_NEWMADDR) || defined(__NetBSD__) +#define OTBR_NETIF_USE_MLD_MONITOR 0 +#else +#define OTBR_NETIF_USE_MLD_MONITOR 1 +#endif + class Netif { public: @@ -55,6 +61,7 @@ class Netif virtual ~Dependencies(void) = default; virtual otbrError Ip6Send(const uint8_t *aData, uint16_t aLength); + virtual otbrError Ip6MulAddrUpdateSubscription(const otIp6Address &aAddress, bool aIsAdded); }; Netif(Dependencies &aDependencies); @@ -78,16 +85,25 @@ class Netif otbrError CreateTunDevice(const std::string &aInterfaceName); otbrError InitNetlink(void); +#if OTBR_NETIF_USE_MLD_MONITOR + otbrError InitMldListener(void); +#endif void PlatformSpecificInit(void); void SetAddrGenModeToNone(void); void ProcessUnicastAddressChange(const Ip6AddressInfo &aAddressInfo, bool aIsAdded); otbrError ProcessMulticastAddressChange(const Ip6Address &aAddress, bool aIsAdded); void ProcessIp6Send(void); - - int mTunFd; ///< Used to exchange IPv6 packets. - int mIpFd; ///< Used to manage IPv6 stack on the network interface. - int mNetlinkFd; ///< Used to receive netlink events. +#if OTBR_NETIF_USE_MLD_MONITOR + void ProcessMldEvent(void); +#endif + + int mTunFd; ///< Used to exchange IPv6 packets. + int mIpFd; ///< Used to manage IPv6 stack on the network interface. + int mNetlinkFd; ///< Used to receive netlink events. +#if OTBR_NETIF_USE_MLD_MONITOR + int mMldFd; ///< Used to receive MLD events. +#endif uint32_t mNetlinkSequence; ///< Netlink message sequence. unsigned int mNetifIndex; diff --git a/tests/gtest/test_netif.cpp b/tests/gtest/test_netif.cpp index 36ff2f97e89..05f51bbfec2 100644 --- a/tests/gtest/test_netif.cpp +++ b/tests/gtest/test_netif.cpp @@ -380,6 +380,7 @@ TEST(Netif, WpanIfHasCorrectMulticastAddresses_AfterUpdatingMulticastAddresses) const char *kDefaultMulAddr1Str = "ff01::1"; const char *kDefaultMulAddr2Str = "ff02::1"; const char *kDefaultMulAddr3Str = "ff02::2"; + const char *kDefaultMulAddr4Str = "ff02::16"; otbr::Ip6Address kMulAddr1 = { {0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc}}; @@ -394,39 +395,43 @@ TEST(Netif, WpanIfHasCorrectMulticastAddresses_AfterUpdatingMulticastAddresses) std::vector testVec1(testArray1, testArray1 + sizeof(testArray1) / sizeof(otbr::Ip6Address)); netif.UpdateIp6MulticastAddresses(testVec1); std::vector wpanMulAddrs = GetAllIp6MulAddrs(wpan); - EXPECT_EQ(wpanMulAddrs.size(), 4); + EXPECT_EQ(wpanMulAddrs.size(), 5); EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kMulAddr1Str)); EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kDefaultMulAddr1Str)); EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kDefaultMulAddr2Str)); EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kDefaultMulAddr3Str)); + EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kDefaultMulAddr4Str)); otbr::Ip6Address testArray2[] = {kMulAddr1, kMulAddr2}; std::vector testVec2(testArray2, testArray2 + sizeof(testArray2) / sizeof(otbr::Ip6Address)); netif.UpdateIp6MulticastAddresses(testVec2); wpanMulAddrs = GetAllIp6MulAddrs(wpan); - EXPECT_EQ(wpanMulAddrs.size(), 5); + EXPECT_EQ(wpanMulAddrs.size(), 6); EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kMulAddr1Str)); EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kMulAddr2Str)); EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kDefaultMulAddr1Str)); EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kDefaultMulAddr2Str)); EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kDefaultMulAddr3Str)); + EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kDefaultMulAddr4Str)); otbr::Ip6Address testArray3[] = {kDefaultMulAddr1}; std::vector testVec3(testArray3, testArray3 + sizeof(testArray3) / sizeof(otbr::Ip6Address)); netif.UpdateIp6MulticastAddresses(testVec3); wpanMulAddrs = GetAllIp6MulAddrs(wpan); - EXPECT_EQ(wpanMulAddrs.size(), 3); + EXPECT_EQ(wpanMulAddrs.size(), 4); EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kDefaultMulAddr1Str)); EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kDefaultMulAddr2Str)); EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kDefaultMulAddr3Str)); + EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kDefaultMulAddr4Str)); std::vector empty; netif.UpdateIp6MulticastAddresses(empty); wpanMulAddrs = GetAllIp6MulAddrs(wpan); - EXPECT_EQ(wpanMulAddrs.size(), 3); + EXPECT_EQ(wpanMulAddrs.size(), 4); EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kDefaultMulAddr1Str)); EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kDefaultMulAddr2Str)); EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kDefaultMulAddr3Str)); + EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kDefaultMulAddr4Str)); netif.Deinit(); } @@ -613,4 +618,108 @@ TEST(Netif, WpanIfSendIp6PacketCorrectly_AfterReceivingOnIf) netif.Deinit(); } + +class NetifDependencyTestMulSub : public otbr::Netif::Dependencies +{ +public: + NetifDependencyTestMulSub(bool &aReceived, otIp6Address &aMulAddr, bool &aIsAdded) + : mReceived(aReceived) + , mMulAddr(aMulAddr) + , mIsAdded(aIsAdded) + { + } + + otbrError Ip6MulAddrUpdateSubscription(const otIp6Address &aAddress, bool aIsAdded) override + { + mMulAddr = aAddress; + mIsAdded = aIsAdded; + mReceived = true; + return OTBR_ERROR_NONE; + } + + bool &mReceived; + otIp6Address &mMulAddr; + bool &mIsAdded; +}; + +TEST(Netif, WpanIfUpdateMulAddrSubscription_AfterAppJoiningMulGrp) +{ + bool received = false; + otIp6Address subscribedMulAddr; + bool isAdded = false; + NetifDependencyTestMulSub dependency(received, subscribedMulAddr, isAdded); + const char *multicastGroup = "ff99::1"; + const char *wpan = "wpan0"; + int sockFd; + otbr::Netif netif(dependency); + const otIp6Address expectedMulAddr = {0xff, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; + + EXPECT_EQ(netif.Init("wpan0"), OT_ERROR_NONE); + + const otIp6Address kLl = { + {0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x14, 0x03, 0x32, 0x4c, 0xc2, 0xf8, 0xd0}}; + std::vector addrs = { + {kLl, 64, 0, 1, 0}, + }; + netif.UpdateIp6UnicastAddresses(addrs); + netif.SetNetifState(true); + + { + struct ipv6_mreq mreq; + struct sockaddr_in6 addr; + + if ((sockFd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) + { + perror("socket creation failed"); + exit(EXIT_FAILURE); + } + + memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_addr = in6addr_any; + addr.sin6_port = htons(9999); + + if (bind(sockFd, (const struct sockaddr *)&addr, sizeof(addr)) < 0) + { + perror("bind failed"); + exit(EXIT_FAILURE); + } + + inet_pton(AF_INET6, multicastGroup, &(mreq.ipv6mr_multiaddr)); + mreq.ipv6mr_interface = if_nametoindex(wpan); + + if (setsockopt(sockFd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) < 0) + { + perror("Error joining multicast group"); + exit(EXIT_FAILURE); + } + } + + otbr::MainloopContext context; + while (!received) + { + context.mMaxFd = -1; + context.mTimeout = {100, 0}; + FD_ZERO(&context.mReadFdSet); + FD_ZERO(&context.mWriteFdSet); + FD_ZERO(&context.mErrorFdSet); + + netif.UpdateFdSet(&context); + int rval = select(context.mMaxFd + 1, &context.mReadFdSet, &context.mWriteFdSet, &context.mErrorFdSet, + &context.mTimeout); + if (rval < 0) + { + perror("select failed"); + exit(EXIT_FAILURE); + } + netif.Process(&context); + } + + EXPECT_EQ(otbr::Ip6Address(subscribedMulAddr), otbr::Ip6Address(expectedMulAddr)); + EXPECT_EQ(isAdded, true); + close(sockFd); + netif.Deinit(); +} + #endif // __linux__