summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/Android.bp5
-rw-r--r--server/NetdNativeService.cpp13
-rw-r--r--server/NetdNativeService.h9
-rw-r--r--server/TetherController.cpp69
-rw-r--r--server/TetherController.h11
-rw-r--r--server/aidl_api/netd_aidl_interface/current/android/net/INetd.aidl4
-rw-r--r--server/aidl_api/netd_aidl_interface/current/android/net/TetherOffloadRuleParcel.aidl27
-rw-r--r--server/binder/android/net/INetd.aidl28
-rw-r--r--server/binder/android/net/TetherOffloadRuleParcel.aidl42
-rw-r--r--tests/binder_test.cpp73
10 files changed, 202 insertions, 79 deletions
diff --git a/server/Android.bp b/server/Android.bp
index de94e235a..1af50208a 100644
--- a/server/Android.bp
+++ b/server/Android.bp
@@ -33,6 +33,7 @@ aidl_interface {
"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",
],
@@ -102,7 +103,7 @@ cc_library_static {
"libpcap",
"libqtaguid",
"libssl",
- "netd_aidl_interface-cpp",
+ "netd_aidl_interface-unstable-cpp",
"netd_event_listener_interface-cpp",
],
aidl: {
@@ -243,7 +244,7 @@ cc_test {
"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/NetdNativeService.cpp b/server/NetdNativeService.cpp
index fb7f83942..b6c0d4e87 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 @@ binder::Status NetdNativeService::getFwmarkForNetwork(int32_t netId, MarkMaskPar
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 0c241d736..0d95c8ea4 100644
--- a/server/NetdNativeService.h
+++ b/server/NetdNativeService.h
@@ -136,12 +136,9 @@ class NetdNativeService : public BinderService<NetdNativeService>, public BnNetd
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/TetherController.cpp b/server/TetherController.cpp
index cc3b49c0b..144d24f46 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::Result;
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;
@@ -821,47 +824,63 @@ int TetherController::disableNat(const char* intIface, const char* extIface) {
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) {
- ethhdr hdr = {
- .h_proto = htons(ETH_P_IPV6),
- };
- if (ipAddress.size() != sizeof(in6_addr)) {
- return Error(EINVAL) << "Invalid IP address length " << ipAddress.size();
+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 (srcL2Address.size() != sizeof(hdr.h_source)) {
- return Error(EINVAL) << "Invalid L2 src address length " << srcL2Address.size();
+ if (rule.srcL2Address.size() != sizeof(hdr.h_source)) {
+ return Error(ENXIO) << "Invalid L2 src address length " << rule.srcL2Address.size();
}
- if (dstL2Address.size() != sizeof(hdr.h_dest)) {
- return Error(EINVAL) << "Invalid L2 dst address length " << dstL2Address.size();
+ if (rule.dstL2Address.size() != sizeof(hdr.h_dest)) {
+ return Error(ENXIO) << "Invalid L2 dst address length " << rule.dstL2Address.size();
}
- memcpy(&hdr.h_dest, dstL2Address.data(), sizeof(hdr.h_dest));
- memcpy(&hdr.h_source, srcL2Address.data(), sizeof(hdr.h_source));
+ 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),
+ };
+ 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 7835cf6b9..7fd716619 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 @@ class TetherController {
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/aidl_api/netd_aidl_interface/current/android/net/INetd.aidl b/server/aidl_api/netd_aidl_interface/current/android/net/INetd.aidl
index 7ba363c39..135b73850 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 @@ interface INetd {
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 000000000..3abf0f89a
--- /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 fa18aeab7..dff021c5c 100644
--- a/server/binder/android/net/INetd.aidl
+++ b/server/binder/android/net/INetd.aidl
@@ -21,6 +21,7 @@ import android.net.InterfaceConfigurationParcel;
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 @@ interface INetd {
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.
+ *
+ * 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 extifaceIndex the interface index of the external interface.
- * @param ipAddress the IPv6 address as a byte array.
+ * @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 000000000..5577bfb08
--- /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/binder_test.cpp b/tests/binder_test.cpp
index 4f6fc076e..0b8771544 100644
--- a/tests/binder_test.cpp
+++ b/tests/binder_test.cpp
@@ -93,6 +93,7 @@ using android::net::INetd;
using android::net::InterfaceConfigurationParcel;
using android::net::InterfaceController;
using android::net::MarkMaskParcel;
+using android::net::TetherOffloadRuleParcel;
using android::net::TetherStatsParcel;
using android::net::TunInterface;
using android::net::UidRangeParcel;
@@ -3372,7 +3373,26 @@ TEST_F(BinderTest, GetFwmarkForNetwork) {
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 +3409,56 @@ TEST_F(BinderTest, TetherRuleDownstreamIpv6) {
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());
}