diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-06-20 20:35:11 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-06-20 20:35:11 +0000 |
commit | 719109b5f45ae83ad58cad15b339f5f28ed2a046 (patch) | |
tree | 7079b623b3832bfd7ec0ac58c772d4d3621a28cb | |
parent | c5dfeeddc7725307937b56498981a17fc0861cc5 (diff) | |
parent | e4bff459efa16b95eb00c9114f36f3243756712f (diff) | |
download | netd-aml_sch_331111000.tar.gz |
Snap for 8746144 from e4bff459efa16b95eb00c9114f36f3243756712f to mainline-scheduling-releaseaml_sch_331113000aml_sch_331111000android13-mainline-scheduling-release
Change-Id: Ib763a4df8b070a910f7740c00a625a6a18ce093c
-rw-r--r-- | Android.bp | 11 | ||||
-rw-r--r-- | include/mainline/XtBpfProgLocations.h | 37 | ||||
-rw-r--r-- | server/Android.bp | 30 | ||||
-rw-r--r-- | server/BandwidthController.cpp | 7 | ||||
-rw-r--r-- | server/BandwidthControllerTest.cpp | 2 | ||||
-rw-r--r-- | server/Controllers.cpp | 10 | ||||
-rw-r--r-- | server/MDnsEventReporter.cpp | 52 | ||||
-rw-r--r-- | server/MDnsEventReporter.h | 21 | ||||
-rw-r--r-- | server/MDnsSdListener.cpp | 8 | ||||
-rw-r--r-- | server/RouteController.cpp | 127 | ||||
-rw-r--r-- | server/RouteController.h | 27 | ||||
-rw-r--r-- | server/TcUtils.h | 52 | ||||
-rw-r--r-- | server/TetherController.h | 3 | ||||
-rw-r--r-- | tests/Android.bp | 12 | ||||
-rw-r--r-- | tests/benchmarks/Android.bp | 6 | ||||
-rw-r--r-- | tests/binder_test.cpp | 475 |
16 files changed, 426 insertions, 454 deletions
@@ -33,6 +33,17 @@ cc_library_headers { min_sdk_version: "29", } +cc_library_headers { + name: "netd_mainline_headers", + export_include_dirs: ["include/mainline"], + apex_available: [ + "//apex_available:platform", + "com.android.tethering", + ], + sdk_version: "29", + min_sdk_version: "29", +} + cc_defaults { name: "netd_defaults", cflags: [ diff --git a/include/mainline/XtBpfProgLocations.h b/include/mainline/XtBpfProgLocations.h new file mode 100644 index 00000000..95a5742c --- /dev/null +++ b/include/mainline/XtBpfProgLocations.h @@ -0,0 +1,37 @@ +/* + * 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 + +/* -=-=-=-=-= WARNING -=-=-=-=-=- + * + * DO *NOT* *EVER* CHANGE THESE - they *MUST* match what the Tethering mainline module provides! + * + * You cannot even change them in sync, since newer module must work on older Android T releases. + * + * You could with difficulty, uprevs of the bpfloader, api detection logic in mainline, etc, + * change this in Android U or later, but even that is a very bad idea and not worth the hassle. + * + * + * Mainline Tethering module on T+ is expected to make available to netd (for use by + * BandwidthController iptables initialization code) four xt_bpf programs at the following + * locations: + */ +#define XT_BPF_NETD(NAME) "/sys/fs/bpf/netd_shared/prog_netd_skfilter_" NAME "_xtbpf" +#define XT_BPF_ALLOWLIST_PROG_PATH XT_BPF_NETD("allowlist") +#define XT_BPF_DENYLIST_PROG_PATH XT_BPF_NETD("denylist") +#define XT_BPF_EGRESS_PROG_PATH XT_BPF_NETD("egress") +#define XT_BPF_INGRESS_PROG_PATH XT_BPF_NETD("ingress") diff --git a/server/Android.bp b/server/Android.bp index f29f6cfc..06ea0bab 100644 --- a/server/Android.bp +++ b/server/Android.bp @@ -37,12 +37,15 @@ filegroup { // Modules common to both netd and netd_unit_test cc_library_static { name: "libnetd_server", - defaults: ["netd_defaults"], + defaults: [ + "netd_aidl_interface_lateststable_cpp_shared", + "netd_defaults", + ], include_dirs: [ "system/netd/include", "system/netd/server/binder", ], - header_libs: ["bpf_connectivity_headers"], + header_libs: ["bpf_headers"], srcs: [ "BandwidthController.cpp", "Controllers.cpp", @@ -71,7 +74,6 @@ cc_library_static { "libpcap", "libssl", "libsysutils", - "netd_aidl_interface-V8-cpp", "netd_event_listener_interface-V1-cpp", ], static_libs: [ @@ -86,7 +88,10 @@ cc_library_static { cc_binary { name: "netd", - defaults: ["netd_defaults"], + defaults: [ + "netd_aidl_interface_lateststable_cpp_shared", + "netd_defaults", + ], include_dirs: [ "external/mdnsresponder/mDNSShared", "system/netd/include", @@ -95,7 +100,7 @@ cc_binary { required: [ "bpfloader", ], - header_libs: ["bpf_connectivity_headers"], + header_libs: ["bpf_headers"], shared_libs: [ "android.system.net.netd@1.0", "android.system.net.netd@1.1", @@ -116,7 +121,6 @@ cc_binary { "libsysutils", "libutils", "mdns_aidl_interface-V1-cpp", - "netd_aidl_interface-V8-cpp", "netd_event_listener_interface-V1-cpp", "oemnetd_aidl_interface-cpp", ], @@ -156,7 +160,10 @@ cc_binary { cc_binary { name: "ndc", - defaults: ["netd_defaults"], + defaults: [ + "netd_aidl_interface_lateststable_cpp_shared", + "netd_defaults", + ], include_dirs: [ "system/netd/include", ], @@ -172,7 +179,6 @@ cc_binary { "libutils", "libbinder", "dnsresolver_aidl_interface-V7-cpp", - "netd_aidl_interface-V8-cpp", ], srcs: [ "ndc.cpp", @@ -187,7 +193,10 @@ cc_binary { cc_test { name: "netd_unit_test", - defaults: ["netd_defaults"], + defaults: [ + "netd_aidl_interface_lateststable_cpp_static", + "netd_defaults", + ], test_suites: ["device-tests"], require_root: true, include_dirs: [ @@ -195,7 +204,7 @@ cc_test { "system/netd/server/binder", "system/netd/tests", ], - header_libs: ["bpf_connectivity_headers"], + header_libs: ["bpf_headers"], tidy_timeout_srcs: [ "BandwidthControllerTest.cpp", "InterfaceControllerTest.cpp", @@ -223,7 +232,6 @@ cc_test { "libnetd_server", "libnetd_test_tun_interface", "libtcutils", - "netd_aidl_interface-V8-cpp", "netd_event_listener_interface-V1-cpp", ], shared_libs: [ diff --git a/server/BandwidthController.cpp b/server/BandwidthController.cpp index 96a82e23..8dba75a5 100644 --- a/server/BandwidthController.cpp +++ b/server/BandwidthController.cpp @@ -55,6 +55,7 @@ #include "FirewallController.h" /* For makeCriticalCommands */ #include "Fwmark.h" #include "NetdConstants.h" +#include "android/net/INetd.h" #include "bpf/BpfUtils.h" /* Alphabetical */ @@ -66,9 +67,6 @@ const char BandwidthController::LOCAL_RAW_PREROUTING[] = "bw_raw_PREROUTING"; const char BandwidthController::LOCAL_MANGLE_POSTROUTING[] = "bw_mangle_POSTROUTING"; const char BandwidthController::LOCAL_GLOBAL_ALERT[] = "bw_global_alert"; -// Sync from packages/modules/Connectivity/bpf_progs/clatd.c -#define CLAT_MARK 0xdeadc1a7 - auto BandwidthController::iptablesRestoreFunction = execIptablesRestoreWithOutput; using android::base::Join; @@ -76,6 +74,7 @@ using android::base::StartsWith; using android::base::StringAppendF; using android::base::StringPrintf; using android::net::FirewallController; +using android::net::INetd::CLAT_MARK; using android::netdutils::StatusOr; using android::netdutils::UniqueFile; @@ -147,8 +146,6 @@ const std::string NEW_CHAIN_COMMAND = "-N "; */ const std::string COMMIT_AND_CLOSE = "COMMIT\n"; -const std::string BPF_PENALTY_BOX_MATCH_DENYLIST_COMMAND = StringPrintf( - "-I bw_penalty_box -m bpf --object-pinned %s -j REJECT", XT_BPF_DENYLIST_PROG_PATH); static const std::vector<std::string> IPT_FLUSH_COMMANDS = { /* diff --git a/server/BandwidthControllerTest.cpp b/server/BandwidthControllerTest.cpp index e7d29d23..844681d0 100644 --- a/server/BandwidthControllerTest.cpp +++ b/server/BandwidthControllerTest.cpp @@ -34,7 +34,7 @@ #include "BandwidthController.h" #include "Fwmark.h" #include "IptablesBaseTest.h" -#include "bpf_shared.h" +#include "mainline/XtBpfProgLocations.h" #include "tun_interface.h" using ::testing::_; diff --git a/server/Controllers.cpp b/server/Controllers.cpp index 0df6b0ee..00ee186d 100644 --- a/server/Controllers.cpp +++ b/server/Controllers.cpp @@ -277,7 +277,15 @@ void Controllers::init() { initIptablesRules(); Stopwatch s; - bandwidthCtrl.enableBandwidthControl(); + if (int ret = bandwidthCtrl.enableBandwidthControl()) { + gLog.error("Failed to initialize BandwidthController (%s)", strerror(-ret)); + // A failure to init almost definitely means that iptables failed to load + // our static ruleset, which then basically means network accounting will not work. + // As such simply exit netd. This may crash loop the system, but by failing + // to bootup we will trigger rollback and thus this offers us protection against + // a mainline update breaking things. + exit(1); + } gLog.info("Enabling bandwidth control: %" PRId64 "us", s.getTimeAndResetUs()); if (int ret = RouteController::Init(NetworkController::LOCAL_NET_ID)) { diff --git a/server/MDnsEventReporter.cpp b/server/MDnsEventReporter.cpp index db9021ae..e94de367 100644 --- a/server/MDnsEventReporter.cpp +++ b/server/MDnsEventReporter.cpp @@ -18,6 +18,8 @@ #include "MDnsEventReporter.h" +using android::IInterface; +using android::sp; using android::net::mdns::aidl::IMDnsEventListener; MDnsEventReporter& MDnsEventReporter::getInstance() { @@ -30,11 +32,11 @@ const MDnsEventReporter::EventListenerSet& MDnsEventReporter::getEventListeners( return getEventListenersImpl(); } -int MDnsEventReporter::addEventListener(const android::sp<IMDnsEventListener>& listener) { +int MDnsEventReporter::addEventListener(const sp<IMDnsEventListener>& listener) { return addEventListenerImpl(listener); } -int MDnsEventReporter::removeEventListener(const android::sp<IMDnsEventListener>& listener) { +int MDnsEventReporter::removeEventListener(const sp<IMDnsEventListener>& listener) { return removeEventListenerImpl(listener); } @@ -43,7 +45,7 @@ const MDnsEventReporter::EventListenerSet& MDnsEventReporter::getEventListenersI return mEventListeners; } -int MDnsEventReporter::addEventListenerImpl(const android::sp<IMDnsEventListener>& listener) { +int MDnsEventReporter::addEventListenerImpl(const sp<IMDnsEventListener>& listener) { if (listener == nullptr) { ALOGE("The event listener should not be null"); return -EINVAL; @@ -52,39 +54,19 @@ int MDnsEventReporter::addEventListenerImpl(const android::sp<IMDnsEventListener std::lock_guard lock(mMutex); for (const auto& it : mEventListeners) { - if (android::IInterface::asBinder(it).get() == - android::IInterface::asBinder(listener).get()) { + if (IInterface::asBinder(it->getListener()).get() == IInterface::asBinder(listener).get()) { ALOGW("The event listener was already subscribed"); return -EEXIST; } } - // Create the death listener. - class DeathRecipient : public android::IBinder::DeathRecipient { - public: - DeathRecipient(MDnsEventReporter* eventReporter, - const android::sp<IMDnsEventListener>& listener) - : mEventReporter(eventReporter), mListener(listener) {} - ~DeathRecipient() override = default; - void binderDied(const android::wp<android::IBinder>& /* who */) override { - mEventReporter->removeEventListenerImpl(mListener); - } - - private: - MDnsEventReporter* mEventReporter; - android::sp<IMDnsEventListener> mListener; - }; - - android::sp<android::IBinder::DeathRecipient> deathRecipient = - new DeathRecipient(this, listener); - - android::IInterface::asBinder(listener)->linkToDeath(deathRecipient); - - mEventListeners.insert(listener); + auto eventListener = sp<EventListener>::make(this, listener); + IInterface::asBinder(listener)->linkToDeath(eventListener); + mEventListeners.insert(eventListener); return 0; } -int MDnsEventReporter::removeEventListenerImpl(const android::sp<IMDnsEventListener>& listener) { +int MDnsEventReporter::removeEventListenerImpl(const sp<IMDnsEventListener>& listener) { if (listener == nullptr) { ALOGE("The event listener should not be null"); return -EINVAL; @@ -92,6 +74,14 @@ int MDnsEventReporter::removeEventListenerImpl(const android::sp<IMDnsEventListe std::lock_guard lock(mMutex); - mEventListeners.erase(listener); - return 0; -} + for (const auto& it : mEventListeners) { + const auto& binder = IInterface::asBinder(it->getListener()); + if (binder == IInterface::asBinder(listener)) { + binder->unlinkToDeath(it); + mEventListeners.erase(it); + return 0; + } + } + ALOGE("The event listener does not exist"); + return -ENOENT; +}
\ No newline at end of file diff --git a/server/MDnsEventReporter.h b/server/MDnsEventReporter.h index e49c3e3f..cbc43ecb 100644 --- a/server/MDnsEventReporter.h +++ b/server/MDnsEventReporter.h @@ -23,10 +23,28 @@ class MDnsEventReporter final { public: + class EventListener : public android::IBinder::DeathRecipient { + public: + EventListener(MDnsEventReporter* eventReporter, + const android::sp<android::net::mdns::aidl::IMDnsEventListener>& listener) + : mEventReporter(eventReporter), mListener(listener) {} + ~EventListener() override = default; + void binderDied(const android::wp<android::IBinder>& /* who */) override { + mEventReporter->removeEventListenerImpl(mListener); + } + android::sp<android::net::mdns::aidl::IMDnsEventListener> getListener() { + return mListener; + } + + private: + MDnsEventReporter* mEventReporter; + android::sp<android::net::mdns::aidl::IMDnsEventListener> mListener; + }; + MDnsEventReporter(const MDnsEventReporter&) = delete; MDnsEventReporter& operator=(const MDnsEventReporter&) = delete; - using EventListenerSet = std::set<android::sp<android::net::mdns::aidl::IMDnsEventListener>>; + using EventListenerSet = std::set<android::sp<EventListener>>; // Get the instance of the singleton MDnsEventReporter. static MDnsEventReporter& getInstance(); @@ -56,5 +74,4 @@ class MDnsEventReporter final { const android::sp<android::net::mdns::aidl::IMDnsEventListener>& listener) EXCLUDES(mMutex); const EventListenerSet& getEventListenersImpl() const EXCLUDES(mMutex); - void handleEventBinderDied(const void* who) EXCLUDES(mMutex); }; diff --git a/server/MDnsSdListener.cpp b/server/MDnsSdListener.cpp index 16364008..1d1ea40a 100644 --- a/server/MDnsSdListener.cpp +++ b/server/MDnsSdListener.cpp @@ -140,7 +140,7 @@ void MDnsSdListenerDiscoverCallback(DNSServiceRef /* sdRef */, DNSServiceFlags f } for (const auto& it : listeners) { - it->onServiceDiscoveryStatus(info); + it->getListener()->onServiceDiscoveryStatus(info); } } @@ -212,7 +212,7 @@ void MDnsSdListenerRegisterCallback(DNSServiceRef /* sdRef */, DNSServiceFlags / } for (const auto& it : listeners) { - it->onServiceRegistrationStatus(info); + it->getListener()->onServiceRegistrationStatus(info); } } @@ -277,7 +277,7 @@ void MDnsSdListenerResolveCallback(DNSServiceRef /* sdRef */, DNSServiceFlags /* } for (const auto& it : listeners) { - it->onServiceResolutionStatus(info); + it->getListener()->onServiceResolutionStatus(info); } } @@ -343,7 +343,7 @@ void MDnsSdListenerGetAddrInfoCallback(DNSServiceRef /* sdRef */, DNSServiceFlag info.result = IMDnsEventListener::SERVICE_GET_ADDR_FAILED; } for (const auto& it : listeners) { - it->onGettingServiceAddressStatus(info); + it->getListener()->onGettingServiceAddressStatus(info); } } diff --git a/server/RouteController.cpp b/server/RouteController.cpp index 5ed33cdd..d2af9a37 100644 --- a/server/RouteController.cpp +++ b/server/RouteController.cpp @@ -21,9 +21,9 @@ #include <fcntl.h> #include <linux/fib_rules.h> #include <net/if.h> -#include <sys/stat.h> - +#include <netdutils/InternetAddresses.h> #include <private/android_filesystem_config.h> +#include <sys/stat.h> #include <map> @@ -43,6 +43,7 @@ using android::base::StartsWith; using android::base::StringPrintf; using android::base::WriteStringToFile; +using android::netdutils::IPPrefix; namespace android::net { @@ -63,6 +64,14 @@ const char* const ROUTE_TABLE_NAME_MAIN = "main"; const char* const RouteController::LOCAL_MANGLE_INPUT = "routectrl_mangle_INPUT"; +const IPPrefix V4_LOCAL_ADDR[] = { + 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 + IPPrefix::forString("172.16.0.0/12"), // RFC1918 + IPPrefix::forString("192.168.0.0/16") // RFC1918 +}; + const uint8_t AF_FAMILIES[] = {AF_INET, AF_INET6}; const uid_t UID_ROOT = 0; @@ -859,6 +868,13 @@ int RouteController::modifyPhysicalNetwork(unsigned netId, const char* interface subPriority, add)) { return ret; } + + // Per-UID local network rules must always match per-app default network rules, + // because their purpose is to allow the UIDs to use the default network for + // local destinations within it. + if (int ret = modifyUidLocalNetworkRule(interface, range.start, range.stop, add)) { + return ret; + } } } } @@ -906,6 +922,32 @@ int RouteController::modifyPhysicalNetwork(unsigned netId, const char* interface return 0; } +int RouteController::modifyUidLocalNetworkRule(const char* interface, uid_t uidStart, uid_t uidEnd, + bool add) { + uint32_t table = getRouteTableForInterface(interface, true /* local */); + if (table == RT_TABLE_UNSPEC) { + return -ESRCH; + } + + if ((uidStart == INVALID_UID) || (uidEnd == INVALID_UID)) { + ALOGE("modifyUidLocalNetworkRule, invalid UIDs (%u, %u)", uidStart, uidEnd); + return -EUSERS; + } + + Fwmark fwmark; + Fwmark mask; + + fwmark.explicitlySelected = false; + mask.explicitlySelected = true; + + // Access to this network is controlled by UID rules, not permission bits. + fwmark.permission = PERMISSION_NONE; + mask.permission = PERMISSION_NONE; + + return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_UID_LOCAL_ROUTES, table, + fwmark.intValue, mask.intValue, IIF_LOOPBACK, OIF_NONE, uidStart, uidEnd); +} + [[nodiscard]] static int modifyUidUnreachableRule(unsigned netId, uid_t uidStart, uid_t uidEnd, int32_t subPriority, bool add, bool explicitSelect) { @@ -1074,11 +1116,11 @@ int RouteController::modifyTetheredNetwork(uint16_t action, const char* inputInt // Returns 0 on success or negative errno on failure. int RouteController::modifyRoute(uint16_t action, uint16_t flags, const char* interface, const char* destination, const char* nexthop, TableType tableType, - int mtu, int priority) { + int mtu, int priority, bool isLocal) { uint32_t table; switch (tableType) { case RouteController::INTERFACE: { - table = getRouteTableForInterface(interface, false /* local */); + table = getRouteTableForInterface(interface, isLocal); if (table == RT_TABLE_UNSPEC) { return -ESRCH; } @@ -1252,10 +1294,6 @@ int RouteController::addInterfaceToPhysicalNetwork(unsigned netId, const char* i MODIFY_NON_UID_BASED_RULES)) { return ret; } - // TODO: Consider to remove regular table if adding local table failed. - if (int ret = modifyVpnLocalExclusionRule(true, interface)) { - return ret; - } maybeModifyQdiscClsact(interface, ACTION_ADD); updateTableNamesFile(); @@ -1270,10 +1308,7 @@ int RouteController::removeInterfaceFromPhysicalNetwork(unsigned netId, const ch return ret; } - int ret = modifyVpnLocalExclusionRule(false, interface); - // Always perform flushRoute even if removing local exclusion rules failed. - ret |= flushRoutes(interface); - if (ret) { + if (int ret = flushRoutes(interface)) { return ret; } @@ -1357,22 +1392,66 @@ int RouteController::removeInterfaceFromDefaultNetwork(const char* interface, return modifyDefaultNetwork(RTM_DELRULE, interface, permission); } +bool RouteController::isTargetV4LocalRange(const char* dst) { + for (IPPrefix addr : V4_LOCAL_ADDR) { + if (addr.contains(IPPrefix::forString(dst))) { + return true; + } + } + return false; +} + +bool RouteController::isLocalAddress(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))); +} + int RouteController::addRoute(const char* interface, const char* destination, const char* nexthop, TableType tableType, int mtu, int priority) { - return modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, interface, destination, nexthop, - tableType, mtu, priority); + if (int ret = modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, interface, destination, + nexthop, tableType, mtu, priority, false /* isLocal */)) { + return ret; + } + + if (isLocalAddress(tableType, destination, nexthop)) { + return modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, interface, destination, + nexthop, tableType, mtu, priority, true /* isLocal */); + } + + return 0; } int RouteController::removeRoute(const char* interface, const char* destination, const char* nexthop, TableType tableType, int priority) { - return modifyRoute(RTM_DELROUTE, NETLINK_REQUEST_FLAGS, interface, destination, nexthop, - tableType, 0 /* mtu */, priority); + if (int ret = modifyRoute(RTM_DELROUTE, NETLINK_REQUEST_FLAGS, interface, destination, nexthop, + tableType, 0 /* mtu */, priority, false /* isLocal */)) { + return ret; + } + + if (isLocalAddress(tableType, destination, nexthop)) { + return modifyRoute(RTM_DELROUTE, NETLINK_REQUEST_FLAGS, interface, destination, nexthop, + tableType, 0 /* mtu */, priority, true /* isLocal */); + } + return 0; } int RouteController::updateRoute(const char* interface, const char* destination, const char* nexthop, TableType tableType, int mtu) { - return modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_REPLACE_FLAGS, interface, destination, nexthop, - tableType, mtu, 0 /* priority */); + if (int ret = modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_REPLACE_FLAGS, interface, destination, + nexthop, tableType, mtu, 0 /* priority */, false /* isLocal */)) { + return ret; + } + + if (isLocalAddress(tableType, destination, nexthop)) { + return modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_REPLACE_FLAGS, interface, destination, + nexthop, tableType, mtu, 0 /* priority */, true /* isLocal */); + } + return 0; } int RouteController::enableTethering(const char* inputInterface, const char* outputInterface) { @@ -1385,13 +1464,21 @@ int RouteController::disableTethering(const char* inputInterface, const char* ou int RouteController::addVirtualNetworkFallthrough(unsigned vpnNetId, const char* physicalInterface, Permission permission) { - return modifyVpnFallthroughRule(RTM_NEWRULE, vpnNetId, physicalInterface, permission); + if (int ret = modifyVpnFallthroughRule(RTM_NEWRULE, vpnNetId, physicalInterface, permission)) { + return ret; + } + + return modifyVpnLocalExclusionRule(true /* add */, physicalInterface); } int RouteController::removeVirtualNetworkFallthrough(unsigned vpnNetId, const char* physicalInterface, Permission permission) { - return modifyVpnFallthroughRule(RTM_DELRULE, vpnNetId, physicalInterface, permission); + if (int ret = modifyVpnFallthroughRule(RTM_DELRULE, vpnNetId, physicalInterface, permission)) { + return ret; + } + + return modifyVpnLocalExclusionRule(false /* add */, physicalInterface); } int RouteController::addUsersToPhysicalNetwork(unsigned netId, const char* interface, diff --git a/server/RouteController.h b/server/RouteController.h index 9b04cfd2..ff41678d 100644 --- a/server/RouteController.h +++ b/server/RouteController.h @@ -55,11 +55,17 @@ constexpr int32_t RULE_PRIORITY_TETHERING = 21000; constexpr int32_t RULE_PRIORITY_UID_IMPLICIT_NETWORK = 22000; constexpr int32_t RULE_PRIORITY_IMPLICIT_NETWORK = 23000; constexpr int32_t RULE_PRIORITY_BYPASSABLE_VPN_NO_LOCAL_EXCLUSION = 24000; -// Rules used for excluding local route in the VPN network. -constexpr int32_t RULE_PRIORITY_LOCAL_ROUTES = 25000; -constexpr int32_t RULE_PRIORITY_BYPASSABLE_VPN_LOCAL_EXCLUSION = 26000; -constexpr int32_t RULE_PRIORITY_VPN_FALLTHROUGH = 27000; -constexpr int32_t RULE_PRIORITY_UID_DEFAULT_NETWORK = 28000; +// Sets of rules used for excluding local routes from the VPN. Look up tables +// that contain directly-connected local routes taken from the default network. +// The first set is used for apps that have a per-UID default network. The rule +// UID ranges match those of the per-UID default network rule for that network. +// The second set has no UID ranges and is used for apps whose default network +// is the system default network network. +constexpr int32_t RULE_PRIORITY_UID_LOCAL_ROUTES = 25000; +constexpr int32_t RULE_PRIORITY_LOCAL_ROUTES = 26000; +constexpr int32_t RULE_PRIORITY_BYPASSABLE_VPN_LOCAL_EXCLUSION = 27000; +constexpr int32_t RULE_PRIORITY_VPN_FALLTHROUGH = 28000; +constexpr int32_t RULE_PRIORITY_UID_DEFAULT_NETWORK = 29000; // Rule used when framework wants to disable default network from specified applications. There will // be a small interval the same uid range exists in both UID_DEFAULT_UNREACHABLE and // UID_DEFAULT_NETWORK when framework is switching user preferences. @@ -74,8 +80,8 @@ constexpr int32_t RULE_PRIORITY_UID_DEFAULT_NETWORK = 28000; // The priority is lower than UID_DEFAULT_NETWORK. Otherwise, the app will be told by // ConnectivityService that it has a network in step 1 of the second case. But if it tries to use // the network, it will not work. That will potentially cause a user-visible error. -constexpr int32_t RULE_PRIORITY_UID_DEFAULT_UNREACHABLE = 29000; -constexpr int32_t RULE_PRIORITY_DEFAULT_NETWORK = 30000; +constexpr int32_t RULE_PRIORITY_UID_DEFAULT_UNREACHABLE = 30000; +constexpr int32_t RULE_PRIORITY_DEFAULT_NETWORK = 31000; constexpr int32_t RULE_PRIORITY_UNREACHABLE = 32000; // clang-format on @@ -211,7 +217,7 @@ public: static int modifyUnreachableNetwork(unsigned netId, const UidRangeMap& uidRangeMap, bool add); static int modifyRoute(uint16_t action, uint16_t flags, const char* interface, const char* destination, const char* nexthop, TableType tableType, - int mtu, int priority); + int mtu, int priority, bool isLocal); static int modifyTetheredNetwork(uint16_t action, const char* inputInterface, const char* outputInterface); static int modifyVpnFallthroughRule(uint16_t action, unsigned vpnNetId, @@ -221,6 +227,11 @@ public: bool modifyNonUidBasedRules, bool excludeLocalRoutes); static void updateTableNamesFile() EXCLUDES(sInterfaceToTableLock); static int modifyVpnLocalExclusionRule(bool add, const char* physicalInterface); + + 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); }; // Public because they are called by by RouteControllerTest.cpp. diff --git a/server/TcUtils.h b/server/TcUtils.h index d205c04b..4b1b2b9f 100644 --- a/server/TcUtils.h +++ b/server/TcUtils.h @@ -26,42 +26,10 @@ #include <string> #include "bpf/BpfUtils.h" -#include "bpf_shared.h" namespace android { namespace net { -// For better code clarity - do not change values - used for booleans like -// with_ethernet_header or isEthernet. -constexpr bool RAWIP = false; -constexpr bool ETHER = true; - -// For better code clarity when used for 'bool ingress' parameter. -constexpr bool EGRESS = false; -constexpr bool INGRESS = true; - -// The priority of clat hook - must be after tethering. -constexpr uint16_t PRIO_CLAT = 4; - -inline base::Result<bool> isEthernet(const std::string& interface) { - bool result = false; - if (int error = ::android::isEthernet(interface.c_str(), result)) { - errno = error; - return ErrnoErrorf("isEthernet failed for interface {}", interface); - } - return result; -} - -inline int getClatEgress4MapFd(void) { - const int fd = bpf::mapRetrieveRW(CLAT_EGRESS4_MAP_PATH); - return (fd == -1) ? -errno : fd; -} - -inline int getClatIngress6MapFd(void) { - const int fd = bpf::mapRetrieveRW(CLAT_INGRESS6_MAP_PATH); - return (fd == -1) ? -errno : fd; -} - inline int tcQdiscAddDevClsact(int ifIndex) { return doTcQdiscClsact(ifIndex, RTM_NEWQDISC, NLM_F_EXCL | NLM_F_CREATE); } @@ -74,25 +42,5 @@ inline int tcQdiscDelDevClsact(int ifIndex) { return doTcQdiscClsact(ifIndex, RTM_DELQDISC, 0); } -// tc filter add dev .. ingress prio 4 protocol ipv6 bpf object-pinned /sys/fs/bpf/... direct-action -inline int tcFilterAddDevIngressClatIpv6(int ifIndex, const std::string& bpfProgPath) { - return tcAddBpfFilter(ifIndex, INGRESS, PRIO_CLAT, ETH_P_IPV6, bpfProgPath.c_str()); -} - -// tc filter add dev .. egress prio 4 protocol ip bpf object-pinned /sys/fs/bpf/... direct-action -inline int tcFilterAddDevEgressClatIpv4(int ifIndex, const std::string& bpfProgPath) { - return tcAddBpfFilter(ifIndex, EGRESS, PRIO_CLAT, ETH_P_IP, bpfProgPath.c_str()); -} - -// tc filter del dev .. ingress prio 4 protocol ipv6 -inline int tcFilterDelDevIngressClatIpv6(int ifIndex) { - return tcDeleteFilter(ifIndex, INGRESS, PRIO_CLAT, ETH_P_IPV6); -} - -// tc filter del dev .. egress prio 4 protocol ip -inline int tcFilterDelDevEgressClatIpv4(int ifIndex) { - return tcDeleteFilter(ifIndex, EGRESS, PRIO_CLAT, ETH_P_IP); -} - } // namespace net } // namespace android diff --git a/server/TetherController.h b/server/TetherController.h index b4472bd5..d2195f23 100644 --- a/server/TetherController.h +++ b/server/TetherController.h @@ -28,10 +28,11 @@ #include "NetdConstants.h" #include "android-base/result.h" #include "bpf/BpfMap.h" -#include "bpf_shared.h" #include "android/net/TetherOffloadRuleParcel.h" +#include "mainline/XtBpfProgLocations.h" + namespace android { namespace net { diff --git a/tests/Android.bp b/tests/Android.bp index 21830d6d..ff918cc1 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -37,7 +37,10 @@ cc_test_library { cc_test_library { name: "libnetd_test_unsol_service", - defaults: ["netd_defaults"], + defaults: [ + "netd_aidl_interface_lateststable_cpp_shared", + "netd_defaults", + ], srcs: [ "TestUnsolService.cpp" ], @@ -52,7 +55,6 @@ cc_test_library { "libnetutils", "libsysutils", "libutils", - "netd_aidl_interface-V8-cpp", ], } @@ -74,7 +76,10 @@ cc_test { "vts" ], require_root: true, - defaults: ["netd_defaults"], + defaults: [ + "netd_aidl_interface_lateststable_cpp_static", + "netd_defaults", + ], tidy: false, // cuts test build time by almost 1 minute srcs: [ ":netd_integration_test_shared", @@ -109,7 +114,6 @@ cc_test { "libnettestutils", "libtcutils", "mdns_aidl_interface-V1-cpp", - "netd_aidl_interface-V8-cpp", "netd_event_listener_interface-V1-cpp", "oemnetd_aidl_interface-cpp", ], diff --git a/tests/benchmarks/Android.bp b/tests/benchmarks/Android.bp index 95c00aa6..13c8105f 100644 --- a/tests/benchmarks/Android.bp +++ b/tests/benchmarks/Android.bp @@ -11,7 +11,10 @@ package { cc_benchmark { name: "netd_benchmark", - defaults: ["netd_defaults"], + defaults: [ + "netd_aidl_interface_lateststable_cpp_static", + "netd_defaults", + ], shared_libs: [ "libbase", "libbinder_ndk", @@ -23,7 +26,6 @@ cc_benchmark { static_libs: [ "libnetd_test_dnsresponder_ndk", "dnsresolver_aidl_interface-lateststable-ndk", - "netd_aidl_interface-lateststable-cpp", // system/netd/server/UidRanges.h "netd_aidl_interface-lateststable-ndk", "netd_event_listener_interface-lateststable-ndk", ], diff --git a/tests/binder_test.cpp b/tests/binder_test.cpp index 519effea..bc1e7393 100644 --- a/tests/binder_test.cpp +++ b/tests/binder_test.cpp @@ -54,7 +54,6 @@ #include <binder/IPCThreadState.h> #include <bpf/BpfMap.h> #include <bpf/BpfUtils.h> -#include <bpf_shared.h> #include <com/android/internal/net/BnOemNetdUnsolicitedEventListener.h> #include <com/android/internal/net/IOemNetd.h> #include <cutils/multiuser.h> @@ -129,6 +128,7 @@ using android::net::RULE_PRIORITY_UID_DEFAULT_NETWORK; using android::net::RULE_PRIORITY_UID_DEFAULT_UNREACHABLE; using android::net::RULE_PRIORITY_UID_EXPLICIT_NETWORK; using android::net::RULE_PRIORITY_UID_IMPLICIT_NETWORK; +using android::net::RULE_PRIORITY_UID_LOCAL_ROUTES; using android::net::RULE_PRIORITY_VPN_FALLTHROUGH; using android::net::SockDiag; using android::net::TetherOffloadRuleParcel; @@ -225,7 +225,8 @@ class NetdBinderTest : public ::testing::Test { unique_fd* acceptedSocket); void createVpnNetworkWithUid(bool secure, uid_t uid, int vpnNetId = TEST_NETID2, - int fallthroughNetId = TEST_NETID1); + int fallthroughNetId = TEST_NETID1, + int nonDefaultNetId = TEST_NETID3); void createAndSetDefaultNetwork(int netId, const std::string& interface, int permission = INetd::PERMISSION_NONE); @@ -1625,6 +1626,121 @@ void expectProcessDoesNotExist(const std::string& processName) { } // namespace +TEST_F(NetdBinderTest, NetworkAddRemoveRouteToLocalExcludeTable) { + static const struct { + const char* ipVersion; + const char* testDest; + const char* testNextHop; + const bool expectInLocalTable; + } kTestData[] = {{IP_RULE_V6, "::/0", "fe80::", false}, + {IP_RULE_V6, "::/0", "", false}, + {IP_RULE_V6, "2001:db8:cafe::/64", "fe80::", false}, + {IP_RULE_V6, "fe80::/64", "", true}, + {IP_RULE_V6, "2001:db8:cafe::/48", "", true}, + {IP_RULE_V6, "2001:db8:cafe::/64", "unreachable", false}, + {IP_RULE_V6, "2001:db8:ca00::/40", "", true}, + {IP_RULE_V4, "0.0.0.0/0", "10.251.10.1", false}, + {IP_RULE_V4, "192.1.0.0/16", "", false}, + {IP_RULE_V4, "192.168.0.0/15", "", false}, + {IP_RULE_V4, "192.168.0.0/16", "", true}, + {IP_RULE_V4, "192.168.0.0/24", "", true}, + {IP_RULE_V4, "100.1.0.0/16", "", false}, + {IP_RULE_V4, "100.0.0.0/8", "", false}, + {IP_RULE_V4, "100.64.0.0/10", "", true}, + {IP_RULE_V4, "100.64.0.0/16", "", true}, + {IP_RULE_V4, "100.64.0.0/10", "throw", false}, + {IP_RULE_V4, "172.0.0.0/8", "", false}, + {IP_RULE_V4, "172.16.0.0/12", "", true}, + {IP_RULE_V4, "172.16.0.0/16", "", true}, + {IP_RULE_V4, "172.16.0.0/12", "unreachable", false}, + {IP_RULE_V4, "172.32.0.0/12", "", false}, + {IP_RULE_V4, "169.0.0.0/8", "", false}, + {IP_RULE_V4, "169.254.0.0/16", "", true}, + {IP_RULE_V4, "169.254.0.0/20", "", true}, + {IP_RULE_V4, "169.254.3.0/24", "", true}, + {IP_RULE_V4, "170.254.0.0/16", "", false}, + {IP_RULE_V4, "10.0.0.0/8", "", true}, + {IP_RULE_V4, "10.0.0.0/7", "", false}, + {IP_RULE_V4, "10.0.0.0/16", "", true}, + {IP_RULE_V4, "10.251.0.0/16", "", true}, + {IP_RULE_V4, "10.251.250.0/24", "", true}, + {IP_RULE_V4, "10.251.10.2/31", "throw", false}, + {IP_RULE_V4, "10.251.10.2/31", "unreachable", false}}; + + // To ensure that the nexthops for the above are reachable. + // Otherwise, the routes can't be created. + static const struct { + const char* ipVersion; + const char* testDest; + const char* testNextHop; + } kDirectlyConnectedRoutes[] = { + {IP_RULE_V4, "10.251.10.0/30", ""}, + {IP_RULE_V6, "2001:db8::/32", ""}, + }; + + // Add test physical network + const auto& config = makeNativeNetworkConfig(TEST_NETID1, NativeNetworkType::PHYSICAL, + INetd::PERMISSION_NONE, false, false); + EXPECT_TRUE(mNetd->networkCreate(config).isOk()); + EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk()); + + // Get current default network NetId + binder::Status status = mNetd->networkGetDefault(&mStoredDefaultNetwork); + ASSERT_TRUE(status.isOk()) << status.exceptionMessage(); + + // Set default network + EXPECT_TRUE(mNetd->networkSetDefault(TEST_NETID1).isOk()); + + std::string localTableName = std::string(sTun.name() + "_local"); + // 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]; + + binder::Status status = + mNetd->networkAddRoute(TEST_NETID1, sTun.name(), td.testDest, td.testNextHop); + EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); + expectNetworkRouteExists(td.ipVersion, sTun.name(), td.testDest, td.testNextHop, + sTun.name().c_str()); + // Verify routes in local table + expectNetworkRouteExists(td.ipVersion, sTun.name(), td.testDest, td.testNextHop, + localTableName.c_str()); + } + + for (size_t i = 0; i < std::size(kTestData); i++) { + const auto& td = kTestData[i]; + SCOPED_TRACE(StringPrintf("case ip:%s, dest:%s, nexHop:%s, expect:%d", td.ipVersion, + td.testDest, td.testNextHop, td.expectInLocalTable)); + binder::Status status = + mNetd->networkAddRoute(TEST_NETID1, sTun.name(), td.testDest, td.testNextHop); + EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); + // Verify routes in local table + if (td.expectInLocalTable) { + expectNetworkRouteExists(td.ipVersion, sTun.name(), td.testDest, td.testNextHop, + localTableName.c_str()); + } else { + expectNetworkRouteDoesNotExist(td.ipVersion, sTun.name(), td.testDest, td.testNextHop, + localTableName.c_str()); + } + + status = mNetd->networkRemoveRoute(TEST_NETID1, sTun.name(), td.testDest, td.testNextHop); + EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); + expectNetworkRouteDoesNotExist(td.ipVersion, sTun.name(), td.testDest, td.testNextHop, + localTableName.c_str()); + } + + for (size_t i = 0; i < std::size(kDirectlyConnectedRoutes); i++) { + const auto& td = kDirectlyConnectedRoutes[i]; + status = mNetd->networkRemoveRoute(TEST_NETID1, sTun.name(), td.testDest, td.testNextHop); + EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); + } + + // Set default network back + status = mNetd->networkSetDefault(mStoredDefaultNetwork); + + // Remove test physical network + EXPECT_TRUE(mNetd->networkDestroy(TEST_NETID1).isOk()); +} + namespace { bool getIpfwdV4Enable() { @@ -3272,18 +3388,26 @@ TEST_F(NetdBinderTest, OemNetdRelated) { } void NetdBinderTest::createVpnNetworkWithUid(bool secure, uid_t uid, int vpnNetId, - int fallthroughNetId) { + int fallthroughNetId, int nonDefaultNetId) { // Re-init sTun* to ensure route rule exists. sTun.destroy(); sTun.init(); sTun2.destroy(); sTun2.init(); + sTun3.destroy(); + sTun3.init(); // Create physical network with fallthroughNetId but not set it as default network auto config = makeNativeNetworkConfig(fallthroughNetId, NativeNetworkType::PHYSICAL, INetd::PERMISSION_NONE, false, false); EXPECT_TRUE(mNetd->networkCreate(config).isOk()); EXPECT_TRUE(mNetd->networkAddInterface(fallthroughNetId, sTun.name()).isOk()); + // Create another physical network in order to test VPN behaviour with multiple networks + // connected, of which one may be the default. + auto nonDefaultNetworkConfig = makeNativeNetworkConfig( + nonDefaultNetId, NativeNetworkType::PHYSICAL, INetd::PERMISSION_NONE, false, false); + EXPECT_TRUE(mNetd->networkCreate(nonDefaultNetworkConfig).isOk()); + EXPECT_TRUE(mNetd->networkAddInterface(nonDefaultNetId, sTun3.name()).isOk()); // Create VPN with vpnNetId config.netId = vpnNetId; @@ -3299,6 +3423,9 @@ void NetdBinderTest::createVpnNetworkWithUid(bool secure, uid_t uid, int vpnNetI EXPECT_TRUE(mNetd->networkAddRoute(TEST_NETID1, sTun.name(), "::/0", "").isOk()); // Add limited route EXPECT_TRUE(mNetd->networkAddRoute(TEST_NETID2, sTun2.name(), "2001:db8::/32", "").isOk()); + + // Also add default route to non-default network for per app default use. + EXPECT_TRUE(mNetd->networkAddRoute(TEST_NETID3, sTun3.name(), "::/0", "").isOk()); } void NetdBinderTest::createAndSetDefaultNetwork(int netId, const std::string& interface, @@ -3463,9 +3590,10 @@ void expectVpnFallthroughRuleExists(const std::string& ifName, int vpnNetId) { } void expectVpnFallthroughWorks(android::net::INetd* netdService, bool bypassable, uid_t uid, - const TunInterface& fallthroughNetwork, - const TunInterface& vpnNetwork, int vpnNetId = TEST_NETID2, - int fallthroughNetId = TEST_NETID1) { + uid_t uidNotInVpn, const TunInterface& fallthroughNetwork, + const TunInterface& vpnNetwork, const TunInterface& otherNetwork, + int vpnNetId = TEST_NETID2, int fallthroughNetId = TEST_NETID1, + int otherNetId = TEST_NETID3) { // Set default network to NETID_UNSET EXPECT_TRUE(netdService->networkSetDefault(NETID_UNSET).isOk()); @@ -3499,8 +3627,10 @@ void expectVpnFallthroughWorks(android::net::INetd* netdService, bool bypassable // Check if fallthrough rule exists expectVpnFallthroughRuleExists(fallthroughNetwork.name(), vpnNetId); - // Check if local exclusion rule exists + // Check if local exclusion rule exists for default network expectVpnLocalExclusionRuleExists(fallthroughNetwork.name(), true); + // No local exclusion rule for non-default network + expectVpnLocalExclusionRuleExists(otherNetwork.name(), false); // Expect fallthrough to default network // The fwmark differs depending on whether the VPN is bypassable or not. @@ -3540,6 +3670,25 @@ void expectVpnFallthroughWorks(android::net::INetd* netdService, bool bypassable EXPECT_FALSE(sendIPv6PacketFromUid(uid, outsideVpnAddr, &fwmark, fallthroughFd)); EXPECT_FALSE(sendIPv6PacketFromUid(uid, insideVpnAddr, &fwmark, fallthroughFd)); } + + // Add per-app uid ranges. + EXPECT_TRUE(netdService + ->networkAddUidRanges(otherNetId, + {makeUidRangeParcel(uidNotInVpn, uidNotInVpn)}) + .isOk()); + + int appDefaultFd = otherNetwork.getFdForTesting(); + + // UID is not inside the VPN range, so it won't go to vpn network. + // It won't fall into per app local rule because it's explicitly selected. + EXPECT_TRUE(sendIPv6PacketFromUid(uidNotInVpn, outsideVpnAddr, &fwmark, fallthroughFd)); + EXPECT_TRUE(sendIPv6PacketFromUid(uidNotInVpn, insideVpnAddr, &fwmark, fallthroughFd)); + + // Reset explicitly selection. + setNetworkForProcess(NETID_UNSET); + // Connections can go to app default network. + EXPECT_TRUE(sendIPv6PacketFromUid(uidNotInVpn, insideVpnAddr, &fwmark, appDefaultFd)); + EXPECT_TRUE(sendIPv6PacketFromUid(uidNotInVpn, outsideVpnAddr, &fwmark, appDefaultFd)); } } // namespace @@ -3548,14 +3697,16 @@ TEST_F(NetdBinderTest, SecureVPNFallthrough) { createVpnNetworkWithUid(true /* secure */, TEST_UID1); // Get current default network NetId ASSERT_TRUE(mNetd->networkGetDefault(&mStoredDefaultNetwork).isOk()); - expectVpnFallthroughWorks(mNetd.get(), false /* bypassable */, TEST_UID1, sTun, sTun2); + expectVpnFallthroughWorks(mNetd.get(), false /* bypassable */, TEST_UID1, TEST_UID2, sTun, + sTun2, sTun3); } TEST_F(NetdBinderTest, BypassableVPNFallthrough) { createVpnNetworkWithUid(false /* secure */, TEST_UID1); // Get current default network NetId ASSERT_TRUE(mNetd->networkGetDefault(&mStoredDefaultNetwork).isOk()); - expectVpnFallthroughWorks(mNetd.get(), true /* bypassable */, TEST_UID1, sTun, sTun2); + expectVpnFallthroughWorks(mNetd.get(), true /* bypassable */, TEST_UID1, TEST_UID2, sTun, sTun2, + sTun3); } namespace { @@ -3617,275 +3768,6 @@ TEST_F(NetdBinderTest, GetFwmarkForNetwork) { EXPECT_TRUE(mNetd->networkDestroy(TEST_NETID1).isOk()); } -namespace { - -TetherOffloadRuleParcel makeTetherOffloadRule(int inputInterfaceIndex, int outputInterfaceIndex, - const std::vector<uint8_t>& destination, - int prefixLength, - const std::vector<uint8_t>& srcL2Address, - const std::vector<uint8_t>& dstL2Address, int pmtu) { - android::net::TetherOffloadRuleParcel parcel; - parcel.inputInterfaceIndex = inputInterfaceIndex; - parcel.outputInterfaceIndex = outputInterfaceIndex; - parcel.destination = destination; - parcel.prefixLength = prefixLength; - parcel.srcL2Address = srcL2Address; - parcel.dstL2Address = dstL2Address; - parcel.pmtu = pmtu; - return parcel; -} - -} // namespace - -// TODO: probably remove the test because TetherOffload* binder calls are deprecated. -TEST_F(NetdBinderTest, DISABLED_TetherOffloadRule) { - // TODO: Perhaps verify invalid interface index once the netd handle the error in methods. - constexpr uint32_t kIfaceInt = 101; - constexpr uint32_t kIfaceExt = 102; - constexpr uint32_t kIfaceNonExistent = 103; - - const std::vector<uint8_t> kAddr6 = {0x20, 0x01, 0x0d, 0xb8, 0xca, 0xfe, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88}; - const std::vector<uint8_t> kSrcMac = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0a}; - const std::vector<uint8_t> kDstMac = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0b}; - - const std::vector<uint8_t> kInvalidAddr4 = {0xac, 0x0a, 0x0d, 0xb8}; // should be IPv6 address - const std::vector<uint8_t> kInvalidMac = {0xde, 0xad, 0xbe, 0xef}; // should be 6-byte length - - // Invalid IP address, add rule - TetherOffloadRuleParcel rule = makeTetherOffloadRule( - kIfaceExt, kIfaceInt, kInvalidAddr4 /*bad*/, 128, kSrcMac, kDstMac, 1500); - auto status = mNetd->tetherOffloadRuleAdd(rule); - EXPECT_FALSE(status.isOk()); - EXPECT_EQ(EAFNOSUPPORT, status.serviceSpecificErrorCode()); - - // Invalid source L2 address, add rule - rule = makeTetherOffloadRule(kIfaceExt, kIfaceInt, kAddr6, 128, kInvalidMac /*bad*/, kDstMac, - 1500); - status = mNetd->tetherOffloadRuleAdd(rule); - EXPECT_FALSE(status.isOk()); - EXPECT_EQ(ENXIO, status.serviceSpecificErrorCode()); - - // Invalid destination L2 address, add rule - rule = makeTetherOffloadRule(kIfaceExt, kIfaceInt, kAddr6, 128, kSrcMac, kInvalidMac /*bad*/, - 1500); - status = mNetd->tetherOffloadRuleAdd(rule); - EXPECT_FALSE(status.isOk()); - EXPECT_EQ(ENXIO, status.serviceSpecificErrorCode()); - - // Invalid IP address, remove rule - rule = makeTetherOffloadRule(kIfaceExt, kIfaceInt, kInvalidAddr4 /*bad*/, 128, kSrcMac, kDstMac, - 1500); - status = mNetd->tetherOffloadRuleRemove(rule); - EXPECT_FALSE(status.isOk()); - EXPECT_EQ(EAFNOSUPPORT, status.serviceSpecificErrorCode()); - - // Invalid prefix length - rule = makeTetherOffloadRule(kIfaceExt, kIfaceInt, kAddr6, 64 /*bad*/, kSrcMac, kDstMac, 1500); - status = mNetd->tetherOffloadRuleAdd(rule); - EXPECT_FALSE(status.isOk()); - EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); - status = mNetd->tetherOffloadRuleRemove(rule); - EXPECT_FALSE(status.isOk()); - EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); - - // Invalid interface index - rule = makeTetherOffloadRule(kIfaceExt, 0, kAddr6, 128, kSrcMac, kDstMac, 1500); - status = mNetd->tetherOffloadRuleAdd(rule); - EXPECT_FALSE(status.isOk()); - EXPECT_EQ(ENODEV, status.serviceSpecificErrorCode()); - rule = makeTetherOffloadRule(0, kIfaceInt, kAddr6, 64, kSrcMac, kDstMac, 1500); - status = mNetd->tetherOffloadRuleRemove(rule); - EXPECT_FALSE(status.isOk()); - EXPECT_EQ(ENODEV, status.serviceSpecificErrorCode()); - - // Invalid pmtu (too low) - rule = makeTetherOffloadRule(kIfaceExt, kIfaceInt, kAddr6, 128, kSrcMac, kDstMac, 1279); - status = mNetd->tetherOffloadRuleAdd(rule); - EXPECT_FALSE(status.isOk()); - EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); - - // Invalid pmtu (too high) - rule = makeTetherOffloadRule(kIfaceExt, kIfaceInt, kAddr6, 128, kSrcMac, kDstMac, 65536); - status = mNetd->tetherOffloadRuleAdd(rule); - EXPECT_FALSE(status.isOk()); - EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode()); - - // Remove non existent rule. Expect that silently return success if the rule did not exist. - rule = makeTetherOffloadRule(kIfaceNonExistent, kIfaceInt, kAddr6, 128, kSrcMac, kDstMac, 1500); - EXPECT_TRUE(mNetd->tetherOffloadRuleRemove(rule).isOk()); - - // Add and remove rule normally. - rule = makeTetherOffloadRule(kIfaceExt, kIfaceInt, kAddr6, 128, kSrcMac, kDstMac, 1500); - EXPECT_TRUE(mNetd->tetherOffloadRuleAdd(rule).isOk()); - EXPECT_TRUE(mNetd->tetherOffloadRuleRemove(rule).isOk()); -} - -static bool expectPacket(int fd, uint8_t* ipPacket, ssize_t ipLen) { - constexpr bool kDebug = false; - - uint8_t buf[ETHER_HDR_LEN + 1500]; - - // Wait a bit to ensure that the packet we're interested in has arrived. - // TODO: speed this up. - usleep(100 * 1000); - - ssize_t bytesRead; - ssize_t expectedLen = ipLen + ETHER_HDR_LEN; - while ((bytesRead = read(fd, buf, sizeof(buf))) >= 0) { - if (kDebug) { - std::cerr << fmt::format( - "Expected: {:02x}\n Actual: {:02x}\n", - fmt::join(ipPacket, ipPacket + ipLen, " "), - fmt::join(buf + ETHER_HDR_LEN, buf + ETHER_HDR_LEN + ipLen, " ")); - } - - if (bytesRead != expectedLen) { - continue; - } - - if (!memcmp(ipPacket, buf + ETHER_HDR_LEN, ipLen)) { - return true; - } - } - - return false; -} - -static bool tcQdiscExists(const std::string& interface) { - std::string command = StringPrintf("tc qdisc show dev %s", interface.c_str()); - std::vector<std::string> lines = runCommand(command); - for (const auto& line : lines) { - if (StartsWith(line, "qdisc clsact ffff:")) return true; - } - return false; -} - -static bool tcFilterExists(const std::string& interface) { - std::string command = StringPrintf("tc filter show dev %s ingress", interface.c_str()); - std::vector<std::string> lines = runCommand(command); - const std::basic_regex regex("^filter .* bpf .* prog_offload_schedcls_tether_.*$"); - for (const auto& line : lines) { - if (std::regex_match(Trim(line), regex)) return true; - } - return false; -} - -// TODO: probably remove the test because TetherOffload* binder calls are deprecated. -TEST_F(NetdBinderTest, DISABLED_TetherOffloadForwarding) { - SKIP_IF_EXTENDED_BPF_NOT_SUPPORTED; - - constexpr const char* kDownstreamPrefix = "2001:db8:2::/64"; - - // 1500-byte packet. - constexpr unsigned short kPayloadLen = 1500 - sizeof(ipv6hdr); - struct packet { - ipv6hdr hdr; - char data[kPayloadLen]; - } __attribute__((packed)) pkt = { - .hdr = - { - .version = 6, - .payload_len = htons(kPayloadLen), - .nexthdr = 59, // No next header. - .hop_limit = 64, - .saddr = {{{0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}}, - .daddr = {{{0x20, 0x01, 0x0d, 0xb8, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0f, 0x00, 0xca, 0xfe}}}, - }, - }; - ASSERT_EQ(1500U, sizeof(pkt)); - - // Use one of the test's tun interfaces as upstream. - // It must be part of a network or it will not have the clsact attached. - const auto& config = makeNativeNetworkConfig(TEST_NETID1, NativeNetworkType::PHYSICAL, - INetd::PERMISSION_NONE, false, false); - EXPECT_TRUE(mNetd->networkCreate(config).isOk()); - EXPECT_TRUE(mNetd->networkAddInterface(TEST_NETID1, sTun.name()).isOk()); - int fd1 = sTun.getFdForTesting(); - EXPECT_TRUE(tcQdiscExists(sTun.name())); - - // Create our own tap as a downstream. - TunInterface tap; - ASSERT_EQ(0, tap.init(true /* isTap */)); - ASSERT_LE(tap.name().size(), static_cast<size_t>(IFNAMSIZ)); - int fd2 = tap.getFdForTesting(); - - // Set it to nonblocking so that expectPacket can work. - int flags = fcntl(fd2, F_GETFL, 0); - fcntl(fd2, F_SETFL, flags | O_NONBLOCK); - - // Downstream interface setup. Add to local network, add directly-connected route, etc. - binder::Status status = mNetd->networkAddInterface(INetd::LOCAL_NET_ID, tap.name()); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - status = mNetd->tetherInterfaceAdd(tap.name()); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - expectTetherInterfaceConfigureForIPv6Router(tap.name()); - EXPECT_TRUE(tcQdiscExists(tap.name())); - - // Can't easily use INetd::NEXTHOP_NONE because it is a String16 constant. Use "" instead. - status = mNetd->networkAddRoute(INetd::LOCAL_NET_ID, tap.name(), kDownstreamPrefix, ""); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - - // Set up forwarding. All methods take intIface first and extIface second. - status = mNetd->tetherAddForward(tap.name(), sTun.name()); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - status = mNetd->ipfwdAddInterfaceForward(tap.name(), sTun.name()); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - EXPECT_TRUE(tcFilterExists(sTun.name())); - - std::vector<uint8_t> kDummyMac = {02, 00, 00, 00, 00, 00}; - uint8_t* daddr = reinterpret_cast<uint8_t*>(&pkt.hdr.daddr); - std::vector<uint8_t> dstAddr(daddr, daddr + sizeof(pkt.hdr.daddr)); - - TetherOffloadRuleParcel rule = makeTetherOffloadRule(sTun.ifindex(), tap.ifindex(), dstAddr, - 128, kDummyMac, kDummyMac, sizeof(pkt)); - status = mNetd->tetherOffloadRuleAdd(rule); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - - // Set data limit to one byte less than two packets. - // If you get rid of the '- 1' then the second packet will get forwarded - // and the EXPECT_FALSE(expectPacket(...)) a dozen lines down will fail. - status = mNetd->tetherOffloadSetInterfaceQuota(sTun.ifindex(), sizeof(pkt) * 2 - 1); - EXPECT_TRUE(status.isOk()) << status.exceptionMessage(); - - // Receive a packet on sTun. - EXPECT_EQ((ssize_t)sizeof(pkt), write(fd1, &pkt, sizeof(pkt))); - - // Expect a packet identical to pkt, except with a TTL of 63. - struct packet pkt2 = pkt; - ASSERT_EQ(1500U, sizeof(pkt2)); - pkt2.hdr.hop_limit = pkt.hdr.hop_limit - 1; - EXPECT_TRUE(expectPacket(fd2, (uint8_t*)&pkt2, sizeof(pkt2))); - - // Receive a second packet on sTun. - EXPECT_EQ((ssize_t)sizeof(pkt), write(fd1, &pkt, sizeof(pkt))); - - // Should fail to forward due to quota limit. - EXPECT_FALSE(expectPacket(fd2, (uint8_t*)&pkt2, sizeof(pkt2))); - - // Clean up. - EXPECT_TRUE(mNetd->tetherOffloadRuleRemove(rule).isOk()); - - TetherStatsParcel tetherStats; - EXPECT_TRUE(mNetd->tetherOffloadGetAndClearStats(sTun.ifindex(), &tetherStats).isOk()); - EXPECT_EQ("", tetherStats.iface); - EXPECT_EQ(static_cast<int64_t>(sizeof(pkt)), tetherStats.rxBytes); - EXPECT_EQ(1, tetherStats.rxPackets); - EXPECT_EQ(0, tetherStats.txBytes); - EXPECT_EQ(0, tetherStats.txPackets); - EXPECT_EQ(sTun.ifindex(), tetherStats.ifIndex); - - EXPECT_TRUE(mNetd->ipfwdRemoveInterfaceForward(tap.name(), sTun.name()).isOk()); - EXPECT_TRUE(mNetd->tetherRemoveForward(tap.name(), sTun.name()).isOk()); - EXPECT_TRUE(mNetd->networkRemoveRoute(INetd::LOCAL_NET_ID, tap.name(), kDownstreamPrefix, "") - .isOk()); - EXPECT_TRUE(mNetd->tetherInterfaceRemove(tap.name()).isOk()); - EXPECT_TRUE(mNetd->networkRemoveInterface(INetd::LOCAL_NET_ID, tap.name()).isOk()); - EXPECT_TRUE(mNetd->networkRemoveInterface(TEST_NETID1, sTun.name()).isOk()); -} - TEST_F(NetdBinderTest, TestServiceDump) { sp<IBinder> binder = INetd::asBinder(mNetd); ASSERT_NE(nullptr, binder); @@ -3967,40 +3849,6 @@ TEST_F(NetdBinderTest, TestServiceDump) { } } -TEST_F(NetdBinderTest, DeprecatedTetherOffloadRuleAdd) { - TetherOffloadRuleParcel emptyRule; - auto status = mNetd->tetherOffloadRuleAdd(emptyRule); - ASSERT_FALSE(status.isOk()); - ASSERT_EQ(status.exceptionCode(), binder::Status::EX_UNSUPPORTED_OPERATION); -} - -TEST_F(NetdBinderTest, DeprecatedTetherOffloadRuleRemove) { - TetherOffloadRuleParcel emptyRule; - auto status = mNetd->tetherOffloadRuleRemove(emptyRule); - ASSERT_FALSE(status.isOk()); - ASSERT_EQ(status.exceptionCode(), binder::Status::EX_UNSUPPORTED_OPERATION); -} - -TEST_F(NetdBinderTest, DeprecatedTetherOffloadGetStats) { - std::vector<TetherStatsParcel> tetherStatsList; - auto status = mNetd->tetherOffloadGetStats(&tetherStatsList); - ASSERT_FALSE(status.isOk()); - ASSERT_EQ(status.exceptionCode(), binder::Status::EX_UNSUPPORTED_OPERATION); -} - -TEST_F(NetdBinderTest, DeprecatedTetherOffloadSetInterfaceQuota) { - auto status = mNetd->tetherOffloadSetInterfaceQuota(0 /* ifIndex */, 0 /* quotaBytes */); - ASSERT_FALSE(status.isOk()); - ASSERT_EQ(status.exceptionCode(), binder::Status::EX_UNSUPPORTED_OPERATION); -} - -TEST_F(NetdBinderTest, DeprecatedTetherOffloadGetAndClearStats) { - TetherStatsParcel tetherStats; - auto status = mNetd->tetherOffloadGetAndClearStats(0 /* ifIndex */, &tetherStats); - ASSERT_FALSE(status.isOk()); - ASSERT_EQ(status.exceptionCode(), binder::Status::EX_UNSUPPORTED_OPERATION); -} - namespace { // aliases for better reading @@ -4013,6 +3861,7 @@ void verifyAppUidRules(std::vector<bool>&& expectedResults, std::vector<UidRange ASSERT_EQ(expectedResults.size(), uidRanges.size()); if (iface.size()) { std::string action = StringPrintf("lookup %s ", iface.c_str()); + std::string action_local = StringPrintf("lookup %s_local ", iface.c_str()); for (unsigned long i = 0; i < uidRanges.size(); i++) { EXPECT_EQ(expectedResults[i], ipRuleExistsForRange(RULE_PRIORITY_UID_EXPLICIT_NETWORK + subPriority, @@ -4023,6 +3872,8 @@ void verifyAppUidRules(std::vector<bool>&& expectedResults, std::vector<UidRange EXPECT_EQ(expectedResults[i], ipRuleExistsForRange(RULE_PRIORITY_UID_DEFAULT_NETWORK + subPriority, uidRanges[i], action)); + EXPECT_EQ(expectedResults[i], ipRuleExistsForRange(RULE_PRIORITY_UID_LOCAL_ROUTES, + uidRanges[i], action_local)); } } else { std::string action = "unreachable"; |