diff --git a/src/ncp/posix/netif.cpp b/src/ncp/posix/netif.cpp index 90ff4f20cec..30332bf4d0c 100644 --- a/src/ncp/posix/netif.cpp +++ b/src/ncp/posix/netif.cpp @@ -114,6 +114,65 @@ void Netif::UpdateIp6UnicastAddresses(const std::vector &aAddrIn mIp6UnicastAddresses.assign(aAddrInfos.begin(), aAddrInfos.end()); } +otbrError Netif::UpdateIp6MulticastAddresses(const std::vector &aAddrs) +{ + otbrError error = OTBR_ERROR_NONE; + + // Remove stale addresses + for (const Ip6Address &address : mIp6MulticastAddresses) + { + if (std::find(aAddrs.begin(), aAddrs.end(), address) == aAddrs.end()) + { + otbrLogInfo("Remove address: %s", Ip6Address(address).ToString().c_str()); + SuccessOrExit(error = ProcessMulticastAddressChange(address, /* aIsAdded */ false)); + } + } + + // Add new addresses + for (const Ip6Address &address : aAddrs) + { + if (std::find(mIp6MulticastAddresses.begin(), mIp6MulticastAddresses.end(), address) == + mIp6MulticastAddresses.end()) + { + otbrLogInfo("Add address: %s", Ip6Address(address).ToString().c_str()); + SuccessOrExit(error = ProcessMulticastAddressChange(address, /* aIsAdded */ true)); + } + } + + mIp6MulticastAddresses.assign(aAddrs.begin(), aAddrs.end()); + +exit: + if (error != OTBR_ERROR_NONE) + { + mIp6MulticastAddresses.clear(); + } + return error; +} + +otbrError Netif::ProcessMulticastAddressChange(const Ip6Address &aAddress, bool aIsAdded) +{ + struct ipv6_mreq mreq; + otbrError error = OTBR_ERROR_NONE; + int err; + + VerifyOrExit(mIpFd >= 0, error = OTBR_ERROR_INVALID_STATE); + memcpy(&mreq.ipv6mr_multiaddr, &aAddress, sizeof(mreq.ipv6mr_multiaddr)); + mreq.ipv6mr_interface = mNetifIndex; + + err = setsockopt(mIpFd, IPPROTO_IPV6, (aIsAdded ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP), &mreq, sizeof(mreq)); + + if (err != 0) + { + otbrLogWarning("%s failure (%d)", aIsAdded ? "IPV6_JOIN_GROUP" : "IPV6_LEAVE_GROUP", errno); + ExitNow(error = OTBR_ERROR_ERRNO); + } + + otbrLogInfo("%s multicast address %s", aIsAdded ? "Added" : "Removed", Ip6Address(aAddress).ToString().c_str()); + +exit: + return error; +} + void Netif::Clear(void) { if (mTunFd != -1) @@ -135,6 +194,8 @@ void Netif::Clear(void) } mNetifIndex = 0; + mIp6UnicastAddresses.clear(); + mIp6MulticastAddresses.clear(); } } // namespace otbr diff --git a/src/ncp/posix/netif.hpp b/src/ncp/posix/netif.hpp index 24a82439411..84eb7ac098d 100644 --- a/src/ncp/posix/netif.hpp +++ b/src/ncp/posix/netif.hpp @@ -52,7 +52,8 @@ class Netif otbrError Init(const std::string &aInterfaceName); void Deinit(void); - void UpdateIp6UnicastAddresses(const std::vector &aAddrInfos); + void UpdateIp6UnicastAddresses(const std::vector &aOtAddrInfos); + otbrError UpdateIp6MulticastAddresses(const std::vector &aAddrs); private: // TODO: Retrieve the Maximum Ip6 size from the coprocessor. @@ -63,9 +64,10 @@ class Netif otbrError CreateTunDevice(const std::string &aInterfaceName); otbrError InitNetlink(void); - void PlatformSpecificInit(void); - void SetAddrGenModeToNone(void); - void ProcessUnicastAddressChange(const Ip6AddressInfo &aAddressInfo, bool aIsAdded); + void PlatformSpecificInit(void); + void SetAddrGenModeToNone(void); + void ProcessUnicastAddressChange(const Ip6AddressInfo &aAddressInfo, bool aIsAdded); + otbrError ProcessMulticastAddressChange(const Ip6Address &aAddress, bool aIsAdded); int mTunFd; ///< Used to exchange IPv6 packets. int mIpFd; ///< Used to manage IPv6 stack on the network interface. @@ -76,6 +78,7 @@ class Netif std::string mNetifName; std::vector mIp6UnicastAddresses; + std::vector mIp6MulticastAddresses; }; } // namespace otbr diff --git a/tests/gtest/test_netif.cpp b/tests/gtest/test_netif.cpp index 6925a0941da..ee4fcef0567 100644 --- a/tests/gtest/test_netif.cpp +++ b/tests/gtest/test_netif.cpp @@ -98,6 +98,69 @@ std::vector GetAllIp6Addrs(const char *aInterfaceName) return ip6Addrs; } +static int ParseHex(char *aStr, unsigned char *aAddr) +{ + int len = 0; + + while (*aStr) + { + int tmp; + if (aStr[1] == 0) + { + return -1; + } + if (sscanf(aStr, "%02x", &tmp) != 1) + { + return -1; + } + aAddr[len] = tmp; + len++; + aStr += 2; + } + + return len; +} + +std::vector GetAllIp6MulAddrs(const char *aInterfaceName) +{ + const char *kPathIgmp6 = "/proc/net/igmp6"; + std::string line; + std::vector ip6MulAddrs; + + std::ifstream file(kPathIgmp6); + if (!file.is_open()) + { + perror("Cannot open IGMP6 file"); + exit(EXIT_FAILURE); + } + + while (std::getline(file, line)) + { + char interfaceName[256] = {0}; + char hexa[256] = {0}; + int index; + int users; + unsigned char addr[16]; + + sscanf(line.c_str(), "%d%s%s%d", &index, interfaceName, hexa, &users); + if (strcmp(interfaceName, aInterfaceName) == 0) + { + char addrStr[INET6_ADDRSTRLEN]; + ParseHex(hexa, addr); + if (inet_ntop(AF_INET6, addr, addrStr, sizeof(addrStr)) == NULL) + { + perror("inet_ntop"); + exit(EXIT_FAILURE); + } + ip6MulAddrs.emplace_back(addrStr); + } + } + + file.close(); + + return ip6MulAddrs; +} + TEST(Netif, WpanInitWithFullInterfaceName) { const char *wpan = "wpan0"; @@ -297,4 +360,66 @@ TEST(Netif, WpanIfHasCorrectUnicastAddresses_AfterUpdatingUnicastAddresses) netif.Deinit(); } + +TEST(Netif, WpanIfHasCorrectMulticastAddresses_AfterUpdatingMulticastAddresses) +{ + const char *wpan = "wpan0"; + otbr::Netif netif; + EXPECT_EQ(netif.Init(wpan), OT_ERROR_NONE); + + otbr::Ip6Address kDefaultMulAddr1 = { + {0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}; + const char *kDefaultMulAddr1Str = "ff01::1"; + const char *kDefaultMulAddr2Str = "ff02::1"; + const char *kDefaultMulAddr3Str = "ff02::2"; + + otbr::Ip6Address kMulAddr1 = { + {0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc}}; + otbr::Ip6Address kMulAddr2 = { + {0xff, 0x32, 0x00, 0x40, 0xfd, 0x0d, 0x07, 0xfc, 0xa1, 0xb9, 0xf0, 0x50, 0x00, 0x00, 0x00, 0x01}}; + const char *kMulAddr1Str = "ff03::fc"; + const char *kMulAddr2Str = "ff32:40:fd0d:7fc:a1b9:f050:0:1"; + + otbr::Ip6Address testArray1[] = { + kMulAddr1, + }; + std::vector testVec1(testArray1, testArray1 + sizeof(testArray1) / sizeof(otbr::Ip6Address)); + netif.UpdateIp6MulticastAddresses(testVec1); + std::vector wpanMulAddrs = GetAllIp6MulAddrs(wpan); + EXPECT_EQ(wpanMulAddrs.size(), 4); + EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kMulAddr1Str)); + EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kDefaultMulAddr1Str)); + EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kDefaultMulAddr2Str)); + EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kDefaultMulAddr3Str)); + + 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_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)); + + 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_THAT(wpanMulAddrs, ::testing::Contains(kDefaultMulAddr1Str)); + EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kDefaultMulAddr2Str)); + EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kDefaultMulAddr3Str)); + + std::vector empty; + netif.UpdateIp6MulticastAddresses(empty); + wpanMulAddrs = GetAllIp6MulAddrs(wpan); + EXPECT_EQ(wpanMulAddrs.size(), 3); + EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kDefaultMulAddr1Str)); + EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kDefaultMulAddr2Str)); + EXPECT_THAT(wpanMulAddrs, ::testing::Contains(kDefaultMulAddr3Str)); + + netif.Deinit(); +} #endif // __linux__