summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-06-20 20:35:11 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-06-20 20:35:11 +0000
commit719109b5f45ae83ad58cad15b339f5f28ed2a046 (patch)
tree7079b623b3832bfd7ec0ac58c772d4d3621a28cb
parentc5dfeeddc7725307937b56498981a17fc0861cc5 (diff)
parente4bff459efa16b95eb00c9114f36f3243756712f (diff)
downloadnetd-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.bp11
-rw-r--r--include/mainline/XtBpfProgLocations.h37
-rw-r--r--server/Android.bp30
-rw-r--r--server/BandwidthController.cpp7
-rw-r--r--server/BandwidthControllerTest.cpp2
-rw-r--r--server/Controllers.cpp10
-rw-r--r--server/MDnsEventReporter.cpp52
-rw-r--r--server/MDnsEventReporter.h21
-rw-r--r--server/MDnsSdListener.cpp8
-rw-r--r--server/RouteController.cpp127
-rw-r--r--server/RouteController.h27
-rw-r--r--server/TcUtils.h52
-rw-r--r--server/TetherController.h3
-rw-r--r--tests/Android.bp12
-rw-r--r--tests/benchmarks/Android.bp6
-rw-r--r--tests/binder_test.cpp475
16 files changed, 426 insertions, 454 deletions
diff --git a/Android.bp b/Android.bp
index 88379109..b654f76b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -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";