Snap for 8953554 from e4bff459efa16b95eb00c9114f36f3243756712f to mainline-tzdata4-release

Change-Id: I38bbcaf1ebc40a969caeaaac4a9b8ab763f1cee9
diff --git a/Android.bp b/Android.bp
index 8837910..b654f76 100644
--- a/Android.bp
+++ b/Android.bp
@@ -33,6 +33,17 @@
     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 0000000..95a5742
--- /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 f29f6cf..06ea0ba 100644
--- a/server/Android.bp
+++ b/server/Android.bp
@@ -37,12 +37,15 @@
 // 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 @@
         "libpcap",
         "libssl",
         "libsysutils",
-        "netd_aidl_interface-V8-cpp",
         "netd_event_listener_interface-V1-cpp",
     ],
     static_libs: [
@@ -86,7 +88,10 @@
 
 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 @@
     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 @@
         "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 {
     name: "ndc",
-    defaults: ["netd_defaults"],
+    defaults: [
+        "netd_aidl_interface_lateststable_cpp_shared",
+        "netd_defaults",
+    ],
     include_dirs: [
         "system/netd/include",
     ],
@@ -172,7 +179,6 @@
         "libutils",
         "libbinder",
         "dnsresolver_aidl_interface-V7-cpp",
-        "netd_aidl_interface-V8-cpp",
     ],
     srcs: [
         "ndc.cpp",
@@ -187,7 +193,10 @@
 
 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 @@
         "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 @@
         "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 96a82e2..8dba75a 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_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::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 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 e7d29d2..844681d 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 0df6b0e..00ee186 100644
--- a/server/Controllers.cpp
+++ b/server/Controllers.cpp
@@ -277,7 +277,15 @@
     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 db9021a..e94de36 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 @@
     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 @@
     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 @@
     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 @@
 
     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 e49c3e3..cbc43ec 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 @@
             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 1636400..1d1ea40 100644
--- a/server/MDnsSdListener.cpp
+++ b/server/MDnsSdListener.cpp
@@ -140,7 +140,7 @@
     }
 
     for (const auto& it : listeners) {
-        it->onServiceDiscoveryStatus(info);
+        it->getListener()->onServiceDiscoveryStatus(info);
     }
 }
 
@@ -212,7 +212,7 @@
     }
 
     for (const auto& it : listeners) {
-        it->onServiceRegistrationStatus(info);
+        it->getListener()->onServiceRegistrationStatus(info);
     }
 }
 
@@ -277,7 +277,7 @@
     }
 
     for (const auto& it : listeners) {
-        it->onServiceResolutionStatus(info);
+        it->getListener()->onServiceResolutionStatus(info);
     }
 }
 
@@ -343,7 +343,7 @@
         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 5ed33cd..d2af9a3 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 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 @@
                                                           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 @@
     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 @@
 // 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 @@
                                         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 @@
         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 @@
     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::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 9b04cfd..ff41678 100644
--- a/server/RouteController.h
+++ b/server/RouteController.h
@@ -55,11 +55,17 @@
 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 @@
 // 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 @@
     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 @@
                                     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 d205c04..4b1b2b9 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 @@
     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 b4472bd..d2195f2 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 21830d6..ff918cc 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -37,7 +37,10 @@
 
 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 @@
         "libnetutils",
         "libsysutils",
         "libutils",
-        "netd_aidl_interface-V8-cpp",
     ],
 }
 
@@ -74,7 +76,10 @@
         "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 @@
         "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 95c00aa..13c8105 100644
--- a/tests/benchmarks/Android.bp
+++ b/tests/benchmarks/Android.bp
@@ -11,7 +11,10 @@
 
 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 @@
     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 519effe..bc1e739 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_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 @@
                                      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 @@
 
 }  // 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 @@
 }
 
 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 @@
     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 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 @@
     // 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 @@
         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 @@
     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 @@
     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, 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 @@
     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 @@
             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";