Snap for 6405781 from 00e662588990034e7d08cedd79b65097fe19c8de to sdk-release

Change-Id: I1eace4f2c37720a0ee84bde7d4b570afd8fe95dd
diff --git a/Android.bp b/Android.bp
index 44f3f03..d4afc1e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,6 +1,10 @@
 cc_library_headers {
     name: "libnetd_client_headers",
     export_include_dirs: ["include"],
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.resolv",
+    ],
 }
 
 cc_defaults {
diff --git a/bpf_progs/offload.c b/bpf_progs/offload.c
index 9b475b1..7d33e46 100644
--- a/bpf_progs/offload.c
+++ b/bpf_progs/offload.c
@@ -146,8 +146,9 @@
     return do_forward(skb, true);
 }
 
-SEC("schedcls/ingress/tether_rawip")
-int sched_cls_ingress_tether_rawip(struct __sk_buff* skb) {
+DEFINE_BPF_PROG_KVER("schedcls/ingress/tether_rawip", AID_ROOT, AID_ROOT,
+                     sched_cls_ingress_tether_rawip, KVER(4, 14, 0))
+(struct __sk_buff* skb) {
     return do_forward(skb, false);
 }
 
diff --git a/client/NetdClient.cpp b/client/NetdClient.cpp
index 147e63e..643188b 100644
--- a/client/NetdClient.cpp
+++ b/client/NetdClient.cpp
@@ -195,7 +195,7 @@
 }
 
 int netdClientSendmmsg(int sockfd, const mmsghdr* msgs, unsigned int msgcount, int flags) {
-    if (propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS_HOOKED)) {
+    if (propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS_HOOKED) && !checkSocket(sockfd)) {
         const sockaddr* addr = nullptr;
         if ((msgcount > 0) && (msgs != nullptr) && (msgs[0].msg_hdr.msg_name != nullptr)) {
             addr = reinterpret_cast<const sockaddr*>(msgs[0].msg_hdr.msg_name);
@@ -210,7 +210,7 @@
 }
 
 ssize_t netdClientSendmsg(int sockfd, const msghdr* msg, unsigned int flags) {
-    if (propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS_HOOKED)) {
+    if (propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS_HOOKED) && !checkSocket(sockfd)) {
         const sockaddr* addr = nullptr;
         if ((msg != nullptr) && (msg->msg_name != nullptr)) {
             addr = reinterpret_cast<const sockaddr*>(msg->msg_name);
@@ -226,7 +226,7 @@
 
 int netdClientSendto(int sockfd, const void* buf, size_t bufsize, int flags, const sockaddr* addr,
                      socklen_t addrlen) {
-    if (propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS_HOOKED)) {
+    if (propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS_HOOKED) && !checkSocket(sockfd)) {
         if ((addr != nullptr) && (FwmarkCommand::isSupportedFamily(addr->sa_family))) {
             FwmarkConnectInfo sendtoInfo(0, 0, addr);
             FwmarkCommand command = {FwmarkCommand::ON_SENDTO, 0, 0, 0};
@@ -382,19 +382,17 @@
 
 }  // namespace
 
-#define CHECK_SOCKET_IS_MARKABLE(sock)          \
-    do {                                        \
-        int err;                                \
-        if ((err = checkSocket(sock)) != 0) {   \
-            return err;                         \
-        }                                       \
-    } while (false);
+#define CHECK_SOCKET_IS_MARKABLE(sock) \
+    do {                               \
+        int err = checkSocket(sock);   \
+        if (err) return err;           \
+    } while (false)
 
 #define HOOK_ON_FUNC(remoteFunc, nativeFunc, localFunc) \
     do {                                                \
-        if (remoteFunc && *remoteFunc) {                \
-            nativeFunc = *remoteFunc;                   \
-            *remoteFunc = localFunc;                    \
+        if ((remoteFunc) && *(remoteFunc)) {            \
+            (nativeFunc) = *(remoteFunc);               \
+            *(remoteFunc) = (localFunc);                \
         }                                               \
     } while (false)
 
@@ -476,9 +474,7 @@
 }
 
 extern "C" int protectFromVpn(int socketFd) {
-    if (socketFd < 0) {
-        return -EBADF;
-    }
+    CHECK_SOCKET_IS_MARKABLE(socketFd);
     FwmarkCommand command = {FwmarkCommand::PROTECT_FROM_VPN, 0, 0, 0};
     return FwmarkClient().send(&command, socketFd, nullptr);
 }
diff --git a/client/NetdClientTest.cpp b/client/NetdClientTest.cpp
index b523ccc..126c7fd 100644
--- a/client/NetdClientTest.cpp
+++ b/client/NetdClientTest.cpp
@@ -74,3 +74,21 @@
     unsigned* testNull = nullptr;
     EXPECT_EQ(-EFAULT, getNetworkForDns(testNull));
 }
+
+TEST(NetdClientTest, protectFromVpnBadFd) {
+    EXPECT_EQ(-EBADF, protectFromVpn(-1));
+}
+
+TEST(NetdClientTest, protectFromVpnUnixStream) {
+    int s = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+    ASSERT_GE(s, 3);
+    EXPECT_EQ(-EAFNOSUPPORT, protectFromVpn(s));
+    close(s);
+}
+
+TEST(NetdClientTest, protectFromVpnTcp6) {
+    int s = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
+    ASSERT_GE(s, 3);
+    EXPECT_EQ(0, protectFromVpn(s));
+    close(s);
+}
diff --git a/libnetdutils/Android.bp b/libnetdutils/Android.bp
index c124aa7..fc9b724 100644
--- a/libnetdutils/Android.bp
+++ b/libnetdutils/Android.bp
@@ -28,6 +28,11 @@
     sanitize: {
         cfi: true,
     },
+
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.resolv",
+    ],
 }
 
 cc_test {
diff --git a/libnetdutils/FdTest.cpp b/libnetdutils/FdTest.cpp
index 889c1b7..7080f83 100644
--- a/libnetdutils/FdTest.cpp
+++ b/libnetdutils/FdTest.cpp
@@ -102,6 +102,7 @@
     UniqueFd u1(kFd);
     {
         UniqueFd u2(std::move(u1));
+        // NOLINTNEXTLINE bugprone-use-after-move
         EXPECT_FALSE(isWellFormed(u1));
         EXPECT_TRUE(isWellFormed(u2));
         EXPECT_CALL(mSyscalls, close(kFd)).WillOnce(Return(status::ok));
@@ -115,10 +116,12 @@
     UniqueFd u1(kFd);
     {
         UniqueFd u2 = std::move(u1);
+        // NOLINTNEXTLINE bugprone-use-after-move
         EXPECT_FALSE(isWellFormed(u1));
         EXPECT_TRUE(isWellFormed(u2));
         UniqueFd u3;
         u3 = std::move(u2);
+        // NOLINTNEXTLINE bugprone-use-after-move
         EXPECT_FALSE(isWellFormed(u2));
         EXPECT_TRUE(isWellFormed(u3));
         EXPECT_CALL(mSyscalls, close(kFd)).WillOnce(Return(status::ok));
diff --git a/server/Android.bp b/server/Android.bp
index de94e23..b21b070 100644
--- a/server/Android.bp
+++ b/server/Android.bp
@@ -6,6 +6,14 @@
         "binder/android/net/metrics/INetdEventListener.aidl",
     ],
     versions: ["1"],
+    backend: {
+        ndk: {
+            apex_available: [
+                "//apex_available:platform",
+                "com.android.resolv",
+            ],
+        },
+    },
 }
 
 // These are used in netd_integration_test
@@ -17,6 +25,7 @@
         "InterfaceController.cpp",
         "NetlinkCommands.cpp",
         "NetlinkListener.cpp",
+        "SockDiag.cpp",
         "XfrmController.cpp",
         "TrafficController.cpp",
     ],
@@ -33,6 +42,7 @@
         "binder/android/net/MarkMaskParcel.aidl",
         "binder/android/net/RouteInfoParcel.aidl",
         "binder/android/net/TetherConfigParcel.aidl",
+        "binder/android/net/TetherOffloadRuleParcel.aidl",
         "binder/android/net/TetherStatsParcel.aidl",
         "binder/android/net/UidRangeParcel.aidl",
     ],
@@ -40,10 +50,17 @@
         cpp: {
             gen_log: true,
         },
+        java: {
+            apex_available: [
+                "//apex_available:platform", // used from services.net
+                "com.android.bluetooth.updatable",
+            ],
+        },
     },
     versions: [
         "1",
         "2",
+        "3",
     ],
 }
 
@@ -147,7 +164,7 @@
         "libselinux",
         "libsysutils",
         "libutils",
-        "netd_aidl_interface-unstable-cpp",
+        "netd_aidl_interface-cpp",
         "netd_event_listener_interface-cpp",
         "oemnetd_aidl_interface-cpp",
     ],
@@ -243,7 +260,7 @@
         "libnetd_server",
         "libnetd_test_tun_interface",
         "libqtaguid",
-        "netd_aidl_interface-cpp",
+        "netd_aidl_interface-unstable-cpp",
         "netd_event_listener_interface-cpp",
     ],
     shared_libs: [
diff --git a/server/NdcDispatcher.cpp b/server/NdcDispatcher.cpp
index 7692a9c..e957db8 100644
--- a/server/NdcDispatcher.cpp
+++ b/server/NdcDispatcher.cpp
@@ -304,7 +304,7 @@
                              false);
                 return 0;
             }
-            int enable = !strncmp(argv[3], "enable", 7);
+            int enable = !strncmp(argv[3], "enable", 6);
             Status status = mNetd->interfaceSetIPv6PrivacyExtensions(std::string(argv[2]), enable);
             if (status.isOk()) {
                 cli->sendMsg(ResponseCode::CommandOkay, "IPv6 privacy extensions changed", false);
@@ -321,7 +321,7 @@
                 return 0;
             }
 
-            int enable = !strncmp(argv[3], "enable", 7);
+            int enable = !strncmp(argv[3], "enable", 6);
             Status status = mNetd->interfaceSetEnableIPv6(std::string(argv[2]), enable);
             if (status.isOk()) {
                 cli->sendMsg(ResponseCode::CommandOkay, "IPv6 state changed", false);
diff --git a/server/NetdNativeService.cpp b/server/NetdNativeService.cpp
index fb7f839..b6c0d4e 100644
--- a/server/NetdNativeService.cpp
+++ b/server/NetdNativeService.cpp
@@ -52,6 +52,7 @@
 
 using android::base::StringPrintf;
 using android::base::WriteStringToFile;
+using android::net::TetherOffloadRuleParcel;
 using android::net::TetherStatsParcel;
 using android::net::UidRangeParcel;
 using android::netdutils::DumpWriter;
@@ -1247,20 +1248,16 @@
     return binder::Status::ok();
 }
 
-binder::Status NetdNativeService::tetherRuleAddDownstreamIpv6(
-        int intIfaceIndex, int extIfaceIndex, const std::vector<uint8_t>& ipAddress,
-        const std::vector<uint8_t>& srcL2Address, const std::vector<uint8_t>& dstL2Address) {
+binder::Status NetdNativeService::tetherOffloadRuleAdd(const TetherOffloadRuleParcel& rule) {
     ENFORCE_NETWORK_STACK_PERMISSIONS();
 
-    return asBinderStatus(gCtls->tetherCtrl.addDownstreamIpv6Rule(
-            intIfaceIndex, extIfaceIndex, ipAddress, srcL2Address, dstL2Address));
+    return asBinderStatus(gCtls->tetherCtrl.addOffloadRule(rule));
 }
 
-binder::Status NetdNativeService::tetherRuleRemoveDownstreamIpv6(
-        int extIfaceIndex, const std::vector<uint8_t>& ipAddress) {
+binder::Status NetdNativeService::tetherOffloadRuleRemove(const TetherOffloadRuleParcel& rule) {
     ENFORCE_NETWORK_STACK_PERMISSIONS();
 
-    return asBinderStatus(gCtls->tetherCtrl.removeDownstreamIpv6Rule(extIfaceIndex, ipAddress));
+    return asBinderStatus(gCtls->tetherCtrl.removeOffloadRule(rule));
 }
 
 }  // namespace net
diff --git a/server/NetdNativeService.h b/server/NetdNativeService.h
index 0c241d7..0d95c8e 100644
--- a/server/NetdNativeService.h
+++ b/server/NetdNativeService.h
@@ -136,12 +136,9 @@
                                     const std::string& extIface) override;
     binder::Status tetherRemoveForward(const std::string& intIface,
                                        const std::string& extIface) override;
-    binder::Status tetherRuleAddDownstreamIpv6(int intIfaceIndex, int extIfaceIndex,
-                                               const std::vector<uint8_t>& ipAddress,
-                                               const std::vector<uint8_t>& srcL2Address,
-                                               const std::vector<uint8_t>& dstL2Address) override;
-    binder::Status tetherRuleRemoveDownstreamIpv6(int extIfaceIndex,
-                                                  const std::vector<uint8_t>& ipAddress) override;
+    binder::Status tetherOffloadRuleAdd(const android::net::TetherOffloadRuleParcel& rule) override;
+    binder::Status tetherOffloadRuleRemove(
+            const android::net::TetherOffloadRuleParcel& rule) override;
 
     // Interface-related commands.
     binder::Status interfaceAddAddress(const std::string &ifName,
diff --git a/server/NetlinkManager.cpp b/server/NetlinkManager.cpp
index d014443..e3c1db9 100644
--- a/server/NetlinkManager.cpp
+++ b/server/NetlinkManager.cpp
@@ -17,6 +17,7 @@
 #include <errno.h>
 #include <stdio.h>
 #include <string.h>
+#include <unistd.h>
 
 #include <sys/socket.h>
 #include <sys/time.h>
diff --git a/server/OffloadUtils.cpp b/server/OffloadUtils.cpp
index 53665f6..a743458 100644
--- a/server/OffloadUtils.cpp
+++ b/server/OffloadUtils.cpp
@@ -71,6 +71,7 @@
     switch (rv) {
         case ARPHRD_ETHER:
             return true;
+        case ARPHRD_NONE:
         case ARPHRD_RAWIP:  // in Linux 4.14+ rmnet support was upstreamed and this is 519
         case 530:           // this is ARPHRD_RAWIP on some Android 4.9 kernels with rmnet
             return false;
diff --git a/server/PppController.cpp b/server/PppController.cpp
index 80a36e9..b80e1a6 100644
--- a/server/PppController.cpp
+++ b/server/PppController.cpp
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
-#include <stdlib.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 
 #include <sys/socket.h>
 #include <sys/stat.h>
diff --git a/server/SockDiag.h b/server/SockDiag.h
index af96409..745c09e 100644
--- a/server/SockDiag.h
+++ b/server/SockDiag.h
@@ -34,7 +34,6 @@
 
 struct inet_diag_msg;
 struct tcp_info;
-class SockDiagTest;
 
 namespace android {
 namespace net {
diff --git a/server/TcpSocketMonitor.cpp b/server/TcpSocketMonitor.cpp
index f4b505d..c18fe92 100644
--- a/server/TcpSocketMonitor.cpp
+++ b/server/TcpSocketMonitor.cpp
@@ -70,9 +70,10 @@
 
 // Helper macro for reading fields into struct tcp_info and handling different struct tcp_info
 // versions in the kernel.
-#define TCPINFO_GET(ptr, fld, len, zero) \
-        (((ptr) != nullptr && (offsetof(struct tcp_info, fld) + sizeof((ptr)->fld)) < len) ? \
-        (ptr)->fld : zero)
+#define TCPINFO_GET(ptr, fld, len, zero)                                                 \
+    (((ptr) != nullptr && (offsetof(struct tcp_info, fld) + sizeof((ptr)->fld)) < (len)) \
+             ? (ptr)->fld                                                                \
+             : (zero))
 
 static void tcpInfoPrint(DumpWriter &dw, Fwmark mark, const struct inet_diag_msg *sockinfo,
                          const struct tcp_info *tcpinfo, uint32_t tcpinfoLen) {
diff --git a/server/TetherController.cpp b/server/TetherController.cpp
index cc3b49c..dd8622b 100644
--- a/server/TetherController.cpp
+++ b/server/TetherController.cpp
@@ -55,6 +55,8 @@
 #include "Permission.h"
 #include "TetherController.h"
 
+#include "android/net/TetherOffloadRuleParcel.h"
+
 namespace android {
 namespace net {
 
@@ -65,6 +67,7 @@
 using android::base::StringAppendF;
 using android::base::StringPrintf;
 using android::base::unique_fd;
+using android::net::TetherOffloadRuleParcel;
 using android::netdutils::DumpWriter;
 using android::netdutils::ScopedIndent;
 using android::netdutils::statusFromErrno;
@@ -603,7 +606,8 @@
     }
 
     // add this if we are the first enabled nat for this upstream
-    if (!isAnyForwardingEnabledOnUpstream(extIface)) {
+    bool firstDownstreamForThisUpstream = !isAnyForwardingEnabledOnUpstream(extIface);
+    if (firstDownstreamForThisUpstream) {
         std::vector<std::string> v4Cmds = {
             "*nat",
             StringPrintf("-A %s -o %s -j MASQUERADE", LOCAL_NAT_POSTROUTING, extIface),
@@ -629,7 +633,7 @@
         return -ENODEV;
     }
 
-    maybeStartBpf(extIface);
+    if (firstDownstreamForThisUpstream) maybeStartBpf(extIface);
     return 0;
 }
 
@@ -813,55 +817,68 @@
     }
 
     setForwardRules(false, intIface, extIface);
-    if (!isAnyForwardingPairEnabled()) {
-        setDefaults();
-    }
-
-    maybeStopBpf(extIface);
+    if (!isAnyForwardingEnabledOnUpstream(extIface)) maybeStopBpf(extIface);
+    if (!isAnyForwardingPairEnabled()) setDefaults();
     return 0;
 }
 
-Result<void> TetherController::addDownstreamIpv6Rule(int intIfaceIndex, int extIfaceIndex,
-                                                     const std::vector<uint8_t>& ipAddress,
-                                                     const std::vector<uint8_t>& srcL2Address,
-                                                     const std::vector<uint8_t>& dstL2Address) {
+namespace {
+Result<void> validateOffloadRule(const TetherOffloadRuleParcel& rule) {
+    struct ethhdr hdr;
+
+    if (rule.inputInterfaceIndex <= 0) {
+        return Error(ENODEV) << "Invalid input interface " << rule.inputInterfaceIndex;
+    }
+    if (rule.outputInterfaceIndex <= 0) {
+        return Error(ENODEV) << "Invalid output interface " << rule.inputInterfaceIndex;
+    }
+    if (rule.prefixLength != 128) {
+        return Error(EINVAL) << "Prefix length must be 128, not " << rule.prefixLength;
+    }
+    if (rule.destination.size() != sizeof(in6_addr)) {
+        return Error(EAFNOSUPPORT) << "Invalid IP address length " << rule.destination.size();
+    }
+    if (rule.srcL2Address.size() != sizeof(hdr.h_source)) {
+        return Error(ENXIO) << "Invalid L2 src address length " << rule.srcL2Address.size();
+    }
+    if (rule.dstL2Address.size() != sizeof(hdr.h_dest)) {
+        return Error(ENXIO) << "Invalid L2 dst address length " << rule.dstL2Address.size();
+    }
+    return Result<void>();
+}
+}  // namespace
+
+Result<void> TetherController::addOffloadRule(const TetherOffloadRuleParcel& rule) {
+    Result<void> res = validateOffloadRule(rule);
+    if (!res.ok()) return res;
+
     ethhdr hdr = {
             .h_proto = htons(ETH_P_IPV6),
     };
-    if (ipAddress.size() != sizeof(in6_addr)) {
-        return Error(EINVAL) << "Invalid IP address length " << ipAddress.size();
-    }
-    if (srcL2Address.size() != sizeof(hdr.h_source)) {
-        return Error(EINVAL) << "Invalid L2 src address length " << srcL2Address.size();
-    }
-    if (dstL2Address.size() != sizeof(hdr.h_dest)) {
-        return Error(EINVAL) << "Invalid L2 dst address length " << dstL2Address.size();
-    }
-    memcpy(&hdr.h_dest, dstL2Address.data(), sizeof(hdr.h_dest));
-    memcpy(&hdr.h_source, srcL2Address.data(), sizeof(hdr.h_source));
+    memcpy(&hdr.h_dest, rule.dstL2Address.data(), sizeof(hdr.h_dest));
+    memcpy(&hdr.h_source, rule.srcL2Address.data(), sizeof(hdr.h_source));
 
+    // Only downstream supported for now.
     TetherIngressKey key = {
-            .iif = static_cast<uint32_t>(extIfaceIndex),
-            .neigh6 = *(const in6_addr*)ipAddress.data(),
+            .iif = static_cast<uint32_t>(rule.inputInterfaceIndex),
+            .neigh6 = *(const in6_addr*)rule.destination.data(),
     };
 
     TetherIngressValue value = {
-            static_cast<uint32_t>(intIfaceIndex),
+            static_cast<uint32_t>(rule.outputInterfaceIndex),
             hdr,
     };
 
     return mBpfIngressMap.writeValue(key, value, BPF_ANY);
 }
 
-Result<void> TetherController::removeDownstreamIpv6Rule(int extIfaceIndex,
-                                                        const std::vector<uint8_t>& ipAddress) {
-    if (ipAddress.size() != sizeof(in6_addr)) {
-        return Error(EINVAL) << "Invalid IP address length " << ipAddress.size();
-    }
+Result<void> TetherController::removeOffloadRule(const TetherOffloadRuleParcel& rule) {
+    Result<void> res = validateOffloadRule(rule);
+    if (!res.ok()) return res;
 
     TetherIngressKey key = {
-            .iif = static_cast<uint32_t>(extIfaceIndex),
-            .neigh6 = *(const in6_addr*)ipAddress.data(),
+            .iif = static_cast<uint32_t>(rule.inputInterfaceIndex),
+            .neigh6 = *(const in6_addr*)rule.destination.data(),
     };
 
     Result<void> ret = mBpfIngressMap.deleteValue(key);
diff --git a/server/TetherController.h b/server/TetherController.h
index 7835cf6..7fd7166 100644
--- a/server/TetherController.h
+++ b/server/TetherController.h
@@ -30,6 +30,8 @@
 #include "bpf/BpfMap.h"
 #include "netdbpf/bpf_shared.h"
 
+#include "android/net/TetherOffloadRuleParcel.h"
+
 namespace android {
 namespace net {
 
@@ -103,13 +105,8 @@
     int disableNat(const char* intIface, const char* extIface);
     int setupIptablesHooks();
 
-    base::Result<void> addDownstreamIpv6Rule(int intIfaceIndex, int extIfaceIndex,
-                                             const std::vector<uint8_t>& ipAddress,
-                                             const std::vector<uint8_t>& srcL2Address,
-                                             const std::vector<uint8_t>& dstL2Address);
-
-    base::Result<void> removeDownstreamIpv6Rule(int extifaceIndex,
-                                                const std::vector<uint8_t>& ipAddress);
+    base::Result<void> addOffloadRule(const TetherOffloadRuleParcel& rule);
+    base::Result<void> removeOffloadRule(const TetherOffloadRuleParcel& rule);
 
     class TetherStats {
       public:
diff --git a/server/TrafficController.cpp b/server/TrafficController.cpp
index 36ccf53..e2b88a6 100644
--- a/server/TrafficController.cpp
+++ b/server/TrafficController.cpp
@@ -85,12 +85,12 @@
 static_assert(STATS_MAP_SIZE - TOTAL_UID_STATS_ENTRIES_LIMIT > 100,
               "The limit for stats map is to high, stats data may be lost due to overflow");
 
-#define FLAG_MSG_TRANS(result, flag, value)            \
-    do {                                               \
-        if (value & flag) {                            \
-            result.append(StringPrintf(" %s", #flag)); \
-            value &= ~flag;                            \
-        }                                              \
+#define FLAG_MSG_TRANS(result, flag, value) \
+    do {                                    \
+        if ((value) & (flag)) {             \
+            (result).append(" " #flag);     \
+            (value) &= ~(flag);             \
+        }                                   \
     } while (0)
 
 const std::string uidMatchTypeToString(uint8_t match) {
diff --git a/server/aidl_api/netd_aidl_interface/3/.hash b/server/aidl_api/netd_aidl_interface/3/.hash
new file mode 100644
index 0000000..59cf708
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/3/.hash
@@ -0,0 +1 @@
+e17c1f9b2068b539b22e3a4a447edea3c80aee4b
diff --git a/server/aidl_api/netd_aidl_interface/3/android/net/INetd.aidl b/server/aidl_api/netd_aidl_interface/3/android/net/INetd.aidl
new file mode 100644
index 0000000..135b738
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/3/android/net/INetd.aidl
@@ -0,0 +1,161 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+interface INetd {
+  boolean isAlive();
+  boolean firewallReplaceUidChain(in @utf8InCpp String chainName, boolean isWhitelist, in int[] uids);
+  boolean bandwidthEnableDataSaver(boolean enable);
+  void networkCreatePhysical(int netId, int permission);
+  void networkCreateVpn(int netId, boolean secure);
+  void networkDestroy(int netId);
+  void networkAddInterface(int netId, in @utf8InCpp String iface);
+  void networkRemoveInterface(int netId, in @utf8InCpp String iface);
+  void networkAddUidRanges(int netId, in android.net.UidRangeParcel[] uidRanges);
+  void networkRemoveUidRanges(int netId, in android.net.UidRangeParcel[] uidRanges);
+  void networkRejectNonSecureVpn(boolean add, in android.net.UidRangeParcel[] uidRanges);
+  void socketDestroy(in android.net.UidRangeParcel[] uidRanges, in int[] exemptUids);
+  boolean tetherApplyDnsInterfaces();
+  android.net.TetherStatsParcel[] tetherGetStats();
+  void interfaceAddAddress(in @utf8InCpp String ifName, in @utf8InCpp String addrString, int prefixLength);
+  void interfaceDelAddress(in @utf8InCpp String ifName, in @utf8InCpp String addrString, int prefixLength);
+  @utf8InCpp String getProcSysNet(int ipversion, int which, in @utf8InCpp String ifname, in @utf8InCpp String parameter);
+  void setProcSysNet(int ipversion, int which, in @utf8InCpp String ifname, in @utf8InCpp String parameter, in @utf8InCpp String value);
+  void ipSecSetEncapSocketOwner(in ParcelFileDescriptor socket, int newUid);
+  int ipSecAllocateSpi(int transformId, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi);
+  void ipSecAddSecurityAssociation(int transformId, int mode, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int underlyingNetId, int spi, int markValue, int markMask, in @utf8InCpp String authAlgo, in byte[] authKey, in int authTruncBits, in @utf8InCpp String cryptAlgo, in byte[] cryptKey, in int cryptTruncBits, in @utf8InCpp String aeadAlgo, in byte[] aeadKey, in int aeadIcvBits, int encapType, int encapLocalPort, int encapRemotePort, int interfaceId);
+  void ipSecDeleteSecurityAssociation(int transformId, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi, int markValue, int markMask, int interfaceId);
+  void ipSecApplyTransportModeTransform(in ParcelFileDescriptor socket, int transformId, int direction, in @utf8InCpp String sourceAddress, in @utf8InCpp String destinationAddress, int spi);
+  void ipSecRemoveTransportModeTransform(in ParcelFileDescriptor socket);
+  void ipSecAddSecurityPolicy(int transformId, int selAddrFamily, int direction, in @utf8InCpp String tmplSrcAddress, in @utf8InCpp String tmplDstAddress, int spi, int markValue, int markMask, int interfaceId);
+  void ipSecUpdateSecurityPolicy(int transformId, int selAddrFamily, int direction, in @utf8InCpp String tmplSrcAddress, in @utf8InCpp String tmplDstAddress, int spi, int markValue, int markMask, int interfaceId);
+  void ipSecDeleteSecurityPolicy(int transformId, int selAddrFamily, int direction, int markValue, int markMask, int interfaceId);
+  void ipSecAddTunnelInterface(in @utf8InCpp String deviceName, in @utf8InCpp String localAddress, in @utf8InCpp String remoteAddress, int iKey, int oKey, int interfaceId);
+  void ipSecUpdateTunnelInterface(in @utf8InCpp String deviceName, in @utf8InCpp String localAddress, in @utf8InCpp String remoteAddress, int iKey, int oKey, int interfaceId);
+  void ipSecRemoveTunnelInterface(in @utf8InCpp String deviceName);
+  void wakeupAddInterface(in @utf8InCpp String ifName, in @utf8InCpp String prefix, int mark, int mask);
+  void wakeupDelInterface(in @utf8InCpp String ifName, in @utf8InCpp String prefix, int mark, int mask);
+  void setIPv6AddrGenMode(in @utf8InCpp String ifName, int mode);
+  void idletimerAddInterface(in @utf8InCpp String ifName, int timeout, in @utf8InCpp String classLabel);
+  void idletimerRemoveInterface(in @utf8InCpp String ifName, int timeout, in @utf8InCpp String classLabel);
+  void strictUidCleartextPenalty(int uid, int policyPenalty);
+  @utf8InCpp String clatdStart(in @utf8InCpp String ifName, in @utf8InCpp String nat64Prefix);
+  void clatdStop(in @utf8InCpp String ifName);
+  boolean ipfwdEnabled();
+  @utf8InCpp String[] ipfwdGetRequesterList();
+  void ipfwdEnableForwarding(in @utf8InCpp String requester);
+  void ipfwdDisableForwarding(in @utf8InCpp String requester);
+  void ipfwdAddInterfaceForward(in @utf8InCpp String fromIface, in @utf8InCpp String toIface);
+  void ipfwdRemoveInterfaceForward(in @utf8InCpp String fromIface, in @utf8InCpp String toIface);
+  void bandwidthSetInterfaceQuota(in @utf8InCpp String ifName, long bytes);
+  void bandwidthRemoveInterfaceQuota(in @utf8InCpp String ifName);
+  void bandwidthSetInterfaceAlert(in @utf8InCpp String ifName, long bytes);
+  void bandwidthRemoveInterfaceAlert(in @utf8InCpp String ifName);
+  void bandwidthSetGlobalAlert(long bytes);
+  void bandwidthAddNaughtyApp(int uid);
+  void bandwidthRemoveNaughtyApp(int uid);
+  void bandwidthAddNiceApp(int uid);
+  void bandwidthRemoveNiceApp(int uid);
+  void tetherStart(in @utf8InCpp String[] dhcpRanges);
+  void tetherStop();
+  boolean tetherIsEnabled();
+  void tetherInterfaceAdd(in @utf8InCpp String ifName);
+  void tetherInterfaceRemove(in @utf8InCpp String ifName);
+  @utf8InCpp String[] tetherInterfaceList();
+  void tetherDnsSet(int netId, in @utf8InCpp String[] dnsAddrs);
+  @utf8InCpp String[] tetherDnsList();
+  void networkAddRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop);
+  void networkRemoveRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop);
+  void networkAddLegacyRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop, int uid);
+  void networkRemoveLegacyRoute(int netId, in @utf8InCpp String ifName, in @utf8InCpp String destination, in @utf8InCpp String nextHop, int uid);
+  int networkGetDefault();
+  void networkSetDefault(int netId);
+  void networkClearDefault();
+  void networkSetPermissionForNetwork(int netId, int permission);
+  void networkSetPermissionForUser(int permission, in int[] uids);
+  void networkClearPermissionForUser(in int[] uids);
+  void trafficSetNetPermForUids(int permission, in int[] uids);
+  void networkSetProtectAllow(int uid);
+  void networkSetProtectDeny(int uid);
+  boolean networkCanProtect(int uid);
+  void firewallSetFirewallType(int firewalltype);
+  void firewallSetInterfaceRule(in @utf8InCpp String ifName, int firewallRule);
+  void firewallSetUidRule(int childChain, int uid, int firewallRule);
+  void firewallEnableChildChain(int childChain, boolean enable);
+  @utf8InCpp String[] interfaceGetList();
+  android.net.InterfaceConfigurationParcel interfaceGetCfg(in @utf8InCpp String ifName);
+  void interfaceSetCfg(in android.net.InterfaceConfigurationParcel cfg);
+  void interfaceSetIPv6PrivacyExtensions(in @utf8InCpp String ifName, boolean enable);
+  void interfaceClearAddrs(in @utf8InCpp String ifName);
+  void interfaceSetEnableIPv6(in @utf8InCpp String ifName, boolean enable);
+  void interfaceSetMtu(in @utf8InCpp String ifName, int mtu);
+  void tetherAddForward(in @utf8InCpp String intIface, in @utf8InCpp String extIface);
+  void tetherRemoveForward(in @utf8InCpp String intIface, in @utf8InCpp String extIface);
+  void setTcpRWmemorySize(in @utf8InCpp String rmemValues, in @utf8InCpp String wmemValues);
+  void registerUnsolicitedEventListener(android.net.INetdUnsolicitedEventListener listener);
+  void firewallAddUidInterfaceRules(in @utf8InCpp String ifName, in int[] uids);
+  void firewallRemoveUidInterfaceRules(in int[] uids);
+  void trafficSwapActiveStatsMap();
+  IBinder getOemNetd();
+  void tetherStartWithConfiguration(in android.net.TetherConfigParcel config);
+  android.net.MarkMaskParcel getFwmarkForNetwork(int netId);
+  void networkAddRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
+  void networkUpdateRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
+  void networkRemoveRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
+  void tetherOffloadRuleAdd(in android.net.TetherOffloadRuleParcel rule);
+  void tetherOffloadRuleRemove(in android.net.TetherOffloadRuleParcel rule);
+  const int IPV4 = 4;
+  const int IPV6 = 6;
+  const int CONF = 1;
+  const int NEIGH = 2;
+  const String IPSEC_INTERFACE_PREFIX = "ipsec";
+  const int IPV6_ADDR_GEN_MODE_EUI64 = 0;
+  const int IPV6_ADDR_GEN_MODE_NONE = 1;
+  const int IPV6_ADDR_GEN_MODE_STABLE_PRIVACY = 2;
+  const int IPV6_ADDR_GEN_MODE_RANDOM = 3;
+  const int IPV6_ADDR_GEN_MODE_DEFAULT = 0;
+  const int PENALTY_POLICY_ACCEPT = 1;
+  const int PENALTY_POLICY_LOG = 2;
+  const int PENALTY_POLICY_REJECT = 3;
+  const int LOCAL_NET_ID = 99;
+  const String NEXTHOP_NONE = "";
+  const String NEXTHOP_UNREACHABLE = "unreachable";
+  const String NEXTHOP_THROW = "throw";
+  const int PERMISSION_NONE = 0;
+  const int PERMISSION_NETWORK = 1;
+  const int PERMISSION_SYSTEM = 2;
+  const int NO_PERMISSIONS = 0;
+  const int PERMISSION_INTERNET = 4;
+  const int PERMISSION_UPDATE_DEVICE_STATS = 8;
+  const int PERMISSION_UNINSTALLED = -1;
+  const int FIREWALL_WHITELIST = 0;
+  const int FIREWALL_BLACKLIST = 1;
+  const int FIREWALL_RULE_ALLOW = 1;
+  const int FIREWALL_RULE_DENY = 2;
+  const int FIREWALL_CHAIN_NONE = 0;
+  const int FIREWALL_CHAIN_DOZABLE = 1;
+  const int FIREWALL_CHAIN_STANDBY = 2;
+  const int FIREWALL_CHAIN_POWERSAVE = 3;
+  const String IF_STATE_UP = "up";
+  const String IF_STATE_DOWN = "down";
+  const String IF_FLAG_BROADCAST = "broadcast";
+  const String IF_FLAG_LOOPBACK = "loopback";
+  const String IF_FLAG_POINTOPOINT = "point-to-point";
+  const String IF_FLAG_RUNNING = "running";
+  const String IF_FLAG_MULTICAST = "multicast";
+}
diff --git a/server/aidl_api/netd_aidl_interface/3/android/net/INetdUnsolicitedEventListener.aidl b/server/aidl_api/netd_aidl_interface/3/android/net/INetdUnsolicitedEventListener.aidl
new file mode 100644
index 0000000..4459363
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/3/android/net/INetdUnsolicitedEventListener.aidl
@@ -0,0 +1,32 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+interface INetdUnsolicitedEventListener {
+  oneway void onInterfaceClassActivityChanged(boolean isActive, int timerLabel, long timestampNs, int uid);
+  oneway void onQuotaLimitReached(@utf8InCpp String alertName, @utf8InCpp String ifName);
+  oneway void onInterfaceDnsServerInfo(@utf8InCpp String ifName, long lifetimeS, in @utf8InCpp String[] servers);
+  oneway void onInterfaceAddressUpdated(@utf8InCpp String addr, @utf8InCpp String ifName, int flags, int scope);
+  oneway void onInterfaceAddressRemoved(@utf8InCpp String addr, @utf8InCpp String ifName, int flags, int scope);
+  oneway void onInterfaceAdded(@utf8InCpp String ifName);
+  oneway void onInterfaceRemoved(@utf8InCpp String ifName);
+  oneway void onInterfaceChanged(@utf8InCpp String ifName, boolean up);
+  oneway void onInterfaceLinkStateChanged(@utf8InCpp String ifName, boolean up);
+  oneway void onRouteChanged(boolean updated, @utf8InCpp String route, @utf8InCpp String gateway, @utf8InCpp String ifName);
+  oneway void onStrictCleartextDetected(int uid, @utf8InCpp String hex);
+}
diff --git a/server/aidl_api/netd_aidl_interface/3/android/net/InterfaceConfigurationParcel.aidl b/server/aidl_api/netd_aidl_interface/3/android/net/InterfaceConfigurationParcel.aidl
new file mode 100644
index 0000000..01e0f95
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/3/android/net/InterfaceConfigurationParcel.aidl
@@ -0,0 +1,26 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+parcelable InterfaceConfigurationParcel {
+  @utf8InCpp String ifName;
+  @utf8InCpp String hwAddr;
+  @utf8InCpp String ipv4Addr;
+  int prefixLength;
+  @utf8InCpp String[] flags;
+}
diff --git a/server/aidl_api/netd_aidl_interface/3/android/net/MarkMaskParcel.aidl b/server/aidl_api/netd_aidl_interface/3/android/net/MarkMaskParcel.aidl
new file mode 100644
index 0000000..62be838
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/3/android/net/MarkMaskParcel.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+parcelable MarkMaskParcel {
+  int mark;
+  int mask;
+}
diff --git a/server/aidl_api/netd_aidl_interface/3/android/net/RouteInfoParcel.aidl b/server/aidl_api/netd_aidl_interface/3/android/net/RouteInfoParcel.aidl
new file mode 100644
index 0000000..5e0ee62
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/3/android/net/RouteInfoParcel.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+parcelable RouteInfoParcel {
+  @utf8InCpp String destination;
+  @utf8InCpp String ifName;
+  @utf8InCpp String nextHop;
+  int mtu;
+}
diff --git a/server/aidl_api/netd_aidl_interface/3/android/net/TetherConfigParcel.aidl b/server/aidl_api/netd_aidl_interface/3/android/net/TetherConfigParcel.aidl
new file mode 100644
index 0000000..b136454
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/3/android/net/TetherConfigParcel.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+parcelable TetherConfigParcel {
+  boolean usingLegacyDnsProxy;
+  @utf8InCpp String[] dhcpRanges;
+}
diff --git a/server/aidl_api/netd_aidl_interface/3/android/net/TetherOffloadRuleParcel.aidl b/server/aidl_api/netd_aidl_interface/3/android/net/TetherOffloadRuleParcel.aidl
new file mode 100644
index 0000000..3abf0f8
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/3/android/net/TetherOffloadRuleParcel.aidl
@@ -0,0 +1,27 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+parcelable TetherOffloadRuleParcel {
+  int inputInterfaceIndex;
+  int outputInterfaceIndex;
+  byte[] destination;
+  int prefixLength;
+  byte[] srcL2Address;
+  byte[] dstL2Address;
+}
diff --git a/server/aidl_api/netd_aidl_interface/3/android/net/TetherStatsParcel.aidl b/server/aidl_api/netd_aidl_interface/3/android/net/TetherStatsParcel.aidl
new file mode 100644
index 0000000..71ffb9b
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/3/android/net/TetherStatsParcel.aidl
@@ -0,0 +1,26 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+parcelable TetherStatsParcel {
+  @utf8InCpp String iface;
+  long rxBytes;
+  long rxPackets;
+  long txBytes;
+  long txPackets;
+}
diff --git a/server/aidl_api/netd_aidl_interface/3/android/net/UidRangeParcel.aidl b/server/aidl_api/netd_aidl_interface/3/android/net/UidRangeParcel.aidl
new file mode 100644
index 0000000..84ff457
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/3/android/net/UidRangeParcel.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+parcelable UidRangeParcel {
+  int start;
+  int stop;
+}
diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/INetd.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/INetd.aidl
index 7ba363c..135b738 100644
--- a/server/aidl_api/netd_aidl_interface/current/android/net/INetd.aidl
+++ b/server/aidl_api/netd_aidl_interface/current/android/net/INetd.aidl
@@ -117,8 +117,8 @@
   void networkAddRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
   void networkUpdateRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
   void networkRemoveRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
-  void tetherRuleAddDownstreamIpv6(int intIfaceIndex, int extIfaceIndex, in byte[] ipAddress, in byte[] srcL2Address, in byte[] dstL2Address);
-  void tetherRuleRemoveDownstreamIpv6(int extIfaceIndex, in byte[] ipAddress);
+  void tetherOffloadRuleAdd(in android.net.TetherOffloadRuleParcel rule);
+  void tetherOffloadRuleRemove(in android.net.TetherOffloadRuleParcel rule);
   const int IPV4 = 4;
   const int IPV6 = 6;
   const int CONF = 1;
diff --git a/server/aidl_api/netd_aidl_interface/current/android/net/TetherOffloadRuleParcel.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/TetherOffloadRuleParcel.aidl
new file mode 100644
index 0000000..3abf0f8
--- /dev/null
+++ b/server/aidl_api/netd_aidl_interface/current/android/net/TetherOffloadRuleParcel.aidl
@@ -0,0 +1,27 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.net;
+/* @hide */
+parcelable TetherOffloadRuleParcel {
+  int inputInterfaceIndex;
+  int outputInterfaceIndex;
+  byte[] destination;
+  int prefixLength;
+  byte[] srcL2Address;
+  byte[] dstL2Address;
+}
diff --git a/server/binder/android/net/INetd.aidl b/server/binder/android/net/INetd.aidl
index fa18aea..dff021c 100644
--- a/server/binder/android/net/INetd.aidl
+++ b/server/binder/android/net/INetd.aidl
@@ -21,6 +21,7 @@
 import android.net.MarkMaskParcel;
 import android.net.RouteInfoParcel;
 import android.net.TetherConfigParcel;
+import android.net.TetherOffloadRuleParcel;
 import android.net.TetherStatsParcel;
 import android.net.UidRangeParcel;
 
@@ -1245,28 +1246,27 @@
     void networkRemoveRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
 
     /**
-     * Adds or updates a tethering rule to forward downstream IPv6 traffic.
+     * Adds a tethering offload rule, or updates it if it already exists.
      *
-     * @param intifaceIndex the interface index of the internal interface.
-     * @param extifaceIndex the interface index of the external interface.
-     * @param ipAddress the IPv6 address as a byte array.
-     * @param srcL2Address the source (i.e., local) L2 address as a byte array. Currently, must be a
-     *                     6-byte MAC address.
-     * @param dstL2Address the destination L2 address as a byte array. Currently, must be a 6-byte
-     *                     MAC address.
+     * Currently, only downstream /128 IPv6 entries are supported. An existing rule will be updated
+     * if the input interface and destination prefix match. Otherwise, a new rule will be created.
+     *
+     * @param rule The rule to add or update.
      * @throws ServiceSpecificException in case of failure, with an error code indicating the
      *                                  cause of the failure.
      */
-    void tetherRuleAddDownstreamIpv6(int intIfaceIndex, int extIfaceIndex, in byte[] ipAddress,
-                                     in byte[] srcL2Address, in byte[] dstL2Address);
+    void tetherOffloadRuleAdd(in TetherOffloadRuleParcel rule);
 
     /**
-     * Removes a tethering rule to forward downstream IPv6 traffic.
+     * Deletes a tethering offload rule.
      *
-     * @param extifaceIndex the interface index of the external interface.
-     * @param ipAddress the IPv6 address as a byte array.
+     * Currently, only downstream /128 IPv6 entries are supported. An existing rule will be deleted
+     * if the destination IP address and the source interface match. It is not an error if there is
+     * no matching rule to delete.
+     *
+     * @param rule The rule to delete.
      * @throws ServiceSpecificException in case of failure, with an error code indicating the
      *                                  cause of the failure.
      */
-    void tetherRuleRemoveDownstreamIpv6(int extIfaceIndex, in byte[] ipAddress);
+    void tetherOffloadRuleRemove(in TetherOffloadRuleParcel rule);
 }
diff --git a/server/binder/android/net/TetherOffloadRuleParcel.aidl b/server/binder/android/net/TetherOffloadRuleParcel.aidl
new file mode 100644
index 0000000..5577bfb
--- /dev/null
+++ b/server/binder/android/net/TetherOffloadRuleParcel.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+package android.net;
+
+/**
+ * Represents a forwarding rule for tethering offload.
+ *
+ * {@hide}
+ */
+parcelable TetherOffloadRuleParcel {
+    /** The interface index of the input interface. */
+    int inputInterfaceIndex;
+
+    /** The interface index of the output interface. */
+    int outputInterfaceIndex;
+
+    /** The base IP address of the destination prefix as a byte array. */
+    byte[] destination;
+
+    /** The destination prefix length. */
+    int prefixLength;
+
+    /** The source link-layer address. Currently, must be a 6-byte MAC address.*/
+    byte[] srcL2Address;
+
+    /** The destination link-layer address. Currently, must be a 6-byte MAC address. */
+    byte[] dstL2Address;
+}
diff --git a/tests/Android.bp b/tests/Android.bp
index 4734c21..1b8f1f6 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -62,7 +62,7 @@
     name: "netd_integration_test",
     test_suites: [
         "device-tests",
-        "vts-core"
+        "vts",
     ],
     require_root: true,
     defaults: ["netd_defaults"],
diff --git a/tests/binder_test.cpp b/tests/binder_test.cpp
index 4f6fc07..baea57d 100644
--- a/tests/binder_test.cpp
+++ b/tests/binder_test.cpp
@@ -35,12 +35,14 @@
 #include <net/if.h>
 #include <netdb.h>
 #include <netinet/in.h>
+#include <netinet/tcp.h>
 #include <openssl/base64.h>
 #include <sys/socket.h>
 #include <sys/types.h>
 
 #include <android-base/file.h>
 #include <android-base/macros.h>
+#include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android/multinetwork.h>
@@ -58,6 +60,7 @@
 #include "NetdClient.h"
 #include "NetdConstants.h"
 #include "NetworkController.h"
+#include "SockDiag.h"
 #include "TestUnsolService.h"
 #include "XfrmController.h"
 #include "android/net/INetd.h"
@@ -84,6 +87,7 @@
 using android::String16;
 using android::String8;
 using android::base::Join;
+using android::base::make_scope_guard;
 using android::base::ReadFileToString;
 using android::base::StartsWith;
 using android::base::StringPrintf;
@@ -93,9 +97,12 @@
 using android::net::InterfaceConfigurationParcel;
 using android::net::InterfaceController;
 using android::net::MarkMaskParcel;
+using android::net::SockDiag;
+using android::net::TetherOffloadRuleParcel;
 using android::net::TetherStatsParcel;
 using android::net::TunInterface;
 using android::net::UidRangeParcel;
+using android::netdutils::IPAddress;
 using android::netdutils::ScopedAddrinfo;
 using android::netdutils::sSyscalls;
 using android::netdutils::Stopwatch;
@@ -104,6 +111,7 @@
 static const char* IP_RULE_V6 = "-6";
 static const int TEST_NETID1 = 65501;
 static const int TEST_NETID2 = 65502;
+static const char* DNSMASQ = "dnsmasq";
 
 // Use maximum reserved appId for applications to avoid conflict with existing
 // uids.
@@ -2075,8 +2083,6 @@
 
 TEST_F(BinderTest, TetherStartStopStatus) {
     std::vector<std::string> noDhcpRange = {};
-    static const char dnsdName[] = "dnsmasq";
-
     for (bool usingLegacyDnsProxy : {true, false}) {
         android::net::TetherConfigParcel config;
         config.usingLegacyDnsProxy = usingLegacyDnsProxy;
@@ -2085,9 +2091,9 @@
         EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
         SCOPED_TRACE(StringPrintf("usingLegacyDnsProxy: %d", usingLegacyDnsProxy));
         if (usingLegacyDnsProxy == true) {
-            expectProcessExists(dnsdName);
+            expectProcessExists(DNSMASQ);
         } else {
-            expectProcessDoesNotExist(dnsdName);
+            expectProcessDoesNotExist(DNSMASQ);
         }
 
         bool tetherEnabled;
@@ -2097,7 +2103,7 @@
 
         status = mNetd->tetherStop();
         EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
-        expectProcessDoesNotExist(dnsdName);
+        expectProcessDoesNotExist(DNSMASQ);
 
         status = mNetd->tetherIsEnabled(&tetherEnabled);
         EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
@@ -2142,6 +2148,135 @@
 
 namespace {
 
+std::vector<IPAddress> findDnsSockets(SockDiag* sd, unsigned numExpected) {
+    std::vector<IPAddress> listenAddrs;
+
+    // Callback lambda that finds all IPv4 sockets with source port 53.
+    auto findDnsSockets = [&](uint8_t /* proto */, const inet_diag_msg* msg) {
+        // Always return false, which means do not destroy this socket.
+        if (msg->id.idiag_sport != htons(53)) return false;
+        IPAddress addr(*(in_addr*)msg->id.idiag_src);
+        listenAddrs.push_back(addr);
+        return false;
+    };
+
+    // There is no way to know if dnsmasq has finished processing the update_interfaces command and
+    // opened listening sockets. So, just spin a few times and return the first list of sockets
+    // that is at least numExpected long.
+    // Pick a relatively large timeout to avoid flaky tests. Testing suggests that 5 attempts are
+    // sufficient for the test to pass 500 times in a row on crosshatch-eng.  Pick 10 to be safe.
+    constexpr int kMaxAttempts = 10;
+    constexpr int kSleepMs = 100;
+    for (int i = 0; i < kMaxAttempts; i++) {
+        listenAddrs.clear();
+        EXPECT_EQ(0, sd->sendDumpRequest(IPPROTO_TCP, AF_INET, 1 << TCP_LISTEN))
+                << "Failed to dump sockets, attempt " << i << " of " << kMaxAttempts;
+        sd->readDiagMsg(IPPROTO_TCP, findDnsSockets);
+        if (listenAddrs.size() >= numExpected) {
+            break;
+        }
+        usleep(kSleepMs * 1000);
+    }
+
+    return listenAddrs;
+}
+
+}  // namespace
+
+// Checks that when starting dnsmasq on an interface that no longer exists, it doesn't attempt to
+// start on other interfaces instead.
+TEST_F(BinderTest, TetherDeletedInterface) {
+    // Do this first so we don't need to clean up anything else if it fails.
+    SockDiag sd;
+    ASSERT_TRUE(sd.open()) << "Failed to open SOCK_DIAG socket";
+
+    // Create our own TunInterfaces (so we can delete them without affecting other tests), and add
+    // IP addresses to them. They must be IPv4 because tethering an interface disables and
+    // re-enables IPv6 on the interface, which clears all addresses.
+    TunInterface tun1, tun2;
+    ASSERT_EQ(0, tun1.init());
+    ASSERT_EQ(0, tun2.init());
+
+    // Clean up. It is safe to call TunInterface::destroy multiple times.
+    auto guard = android::base::make_scope_guard([&] {
+        tun1.destroy();
+        tun2.destroy();
+        mNetd->tetherStop();
+        mNetd->tetherInterfaceRemove(tun1.name());
+        mNetd->tetherInterfaceRemove(tun2.name());
+    });
+
+    IPAddress addr1, addr2;
+    ASSERT_TRUE(IPAddress::forString("192.0.2.1", &addr1));
+    ASSERT_TRUE(IPAddress::forString("192.0.2.2", &addr2));
+    EXPECT_EQ(0, tun1.addAddress(addr1.toString(), 32));
+    EXPECT_EQ(0, tun2.addAddress(addr2.toString(), 32));
+
+    // Stop tethering.
+    binder::Status status = mNetd->tetherStop();
+    EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+
+    // Start dnsmasq on an interface that doesn't exist.
+    // First, tether our tun interface...
+    status = mNetd->tetherInterfaceAdd(tun1.name());
+    EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+    expectTetherInterfaceConfigureForIPv6Router(tun1.name());
+
+    // ... then delete it...
+    tun1.destroy();
+
+    // ... then start dnsmasq.
+    android::net::TetherConfigParcel config;
+    config.usingLegacyDnsProxy = true;
+    config.dhcpRanges = {};
+    status = mNetd->tetherStartWithConfiguration(config);
+    EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+
+    // Wait for dnsmasq to start.
+    expectProcessExists(DNSMASQ);
+
+    // Make sure that netd thinks the interface is tethered (even though it doesn't exist).
+    std::vector<std::string> ifList;
+    status = mNetd->tetherInterfaceList(&ifList);
+    EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+    ASSERT_EQ(1U, ifList.size());
+    EXPECT_EQ(tun1.name(), ifList[0]);
+
+    // Give dnsmasq some time to start up.
+    usleep(200 * 1000);
+
+    // Check that dnsmasq is not listening on any IP addresses. It shouldn't, because it was only
+    // told to run on tun1, and tun1 does not exist. Ensure it stays running and doesn't listen on
+    // any IP addresses.
+    std::vector<IPAddress> listenAddrs = findDnsSockets(&sd, 0);
+    EXPECT_EQ(0U, listenAddrs.size()) << "Unexpectedly found IPv4 socket(s) listening on port 53";
+
+    // Now add an interface to dnsmasq and check that we can see the sockets. This confirms that
+    // findDnsSockets is actually able to see sockets when they exist.
+    status = mNetd->tetherInterfaceAdd(tun2.name());
+    EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+
+    in_addr loopback = {htonl(INADDR_LOOPBACK)};
+    listenAddrs = findDnsSockets(&sd, 2);
+    EXPECT_EQ(2U, listenAddrs.size()) << "Expected exactly 2 IPv4 sockets listening on port 53";
+    EXPECT_EQ(1, std::count(listenAddrs.begin(), listenAddrs.end(), addr2));
+    EXPECT_EQ(1, std::count(listenAddrs.begin(), listenAddrs.end(), IPAddress(loopback)));
+
+    // Clean up.
+    status = mNetd->tetherStop();
+    EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+
+    expectProcessDoesNotExist(DNSMASQ);
+
+    status = mNetd->tetherInterfaceRemove(tun1.name());
+    EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+
+    status = mNetd->tetherInterfaceRemove(tun2.name());
+    EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+}
+
+namespace {
+
 constexpr char FIREWALL_INPUT[] = "fw_INPUT";
 constexpr char FIREWALL_OUTPUT[] = "fw_OUTPUT";
 constexpr char FIREWALL_FORWARD[] = "fw_FORWARD";
@@ -3372,7 +3507,26 @@
     EXPECT_TRUE(mNetd->networkDestroy(TEST_NETID1).isOk());
 }
 
-TEST_F(BinderTest, TetherRuleDownstreamIpv6) {
+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) {
+    android::net::TetherOffloadRuleParcel parcel;
+    parcel.inputInterfaceIndex = inputInterfaceIndex;
+    parcel.outputInterfaceIndex = outputInterfaceIndex;
+    parcel.destination = destination;
+    parcel.prefixLength = prefixLength;
+    parcel.srcL2Address = srcL2Address;
+    parcel.dstL2Address = dstL2Address;
+    return parcel;
+}
+
+}  // namespace
+
+TEST_F(BinderTest, TetherOffloadRule) {
     SKIP_IF_BPF_NOT_SUPPORTED;
 
     // TODO: Perhaps verify invalid interface index once the netd handle the error in methods.
@@ -3389,33 +3543,56 @@
     const std::vector<uint8_t> kInvalidMac = {0xde, 0xad, 0xbe, 0xef};    // should be 6-byte length
 
     // Invalid IP address, add rule
-    auto status = mNetd->tetherRuleAddDownstreamIpv6(kIfaceInt, kIfaceExt, kInvalidAddr4 /*bad*/,
-                                                     kSrcMac, kDstMac);
+    TetherOffloadRuleParcel rule = makeTetherOffloadRule(
+            kIfaceExt, kIfaceInt, kInvalidAddr4 /*bad*/, 128, kSrcMac, kDstMac);
+    auto status = mNetd->tetherOffloadRuleAdd(rule);
     EXPECT_FALSE(status.isOk());
-    EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode());
+    EXPECT_EQ(EAFNOSUPPORT, status.serviceSpecificErrorCode());
 
     // Invalid source L2 address, add rule
-    status = mNetd->tetherRuleAddDownstreamIpv6(kIfaceInt, kIfaceExt, kAddr6, kInvalidMac /*bad*/,
-                                                kDstMac);
+    rule = makeTetherOffloadRule(kIfaceExt, kIfaceInt, kAddr6, 128, kInvalidMac /*bad*/, kDstMac);
+    status = mNetd->tetherOffloadRuleAdd(rule);
     EXPECT_FALSE(status.isOk());
-    EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode());
+    EXPECT_EQ(ENXIO, status.serviceSpecificErrorCode());
 
     // Invalid destination L2 address, add rule
-    status = mNetd->tetherRuleAddDownstreamIpv6(kIfaceInt, kIfaceExt, kAddr6, kSrcMac,
-                                                kInvalidMac /*bad*/);
+    rule = makeTetherOffloadRule(kIfaceExt, kIfaceInt, kAddr6, 128, kSrcMac, kInvalidMac /*bad*/);
+    status = mNetd->tetherOffloadRuleAdd(rule);
     EXPECT_FALSE(status.isOk());
-    EXPECT_EQ(EINVAL, status.serviceSpecificErrorCode());
+    EXPECT_EQ(ENXIO, status.serviceSpecificErrorCode());
 
     // Invalid IP address, remove rule
-    status = mNetd->tetherRuleRemoveDownstreamIpv6(kIfaceExt, kInvalidAddr4 /*bad*/);
+    rule = makeTetherOffloadRule(kIfaceExt, kIfaceInt, kInvalidAddr4 /*bad*/, 128, kSrcMac,
+                                 kDstMac);
+    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);
+    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);
+    status = mNetd->tetherOffloadRuleAdd(rule);
+    EXPECT_FALSE(status.isOk());
+    EXPECT_EQ(ENODEV, status.serviceSpecificErrorCode());
+    rule = makeTetherOffloadRule(0, kIfaceInt, kAddr6, 64, kSrcMac, kDstMac);
+    status = mNetd->tetherOffloadRuleRemove(rule);
+    EXPECT_FALSE(status.isOk());
+    EXPECT_EQ(ENODEV, status.serviceSpecificErrorCode());
+
     // Remove non existent rule. Expect that silently return success if the rule did not exist.
-    EXPECT_TRUE(mNetd->tetherRuleRemoveDownstreamIpv6(kIfaceNonExistent, kAddr6).isOk());
+    rule = makeTetherOffloadRule(kIfaceNonExistent, kIfaceInt, kAddr6, 128, kSrcMac, kDstMac);
+    EXPECT_TRUE(mNetd->tetherOffloadRuleRemove(rule).isOk());
 
     // Add and remove rule normally.
-    EXPECT_TRUE(mNetd->tetherRuleAddDownstreamIpv6(kIfaceInt, kIfaceExt, kAddr6, kSrcMac, kDstMac)
-                        .isOk());
-    EXPECT_TRUE(mNetd->tetherRuleRemoveDownstreamIpv6(kIfaceExt, kAddr6).isOk());
+    rule = makeTetherOffloadRule(kIfaceExt, kIfaceInt, kAddr6, 128, kSrcMac, kDstMac);
+    EXPECT_TRUE(mNetd->tetherOffloadRuleAdd(rule).isOk());
+    EXPECT_TRUE(mNetd->tetherOffloadRuleRemove(rule).isOk());
 }