diff options
| -rw-r--r-- | server/Android.bp | 5 | ||||
| -rw-r--r-- | server/NetdNativeService.cpp | 13 | ||||
| -rw-r--r-- | server/NetdNativeService.h | 9 | ||||
| -rw-r--r-- | server/TetherController.cpp | 69 | ||||
| -rw-r--r-- | server/TetherController.h | 11 | ||||
| -rw-r--r-- | server/aidl_api/netd_aidl_interface/current/android/net/INetd.aidl | 4 | ||||
| -rw-r--r-- | server/aidl_api/netd_aidl_interface/current/android/net/TetherOffloadRuleParcel.aidl | 27 | ||||
| -rw-r--r-- | server/binder/android/net/INetd.aidl | 28 | ||||
| -rw-r--r-- | server/binder/android/net/TetherOffloadRuleParcel.aidl | 42 | ||||
| -rw-r--r-- | tests/binder_test.cpp | 73 |
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()); } |
