diff options
54 files changed, 1043 insertions, 331 deletions
@@ -85,14 +85,13 @@ cc_defaults { "-performance-noexcept-move-constructor", "-performance-unnecessary-value-param", ], - tidy_flags: [ - "-warnings-as-errors=" - + "android-*," - + "bugprone-*," - + "cert-*," - + "clang-analyzer-security*," - + "google-*," - + "misc-*," - + "performance-*" + tidy_checks_as_errors: [ + "android-*", + "bugprone-*", + "cert-*", + "clang-analyzer-security*", + "google-*", + "misc-*", + "performance-*", ], } @@ -1,3 +1,4 @@ +# Bug component: 31808 set noparent file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking diff --git a/TEST_MAPPING b/TEST_MAPPING index 4a9fa190..4e5a0471 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -3,6 +3,7 @@ { "name": "netd_integration_test" }, { "name": "netd_unit_test" }, { "name": "netdclient_test" }, + { "name": "netdclient_root_test" }, { "name": "netdutils_test" } ], "postsubmit": [ @@ -14,8 +15,11 @@ "imports": [ { "path": "packages/modules/DnsResolver" } ], - "hwasan-postsubmit": [ + "hwasan-presubmit": [ { "name": "netd_integration_test" }, - { "name": "netd_unit_test" } + { "name": "netd_unit_test" }, + { "name": "netdclient_test" }, + { "name": "netdclient_root_test" }, + { "name": "netdutils_test" } ] } diff --git a/client/Android.bp b/client/Android.bp index 22cb5c15..528b3548 100644 --- a/client/Android.bp +++ b/client/Android.bp @@ -65,3 +65,28 @@ cc_test { recover: [ "all" ], }, } + +cc_test { + name: "netdclient_root_test", + require_root: true, // for ScopedUidChange + srcs: [ + "NetdClientRootTest.cpp", + ], + defaults: [ + "netd_aidl_interface_lateststable_cpp_static", + "netd_defaults", + ], + test_suites: ["device-tests"], + include_dirs: [ + "system/netd/include", + ], + static_libs: [ + "libbase", + "libnetd_client", + "libnetd_test_utils", + ], + sanitize: { + address: false, + recover: [ "all" ], + }, +} diff --git a/client/NetdClientRootTest.cpp b/client/NetdClientRootTest.cpp new file mode 100644 index 00000000..3ecbcef6 --- /dev/null +++ b/client/NetdClientRootTest.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022 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 <android-base/unique_fd.h> +#include <gtest/gtest.h> + +#include "NetdClient.h" +#include "android/net/INetd.h" +#include "netid_client.h" +#include "test_utils.h" + +constexpr int TEST_UID1 = 99999; + +TEST(NetdClientTest, setSocketToInvalidNetwork) { + const android::base::unique_fd s(socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0)); + ASSERT_LE(0, s); + + unsigned netId = NETID_UNSET; + const ScopedUidChange scopedUidChange(TEST_UID1); + EXPECT_EQ(-EACCES, setNetworkForSocket(android::net::INetd::LOCAL_NET_ID, s)); + EXPECT_EQ(0, getNetworkForSocket(&netId, s)); + EXPECT_EQ(static_cast<unsigned>(NETID_UNSET), netId); +} diff --git a/server/Android.bp b/server/Android.bp index 06ea0bab..615e740f 100644 --- a/server/Android.bp +++ b/server/Android.bp @@ -97,6 +97,7 @@ cc_binary { "system/netd/include", ], init_rc: ["netd.rc"], + vintf_fragments: ["android.system.net.netd-service.xml"], required: [ "bpfloader", ], @@ -104,8 +105,10 @@ cc_binary { shared_libs: [ "android.system.net.netd@1.0", "android.system.net.netd@1.1", + "android.system.net.netd-V1-ndk", "libbase", "libbinder", + "libbinder_ndk", "libcutils", "libdl", "libhidlbase", @@ -138,6 +141,7 @@ cc_binary { "MDnsSdListener.cpp", "MDnsService.cpp", "NetdCommand.cpp", + "NetdHwAidlService.cpp", "NetdHwService.cpp", "NetdNativeService.cpp", "NetlinkHandler.cpp", diff --git a/server/Controllers.cpp b/server/Controllers.cpp index 00ee186d..43a2d1ee 100644 --- a/server/Controllers.cpp +++ b/server/Controllers.cpp @@ -131,7 +131,7 @@ std::set<std::string> Controllers::findExistingChildChains(const IptablesTarget std::string command = StringPrintf("*%s\n-S %s\nCOMMIT\n", table, parentChain); std::string output; if (Controllers::execIptablesRestoreWithOutput(target, command, &output) == -1) { - ALOGE("Error listing chain %s in table %s\n", parentChain, table); + ALOGE("Error listing chain %s in table %s", parentChain, table); return existing; } diff --git a/server/DummyNetwork.h b/server/DummyNetwork.h index 8f9960b2..e699dada 100644 --- a/server/DummyNetwork.h +++ b/server/DummyNetwork.h @@ -25,6 +25,7 @@ class DummyNetwork : public Network { static const char* INTERFACE_NAME; explicit DummyNetwork(unsigned netId); virtual ~DummyNetwork(); + Permission getPermission() const { return PERMISSION_SYSTEM; }; private: std::string getTypeString() const override { return "DUMMY"; }; diff --git a/server/FirewallController.h b/server/FirewallController.h index 6d6f48fa..1bff064c 100644 --- a/server/FirewallController.h +++ b/server/FirewallController.h @@ -52,8 +52,6 @@ public: /* Match traffic owned by given UID. This is specific to a particular chain. */ int setUidRule(ChildChain, int, FirewallRule); - int enableChildChains(ChildChain, bool); - static std::string makeCriticalCommands(IptablesTarget target, const char* chainName); static const char* TABLE; diff --git a/server/InterfaceControllerTest.cpp b/server/InterfaceControllerTest.cpp index 006018d7..8075431f 100644 --- a/server/InterfaceControllerTest.cpp +++ b/server/InterfaceControllerTest.cpp @@ -22,6 +22,7 @@ #include <gtest/gtest.h> #include <netdutils/MockSyscalls.h> +#include <netdutils/NetNativeTestBase.h> #include <netdutils/Utils.h> #include "InterfaceController.h" @@ -66,7 +67,7 @@ class MockProperties { } // namespace -class StablePrivacyTest : public testing::Test { +class StablePrivacyTest : public NetNativeTestBase { protected: void expectOpenFile(const std::string& path, const Fd fd, int err) { if (err == 0) { @@ -179,7 +180,7 @@ TEST_F(StablePrivacyTest, ExistingPropertyWriteFail) { EXPECT_NE(ok, enableStablePrivacyAddresses(kTestIface)); } -class GetIfaceListTest : public testing::Test {}; +class GetIfaceListTest : public NetNativeTestBase {}; TEST_F(GetIfaceListTest, IfaceNames) { StatusOr<std::vector<std::string>> ifaceNames = getIfaceNames(); diff --git a/server/IptablesBaseTest.cpp b/server/IptablesBaseTest.cpp index ef4d7434..dc70ae7c 100644 --- a/server/IptablesBaseTest.cpp +++ b/server/IptablesBaseTest.cpp @@ -105,7 +105,7 @@ void IptablesBaseTest::expectIptablesRestoreCommands(const std::vector<std::stri } void IptablesBaseTest::expectIptablesRestoreCommands(const ExpectedIptablesCommands& expectedCmds) { - EXPECT_EQ(expectedCmds.size(), sRestoreCmds.size()); + ASSERT_EQ(expectedCmds.size(), sRestoreCmds.size()); for (size_t i = 0; i < expectedCmds.size(); i++) { EXPECT_EQ(expectedCmds[i], sRestoreCmds[i]) << "iptables-restore command " << i << " differs"; diff --git a/server/IptablesBaseTest.h b/server/IptablesBaseTest.h index abe3f84a..bfcc71a1 100644 --- a/server/IptablesBaseTest.h +++ b/server/IptablesBaseTest.h @@ -18,9 +18,11 @@ #include <deque> +#include <netdutils/NetNativeTestBase.h> + #include "NetdConstants.h" -class IptablesBaseTest : public ::testing::Test { +class IptablesBaseTest : public NetNativeTestBase { public: IptablesBaseTest(); diff --git a/server/IptablesRestoreController.cpp b/server/IptablesRestoreController.cpp index f7ba2008..dc718309 100644 --- a/server/IptablesRestoreController.cpp +++ b/server/IptablesRestoreController.cpp @@ -241,12 +241,12 @@ void IptablesRestoreController::maybeLogStderr(const std::unique_ptr<IptablesPro return; } - ALOGE("iptables error:\n"); - ALOGE("------- COMMAND -------\n"); - ALOGE("%s\n", command.c_str()); - ALOGE("------- ERROR -------\n"); + ALOGE("iptables error:"); + ALOGE("------- COMMAND -------"); + ALOGE("%s", command.c_str()); + ALOGE("------- ERROR -------"); ALOGE("%s", process->errBuf.c_str()); - ALOGE("----------------------\n"); + ALOGE("----------------------"); process->errBuf.clear(); } diff --git a/server/IptablesRestoreControllerTest.cpp b/server/IptablesRestoreControllerTest.cpp index a05c76d1..cecdf4d9 100644 --- a/server/IptablesRestoreControllerTest.cpp +++ b/server/IptablesRestoreControllerTest.cpp @@ -32,6 +32,7 @@ #include <android-base/strings.h> #include <log/log.h> #include <netdutils/MockSyscalls.h> +#include <netdutils/NetNativeTestBase.h> #include <netdutils/Stopwatch.h> #include "NetdConstants.h" @@ -55,7 +56,7 @@ using android::netdutils::Stopwatch; using testing::Return; using testing::StrictMock; -class IptablesRestoreControllerTest : public ::testing::Test { +class IptablesRestoreControllerTest : public NetNativeTestBase { public: IptablesRestoreController con; int mDefaultMaxRetries = con.MAX_RETRIES; diff --git a/server/LocalNetwork.h b/server/LocalNetwork.h index c774067c..af56d245 100644 --- a/server/LocalNetwork.h +++ b/server/LocalNetwork.h @@ -24,6 +24,7 @@ class LocalNetwork : public Network { public: explicit LocalNetwork(unsigned netId); virtual ~LocalNetwork(); + Permission getPermission() const { return PERMISSION_SYSTEM; }; private: std::string getTypeString() const override { return "LOCAL"; }; diff --git a/server/MDnsEventReporter.cpp b/server/MDnsEventReporter.cpp index e94de367..92a376fe 100644 --- a/server/MDnsEventReporter.cpp +++ b/server/MDnsEventReporter.cpp @@ -41,7 +41,6 @@ int MDnsEventReporter::removeEventListener(const sp<IMDnsEventListener>& listene } const MDnsEventReporter::EventListenerSet& MDnsEventReporter::getEventListenersImpl() const { - std::lock_guard lock(mMutex); return mEventListeners; } diff --git a/server/MDnsEventReporter.h b/server/MDnsEventReporter.h index cbc43ecb..22edbc41 100644 --- a/server/MDnsEventReporter.h +++ b/server/MDnsEventReporter.h @@ -51,7 +51,7 @@ class MDnsEventReporter final { // Return registered binder services from the singleton MDnsEventReporter. This method is // threadsafe. - const EventListenerSet& getEventListeners() const; + const EventListenerSet& getEventListeners() const REQUIRES(mMutex); // Add the binder to the singleton MDnsEventReporter. This method is threadsafe. int addEventListener(const android::sp<android::net::mdns::aidl::IMDnsEventListener>& listener); @@ -60,11 +60,12 @@ class MDnsEventReporter final { int removeEventListener( const android::sp<android::net::mdns::aidl::IMDnsEventListener>& listener); + mutable std::mutex mMutex; + private: MDnsEventReporter() = default; ~MDnsEventReporter() = default; - mutable std::mutex mMutex; EventListenerSet mEventListeners GUARDED_BY(mMutex); int addEventListenerImpl( @@ -73,5 +74,5 @@ class MDnsEventReporter final { int removeEventListenerImpl( const android::sp<android::net::mdns::aidl::IMDnsEventListener>& listener) EXCLUDES(mMutex); - const EventListenerSet& getEventListenersImpl() const EXCLUDES(mMutex); + const EventListenerSet& getEventListenersImpl() const REQUIRES(mMutex); }; diff --git a/server/MDnsSdListener.cpp b/server/MDnsSdListener.cpp index 1d1ea40a..4bd5f0d4 100644 --- a/server/MDnsSdListener.cpp +++ b/server/MDnsSdListener.cpp @@ -106,6 +106,7 @@ void MDnsSdListenerDiscoverCallback(DNSServiceRef /* sdRef */, DNSServiceFlags f const char* replyDomain, void* inContext) { MDnsSdListener::Context *context = reinterpret_cast<MDnsSdListener::Context *>(inContext); int refNumber = context->mRefNumber; + const std::lock_guard lock(MDnsEventReporter::getInstance().mMutex); const auto& listeners = MDnsEventReporter::getInstance().getEventListeners(); if (listeners.empty()) { ALOGI("Discover callback not sent since no IMDnsEventListener receiver is available."); @@ -193,6 +194,7 @@ void MDnsSdListenerRegisterCallback(DNSServiceRef /* sdRef */, DNSServiceFlags / void* inContext) { MDnsSdListener::Context* context = reinterpret_cast<MDnsSdListener::Context*>(inContext); int refNumber = context->mRefNumber; + const std::lock_guard lock(MDnsEventReporter::getInstance().mMutex); const auto& listeners = MDnsEventReporter::getInstance().getEventListeners(); if (listeners.empty()) { ALOGI("Register callback not sent since no IMDnsEventListener receiver is available."); @@ -251,6 +253,7 @@ void MDnsSdListenerResolveCallback(DNSServiceRef /* sdRef */, DNSServiceFlags /* void* inContext) { MDnsSdListener::Context* context = reinterpret_cast<MDnsSdListener::Context*>(inContext); int refNumber = context->mRefNumber; + const std::lock_guard lock(MDnsEventReporter::getInstance().mMutex); const auto& listeners = MDnsEventReporter::getInstance().getEventListeners(); if (listeners.empty()) { ALOGI("Resolve callback not sent since no IMDnsEventListener receiver is available."); @@ -313,6 +316,7 @@ void MDnsSdListenerGetAddrInfoCallback(DNSServiceRef /* sdRef */, DNSServiceFlag uint32_t /* ttl */, void* inContext) { MDnsSdListener::Context *context = reinterpret_cast<MDnsSdListener::Context *>(inContext); int refNumber = context->mRefNumber; + const std::lock_guard lock(MDnsEventReporter::getInstance().mMutex); const auto& listeners = MDnsEventReporter::getInstance().getEventListeners(); if (listeners.empty()) { ALOGI("Get address callback not sent since no IMDnsEventListener receiver is available."); diff --git a/server/NFLogListenerTest.cpp b/server/NFLogListenerTest.cpp index 88ab2c61..878c9884 100644 --- a/server/NFLogListenerTest.cpp +++ b/server/NFLogListenerTest.cpp @@ -25,6 +25,7 @@ #include <linux/netfilter/nfnetlink_log.h> #include <netdutils/MockSyscalls.h> +#include <netdutils/NetNativeTestBase.h> #include "NFLogListener.h" using ::testing::_; @@ -58,7 +59,7 @@ class MockNetlinkListener : public NetlinkListenerInterface { MOCK_METHOD1(registerSkErrorHandler, void(const SkErrorHandler& handler)); }; -class NFLogListenerTest : public testing::Test { +class NFLogListenerTest : public NetNativeTestBase { protected: NFLogListenerTest() { EXPECT_CALL(*mNLListener, subscribe(kNFLogPacketMsgType, _)) diff --git a/server/NetdConstants.cpp b/server/NetdConstants.cpp index 6de164fb..fa21f44b 100644 --- a/server/NetdConstants.cpp +++ b/server/NetdConstants.cpp @@ -155,8 +155,7 @@ void blockSigpipe() { sigemptyset(&mask); sigaddset(&mask, SIGPIPE); - if (sigprocmask(SIG_BLOCK, &mask, nullptr) != 0) - ALOGW("WARNING: SIGPIPE not blocked\n"); + if (sigprocmask(SIG_BLOCK, &mask, nullptr) != 0) ALOGW("WARNING: SIGPIPE not blocked"); } void setCloseOnExec(const char *sock) { diff --git a/server/NetdHwAidlService.cpp b/server/NetdHwAidlService.cpp new file mode 100644 index 00000000..42581539 --- /dev/null +++ b/server/NetdHwAidlService.cpp @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2022 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 "NetdHwAidlService.h" +#include <android/binder_manager.h> +#include <android/binder_process.h> +#include "Controllers.h" +#include "Fwmark.h" +#include "RouteController.h" +#include "TetherController.h" + +// Tells TetherController::enableForwarding who is requesting forwarding, so that TetherController +// can manage/refcount requests to enable forwarding by multiple parties such as the framework, this +// binder interface, and the legacy "ndc ipfwd enable <requester>" commands. +namespace { +constexpr const char* FORWARDING_REQUESTER = "NetdHwAidlService"; +} + +namespace android { +namespace net { +namespace aidl { + +static int toHalStatus(int ret) { + switch (ret) { + case 0: + return 0; + case -EINVAL: + return NetdHwAidlService::STATUS_INVALID_ARGUMENTS; + case -EEXIST: + return NetdHwAidlService::STATUS_ALREADY_EXISTS; + case -ENONET: + return NetdHwAidlService::STATUS_NO_NETWORK; + case -EPERM: + return NetdHwAidlService::STATUS_PERMISSION_DENIED; + default: + ALOGE("HAL service error=%d", ret); + return NetdHwAidlService::STATUS_UNKNOWN_ERROR; + } +} + +void NetdHwAidlService::run() { + std::shared_ptr<NetdHwAidlService> service = ndk::SharedRefBase::make<NetdHwAidlService>(); + + const std::string instance = std::string() + NetdHwAidlService::descriptor + "/default"; + binder_status_t status = + AServiceManager_addService(service->asBinder().get(), instance.c_str()); + if (status != STATUS_OK) { + ALOGE("Failed to register AIDL INetd service. Status: %d.", status); + return; + } + + ABinderProcess_joinThreadPool(); +} + +ScopedAStatus NetdHwAidlService::createOemNetwork(OemNetwork* network) { + unsigned netId; + Permission permission = PERMISSION_SYSTEM; + + int ret = gCtls->netCtrl.createPhysicalOemNetwork(permission, &netId); + + Fwmark fwmark; + fwmark.netId = netId; + fwmark.explicitlySelected = true; + fwmark.protectedFromVpn = true; + fwmark.permission = PERMISSION_SYSTEM; + network->networkHandle = netIdToNetHandle(netId); + network->packetMark = fwmark.intValue; + if (ret != 0) { + return ScopedAStatus::fromServiceSpecificError(toHalStatus(ret)); + } else { + return ScopedAStatus::ok(); + } +} + +// Vendor code can only modify OEM networks. All other networks are managed by ConnectivityService. +#define RETURN_IF_NOT_OEM_NETWORK(netId) \ + if (((netId) < NetworkController::MIN_OEM_ID) || ((netId) > NetworkController::MAX_OEM_ID)) { \ + return ScopedAStatus::fromServiceSpecificError(STATUS_INVALID_ARGUMENTS); \ + } + +ScopedAStatus NetdHwAidlService::destroyOemNetwork(int64_t netHandle) { + unsigned netId = netHandleToNetId(netHandle); + RETURN_IF_NOT_OEM_NETWORK(netId); + + auto ret = toHalStatus(gCtls->netCtrl.destroyNetwork(netId)); + if (ret != 0) { + return ScopedAStatus::fromServiceSpecificError(toHalStatus(ret)); + } else { + return ScopedAStatus::ok(); + } +} + +const char* maybeNullString(const std::string& nexthop) { + // std::strings can't be null, but RouteController wants null instead of an empty string. + const char* nh = nexthop.c_str(); + if (nh && !*nh) { + nh = nullptr; + } + return nh; +} + +ScopedAStatus NetdHwAidlService::addRouteToOemNetwork(int64_t networkHandle, + const std::string& ifname, + const std::string& destination, + const std::string& nexthop) { + unsigned netId = netHandleToNetId(networkHandle); + RETURN_IF_NOT_OEM_NETWORK(netId); + + auto ret = gCtls->netCtrl.addRoute(netId, ifname.c_str(), destination.c_str(), + maybeNullString(nexthop), false, INVALID_UID, 0 /* mtu */); + if (ret != 0) { + return ScopedAStatus::fromServiceSpecificError(toHalStatus(ret)); + } else { + return ScopedAStatus::ok(); + } +} + +ScopedAStatus NetdHwAidlService::removeRouteFromOemNetwork(int64_t networkHandle, + const std::string& ifname, + const std::string& destination, + const std::string& nexthop) { + unsigned netId = netHandleToNetId(networkHandle); + RETURN_IF_NOT_OEM_NETWORK(netId); + + auto ret = gCtls->netCtrl.removeRoute(netId, ifname.c_str(), destination.c_str(), + maybeNullString(nexthop), false, INVALID_UID); + if (ret != 0) { + return ScopedAStatus::fromServiceSpecificError(toHalStatus(ret)); + } else { + return ScopedAStatus::ok(); + } +} + +ScopedAStatus NetdHwAidlService::addInterfaceToOemNetwork(int64_t networkHandle, + const std::string& ifname) { + unsigned netId = netHandleToNetId(networkHandle); + RETURN_IF_NOT_OEM_NETWORK(netId); + + auto ret = gCtls->netCtrl.addInterfaceToNetwork(netId, ifname.c_str()); + if (ret != 0) { + return ScopedAStatus::fromServiceSpecificError(toHalStatus(ret)); + } else { + return ScopedAStatus::ok(); + } +} + +ScopedAStatus NetdHwAidlService::removeInterfaceFromOemNetwork(int64_t networkHandle, + const std::string& ifname) { + unsigned netId = netHandleToNetId(networkHandle); + RETURN_IF_NOT_OEM_NETWORK(netId); + + auto ret = gCtls->netCtrl.removeInterfaceFromNetwork(netId, ifname.c_str()); + if (ret != 0) { + return ScopedAStatus::fromServiceSpecificError(toHalStatus(ret)); + } else { + return ScopedAStatus::ok(); + } +} + +ScopedAStatus NetdHwAidlService::setIpForwardEnable(bool enable) { + std::lock_guard _lock(gCtls->tetherCtrl.lock); + + bool success = enable ? gCtls->tetherCtrl.enableForwarding(FORWARDING_REQUESTER) + : gCtls->tetherCtrl.disableForwarding(FORWARDING_REQUESTER); + + if (!success) { + return ScopedAStatus::fromServiceSpecificError(STATUS_UNKNOWN_ERROR); + } else { + return ScopedAStatus::ok(); + } +} + +ScopedAStatus NetdHwAidlService::setForwardingBetweenInterfaces(const std::string& inputIfName, + const std::string& outputIfName, + bool enable) { + std::lock_guard _lock(gCtls->tetherCtrl.lock); + + // TODO: check that one interface is an OEM interface and the other is another OEM interface, an + // IPsec interface or a dummy interface. + int ret = enable ? RouteController::enableTethering(inputIfName.c_str(), outputIfName.c_str()) + : RouteController::disableTethering(inputIfName.c_str(), outputIfName.c_str()); + if (ret != 0) { + return ScopedAStatus::fromServiceSpecificError(toHalStatus(ret)); + } else { + return ScopedAStatus::ok(); + } +} + +} // namespace aidl +} // namespace net +} // namespace android diff --git a/server/NetdHwAidlService.h b/server/NetdHwAidlService.h new file mode 100644 index 00000000..5cc5b6fd --- /dev/null +++ b/server/NetdHwAidlService.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2022 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. + */ +#pragma once + +#include <aidl/android/system/net/netd/BnNetd.h> + +namespace android { +namespace net { +namespace aidl { +using NetdHw = ::aidl::android::system::net::netd::BnNetd; +using OemNetwork = ::aidl::android::system::net::netd::INetd::OemNetwork; +using ScopedAStatus = ::ndk::ScopedAStatus; + +class NetdHwAidlService : public NetdHw { + public: + // Start and run the AIDL service. + // This blocks when joining the threadpool so start this in a separate thread. + static void run(); + ScopedAStatus createOemNetwork(OemNetwork* network) override; + ScopedAStatus destroyOemNetwork(int64_t netHandle) override; + ScopedAStatus addRouteToOemNetwork(int64_t networkHandle, const std::string& ifname, + const std::string& destination, + const std::string& nexthop) override; + ScopedAStatus removeRouteFromOemNetwork(int64_t networkHandle, const std::string& ifname, + const std::string& destination, + const std::string& nexthop) override; + ScopedAStatus addInterfaceToOemNetwork(int64_t networkHandle, + const std::string& ifname) override; + ScopedAStatus removeInterfaceFromOemNetwork(int64_t networkHandle, + const std::string& ifname) override; + ScopedAStatus setIpForwardEnable(bool enable) override; + ScopedAStatus setForwardingBetweenInterfaces(const std::string& inputIfName, + const std::string& outputIfName, + bool enable) override; +}; + +} // namespace aidl +} // namespace net +} // namespace android diff --git a/server/NetdHwService.cpp b/server/NetdHwService.cpp index 15855da8..d4a0e58e 100644 --- a/server/NetdHwService.cpp +++ b/server/NetdHwService.cpp @@ -14,15 +14,13 @@ * limitations under the License. */ +#include "NetdHwService.h" #include <binder/IPCThreadState.h> -#include <hidl/HidlTransportSupport.h> #include "Controllers.h" #include "Fwmark.h" -#include "NetdHwService.h" #include "RouteController.h" #include "TetherController.h" -using android::hardware::configureRpcThreadpool; using android::hardware::Void; // Tells TetherController::enableForwarding who is requesting forwarding, so that TetherController @@ -55,9 +53,6 @@ static StatusCode toHalStatus(int ret) { // Minimal service start. status_t NetdHwService::start() { - IPCThreadState::self()->disableBackgroundScheduling(true); - // Usage of this HAL is anticipated to be thin; one thread should suffice. - configureRpcThreadpool(1, false /* callerWillNotJoin */); // Register hardware service with ServiceManager. return INetd::registerAsService(); } diff --git a/server/NetdNativeService.cpp b/server/NetdNativeService.cpp index 466d8ba7..63c004d2 100644 --- a/server/NetdNativeService.cpp +++ b/server/NetdNativeService.cpp @@ -272,7 +272,8 @@ binder::Status NetdNativeService::bandwidthRemoveNiceApp(int32_t) { // tests. binder::Status NetdNativeService::networkCreatePhysical(int32_t netId, int32_t permission) { ENFORCE_NETWORK_STACK_PERMISSIONS(); - int ret = gCtls->netCtrl.createPhysicalNetwork(netId, convertPermission(permission)); + int ret = gCtls->netCtrl.createPhysicalNetwork(netId, convertPermission(permission), + false /* local */); return statusFromErrcode(ret); } @@ -292,8 +293,11 @@ binder::Status NetdNativeService::networkCreate(const NativeNetworkConfig& confi ENFORCE_NETWORK_STACK_PERMISSIONS(); int ret = -EINVAL; if (config.networkType == NativeNetworkType::PHYSICAL) { - ret = gCtls->netCtrl.createPhysicalNetwork(config.netId, - convertPermission(config.permission)); + ret = gCtls->netCtrl.createPhysicalNetwork( + config.netId, convertPermission(config.permission), false /* isLocalNetwork */); + } else if (config.networkType == NativeNetworkType::PHYSICAL_LOCAL) { + ret = gCtls->netCtrl.createPhysicalNetwork( + config.netId, convertPermission(config.permission), true /* isLocalNetwork */); } else if (config.networkType == NativeNetworkType::VIRTUAL) { ret = gCtls->netCtrl.createVirtualNetwork(config.netId, config.secure, config.vpnType, config.excludeLocalRoutes); @@ -664,9 +668,8 @@ binder::Status NetdNativeService::ipSecAddTunnelInterface(const std::string& dev int32_t interfaceId) { // Necessary locking done in IpSecService and kernel ENFORCE_NETWORK_STACK_PERMISSIONS(); - netdutils::Status result = gCtls->xfrmCtrl.ipSecAddTunnelInterface( - deviceName, localAddress, remoteAddress, iKey, oKey, interfaceId, false); - return binder::Status::ok(); + return asBinderStatus(gCtls->xfrmCtrl.ipSecAddTunnelInterface( + deviceName, localAddress, remoteAddress, iKey, oKey, interfaceId, false)); } binder::Status NetdNativeService::ipSecUpdateTunnelInterface(const std::string& deviceName, @@ -676,16 +679,14 @@ binder::Status NetdNativeService::ipSecUpdateTunnelInterface(const std::string& int32_t interfaceId) { // Necessary locking done in IpSecService and kernel ENFORCE_NETWORK_STACK_PERMISSIONS(); - netdutils::Status result = gCtls->xfrmCtrl.ipSecAddTunnelInterface( - deviceName, localAddress, remoteAddress, iKey, oKey, interfaceId, true); - return binder::Status::ok(); + return asBinderStatus(gCtls->xfrmCtrl.ipSecAddTunnelInterface( + deviceName, localAddress, remoteAddress, iKey, oKey, interfaceId, true)); } binder::Status NetdNativeService::ipSecRemoveTunnelInterface(const std::string& deviceName) { // Necessary locking done in IpSecService and kernel ENFORCE_NETWORK_STACK_PERMISSIONS(); - netdutils::Status result = gCtls->xfrmCtrl.ipSecRemoveTunnelInterface(deviceName); - return binder::Status::ok(); + return asBinderStatus(gCtls->xfrmCtrl.ipSecRemoveTunnelInterface(deviceName)); } binder::Status NetdNativeService::setIPv6AddrGenMode(const std::string& ifName, diff --git a/server/NetlinkHandler.cpp b/server/NetlinkHandler.cpp index a4e05b02..9e63a8c0 100644 --- a/server/NetlinkHandler.cpp +++ b/server/NetlinkHandler.cpp @@ -106,7 +106,7 @@ void NetlinkHandler::onEvent(NetlinkEvent *evt) { if (!strcmp(subsys, "net")) { NetlinkEvent::Action action = evt->getAction(); - const char *iface = evt->findParam("INTERFACE"); + const char *iface = evt->findParam("INTERFACE") ?: ""; if (action == NetlinkEvent::Action::kAdd) { notifyInterfaceAdded(iface); } else if (action == NetlinkEvent::Action::kRemove) { diff --git a/server/Network.cpp b/server/Network.cpp index 85f942f4..156cfb3e 100644 --- a/server/Network.cpp +++ b/server/Network.cpp @@ -117,18 +117,12 @@ void Network::removeFromUidRangeMap(const UidRanges& uidRanges, int32_t subPrior } } -bool Network::canAddUidRanges(const UidRanges& uidRanges, int32_t subPriority) const { +bool Network::canAddUidRanges(const UidRanges& uidRanges) const { if (uidRanges.overlapsSelf()) { ALOGE("uid range %s overlaps self", uidRanges.toString().c_str()); return false; } - auto iter = mUidRangeMap.find(subPriority); - if (iter != mUidRangeMap.end() && uidRanges.overlaps(iter->second)) { - ALOGE("uid range %s overlaps priority %d %s", uidRanges.toString().c_str(), subPriority, - iter->second.toString().c_str()); - return false; - } return true; } diff --git a/server/Network.h b/server/Network.h index e18e1cdb..6c3d01d3 100644 --- a/server/Network.h +++ b/server/Network.h @@ -17,6 +17,7 @@ #pragma once #include "NetdConstants.h" +#include "Permission.h" #include "UidRanges.h" #include <set> @@ -48,6 +49,7 @@ public: std::string toString() const; std::string uidRangesToString() const; bool appliesToUser(uid_t uid, int32_t* subPriority) const; + virtual Permission getPermission() const = 0; [[nodiscard]] virtual int addUsers(const UidRanges&, int32_t /*subPriority*/) { return -EINVAL; }; @@ -65,7 +67,7 @@ public: protected: explicit Network(unsigned netId, bool secure = false); - bool canAddUidRanges(const UidRanges& uidRanges, int32_t subPriority) const; + bool canAddUidRanges(const UidRanges& uidRanges) const; const unsigned mNetId; std::set<std::string> mInterfaces; diff --git a/server/NetworkController.cpp b/server/NetworkController.cpp index f1441397..5233a1ea 100644 --- a/server/NetworkController.cpp +++ b/server/NetworkController.cpp @@ -382,7 +382,8 @@ bool NetworkController::isVirtualNetworkLocked(unsigned netId) const { return network && network->isVirtual(); } -int NetworkController::createPhysicalNetworkLocked(unsigned netId, Permission permission) { +int NetworkController::createPhysicalNetworkLocked(unsigned netId, Permission permission, + bool local) { if (!((MIN_NET_ID <= netId && netId <= MAX_NET_ID) || (MIN_OEM_ID <= netId && netId <= MAX_OEM_ID))) { ALOGE("invalid netId %u", netId); @@ -394,7 +395,7 @@ int NetworkController::createPhysicalNetworkLocked(unsigned netId, Permission pe return -EEXIST; } - PhysicalNetwork* physicalNetwork = new PhysicalNetwork(netId, mDelegateImpl); + PhysicalNetwork* physicalNetwork = new PhysicalNetwork(netId, mDelegateImpl, local); if (int ret = physicalNetwork->setPermission(permission)) { ALOGE("inconceivable! setPermission cannot fail on an empty network"); delete physicalNetwork; @@ -408,9 +409,9 @@ int NetworkController::createPhysicalNetworkLocked(unsigned netId, Permission pe return 0; } -int NetworkController::createPhysicalNetwork(unsigned netId, Permission permission) { +int NetworkController::createPhysicalNetwork(unsigned netId, Permission permission, bool local) { ScopedWLock lock(mRWLock); - return createPhysicalNetworkLocked(netId, permission); + return createPhysicalNetworkLocked(netId, permission, local); } int NetworkController::createPhysicalOemNetwork(Permission permission, unsigned *pNetId) { @@ -431,7 +432,7 @@ int NetworkController::createPhysicalOemNetwork(Permission permission, unsigned return -ENONET; } - int ret = createPhysicalNetworkLocked(*pNetId, permission); + int ret = createPhysicalNetworkLocked(*pNetId, permission, false /* local */); if (ret) { *pNetId = 0; } @@ -887,7 +888,7 @@ int NetworkController::checkUserNetworkAccessLocked(uid_t uid, unsigned netId) c // Check whether the UID's permission bits are sufficient to use the network. // Because the permission of the system default network is PERMISSION_NONE(0x0), apps can always // pass the check here when using the system default network. - Permission networkPermission = static_cast<PhysicalNetwork*>(network)->getPermission(); + const Permission networkPermission = network->getPermission(); return ((userPermission & networkPermission) == networkPermission) ? 0 : -EACCES; } diff --git a/server/NetworkController.h b/server/NetworkController.h index e9ef0912..d4156f98 100644 --- a/server/NetworkController.h +++ b/server/NetworkController.h @@ -105,7 +105,7 @@ public: unsigned getNetworkForInterface(const char* interface) const; bool isVirtualNetwork(unsigned netId) const; - [[nodiscard]] int createPhysicalNetwork(unsigned netId, Permission permission); + [[nodiscard]] int createPhysicalNetwork(unsigned netId, Permission permission, bool local); [[nodiscard]] int createPhysicalOemNetwork(Permission permission, unsigned* netId); [[nodiscard]] int createVirtualNetwork(unsigned netId, bool secure, NativeVpnType vpnType, bool excludeLocalRoutes); @@ -165,7 +165,8 @@ public: Network* getPhysicalOrUnreachableNetworkForUserLocked(uid_t uid) const; Permission getPermissionForUserLocked(uid_t uid) const; int checkUserNetworkAccessLocked(uid_t uid, unsigned netId) const; - [[nodiscard]] int createPhysicalNetworkLocked(unsigned netId, Permission permission); + [[nodiscard]] int createPhysicalNetworkLocked(unsigned netId, Permission permission, + bool local); [[nodiscard]] int modifyRoute(unsigned netId, const char* interface, const char* destination, const char* nexthop, RouteOperation op, bool legacy, uid_t uid, diff --git a/server/OWNERS b/server/OWNERS deleted file mode 100644 index 03fedf71..00000000 --- a/server/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -# Bug component: 31808 -lorenzo@google.com -maze@google.com diff --git a/server/PhysicalNetwork.cpp b/server/PhysicalNetwork.cpp index bb3f653d..923412a8 100644 --- a/server/PhysicalNetwork.cpp +++ b/server/PhysicalNetwork.cpp @@ -56,8 +56,16 @@ namespace { PhysicalNetwork::Delegate::~Delegate() {} -PhysicalNetwork::PhysicalNetwork(unsigned netId, PhysicalNetwork::Delegate* delegate) : - Network(netId), mDelegate(delegate), mPermission(PERMISSION_NONE), mIsDefault(false) { +PhysicalNetwork::PhysicalNetwork(unsigned netId, PhysicalNetwork::Delegate* delegate, bool local) + : Network(netId), + mDelegate(delegate), + mPermission(PERMISSION_NONE), + mIsDefault(false), + mIsLocalNetwork(local) { + // TODO : remove this log, it's only present to avoid -Wunused-private-field from blocking + // compilation + ALOGI("Created physical network instance netId=%d local=%s", netId, + mIsLocalNetwork ? "true" : "false"); } PhysicalNetwork::~PhysicalNetwork() {} @@ -165,7 +173,7 @@ int PhysicalNetwork::removeAsDefault() { } int PhysicalNetwork::addUsers(const UidRanges& uidRanges, int32_t subPriority) { - if (!isValidSubPriority(subPriority) || !canAddUidRanges(uidRanges, subPriority)) { + if (!isValidSubPriority(subPriority) || !canAddUidRanges(uidRanges)) { return -EINVAL; } diff --git a/server/PhysicalNetwork.h b/server/PhysicalNetwork.h index f114cca2..7166e0ea 100644 --- a/server/PhysicalNetwork.h +++ b/server/PhysicalNetwork.h @@ -33,7 +33,7 @@ class PhysicalNetwork : public Network { Permission permission) = 0; }; - PhysicalNetwork(unsigned netId, Delegate* delegate); + PhysicalNetwork(unsigned netId, Delegate* delegate, bool local); virtual ~PhysicalNetwork(); // These refer to permissions that apps must have in order to use this network. @@ -58,6 +58,7 @@ class PhysicalNetwork : public Network { Delegate* const mDelegate; Permission mPermission; bool mIsDefault; + const bool mIsLocalNetwork; }; } // namespace android::net diff --git a/server/Process.cpp b/server/Process.cpp index f43e82dc..fa14587a 100644 --- a/server/Process.cpp +++ b/server/Process.cpp @@ -85,7 +85,7 @@ void blockSigPipe() { sigemptyset(&mask); sigaddset(&mask, SIGPIPE); if (sigprocmask(SIG_BLOCK, &mask, nullptr) != 0) { - ALOGW("WARNING: SIGPIPE not blocked\n"); + ALOGW("WARNING: SIGPIPE not blocked"); } } diff --git a/server/RouteController.cpp b/server/RouteController.cpp index d2af9a37..86b23b6d 100644 --- a/server/RouteController.cpp +++ b/server/RouteController.cpp @@ -64,7 +64,7 @@ const char* const ROUTE_TABLE_NAME_MAIN = "main"; const char* const RouteController::LOCAL_MANGLE_INPUT = "routectrl_mangle_INPUT"; -const IPPrefix V4_LOCAL_ADDR[] = { +const IPPrefix V4_LOCAL_PREFIXES[] = { IPPrefix::forString("169.254.0.0/16"), // Link Local IPPrefix::forString("100.64.0.0/10"), // CGNAT IPPrefix::forString("10.0.0.0/8"), // RFC1918 @@ -667,6 +667,19 @@ int RouteController::modifyVpnLocalExclusionRule(bool add, const char* physicalI INVALID_UID); } +int RouteController::addFixedLocalRoutes(const char* interface) { + for (size_t i = 0; i < ARRAY_SIZE(V4_FIXED_LOCAL_PREFIXES); ++i) { + if (int ret = modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, interface, + V4_FIXED_LOCAL_PREFIXES[i], nullptr /* nexthop */, + RouteController::INTERFACE, 0 /* mtu */, 0 /* priority */, + true /* isLocal */)) { + return ret; + } + } + + return 0; +} + // A rule to enable split tunnel VPNs. // // If a packet with a VPN's netId doesn't find a route in the VPN's routing table, it's allowed to @@ -1297,6 +1310,11 @@ int RouteController::addInterfaceToPhysicalNetwork(unsigned netId, const char* i maybeModifyQdiscClsact(interface, ACTION_ADD); updateTableNamesFile(); + + if (int ret = addFixedLocalRoutes(interface)) { + return ret; + } + return 0; } @@ -1392,8 +1410,8 @@ int RouteController::removeInterfaceFromDefaultNetwork(const char* interface, return modifyDefaultNetwork(RTM_DELRULE, interface, permission); } -bool RouteController::isTargetV4LocalRange(const char* dst) { - for (IPPrefix addr : V4_LOCAL_ADDR) { +bool RouteController::isWithinIpv4LocalPrefix(const char* dst) { + for (IPPrefix addr : V4_LOCAL_PREFIXES) { if (addr.contains(IPPrefix::forString(dst))) { return true; } @@ -1401,14 +1419,14 @@ bool RouteController::isTargetV4LocalRange(const char* dst) { return false; } -bool RouteController::isLocalAddress(TableType tableType, const char* destination, - const char* nexthop) { +bool RouteController::isLocalRoute(TableType tableType, const char* destination, + const char* nexthop) { IPPrefix prefix = IPPrefix::forString(destination); return nexthop == nullptr && tableType == RouteController::INTERFACE && // Skip default route to prevent network being modeled as point-to-point interfaces. ((prefix.family() == AF_INET6 && prefix != IPPrefix::forString("::/0")) || // Skip adding non-target local network range. - (prefix.family() == AF_INET && isTargetV4LocalRange(destination))); + (prefix.family() == AF_INET && isWithinIpv4LocalPrefix(destination))); } int RouteController::addRoute(const char* interface, const char* destination, const char* nexthop, @@ -1418,7 +1436,7 @@ int RouteController::addRoute(const char* interface, const char* destination, co return ret; } - if (isLocalAddress(tableType, destination, nexthop)) { + if (isLocalRoute(tableType, destination, nexthop)) { return modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, interface, destination, nexthop, tableType, mtu, priority, true /* isLocal */); } @@ -1433,7 +1451,7 @@ int RouteController::removeRoute(const char* interface, const char* destination, return ret; } - if (isLocalAddress(tableType, destination, nexthop)) { + if (isLocalRoute(tableType, destination, nexthop)) { return modifyRoute(RTM_DELROUTE, NETLINK_REQUEST_FLAGS, interface, destination, nexthop, tableType, 0 /* mtu */, priority, true /* isLocal */); } @@ -1447,7 +1465,7 @@ int RouteController::updateRoute(const char* interface, const char* destination, return ret; } - if (isLocalAddress(tableType, destination, nexthop)) { + if (isLocalRoute(tableType, destination, nexthop)) { return modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_REPLACE_FLAGS, interface, destination, nexthop, tableType, mtu, 0 /* priority */, true /* isLocal */); } diff --git a/server/RouteController.h b/server/RouteController.h index ff41678d..0d4e2b96 100644 --- a/server/RouteController.h +++ b/server/RouteController.h @@ -198,6 +198,14 @@ public: private: friend class RouteControllerTest; + // An expandable array for fixed local prefix though it's only one element now. + static constexpr const char* V4_FIXED_LOCAL_PREFIXES[] = { + // The multicast range is 224.0.0.0/4 but only limit it to 224.0.0.0/24 since the IPv4 + // definitions are not as precise as for IPv6, it is the only range that the standards + // (RFC 2365 and RFC 5771) specify is link-local and must not be forwarded. + "224.0.0.0/24" // Link-local multicast; non-internet routable + }; + static std::mutex sInterfaceToTableLock; static std::map<std::string, uint32_t> sInterfaceToTable GUARDED_BY(sInterfaceToTableLock); @@ -230,8 +238,9 @@ public: static int modifyUidLocalNetworkRule(const char* interface, uid_t uidStart, uid_t uidEnd, bool add); - static bool isLocalAddress(TableType tableType, const char* destination, const char* nexthop); - static bool isTargetV4LocalRange(const char* addrstr); + static bool isLocalRoute(TableType tableType, const char* destination, const char* nexthop); + static bool isWithinIpv4LocalPrefix(const char* addrstr); + static int addFixedLocalRoutes(const char* interface); }; // Public because they are called by by RouteControllerTest.cpp. diff --git a/server/SockDiagTest.cpp b/server/SockDiagTest.cpp index 49601aa4..864d08d5 100644 --- a/server/SockDiagTest.cpp +++ b/server/SockDiagTest.cpp @@ -24,6 +24,7 @@ #include <linux/inet_diag.h> #include <gtest/gtest.h> +#include <netdutils/NetNativeTestBase.h> #include "Fwmark.h" #include "NetdConstants.h" @@ -33,7 +34,7 @@ namespace android { namespace net { -class SockDiagTest : public ::testing::Test { +class SockDiagTest : public NetNativeTestBase { protected: static bool isLoopbackSocket(const inet_diag_msg *msg) { return SockDiag::isLoopbackSocket(msg); diff --git a/server/UidRanges.cpp b/server/UidRanges.cpp index c90f30b9..765df322 100644 --- a/server/UidRanges.cpp +++ b/server/UidRanges.cpp @@ -145,25 +145,6 @@ bool UidRanges::overlapsSelf() const { return false; } -// std::binary_search cannot do partial match. For example, an uid range x-y not only overlaps with -// x-y, but also w-x, y-z, w-z, ...etc. Therefore, we need a specialized binary search. -bool UidRanges::overlaps(const UidRanges& other) const { - for (const auto& target : other.getRanges()) { - int first = 0; - int end = mRanges.size() - 1; - - while (first <= end) { - int middle = (first + end) / 2; - if (isOverlapped(mRanges[middle], target)) return true; - if (compUidRangeParcel(mRanges[middle], target)) - first = middle + 1; - else - end = middle - 1; - } - } - return false; -} - std::string UidRanges::toString() const { std::string s("uids{ "); for (const auto &range : mRanges) { diff --git a/server/UidRanges.h b/server/UidRanges.h index 9123eb17..f20dc443 100644 --- a/server/UidRanges.h +++ b/server/UidRanges.h @@ -51,12 +51,10 @@ public: // check if 'mRanges' has uid overlap between elements. bool overlapsSelf() const; - // check if this object has uid overlap with the input object. - bool overlaps(const UidRanges& other) const; + bool empty() const { return mRanges.empty(); } private: - // Keep it sorted. The overlaps() implements binary search, which requires a sorted data. std::vector<UidRangeParcel> mRanges; }; diff --git a/server/UnreachableNetwork.cpp b/server/UnreachableNetwork.cpp index 68802251..dd6318c0 100644 --- a/server/UnreachableNetwork.cpp +++ b/server/UnreachableNetwork.cpp @@ -27,7 +27,7 @@ namespace net { UnreachableNetwork::UnreachableNetwork(unsigned netId) : Network(netId) {} int UnreachableNetwork::addUsers(const UidRanges& uidRanges, int32_t subPriority) { - if (!isValidSubPriority(subPriority) || !canAddUidRanges(uidRanges, subPriority)) { + if (!isValidSubPriority(subPriority) || !canAddUidRanges(uidRanges)) { return -EINVAL; } diff --git a/server/UnreachableNetwork.h b/server/UnreachableNetwork.h index d2cefde6..0ffc0ce8 100644 --- a/server/UnreachableNetwork.h +++ b/server/UnreachableNetwork.h @@ -23,6 +23,7 @@ namespace android::net { class UnreachableNetwork : public Network { public: explicit UnreachableNetwork(unsigned netId); + Permission getPermission() const { return PERMISSION_SYSTEM; }; [[nodiscard]] int addUsers(const UidRanges& uidRanges, int32_t subPriority) override; [[nodiscard]] int removeUsers(const UidRanges& uidRanges, int32_t subPriority) override; bool isUnreachable() override { return true; } diff --git a/server/VirtualNetwork.cpp b/server/VirtualNetwork.cpp index 495fd161..e0f60407 100644 --- a/server/VirtualNetwork.cpp +++ b/server/VirtualNetwork.cpp @@ -33,7 +33,7 @@ VirtualNetwork::VirtualNetwork(unsigned netId, bool secure, bool excludeLocalRou VirtualNetwork::~VirtualNetwork() {} int VirtualNetwork::addUsers(const UidRanges& uidRanges, int32_t subPriority) { - if (!isValidSubPriority(subPriority) || !canAddUidRanges(uidRanges, subPriority)) { + if (!isValidSubPriority(subPriority) || !canAddUidRanges(uidRanges)) { return -EINVAL; } diff --git a/server/VirtualNetwork.h b/server/VirtualNetwork.h index 63bc5891..6e9bdadb 100644 --- a/server/VirtualNetwork.h +++ b/server/VirtualNetwork.h @@ -35,6 +35,7 @@ class VirtualNetwork : public Network { public: explicit VirtualNetwork(unsigned netId, bool secure, bool excludeLocalRoutes = false); virtual ~VirtualNetwork(); + Permission getPermission() const { return PERMISSION_SYSTEM; }; [[nodiscard]] int addUsers(const UidRanges& uidRanges, int32_t subPriority) override; [[nodiscard]] int removeUsers(const UidRanges& uidRanges, int32_t subPriority) override; bool isVirtual() override { return true; } diff --git a/server/XfrmController.h b/server/XfrmController.h index 4f167c5f..6da0c68a 100644 --- a/server/XfrmController.h +++ b/server/XfrmController.h @@ -286,6 +286,10 @@ public: // Exposed for testing static constexpr size_t MAX_KEY_LENGTH = 128; + // Disable this warning since avoiding it makes the code unreadable. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu-variable-sized-type-not-at-end" + // Container for the content of an XFRMA_ALG_CRYPT netlink attribute. // Exposed for testing struct nlattr_algo_crypt { @@ -310,6 +314,8 @@ public: uint8_t key[MAX_KEY_LENGTH]; }; +#pragma clang diagnostic pop + // Exposed for testing struct nlattr_user_tmpl { nlattr hdr; diff --git a/server/XfrmControllerTest.cpp b/server/XfrmControllerTest.cpp index e7f5cfc0..9328c14b 100644 --- a/server/XfrmControllerTest.cpp +++ b/server/XfrmControllerTest.cpp @@ -41,6 +41,7 @@ #include <android-base/strings.h> #include <android-base/unique_fd.h> #include <gtest/gtest.h> +#include <netdutils/NetNativeTestBase.h> #include "Fwmark.h" #include "NetdConstants.h" @@ -127,7 +128,7 @@ void expectAddressEquals(int family, const std::string& expected, const xfrm_add EXPECT_EQ(expected, actualStr); } -class XfrmControllerTest : public ::testing::Test { +class XfrmControllerTest : public NetNativeTestBase { public: testing::StrictMock<netdutils::ScopedMockSyscalls> mockSyscalls; }; @@ -389,8 +390,12 @@ void testIpSecAddSecurityAssociation(testCaseParams params, const MockSyscalls& Slice attr_buf = drop(nlMsgSlice, NLA_ALIGN(sizeof(xfrm_usersa_info))); // Extract and check the encryption/authentication algorithm - XfrmController::nlattr_algo_crypt encryptAlgo{}; - XfrmController::nlattr_algo_auth authAlgo{}; + XfrmController::nlattr_algo_crypt _encryptAlgo{}; + XfrmController::nlattr_algo_auth _authAlgo{}; + // Need to use a pointer since you can't pass a structure with a variable + // sized array in a lambda. + XfrmController::nlattr_algo_crypt* const encryptAlgo = &_encryptAlgo; + XfrmController::nlattr_algo_auth* const authAlgo = &_authAlgo; XfrmController::nlattr_xfrm_mark mark{}; XfrmController::nlattr_xfrm_output_mark outputmark{}; XfrmController::nlattr_xfrm_interface_id xfrm_if_id{}; @@ -398,15 +403,15 @@ void testIpSecAddSecurityAssociation(testCaseParams params, const MockSyscalls& const nlattr& attr, const Slice& attr_payload) { Slice buf = attr_payload; if (attr.nla_type == XFRMA_ALG_CRYPT) { - encryptAlgo.hdr = attr; - netdutils::extract(buf, encryptAlgo.crypt); + encryptAlgo->hdr = attr; + netdutils::extract(buf, encryptAlgo->crypt); buf = drop(buf, sizeof(xfrm_algo)); - netdutils::extract(buf, encryptAlgo.key); + netdutils::extract(buf, encryptAlgo->key); } else if (attr.nla_type == XFRMA_ALG_AUTH_TRUNC) { - authAlgo.hdr = attr; - netdutils::extract(buf, authAlgo.auth); + authAlgo->hdr = attr; + netdutils::extract(buf, authAlgo->auth); buf = drop(buf, sizeof(xfrm_algo_auth)); - netdutils::extract(buf, authAlgo.key); + netdutils::extract(buf, authAlgo->key); } else if (attr.nla_type == XFRMA_MARK) { mark.hdr = attr; netdutils::extract(buf, mark.mark); @@ -424,9 +429,9 @@ void testIpSecAddSecurityAssociation(testCaseParams params, const MockSyscalls& // TODO: Use ContainerEq or ElementsAreArray to get better test failure messages. EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(cryptKey.data()), - reinterpret_cast<void*>(&encryptAlgo.key), KEY_LENGTH)); + reinterpret_cast<void*>(&encryptAlgo->key), KEY_LENGTH)); EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(authKey.data()), - reinterpret_cast<void*>(&authAlgo.key), KEY_LENGTH)); + reinterpret_cast<void*>(&authAlgo->key), KEY_LENGTH)); if (mode == XfrmMode::TUNNEL) { if (params.xfrmInterfacesEnabled) { diff --git a/server/android.system.net.netd-service.xml b/server/android.system.net.netd-service.xml new file mode 100644 index 00000000..7152da19 --- /dev/null +++ b/server/android.system.net.netd-service.xml @@ -0,0 +1,7 @@ +<manifest version="1.0" type="framework"> + <hal format="aidl"> + <name>android.system.net.netd</name> + <version>1</version> + <fqname>INetd/default</fqname> + </hal> +</manifest> diff --git a/server/main.cpp b/server/main.cpp index 0e81d4e5..35c53de7 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -32,8 +32,11 @@ #include "log/log.h" +#include <android/binder_manager.h> +#include <android/binder_process.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> +#include <hidl/HidlTransportSupport.h> #include <netdutils/Stopwatch.h> #include <processgroup/processgroup.h> @@ -43,6 +46,7 @@ #include "MDnsService.h" #include "NFLogListener.h" #include "NetdConstants.h" +#include "NetdHwAidlService.h" #include "NetdHwService.h" #include "NetdNativeService.h" #include "NetlinkManager.h" @@ -64,6 +68,7 @@ using android::net::NetdHwService; using android::net::NetdNativeService; using android::net::NetlinkManager; using android::net::NFLogListener; +using android::net::aidl::NetdHwAidlService; using android::netdutils::Stopwatch; const char* const PID_FILE_PATH = "/data/misc/net/netd_pid"; @@ -162,8 +167,8 @@ int main() { } logListener = std::move(result.value()); auto status = gCtls->wakeupCtrl.init(logListener.get()); - if (!isOk(result)) { - gLog.error("Unable to init WakeupController: %s", toString(result).c_str()); + if (!isOk(status)) { + gLog.error("Unable to init WakeupController: %s", toString(status).c_str()); // We can still continue without wakeup packet logging. } } @@ -202,16 +207,26 @@ int main() { android::net::process::ScopedPidFile pidFile(PID_FILE_PATH); // Now that netd is ready to process commands, advertise service availability for HAL clients. + // Usage of this HAL is anticipated to be thin; one thread per HAL service should suffice, + // AIDL and HIDL. + android::hardware::configureRpcThreadpool(2, true /* callerWillJoin */); + IPCThreadState::self()->disableBackgroundScheduling(true); + + std::thread aidlService = std::thread(NetdHwAidlService::run); + sp<NetdHwService> mHwSvc(new NetdHwService()); + bool startedHidlService = true; if ((ret = mHwSvc->start()) != android::OK) { - ALOGE("Unable to start NetdHwService: %d", ret); - exit(1); + ALOGE("Unable to start HIDL NetdHwService: %d", ret); + startedHidlService = false; } + gLog.info("Registering NetdHwService: %" PRId64 "us", subTime.getTimeAndResetUs()); gLog.info("Netd started in %" PRId64 "us", s.timeTakenUs()); - - IPCThreadState::self()->joinThreadPool(); - + if (startedHidlService) { + IPCThreadState::self()->joinThreadPool(); + } + aidlService.join(); gLog.info("netd exiting"); exit(0); diff --git a/tests/Android.bp b/tests/Android.bp index ff918cc1..f590f768 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -75,6 +75,7 @@ cc_test { "device-tests", "vts" ], + isolated: false, require_root: true, defaults: [ "netd_aidl_interface_lateststable_cpp_static", @@ -84,7 +85,6 @@ cc_test { srcs: [ ":netd_integration_test_shared", "binder_test.cpp", - "bpf_base_test.cpp", "kernel_test.cpp", "netd_client_test.cpp", "netd_test.cpp", diff --git a/tests/benchmarks/dns_benchmark.cpp b/tests/benchmarks/dns_benchmark.cpp index b8f626e9..060e40a9 100644 --- a/tests/benchmarks/dns_benchmark.cpp +++ b/tests/benchmarks/dns_benchmark.cpp @@ -65,11 +65,12 @@ public: std::vector<std::string> domains = { "example.com" }; std::vector<std::string> servers; dns.SetupMappings(num_hosts, domains, &mappings); - dns.SetupDNSServers(MAXNS, mappings, &mDns, &servers); - - const std::vector<int> mDefaultParams_Binder = {300, 25, 8, 8, 1000}; - dns.SetResolversForNetwork(servers, domains, mDefaultParams_Binder); + dns.SetResolversFromParcel(ResolverParams::Builder() + .setDnsServers(servers) + .setDotServers({}) + .setDomains(domains) + .build()); } } diff --git a/tests/binder_test.cpp b/tests/binder_test.cpp index bc1e7393..33a9d69f 100644 --- a/tests/binder_test.cpp +++ b/tests/binder_test.cpp @@ -50,6 +50,7 @@ #include <android-base/scopeguard.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> +#include <android-base/test_utils.h> #include <android/multinetwork.h> #include <binder/IPCThreadState.h> #include <bpf/BpfMap.h> @@ -58,6 +59,7 @@ #include <com/android/internal/net/IOemNetd.h> #include <cutils/multiuser.h> #include <gtest/gtest.h> +#include <netdutils/NetNativeTestBase.h> #include <netutils/ifc.h> #include <utils/Errors.h> #include "Fwmark.h" @@ -144,6 +146,7 @@ using android::net::mdns::aidl::ResolutionInfo; using android::net::netd::aidl::NativeUidRangeConfig; using android::netdutils::getIfaceNames; using android::netdutils::IPAddress; +using android::netdutils::IPSockAddr; using android::netdutils::ScopedAddrinfo; using android::netdutils::sSyscalls; using android::netdutils::Stopwatch; @@ -175,7 +178,7 @@ static const in6_addr V6_ADDR = { {// 2001:db8:cafe::8888 .u6_addr8 = {0x20, 0x01, 0x0d, 0xb8, 0xca, 0xfe, 0, 0, 0, 0, 0, 0, 0, 0, 0x88, 0x88}}}; -class NetdBinderTest : public ::testing::Test { +class NetdBinderTest : public NetNativeTestBase { public: NetdBinderTest() { sp<IServiceManager> sm = android::defaultServiceManager(); @@ -243,6 +246,13 @@ class NetdBinderTest : public ::testing::Test { int vpnNetId, bool secure, std::vector<UidRangeParcel>&& appDefaultUidRanges, std::vector<UidRangeParcel>&& vpnUidRanges); + + void setupNetworkRoutesForVpnAndDefaultNetworks( + int systemDefaultNetId, int appDefaultNetId, int vpnNetId, int otherNetId, bool secure, + bool testV6, bool differentLocalRoutes, + std::vector<UidRangeParcel>&& appDefaultUidRanges, + std::vector<UidRangeParcel>&& vpnUidRanges); + protected: // Use -1 to represent that default network was not modified because // real netId must be an unsigned value. @@ -637,7 +647,7 @@ NativeUidRangeConfig makeNativeUidRangeConfig(unsigned netId, std::vector<UidRan int32_t subPriority) { NativeUidRangeConfig res; res.netId = netId; - res.uidRanges = move(uidRanges); + res.uidRanges = std::move(uidRanges); res.subPriority = subPriority; return res; @@ -1233,7 +1243,7 @@ TEST_F(NetdBinderTest, TetherGetStats) { for (const auto& path : { IPTABLES_PATH, IP6TABLES_PATH }) { delTetherCounterValues(path, intIface1, extIface1); delTetherCounterValues(path, intIface2, extIface2); - if (path == IP6TABLES_PATH) { + if (strcmp(path, IP6TABLES_PATH) == 0) { delTetherCounterValues(path, intIface3, extIface2); } } @@ -1678,6 +1688,10 @@ TEST_F(NetdBinderTest, NetworkAddRemoveRouteToLocalExcludeTable) { {IP_RULE_V6, "2001:db8::/32", ""}, }; + // This should ba aligned with V4_FIXED_LOCAL_PREFIXES in system/netd/server/RouteController.cpp + // An expandable array for fixed local prefix though it's only one element now. + static const char* kV4LocalPrefixes[] = {"224.0.0.0/24"}; + // Add test physical network const auto& config = makeNativeNetworkConfig(TEST_NETID1, NativeNetworkType::PHYSICAL, INetd::PERMISSION_NONE, false, false); @@ -1692,6 +1706,13 @@ TEST_F(NetdBinderTest, NetworkAddRemoveRouteToLocalExcludeTable) { EXPECT_TRUE(mNetd->networkSetDefault(TEST_NETID1).isOk()); std::string localTableName = std::string(sTun.name() + "_local"); + + // Verify the fixed routes exist in the local table. + for (size_t i = 0; i < std::size(kV4LocalPrefixes); i++) { + expectNetworkRouteExists(IP_RULE_V4, sTun.name(), kV4LocalPrefixes[i], "", + localTableName.c_str()); + } + // Set up link-local routes for connectivity to the "gateway" for (size_t i = 0; i < std::size(kDirectlyConnectedRoutes); i++) { const auto& td = kDirectlyConnectedRoutes[i]; @@ -2460,6 +2481,13 @@ TEST_F(NetdBinderTest, TetherInterfaceAddRemoveList) { status = mNetd->tetherInterfaceList(&ifList); EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); expectTetherInterfaceNotExists(ifList, sTun.name()); + + // Disable IPv6 tethering will disable IPv6 abilities by changing IPv6 settings(accept_ra, + // dad_transmits, accept_dad, disable_ipv6). See tetherInterfaceRemove in details. + // Re-init sTun to reset the interface to prevent affecting other test that requires IPv6 with + // the same interface. + sTun.destroy(); + sTun.init(); } TEST_F(NetdBinderTest, TetherDnsSetList) { @@ -3042,6 +3070,7 @@ TEST_F(NetdBinderTest, InterfaceSetEnableIPv6) { } TEST_F(NetdBinderTest, InterfaceSetMtu) { + const int currentMtu = getInterfaceMtu(sTun.name()); const int testMtu = 1200; // Add test physical network @@ -3054,6 +3083,10 @@ TEST_F(NetdBinderTest, InterfaceSetMtu) { EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); expectInterfaceMtu(sTun.name(), testMtu); + // restore the MTU back + status = mNetd->interfaceSetMtu(sTun.name(), currentMtu); + EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); + // Remove test physical network EXPECT_TRUE(mNetd->networkDestroy(TEST_NETID1).isOk()); } @@ -3486,23 +3519,6 @@ void NetdBinderTest::createVpnAndAppDefaultNetworkWithUid( namespace { -class ScopedUidChange { - public: - explicit ScopedUidChange(uid_t uid) : mInputUid(uid) { - mStoredUid = geteuid(); - if (mInputUid == mStoredUid) return; - EXPECT_TRUE(seteuid(uid) == 0); - } - ~ScopedUidChange() { - if (mInputUid == mStoredUid) return; - EXPECT_TRUE(seteuid(mStoredUid) == 0); - } - - private: - uid_t mInputUid; - uid_t mStoredUid; -}; - void clearQueue(int tunFd) { char buf[4096]; int ret; @@ -3521,34 +3537,53 @@ void checkDataReceived(int udpSocket, int tunFd, sockaddr* dstAddr, int addrLen) EXPECT_GT(read(tunFd, buf, sizeof(buf)), 0); } -bool sendIPv6PacketFromUid(uid_t uid, const in6_addr& dstAddr, Fwmark* fwmark, int tunFd, - bool doConnect = true) { +bool sendPacketFromUid(uid_t uid, IPSockAddr& dstAddr, Fwmark* fwmark, int tunFd, + bool doConnect = true) { + int family = dstAddr.family(); ScopedUidChange scopedUidChange(uid); - unique_fd testSocket(socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0)); - if (testSocket < 0) return false; + unique_fd testSocket(socket(family, SOCK_DGRAM | SOCK_CLOEXEC, 0)); - const sockaddr_in6 dst6 = { - .sin6_family = AF_INET6, - .sin6_port = 42, - .sin6_addr = dstAddr, - }; - if (doConnect && connect(testSocket, (sockaddr*)&dst6, sizeof(dst6)) == -1) return false; + if (testSocket < 0) return false; + const sockaddr_storage dst = IPSockAddr(dstAddr.ip(), dstAddr.port()); + if (doConnect && connect(testSocket, (sockaddr*)&dst, sizeof(dst)) == -1) return false; socklen_t fwmarkLen = sizeof(fwmark->intValue); EXPECT_NE(-1, getsockopt(testSocket, SOL_SOCKET, SO_MARK, &(fwmark->intValue), &fwmarkLen)); - char addr[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &dstAddr, addr, INET6_ADDRSTRLEN); - SCOPED_TRACE(StringPrintf("sendIPv6Packet, addr: %s, uid: %u, doConnect: %s", addr, uid, + int addr_len = (family == AF_INET) ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN; + char addr[addr_len]; + inet_ntop(family, &dstAddr, addr, addr_len); + SCOPED_TRACE(StringPrintf("sendPacket, addr: %s, uid: %u, doConnect: %s", addr, uid, doConnect ? "true" : "false")); if (doConnect) { checkDataReceived(testSocket, tunFd, nullptr, 0); } else { - checkDataReceived(testSocket, tunFd, (sockaddr*)&dst6, sizeof(dst6)); + checkDataReceived(testSocket, tunFd, (sockaddr*)&dst, sizeof(dst)); } + return true; } +bool sendIPv4PacketFromUid(uid_t uid, const in_addr& dstAddr, Fwmark* fwmark, int tunFd, + bool doConnect = true) { + const sockaddr_in dst = {.sin_family = AF_INET, .sin_port = 42, .sin_addr = dstAddr}; + IPSockAddr addr = IPSockAddr(dst); + + return sendPacketFromUid(uid, addr, fwmark, tunFd, doConnect); +} + +bool sendIPv6PacketFromUid(uid_t uid, const in6_addr& dstAddr, Fwmark* fwmark, int tunFd, + bool doConnect = true) { + const sockaddr_in6 dst6 = { + .sin6_family = AF_INET6, + .sin6_port = 42, + .sin6_addr = dstAddr, + }; + IPSockAddr addr = IPSockAddr(dst6); + + return sendPacketFromUid(uid, addr, fwmark, tunFd, doConnect); +} + // Send an IPv6 packet from the uid. Expect to fail and get specified errno. bool sendIPv6PacketFromUidFail(uid_t uid, const in6_addr& dstAddr, Fwmark* fwmark, bool doConnect, int expectedErr) { @@ -3893,7 +3928,7 @@ void verifyAppUidRules(std::vector<bool>&& expectedResults, std::vector<UidRange void verifyAppUidRules(std::vector<bool>&& expectedResults, NativeUidRangeConfig& uidRangeConfig, const std::string& iface) { - verifyAppUidRules(move(expectedResults), uidRangeConfig.uidRanges, iface, + verifyAppUidRules(std::move(expectedResults), uidRangeConfig.uidRanges, iface, uidRangeConfig.subPriority); } @@ -3982,7 +4017,7 @@ void expectUnreachableError(uid_t uid, unsigned netId, int selectionMode) { } // namespace -// Verify whether API reject overlapped UID ranges +// Verify how the API handle overlapped UID ranges TEST_F(NetdBinderTest, PerAppDefaultNetwork_OverlappedUidRanges) { const auto& config = makeNativeNetworkConfig(APP_DEFAULT_NETID, NativeNetworkType::PHYSICAL, INetd::PERMISSION_NONE, false, false); @@ -3996,28 +4031,23 @@ TEST_F(NetdBinderTest, PerAppDefaultNetwork_OverlappedUidRanges) { binder::Status status; status = mNetd->networkAddUidRanges(APP_DEFAULT_NETID, {makeUidRangeParcel(BASE_UID + 1, BASE_UID + 1)}); - EXPECT_FALSE(status.isOk()); - EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); + EXPECT_TRUE(status.isOk()); status = mNetd->networkAddUidRanges(APP_DEFAULT_NETID, {makeUidRangeParcel(BASE_UID + 9, BASE_UID + 10)}); - EXPECT_FALSE(status.isOk()); - EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); + EXPECT_TRUE(status.isOk()); status = mNetd->networkAddUidRanges(APP_DEFAULT_NETID, {makeUidRangeParcel(BASE_UID + 11, BASE_UID + 11)}); - EXPECT_FALSE(status.isOk()); - EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); + EXPECT_TRUE(status.isOk()); status = mNetd->networkAddUidRanges(APP_DEFAULT_NETID, {makeUidRangeParcel(BASE_UID + 12, BASE_UID + 13)}); - EXPECT_FALSE(status.isOk()); - EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); + EXPECT_TRUE(status.isOk()); status = mNetd->networkAddUidRanges(APP_DEFAULT_NETID, {makeUidRangeParcel(BASE_UID + 9, BASE_UID + 13)}); - EXPECT_FALSE(status.isOk()); - EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); + EXPECT_TRUE(status.isOk()); std::vector<UidRangeParcel> selfOverlappedUidRanges = { makeUidRangeParcel(BASE_UID + 20, BASE_UID + 20), @@ -4356,6 +4386,311 @@ TEST_P(VpnParameterizedTest, UnconnectedSocket) { expectPacketSentOnNetId(TEST_UID2, NETID_UNSET, vpnFd, UNCONNECTED_SOCKET); } +class VpnLocalRoutesParameterizedTest + : public NetdBinderTest, + public testing::WithParamInterface<std::tuple<int, int, bool, bool, bool, bool>> { + protected: + // Local/non-local addresses based on the route added in + // setupNetworkRoutesForVpnAndDefaultNetworks. + in_addr V4_LOCAL_ADDR = {htonl(0xC0A80008)}; // 192.168.0.8 + in_addr V4_APP_LOCAL_ADDR = {htonl(0xAC100008)}; // 172.16.0.8 + in_addr V4_GLOBAL_ADDR = {htonl(0x08080808)}; // 8.8.8.8 + + in6_addr V6_LOCAL_ADDR = { + {// 2001:db8:cafe::1 + .u6_addr8 = {0x20, 0x01, 0x0d, 0xb8, 0xca, 0xfe, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}}; + in6_addr V6_APP_LOCAL_ADDR = { + {// 2607:f0d0:1234::4 + .u6_addr8 = {0x26, 0x07, 0xf0, 0xd0, 0x12, 0x34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4}}}; + in6_addr V6_GLOBAL_ADDR = { + {// 2607:1234:1002::4 + .u6_addr8 = {0x26, 0x07, 0x12, 0x34, 0x10, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4}}}; +}; + +const int SEND_TO_GLOBAL = 0; +const int SEND_TO_SYSTEM_DEFAULT_LOCAL = 1; +const int SEND_TO_PER_APP_DEFAULT_LOCAL = 2; + +// Exercise the combination of different explicitly selected network, different uid, local/non-local +// address on local route exclusion VPN. E.g. +// explicitlySelected systemDefault + uid in VPN range + no app default + non local address +// explicitlySelected systemDefault + uid in VPN range + has app default + non local address +// explicitlySelected systemDefault + uid in VPN range + has app default + local address +// explicitlySelected appDefault + uid not in VPN range + has app default + non local address +INSTANTIATE_TEST_SUITE_P( + PerAppDefaultNetwork, VpnLocalRoutesParameterizedTest, + testing::Combine(testing::Values(SYSTEM_DEFAULT_NETID, APP_DEFAULT_NETID, NETID_UNSET), + testing::Values(SEND_TO_GLOBAL, SEND_TO_SYSTEM_DEFAULT_LOCAL, + SEND_TO_PER_APP_DEFAULT_LOCAL), + testing::Bool(), testing::Bool(), testing::Bool(), testing::Bool()), + [](const testing::TestParamInfo<std::tuple<int, int, bool, bool, bool, bool>>& info) { + std::string explicitlySelected; + switch (std::get<0>(info.param)) { + case SYSTEM_DEFAULT_NETID: + explicitlySelected = "explicitlySelectedSystemDefault"; + break; + case APP_DEFAULT_NETID: + explicitlySelected = "explicitlySelectedAppDefault"; + break; + case NETID_UNSET: + explicitlySelected = "implicitlySelected"; + break; + default: + explicitlySelected = "InvalidParameter"; // Should not happen. + } + + std::string sendToAddr; + switch (std::get<1>(info.param)) { + case SEND_TO_GLOBAL: + sendToAddr = "GlobalAddr"; + break; + case SEND_TO_SYSTEM_DEFAULT_LOCAL: + sendToAddr = "SystemLocal"; + break; + case SEND_TO_PER_APP_DEFAULT_LOCAL: + sendToAddr = "AppLocal"; + break; + default: + sendToAddr = "InvalidAddr"; // Should not happen. + } + + const std::string isSubjectToVpn = std::get<2>(info.param) + ? std::string("SubjectToVpn") + : std::string("NotSubjectToVpn"); + + const std::string hasAppDefaultNetwork = std::get<3>(info.param) + ? std::string("HasAppDefault") + : std::string("NothasAppDefault"); + + const std::string testV6 = + std::get<4>(info.param) ? std::string("v6") : std::string("v4"); + + // Apply the same or different local address in app default and system default. + const std::string differentLocalRoutes = std::get<5>(info.param) + ? std::string("DifferentLocalRoutes") + : std::string("SameLocalAddr"); + + return explicitlySelected + "_uid" + isSubjectToVpn + hasAppDefaultNetwork + + "Range_with" + testV6 + sendToAddr + differentLocalRoutes; + }); + +int getTargetIfaceForLocalRoutesExclusion(bool isSubjectToVpn, bool hasAppDefaultNetwork, + bool differentLocalRoutes, int sendToAddr, + int selectedNetId, int fallthroughFd, int appDefaultFd, + int vpnFd) { + int expectedIface; + + // Setup the expected interface based on the condition. + if (isSubjectToVpn && hasAppDefaultNetwork) { + switch (sendToAddr) { + case SEND_TO_GLOBAL: + expectedIface = vpnFd; + break; + case SEND_TO_SYSTEM_DEFAULT_LOCAL: + // Go to app default if the app default and system default are the same range + // TODO(b/237351736): It should go to VPN if the system local and app local are + // different. + expectedIface = differentLocalRoutes ? fallthroughFd : appDefaultFd; + break; + case SEND_TO_PER_APP_DEFAULT_LOCAL: + expectedIface = appDefaultFd; + break; + default: + expectedIface = -1; // should not happen + } + } else if (isSubjectToVpn && !hasAppDefaultNetwork) { + switch (sendToAddr) { + case SEND_TO_GLOBAL: + expectedIface = vpnFd; + break; + case SEND_TO_SYSTEM_DEFAULT_LOCAL: + // TODO(b/237351736): It should go to app default if the system local and app local + // are different. + expectedIface = fallthroughFd; + break; + case SEND_TO_PER_APP_DEFAULT_LOCAL: + // Go to system default if the system default and app default are the same range. + expectedIface = differentLocalRoutes ? vpnFd : fallthroughFd; + break; + default: + expectedIface = -1; // should not happen + } + } else if (!isSubjectToVpn && hasAppDefaultNetwork) { + expectedIface = appDefaultFd; + } else { // !isSubjectToVpn && !hasAppDefaultNetwork + expectedIface = fallthroughFd; + } + + // Override the target if it's explicitly selected. + switch (selectedNetId) { + case SYSTEM_DEFAULT_NETID: + expectedIface = fallthroughFd; + break; + case APP_DEFAULT_NETID: + expectedIface = appDefaultFd; + break; + default: + break; + // Based on the uid range. + } + + return expectedIface; +} + +// Routes configured on the system default network and on the VPN. +// This allows the test to verify the worst case where the physical network and the VPN configure +// the same routes. This ensures that routing is determined by the IP rules and doesn't just happen +// to work because the routes don't overlap. If differentLocalRoutes is false, these routes are also +// configured on the per-app default network. +// For both IPv4 and IPv6, the first route is local, the second is not. +std::vector<std::string> SYSTEM_DEFAULT_ROUTES = {"192.168.0.0/16", "0.0.0.0/0", + "2001:db8:cafe::/48", "::/0"}; +// Routes configured on the per-app default network if differentLocalRoutes is true. +// For both IPv4 and IPv6, the first route is local, the second is not. +std::vector<std::string> APP_DEFAULT_ROUTES = {"172.16.0.0/16", "0.0.0.0/0", "2607:f0d0:1234::/48", + "::/0"}; +void NetdBinderTest::setupNetworkRoutesForVpnAndDefaultNetworks( + int systemDefaultNetId, int appDefaultNetId, int vpnNetId, int otherNetId, bool secure, + bool testV6, bool differentLocalRoutes, std::vector<UidRangeParcel>&& appDefaultUidRanges, + std::vector<UidRangeParcel>&& vpnUidRanges) { + // Create a physical network on sTun, and set it as the system default network + createAndSetDefaultNetwork(systemDefaultNetId, sTun.name()); + + // Routes are configured to system default, app default and vpn network to verify if the packets + // are routed correctly. + + // Setup system default routing. + for (const auto& route : SYSTEM_DEFAULT_ROUTES) { + EXPECT_TRUE(mNetd->networkAddRoute(systemDefaultNetId, sTun.name(), route, "").isOk()); + } + + // Create another physical network on sTun2 as per app default network + createPhysicalNetwork(appDefaultNetId, sTun2.name()); + + // Setup app default routing. + std::vector<std::string> appDefaultRoutes = + (differentLocalRoutes ? APP_DEFAULT_ROUTES : SYSTEM_DEFAULT_ROUTES); + for (const auto& route : appDefaultRoutes) { + EXPECT_TRUE(mNetd->networkAddRoute(appDefaultNetId, sTun2.name(), route, "").isOk()); + } + + // Create a bypassable VPN on sTun3. + auto config = makeNativeNetworkConfig(vpnNetId, NativeNetworkType::VIRTUAL, + INetd::PERMISSION_NONE, secure, true); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); + EXPECT_TRUE(mNetd->networkAddInterface(vpnNetId, sTun3.name()).isOk()); + + // Setup vpn routing. + for (const auto& route : SYSTEM_DEFAULT_ROUTES) { + EXPECT_TRUE(mNetd->networkAddRoute(vpnNetId, sTun3.name(), route, "").isOk()); + } + + // Create another interface that is neither system default nor the app default to make sure + // the traffic won't be mis-routed. + createPhysicalNetwork(otherNetId, sTun4.name()); + EXPECT_TRUE(mNetd->networkAddRoute(otherNetId, sTun4.name(), testV6 ? "::/0" : "0.0.0.0/0", "") + .isOk()); + // Add per-app uid ranges. + EXPECT_TRUE(mNetd->networkAddUidRanges(appDefaultNetId, appDefaultUidRanges).isOk()); + + // Add VPN uid ranges. + EXPECT_TRUE(mNetd->networkAddUidRanges(vpnNetId, vpnUidRanges).isOk()); +} + +// Rules are in approximately the following order for bypassable VPNs that allow local network +// access: +// - Local routes to the per-app default network (UID guarded) +// - Local routes to the system default network +// - Both local and global routs to VPN network (UID guarded) +// - Global routes to per-app default network(UID guarded) +// - Global routes to system default network +TEST_P(VpnLocalRoutesParameterizedTest, localRoutesExclusion) { + int selectedNetId; + int sendToAddr; + bool isSubjectToVpn; + bool hasAppDefaultNetwork; + bool testV6; + bool differentLocalRoutes; + + std::tie(selectedNetId, sendToAddr, isSubjectToVpn, hasAppDefaultNetwork, testV6, + differentLocalRoutes) = GetParam(); + + setupNetworkRoutesForVpnAndDefaultNetworks( + SYSTEM_DEFAULT_NETID, APP_DEFAULT_NETID, VPN_NETID, TEST_NETID4, false /* secure */, + testV6, differentLocalRoutes, + // Setup uid ranges for app default and VPN. Configure TEST_UID2 into both app default + // and VPN to verify the behavior when the uid exists in both network. + {makeUidRangeParcel(TEST_UID2, TEST_UID1)}, {makeUidRangeParcel(TEST_UID3, TEST_UID2)}); + + int fallthroughFd = sTun.getFdForTesting(); + int appDefaultFd = sTun2.getFdForTesting(); + int vpnFd = sTun3.getFdForTesting(); + + // Explicitly select network + setNetworkForProcess(selectedNetId); + + int targetUid; + + // Setup the expected testing uid + if (isSubjectToVpn) { + if (hasAppDefaultNetwork) { + targetUid = TEST_UID2; + } else { + targetUid = TEST_UID3; + } + } else { + if (hasAppDefaultNetwork) { + targetUid = TEST_UID1; + } else { + targetUid = TEST_UID4; // Not in any of the UID ranges. + } + } + + // Get expected interface for the traffic. + int expectedIface = getTargetIfaceForLocalRoutesExclusion( + isSubjectToVpn, hasAppDefaultNetwork, differentLocalRoutes, sendToAddr, selectedNetId, + fallthroughFd, appDefaultFd, vpnFd); + + // Verify the packets are sent to the expected interface. + Fwmark fwmark; + if (testV6) { + in6_addr addr; + switch (sendToAddr) { + case SEND_TO_GLOBAL: + addr = V6_GLOBAL_ADDR; + break; + case SEND_TO_SYSTEM_DEFAULT_LOCAL: + addr = V6_LOCAL_ADDR; + break; + case SEND_TO_PER_APP_DEFAULT_LOCAL: + addr = differentLocalRoutes ? V6_APP_LOCAL_ADDR : V6_LOCAL_ADDR; + break; + default: + break; + // should not happen + } + EXPECT_TRUE(sendIPv6PacketFromUid(targetUid, addr, &fwmark, expectedIface)); + } else { + in_addr addr; + switch (sendToAddr) { + case SEND_TO_GLOBAL: + addr = V4_GLOBAL_ADDR; + break; + case SEND_TO_SYSTEM_DEFAULT_LOCAL: + addr = V4_LOCAL_ADDR; + break; + case SEND_TO_PER_APP_DEFAULT_LOCAL: + addr = differentLocalRoutes ? V4_APP_LOCAL_ADDR : V4_LOCAL_ADDR; + break; + default: + break; + // should not happen + } + + EXPECT_TRUE(sendIPv4PacketFromUid(targetUid, addr, &fwmark, expectedIface)); + } +} + TEST_F(NetdBinderTest, NetworkCreate) { auto config = makeNativeNetworkConfig(TEST_NETID1, NativeNetworkType::PHYSICAL, INetd::PERMISSION_NONE, false, false); @@ -4410,11 +4745,6 @@ TEST_F(NetdBinderTest, UidRangeSubPriority_ValidateInputs) { uidRangeConfig.subPriority = SUB_PRIORITY_2; EXPECT_TRUE(mNetd->networkAddUidRangesParcel(uidRangeConfig).isOk()); - // For a single network, identical UID ranges with the same priority is invalid. - status = mNetd->networkAddUidRangesParcel(uidRangeConfig); - EXPECT_FALSE(status.isOk()); - EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); - // Overlapping ranges is invalid. uidRangeConfig.uidRanges = {makeUidRangeParcel(BASE_UID + 1, BASE_UID + 1), makeUidRangeParcel(BASE_UID + 1, BASE_UID + 1)}; @@ -4792,8 +5122,39 @@ TEST_F(PerAppNetworkPermissionsTest, PermissionOnlyAffectsUid) { } } -class MDnsBinderTest : public ::testing::Test { +class MDnsBinderTest : public NetNativeTestBase { public: + class TestMDnsListener : public android::net::mdns::aidl::BnMDnsEventListener { + public: + Status onServiceRegistrationStatus(const RegistrationInfo& /*status*/) override { + // no-op + return Status::ok(); + } + Status onServiceDiscoveryStatus(const DiscoveryInfo& /*status*/) override { + // no-op + return Status::ok(); + } + Status onServiceResolutionStatus(const ResolutionInfo& /*status*/) override { + // no-op + return Status::ok(); + } + Status onGettingServiceAddressStatus(const GetAddressInfo& status) override { + if (status.id == mOperationId) { + std::lock_guard lock(mCvMutex); + mCv.notify_one(); + } + return Status::ok(); + } + std::condition_variable& getCv() { return mCv; } + std::mutex& getCvMutex() { return mCvMutex; } + void setOperationId(int operationId) { mOperationId = operationId; } + + private: + std::mutex mCvMutex; + std::condition_variable mCv; + int mOperationId; + }; + MDnsBinderTest() { sp<IServiceManager> sm = android::defaultServiceManager(); sp<IBinder> binder = sm->getService(String16("mdns")); @@ -4802,31 +5163,46 @@ class MDnsBinderTest : public ::testing::Test { } } - void SetUp() override { ASSERT_NE(nullptr, mMDns.get()); } + void SetUp() override { + ASSERT_NE(nullptr, mMDns.get()); + // Start the daemon for mdns operations. + mDaemonStarted = mMDns->startDaemon().isOk(); + } - void TearDown() override {} + void TearDown() override { + if (mDaemonStarted) mMDns->stopDaemon(); + } + + std::cv_status getServiceAddress(int operationId, const sp<TestMDnsListener>& listener); protected: sp<IMDns> mMDns; -}; -class TestMDnsListener : public android::net::mdns::aidl::BnMDnsEventListener { - public: - Status onServiceRegistrationStatus(const RegistrationInfo& /* status */) override { - return Status::ok(); - } - Status onServiceDiscoveryStatus(const DiscoveryInfo& /* status */) override { - return Status::ok(); - } - Status onServiceResolutionStatus(const ResolutionInfo& /* status */) override { - return Status::ok(); - } - Status onGettingServiceAddressStatus(const GetAddressInfo& /* status */) override { - return Status::ok(); - } + private: + bool mDaemonStarted = false; }; +std::cv_status MDnsBinderTest::getServiceAddress(int operationId, + const sp<TestMDnsListener>& listener) { + GetAddressInfo info; + info.id = operationId; + info.hostname = "Android.local"; + info.interfaceIdx = 0; + binder::Status status = mMDns->getServiceAddress(info); + EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); + + auto& cv = listener->getCv(); + auto& cvMutex = listener->getCvMutex(); + std::unique_lock lock(cvMutex); + // Wait for a long time to prevent test flaky. + return cv.wait_for(lock, std::chrono::milliseconds(2500)); +} + TEST_F(MDnsBinderTest, EventListenerTest) { + SKIP_WITH_HWASAN; // TODO(b/253513842): Re-enable. + // Start the Binder thread pool. + android::ProcessState::self()->startThreadPool(); + // Register a null listener. binder::Status status = mMDns->registerEventListener(nullptr); EXPECT_FALSE(status.isOk()); @@ -4835,8 +5211,8 @@ TEST_F(MDnsBinderTest, EventListenerTest) { status = mMDns->unregisterEventListener(nullptr); EXPECT_FALSE(status.isOk()); - // Register the test listener. - android::sp<TestMDnsListener> testListener = new TestMDnsListener(); + // Register a test listener + auto testListener = android::sp<TestMDnsListener>::make(); status = mMDns->registerEventListener(testListener); EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); @@ -4844,7 +5220,28 @@ TEST_F(MDnsBinderTest, EventListenerTest) { status = mMDns->registerEventListener(testListener); EXPECT_FALSE(status.isOk()); + // Verify the listener can receive callback. + int id = arc4random_uniform(10000); // use random number + testListener->setOperationId(id); + EXPECT_EQ(std::cv_status::no_timeout, getServiceAddress(id, testListener)); + // Stop getting address operation to release the service reference on MDnsSd + status = mMDns->stopOperation(id); + EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); + // Unregister the test listener status = mMDns->unregisterEventListener(testListener); EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); + + // Verify the listener can not receive callback. + testListener->setOperationId(id + 1); + EXPECT_EQ(std::cv_status::timeout, getServiceAddress(id + 1, testListener)); + // Stop getting address operation to release the service reference on MDnsSd + status = mMDns->stopOperation(id + 1); + EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); + + // Registering and unregistering the listener again should work. + status = mMDns->registerEventListener(testListener); + EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); + status = mMDns->unregisterEventListener(testListener); + EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); } diff --git a/tests/bpf_base_test.cpp b/tests/bpf_base_test.cpp deleted file mode 100644 index 187ab8b3..00000000 --- a/tests/bpf_base_test.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (C) 2018 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 <string> - -#include <fcntl.h> -#include <inttypes.h> -#include <limits.h> -#include <linux/inet_diag.h> -#include <linux/sock_diag.h> -#include <net/if.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> - -#include <gtest/gtest.h> - -#include <cutils/qtaguid.h> -#include <processgroup/processgroup.h> - -#include <android-base/stringprintf.h> -#include <android-base/strings.h> - -#include "bpf/BpfMap.h" -#include "bpf/BpfUtils.h" -#include "bpf_shared.h" - -using android::base::Result; - -namespace android { -namespace bpf { - -// Use the upper limit of uid to avoid conflict with real app uids. We can't use UID_MAX because -// it's -1, which is INVALID_UID. -constexpr uid_t TEST_UID = UID_MAX - 1; -constexpr uint32_t TEST_TAG = 42; - -class BpfBasicTest : public testing::Test { - protected: - BpfBasicTest() {} -}; - -TEST_F(BpfBasicTest, TestCgroupMounted) { - std::string cg2_path; - ASSERT_EQ(true, CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &cg2_path)); - ASSERT_EQ(0, access(cg2_path.c_str(), R_OK)); - ASSERT_EQ(0, access((cg2_path + "/cgroup.controllers").c_str(), R_OK)); -} - -TEST_F(BpfBasicTest, TestTrafficControllerSetUp) { - ASSERT_EQ(0, access(BPF_EGRESS_PROG_PATH, R_OK)); - ASSERT_EQ(0, access(BPF_INGRESS_PROG_PATH, R_OK)); - ASSERT_EQ(0, access(XT_BPF_INGRESS_PROG_PATH, R_OK)); - ASSERT_EQ(0, access(XT_BPF_EGRESS_PROG_PATH, R_OK)); - ASSERT_EQ(0, access(COOKIE_TAG_MAP_PATH, R_OK)); - ASSERT_EQ(0, access(UID_COUNTERSET_MAP_PATH, R_OK)); - ASSERT_EQ(0, access(STATS_MAP_A_PATH, R_OK)); - ASSERT_EQ(0, access(STATS_MAP_B_PATH, R_OK)); - ASSERT_EQ(0, access(IFACE_INDEX_NAME_MAP_PATH, R_OK)); - ASSERT_EQ(0, access(IFACE_STATS_MAP_PATH, R_OK)); - ASSERT_EQ(0, access(CONFIGURATION_MAP_PATH, R_OK)); - ASSERT_EQ(0, access(UID_OWNER_MAP_PATH, R_OK)); -} - -TEST_F(BpfBasicTest, TestSocketFilterSetUp) { - ASSERT_EQ(0, access(CGROUP_SOCKET_PROG_PATH, R_OK)); - ASSERT_EQ(0, access(UID_PERMISSION_MAP_PATH, R_OK)); -} - -TEST_F(BpfBasicTest, TestTagSocket) { - BpfMap<uint64_t, UidTagValue> cookieTagMap(COOKIE_TAG_MAP_PATH); - ASSERT_TRUE(cookieTagMap.isValid()); - int sock = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0); - ASSERT_LE(0, sock); - uint64_t cookie = getSocketCookie(sock); - ASSERT_NE(NONEXISTENT_COOKIE, cookie); - ASSERT_EQ(0, qtaguid_tagSocket(sock, TEST_TAG, TEST_UID)); - Result<UidTagValue> tagResult = cookieTagMap.readValue(cookie); - ASSERT_RESULT_OK(tagResult); - ASSERT_EQ(TEST_UID, tagResult.value().uid); - ASSERT_EQ(TEST_TAG, tagResult.value().tag); - ASSERT_EQ(0, qtaguid_untagSocket(sock)); - tagResult = cookieTagMap.readValue(cookie); - ASSERT_FALSE(tagResult.ok()); - ASSERT_EQ(ENOENT, tagResult.error().code()); -} - -TEST_F(BpfBasicTest, TestCloseSocketWithoutUntag) { - BpfMap<uint64_t, UidTagValue> cookieTagMap(COOKIE_TAG_MAP_PATH); - ASSERT_TRUE(cookieTagMap.isValid()); - int sock = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0); - ASSERT_LE(0, sock); - uint64_t cookie = getSocketCookie(sock); - ASSERT_NE(NONEXISTENT_COOKIE, cookie); - ASSERT_EQ(0, qtaguid_tagSocket(sock, TEST_TAG, TEST_UID)); - Result<UidTagValue> tagResult = cookieTagMap.readValue(cookie); - ASSERT_RESULT_OK(tagResult); - ASSERT_EQ(TEST_UID, tagResult.value().uid); - ASSERT_EQ(TEST_TAG, tagResult.value().tag); - ASSERT_EQ(0, close(sock)); - // Check map periodically until sk destroy handler have done its job. - for (int i = 0; i < 10; i++) { - usleep(5000); // 5ms - tagResult = cookieTagMap.readValue(cookie); - if (!tagResult.ok()) { - ASSERT_EQ(ENOENT, tagResult.error().code()); - return; - } - } - FAIL() << "socket tag still exist after 50ms"; -} - -} -} diff --git a/tests/kernel_test.cpp b/tests/kernel_test.cpp index 2cc5c99a..b9e1875e 100644 --- a/tests/kernel_test.cpp +++ b/tests/kernel_test.cpp @@ -58,6 +58,7 @@ bool isGsiImage() { * CONFIG_NET_CLS_MATCHALL=y * CONFIG_NET_ACT_POLICE=y * CONFIG_NET_ACT_BPF=y + * CONFIG_BPF_JIT=y */ TEST(KernelTest, TestRateLimitingSupport) { if (isGsiImage()) { @@ -68,6 +69,7 @@ TEST(KernelTest, TestRateLimitingSupport) { ASSERT_TRUE(configVerifier.hasOption("CONFIG_NET_CLS_MATCHALL")); ASSERT_TRUE(configVerifier.hasOption("CONFIG_NET_ACT_POLICE")); ASSERT_TRUE(configVerifier.hasOption("CONFIG_NET_ACT_BPF")); + ASSERT_TRUE(configVerifier.hasOption("CONFIG_BPF_JIT")); } } // namespace net diff --git a/tests/netd_test.cpp b/tests/netd_test.cpp index 8d5d8bc0..a04a6a76 100644 --- a/tests/netd_test.cpp +++ b/tests/netd_test.cpp @@ -29,6 +29,7 @@ #include <gtest/gtest.h> +#include <android-base/stringprintf.h> #include <android-base/unique_fd.h> #define LOG_TAG "NetdTest" @@ -70,6 +71,47 @@ TEST(NetdSELinuxTest, CheckProperMTULabels) { "'^u:object_r:sysfs_net:s0 /sys/class/net/'")); } +static void assertBpfContext(const char* const target, const char* const label) { + // Use 'ls' cli utility to print the selinux context of the target directory or file. + // egrep -q will return 0 if it matches, ie. if the selinux context is as expected + std::string cmd = android::base::StringPrintf("ls -dZ %s | egrep -q '^u:object_r:%s:s0 %s$'", + target, label, target); + + // NOLINTNEXTLINE(cert-env33-c) + ASSERT_EQ(W_EXITCODE(0, 0), system(cmd.c_str())) << cmd << " - did not return success(0)" + " - is kernel missing https://android-review.googlesource.com/c/kernel/common/+/1831252" + " 'UPSTREAM: security: selinux: allow per-file labeling for bpffs' ?"; +} + +// This test will fail if kernel is missing: +// https://android-review.googlesource.com/c/kernel/common/+/1831252 +// UPSTREAM: security: selinux: allow per-file labeling for bpffs +TEST(NetdSELinuxTest, CheckProperBpfLabels) { + assertBpfContext("/sys/fs/bpf", "fs_bpf"); + assertBpfContext("/sys/fs/bpf/net_private", "fs_bpf_net_private"); + assertBpfContext("/sys/fs/bpf/net_shared", "fs_bpf_net_shared"); + assertBpfContext("/sys/fs/bpf/netd_readonly", "fs_bpf_netd_readonly"); + assertBpfContext("/sys/fs/bpf/netd_shared", "fs_bpf_netd_shared"); + assertBpfContext("/sys/fs/bpf/vendor", "fs_bpf_vendor"); + assertBpfContext("/sys/fs/bpf/loader", "fs_bpf_loader"); +} + +bool isTetheringInProcess() { + int v = access("/apex/com.android.tethering/etc/flag/in-process", F_OK); + if (!v) return true; + EXPECT_EQ(v, -1) << "expected return of found(0) or notfound(-1/ENOENT)"; + EXPECT_EQ(errno, ENOENT) << "expected return of found(0) or notfound(-1/ENOENT)"; + return false; +} + +TEST(NetdSELinuxTest, CheckProperBpfTetheringLabels) { + if (isTetheringInProcess()) { + assertBpfContext("/sys/fs/bpf/net_shared/tethering", "fs_bpf_net_shared"); + } else { + assertBpfContext("/sys/fs/bpf/tethering", "fs_bpf_tethering"); + } +} + // Trivial thread function that simply immediately terminates successfully. static int thread(void*) { return 0; diff --git a/tests/sock_diag_test.cpp b/tests/sock_diag_test.cpp index 8ee99083..5d142dc4 100644 --- a/tests/sock_diag_test.cpp +++ b/tests/sock_diag_test.cpp @@ -21,6 +21,7 @@ #include <linux/inet_diag.h> #include <gtest/gtest.h> +#include <netdutils/NetNativeTestBase.h> #include "NetdConstants.h" #include "SockDiag.h" @@ -29,7 +30,7 @@ #define NUM_SOCKETS 500 -class SockDiagTest : public ::testing::Test { +class SockDiagTest : public NetNativeTestBase { }; uint16_t bindAndListen(int s) { diff --git a/tests/test_utils.h b/tests/test_utils.h index de6c221f..25642558 100644 --- a/tests/test_utils.h +++ b/tests/test_utils.h @@ -21,6 +21,9 @@ #include <string> #include <vector> +#include <gtest/gtest.h> +#include <unistd.h> + int randomUid(); std::vector<std::string> runCommand(const std::string& command); @@ -37,3 +40,21 @@ std::vector<std::string> listIpRoutes(const char* ipVersion, const char* table); bool ipRouteExists(const char* ipVersion, const char* table, const std::vector<std::string>& ipRouteSubstrings); + +// Require root (for seteuid). +class ScopedUidChange { + public: + explicit ScopedUidChange(uid_t uid) : mInputUid(uid) { + mStoredUid = geteuid(); + if (mInputUid == mStoredUid) return; + EXPECT_TRUE(seteuid(uid) == 0); + } + ~ScopedUidChange() { + if (mInputUid == mStoredUid) return; + EXPECT_TRUE(seteuid(mStoredUid) == 0); + } + + private: + uid_t mInputUid; + uid_t mStoredUid; +}; |