/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <ifaddrs.h> #include <net/if.h> #include <sys/types.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <netdutils/MockSyscalls.h> #include "InterfaceController.h" using testing::ByMove; using testing::Invoke; using testing::Return; using testing::StrictMock; using testing::_; namespace android { namespace net { namespace { using netdutils::Fd; using netdutils::ScopedMockSyscalls; using netdutils::Slice; using netdutils::Status; using netdutils::StatusOr; using netdutils::UniqueFd; using netdutils::makeSlice; using netdutils::status::ok; using netdutils::statusFromErrno; constexpr Fd kDevRandomFd(777); constexpr Fd kStableSecretFd(9999); const char kDevRandomPath[] = "/dev/random"; const char kTestIface[] = "wlan5"; const char kStableSecretProperty[] = "persist.netd.stable_secret"; const char kStableSecretPath[] = "/proc/sys/net/ipv6/conf/wlan5/stable_secret"; const char kTestIPv6Address[] = "\x20\x01\x0d\xb8\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10"; const char kTestIPv6AddressString[] = "2001:db8:506:708:90a:b0c:d0e:f10"; // getProperty() and setProperty() are forwarded to this mock class MockProperties { public: MOCK_CONST_METHOD2(get, std::string(const std::string& key, const std::string& dflt)); MOCK_CONST_METHOD2(set, Status(const std::string& key, const std::string& val)); }; } // namespace class StablePrivacyTest : public testing::Test { protected: void expectOpenFile(const std::string& path, const Fd fd, int err) { if (err == 0) { EXPECT_CALL(mSyscalls, open(path, _, _)).WillOnce(Return(ByMove(UniqueFd(fd)))); EXPECT_CALL(mSyscalls, close(fd)).WillOnce(Return(ok)); } else { EXPECT_CALL(mSyscalls, open(path, _, _)) .WillOnce(Return(ByMove(statusFromErrno(err, "open() failed")))); } } void expectReadFromDevRandom(const std::string& data) { expectOpenFile(kDevRandomPath, kDevRandomFd, 0); EXPECT_CALL(mSyscalls, read(kDevRandomFd, _)).WillOnce(Invoke([data](Fd, const Slice buf) { EXPECT_EQ(data.size(), buf.size()); return take(buf, copy(buf, makeSlice(data))); })); } void expectGetPropertyDefault(const std::string& key) { EXPECT_CALL(mProperties, get(key, _)) .WillOnce(Invoke([](const std::string&, const std::string& dflt) { return dflt; })); } void expectGetProperty(const std::string& key, const std::string& val) { EXPECT_CALL(mProperties, get(key, _)) .WillOnce(Invoke([val](const std::string&, const std::string&) { return val; })); } void expectSetProperty(const std::string& key, const std::string& val, Status status) { EXPECT_CALL(mProperties, set(key, val)).WillOnce(Return(status)); } void expectWriteToFile(const Fd fd, const std::string& val, int err) { EXPECT_CALL(mSyscalls, write(fd, _)) .WillOnce(Invoke([val, err](Fd, const Slice buf) -> StatusOr<size_t> { EXPECT_EQ(val, toString(buf)); if (err) { return statusFromErrno(err, "write() failed"); } return val.size(); })); } Status enableStablePrivacyAddresses(const std::string& iface) { return InterfaceController::enableStablePrivacyAddresses(iface, mGet, mSet); } StrictMock<ScopedMockSyscalls> mSyscalls; StrictMock<MockProperties> mProperties; const std::function<std::string(const std::string&, const std::string&)> mGet = [this](const std::string& key, const std::string& dflt) { return mProperties.get(key, dflt); }; const std::function<Status(const std::string&, const std::string&)> mSet = [this](const std::string& key, const std::string& val) { return mProperties.set(key, val); }; }; TEST_F(StablePrivacyTest, PropertyOpenEnoent) { expectOpenFile(kStableSecretPath, kStableSecretFd, ENOENT); EXPECT_NE(ok, enableStablePrivacyAddresses(kTestIface)); } TEST_F(StablePrivacyTest, PropertyOpenEaccess) { expectOpenFile(kStableSecretPath, kStableSecretFd, EACCES); EXPECT_NE(ok, enableStablePrivacyAddresses(kTestIface)); } TEST_F(StablePrivacyTest, FirstBootWriteOkSetPropertyOk) { expectOpenFile(kStableSecretPath, kStableSecretFd, 0); expectGetPropertyDefault(kStableSecretProperty); expectReadFromDevRandom(kTestIPv6Address); expectWriteToFile(kStableSecretFd, kTestIPv6AddressString, 0); expectSetProperty(kStableSecretProperty, kTestIPv6AddressString, ok); EXPECT_EQ(ok, enableStablePrivacyAddresses(kTestIface)); } TEST_F(StablePrivacyTest, FirstBootWriteOkSetPropertyFail) { const auto kError = statusFromErrno(EINVAL, ""); expectOpenFile(kStableSecretPath, kStableSecretFd, 0); expectGetPropertyDefault(kStableSecretProperty); expectReadFromDevRandom(kTestIPv6Address); expectWriteToFile(kStableSecretFd, kTestIPv6AddressString, 0); expectSetProperty(kStableSecretProperty, kTestIPv6AddressString, kError); EXPECT_EQ(kError, enableStablePrivacyAddresses(kTestIface)); } TEST_F(StablePrivacyTest, FirstBootWriteFail) { expectOpenFile(kStableSecretPath, kStableSecretFd, 0); expectGetPropertyDefault(kStableSecretProperty); expectReadFromDevRandom(kTestIPv6Address); expectWriteToFile(kStableSecretFd, kTestIPv6AddressString, ENOSPC); EXPECT_NE(ok, enableStablePrivacyAddresses(kTestIface)); } TEST_F(StablePrivacyTest, ExistingPropertyWriteOk) { expectOpenFile(kStableSecretPath, kStableSecretFd, 0); expectGetProperty(kStableSecretProperty, kTestIPv6AddressString); expectWriteToFile(kStableSecretFd, kTestIPv6AddressString, 0); EXPECT_EQ(ok, enableStablePrivacyAddresses(kTestIface)); } TEST_F(StablePrivacyTest, ExistingPropertyWriteFail) { expectOpenFile(kStableSecretPath, kStableSecretFd, 0); expectGetProperty(kStableSecretProperty, kTestIPv6AddressString); expectWriteToFile(kStableSecretFd, kTestIPv6AddressString, EACCES); EXPECT_NE(ok, enableStablePrivacyAddresses(kTestIface)); } class GetIfaceListTest : public testing::Test {}; TEST_F(GetIfaceListTest, IfaceNames) { StatusOr<std::vector<std::string>> ifaceNames = InterfaceController::getIfaceNames(); EXPECT_EQ(ok, ifaceNames.status()); struct ifaddrs *ifaddr, *ifa; EXPECT_EQ(0, getifaddrs(&ifaddr)); for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { const auto val = std::find( ifaceNames.value().begin(), ifaceNames.value().end(), ifa->ifa_name); EXPECT_NE(ifaceNames.value().end(), val); } freeifaddrs(ifaddr); } TEST_F(GetIfaceListTest, IfaceExist) { StatusOr<std::map<std::string, uint32_t>> ifaceMap = InterfaceController::getIfaceList(); EXPECT_EQ(ok, ifaceMap.status()); struct ifaddrs *ifaddr, *ifa; EXPECT_EQ(0, getifaddrs(&ifaddr)); for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { uint32_t ifaceIndex = if_nametoindex(ifa->ifa_name); const auto ifacePair = ifaceMap.value().find(ifa->ifa_name); EXPECT_NE(ifaceMap.value().end(), ifacePair); EXPECT_EQ(ifaceIndex, ifacePair->second); } freeifaddrs(ifaddr); } } // namespace net } // namespace android