diff --git a/src/ncp/posix/CMakeLists.txt b/src/ncp/posix/CMakeLists.txt index 1f03fd3c18c..7b8557bb1ce 100644 --- a/src/ncp/posix/CMakeLists.txt +++ b/src/ncp/posix/CMakeLists.txt @@ -27,6 +27,8 @@ # add_library(otbr-posix + infra_if.hpp + infra_if.cpp netif.cpp netif_linux.cpp netif_unix.cpp diff --git a/src/ncp/posix/infra_if.cpp b/src/ncp/posix/infra_if.cpp new file mode 100644 index 00000000000..33071a8f23a --- /dev/null +++ b/src/ncp/posix/infra_if.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2024, 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. + */ + +#define OTBR_LOG_TAG "INFRAIF" + +#include "infra_if.hpp" + +#include +#ifdef __linux__ +#include +#include +#endif +#include +#include + +#include "utils/socket_utils.hpp" + +namespace otbr { + +otbrError InfraIf::Dependencies::SetInfraIf(unsigned int aInfraIfIndex, + bool aIsRunning, + const std::vector &aIp6Addresses) +{ + OTBR_UNUSED_VARIABLE(aInfraIfIndex); + OTBR_UNUSED_VARIABLE(aIsRunning); + OTBR_UNUSED_VARIABLE(aIp6Addresses); + + return OTBR_ERROR_NONE; +} + +InfraIf::InfraIf(Dependencies &aDependencies) + : mDeps(aDependencies) + , mInfraIfIndex(0) +{ +} + +void InfraIf::Init(void) +{ +} + +void InfraIf::Deinit(void) +{ + mInfraIfIndex = 0; +} + +otbrError InfraIf::SetInfraIf(const char *aIfName) +{ + otbrError error = OTBR_ERROR_NONE; + std::vector addresses; + + VerifyOrExit(aIfName != nullptr && strlen(aIfName) > 0, error = OTBR_ERROR_INVALID_ARGS); + VerifyOrExit(strnlen(aIfName, IFNAMSIZ) < IFNAMSIZ, error = OTBR_ERROR_INVALID_ARGS); + strcpy(mInfraIfName, aIfName); + + mInfraIfIndex = if_nametoindex(aIfName); + VerifyOrExit(mInfraIfIndex != 0, error = OTBR_ERROR_INVALID_STATE); + + addresses = GetAddresses(); + + SuccessOrExit(mDeps.SetInfraIf(mInfraIfIndex, IsRunning(addresses), addresses), error = OTBR_ERROR_OPENTHREAD); +exit: + otbrLogResult(error, "SetInfraIf"); + + return error; +} + +bool InfraIf::IsRunning(const std::vector &aAddrs) const +{ + return mInfraIfIndex ? ((GetFlags() & IFF_RUNNING) && HasLinkLocalAddress(aAddrs)) : false; +} + +short InfraIf::GetFlags(void) const +{ + int sock; + struct ifreq ifReq; + + sock = SocketWithCloseExec(AF_INET6, SOCK_DGRAM, IPPROTO_IP, kSocketBlock); + VerifyOrDie(sock != -1, otbrErrorString(OTBR_ERROR_ERRNO)); + + memset(&ifReq, 0, sizeof(ifReq)); + strcpy(ifReq.ifr_name, mInfraIfName); + + if (ioctl(sock, SIOCGIFFLAGS, &ifReq) == -1) + { + otbrLogCrit("The infra link %s may be lost. Exiting.", mInfraIfName); + DieNow(otbrErrorString(OTBR_ERROR_ERRNO)); + } + + close(sock); + + return ifReq.ifr_flags; +} + +std::vector InfraIf::GetAddresses(void) +{ + struct ifaddrs *ifAddrs = nullptr; + std::vector addrs; + + if (getifaddrs(&ifAddrs) < 0) + { + otbrLogCrit("failed to get netif addresses: %s", strerror(errno)); + ExitNow(); + } + + for (struct ifaddrs *addr = ifAddrs; addr != nullptr; addr = addr->ifa_next) + { + struct sockaddr_in6 *ip6Addr; + + if (strncmp(addr->ifa_name, mInfraIfName, sizeof(mInfraIfName)) != 0 || addr->ifa_addr == nullptr || + addr->ifa_addr->sa_family != AF_INET6) + { + continue; + } + + ip6Addr = reinterpret_cast(addr->ifa_addr); + addrs.emplace_back(*reinterpret_cast(&ip6Addr->sin6_addr)); + } + + freeifaddrs(ifAddrs); + +exit: + return addrs; +} + +bool InfraIf::HasLinkLocalAddress(const std::vector &aAddrs) +{ + bool hasLla = false; + + for (const Ip6Address &otAddr : aAddrs) + { + if (IN6_IS_ADDR_LINKLOCAL(reinterpret_cast(&otAddr))) + { + hasLla = true; + break; + } + } + + return hasLla; +} + +} // namespace otbr diff --git a/src/ncp/posix/infra_if.hpp b/src/ncp/posix/infra_if.hpp new file mode 100644 index 00000000000..17c38abf4b6 --- /dev/null +++ b/src/ncp/posix/infra_if.hpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2024, 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 includes definitions of the Infrastructure network interface of otbr-agent. + */ + +#ifndef OTBR_AGENT_POSIX_INFRA_IF_HPP_ +#define OTBR_AGENT_POSIX_INFRA_IF_HPP_ + +#include + +#include + +#include + +#include "common/types.hpp" + +namespace otbr { + +/** + * Host infrastructure network interface module. + * + * The infrastructure network interface MUST be explicitly set by `SetInfraIf` before the InfraIf module can work. + * + */ +class InfraIf +{ +public: + class Dependencies + { + public: + virtual otbrError SetInfraIf(unsigned int aInfraIfIndex, + bool aIsRunning, + const std::vector &aIp6Addresses); + }; + + InfraIf(Dependencies &aDependencies); + + void Init(void); + void Deinit(void); + otbrError SetInfraIf(const char *aIfName); + +private: + bool IsRunning(const std::vector &aAddrs) const; + short GetFlags(void) const; + std::vector GetAddresses(void); + static bool HasLinkLocalAddress(const std::vector &aAddrs); + + Dependencies &mDeps; + char mInfraIfName[IFNAMSIZ]; + unsigned int mInfraIfIndex; +}; + +} // namespace otbr + +#endif // OTBR_AGENT_POSIX_INFRA_IF_HPP_ diff --git a/tests/gtest/CMakeLists.txt b/tests/gtest/CMakeLists.txt index f9674ed6946..4f26ecc9e3f 100644 --- a/tests/gtest/CMakeLists.txt +++ b/tests/gtest/CMakeLists.txt @@ -75,6 +75,7 @@ if(OTBR_MDNS) endif() add_executable(otbr-posix-gtest-unit + test_infra_if.cpp test_netif.cpp ) target_link_libraries(otbr-posix-gtest-unit diff --git a/tests/gtest/test_infra_if.cpp b/tests/gtest/test_infra_if.cpp new file mode 100644 index 00000000000..87129e4536d --- /dev/null +++ b/tests/gtest/test_infra_if.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2024, 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. + */ + +#include +#include + +#include "ncp/posix/infra_if.hpp" +#include "ncp/posix/netif.hpp" + +// Only Test on linux platform for now. +#ifdef __linux__ + +class InfraIfDependencyTest : public otbr::InfraIf::Dependencies +{ +public: + InfraIfDependencyTest(void) + : mInfraIfIndex(0) + , mIsRunning(false) + { + } + + otbrError SetInfraIf(unsigned int aInfraIfIndex, + bool aIsRunning, + const std::vector &aIp6Addresses) override + { + mInfraIfIndex = aInfraIfIndex; + mIsRunning = aIsRunning; + mIp6Addresses = aIp6Addresses; + + return OTBR_ERROR_NONE; + } + + unsigned int mInfraIfIndex; + bool mIsRunning; + std::vector mIp6Addresses; +}; + +TEST(InfraIf, DepsSetInfraIfInvokedCorrectly_AfterSpecifyingInfraIf) +{ + const std::string fakeInfraIf = "wlx123"; + + // Utilize the Netif module to create a network interface as the fake infrastructure interface. + otbr::Netif::Dependencies defaultNetifDep; + otbr::Netif netif(defaultNetifDep); + EXPECT_EQ(netif.Init(fakeInfraIf), OTBR_ERROR_NONE); + + const otIp6Address kTestAddr = { + {0xfd, 0x35, 0x7a, 0x7d, 0x0f, 0x16, 0xe7, 0xe3, 0x73, 0xf3, 0x09, 0x00, 0x8e, 0xbe, 0x1b, 0x65}}; + std::vector addrs = { + {kTestAddr, 64, 0, 1, 0}, + }; + netif.UpdateIp6UnicastAddresses(addrs); + + InfraIfDependencyTest testInfraIfDep; + otbr::InfraIf infraIf(testInfraIfDep); + EXPECT_EQ(infraIf.SetInfraIf(fakeInfraIf.c_str()), OTBR_ERROR_NONE); + + EXPECT_NE(testInfraIfDep.mInfraIfIndex, 0); + EXPECT_EQ(testInfraIfDep.mIsRunning, false); + EXPECT_EQ(testInfraIfDep.mIp6Addresses.size(), 1); + EXPECT_THAT(testInfraIfDep.mIp6Addresses, ::testing::Contains(otbr::Ip6Address(kTestAddr))); +} + +#endif // __linux__