diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-09-13 01:27:54 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-09-13 01:27:54 +0000 |
commit | 750bda7c9b6400826dc5ce5e202b8e9ada15ef69 (patch) | |
tree | 3e0247c27a61816589a6de5e72ff4f2d6779f5c5 | |
parent | 47e2027b2fa33b08e441214baac9a9f5227c21c7 (diff) | |
parent | 293f0d7686958df6621e82bd6d4486b9a058f20e (diff) | |
download | netd-android14-d2-s1-release.tar.gz |
Snap for 10798766 from 293f0d7686958df6621e82bd6d4486b9a058f20e to udc-d2-releaseandroid-14.0.0_r45android-14.0.0_r44android-14.0.0_r43android-14.0.0_r42android-14.0.0_r41android-14.0.0_r40android-14.0.0_r39android-14.0.0_r38android14-d2-s5-releaseandroid14-d2-s4-releaseandroid14-d2-s3-releaseandroid14-d2-s2-releaseandroid14-d2-s1-releaseandroid14-d2-release
Change-Id: I0f31d690b7c636ed80697b5f3b4d61e5afc34751
-rw-r--r-- | client/NetdClient.cpp | 14 | ||||
-rw-r--r-- | include/FwmarkCommand.h | 2 | ||||
-rw-r--r-- | server/FwmarkServer.cpp | 9 | ||||
-rw-r--r-- | server/NetworkController.cpp | 13 | ||||
-rw-r--r-- | server/NetworkController.h | 2 | ||||
-rw-r--r-- | tests/binder_test.cpp | 168 |
6 files changed, 193 insertions, 15 deletions
diff --git a/client/NetdClient.cpp b/client/NetdClient.cpp index 2a09a440..fe0a4838 100644 --- a/client/NetdClient.cpp +++ b/client/NetdClient.cpp @@ -90,11 +90,6 @@ static bool propertyValueIsTrue(const char* prop_name) { return false; } -static bool redirectSocketCallsIsTrue() { - static bool cached_result = propertyValueIsTrue(PROPERTY_REDIRECT_SOCKET_CALLS); - return cached_result; -} - int checkSocket(int socketFd) { if (socketFd < 0) { return -EBADF; @@ -150,13 +145,8 @@ int netdClientConnect(int sockfd, const sockaddr* addr, socklen_t addrlen) { const bool shouldSetFwmark = shouldMarkSocket(sockfd, addr); if (shouldSetFwmark) { FwmarkCommand command = {FwmarkCommand::ON_CONNECT, 0, 0, 0}; - int error; - if (redirectSocketCallsIsTrue()) { - FwmarkConnectInfo connectInfo(0, 0, addr); - error = FwmarkClient().send(&command, sockfd, &connectInfo); - } else { - error = FwmarkClient().send(&command, sockfd, nullptr); - } + FwmarkConnectInfo connectInfo(0, 0, addr); + int error = FwmarkClient().send(&command, sockfd, &connectInfo); if (error) { errno = -error; diff --git a/include/FwmarkCommand.h b/include/FwmarkCommand.h index c215ed44..bf9d5063 100644 --- a/include/FwmarkCommand.h +++ b/include/FwmarkCommand.h @@ -29,7 +29,7 @@ struct FwmarkConnectInfo { sockaddr s; sockaddr_in sin; sockaddr_in6 sin6; - } addr; + } addr = {}; FwmarkConnectInfo() : error(0), latencyMs(0) {} diff --git a/server/FwmarkServer.cpp b/server/FwmarkServer.cpp index 1ae8ed38..4ce22eb5 100644 --- a/server/FwmarkServer.cpp +++ b/server/FwmarkServer.cpp @@ -16,6 +16,7 @@ #include "FwmarkServer.h" +#include <net/if.h> #include <netinet/in.h> #include <selinux/selinux.h> #include <sys/socket.h> @@ -92,7 +93,7 @@ static bool hasDestinationAddress(FwmarkCommand::CmdId cmdId, bool redirectSocke cmdId == FwmarkCommand::ON_SENDMSG || cmdId == FwmarkCommand::ON_SENDMMSG || cmdId == FwmarkCommand::ON_CONNECT_COMPLETE); } else { - return (cmdId == FwmarkCommand::ON_CONNECT_COMPLETE); + return (cmdId == FwmarkCommand::ON_CONNECT || cmdId == FwmarkCommand::ON_CONNECT_COMPLETE); } } @@ -200,7 +201,11 @@ int FwmarkServer::processClient(SocketClient* client, int* socketFd) { // So, overall (when the explicit bit is not set but the protect bit is set), if the // existing NetId is a VPN, don't reset it. Else, set the default network's NetId. if (!fwmark.explicitlySelected) { - if (!fwmark.protectedFromVpn) { + if (family == AF_INET6 && connectInfo.addr.sin6.sin6_scope_id && + IN6_IS_ADDR_LINKLOCAL(&connectInfo.addr.sin6.sin6_addr)) { + fwmark.netId = mNetworkController->getNetworkForInterface( + connectInfo.addr.sin6.sin6_scope_id); + } else if (!fwmark.protectedFromVpn) { fwmark.netId = mNetworkController->getNetworkForConnect(client->getUid()); } else if (!mNetworkController->isVirtualNetwork(fwmark.netId)) { fwmark.netId = mNetworkController->getDefaultNetwork(); diff --git a/server/NetworkController.cpp b/server/NetworkController.cpp index e61ee306..d9b33426 100644 --- a/server/NetworkController.cpp +++ b/server/NetworkController.cpp @@ -373,6 +373,19 @@ unsigned NetworkController::getNetworkForInterface(const char* interface) const return getNetworkForInterfaceLocked(interface); } +unsigned NetworkController::getNetworkForInterfaceLocked(const int ifIndex) const { + char interfaceName[IFNAMSIZ] = {}; + if (if_indextoname(ifIndex, interfaceName)) { + return getNetworkForInterfaceLocked(interfaceName); + } + return NETID_UNSET; +} + +unsigned NetworkController::getNetworkForInterface(const int ifIndex) const { + ScopedRLock lock(mRWLock); + return getNetworkForInterfaceLocked(ifIndex); +} + bool NetworkController::isVirtualNetwork(unsigned netId) const { ScopedRLock lock(mRWLock); return isVirtualNetworkLocked(netId); diff --git a/server/NetworkController.h b/server/NetworkController.h index e7c47da4..a9a6cd4f 100644 --- a/server/NetworkController.h +++ b/server/NetworkController.h @@ -102,6 +102,7 @@ public: unsigned getNetworkForConnect(uid_t uid) const; void getNetworkContext(unsigned netId, uid_t uid, struct android_net_context* netcontext) const; unsigned getNetworkForInterface(const char* interface) const; + unsigned getNetworkForInterface(const int ifIndex) const; bool isVirtualNetwork(unsigned netId) const; [[nodiscard]] int createPhysicalNetwork(unsigned netId, Permission permission, bool local); @@ -160,6 +161,7 @@ public: uint32_t getNetworkForDnsLocked(unsigned* netId, uid_t uid) const; unsigned getNetworkForConnectLocked(uid_t uid) const; unsigned getNetworkForInterfaceLocked(const char* interface) const; + unsigned getNetworkForInterfaceLocked(const int ifIndex) const; bool canProtectLocked(uid_t uid) const; bool isVirtualNetworkLocked(unsigned netId) const; VirtualNetwork* getVirtualNetworkForUserLocked(uid_t uid) const; diff --git a/tests/binder_test.cpp b/tests/binder_test.cpp index 7d3bbc87..065bc126 100644 --- a/tests/binder_test.cpp +++ b/tests/binder_test.cpp @@ -118,6 +118,7 @@ using android::net::MarkMaskParcel; using android::net::NativeNetworkConfig; using android::net::NativeNetworkType; using android::net::NativeVpnType; +using android::net::NetworkController; using android::net::RULE_PRIORITY_BYPASSABLE_VPN_LOCAL_EXCLUSION; using android::net::RULE_PRIORITY_BYPASSABLE_VPN_NO_LOCAL_EXCLUSION; using android::net::RULE_PRIORITY_DEFAULT_NETWORK; @@ -5575,4 +5576,171 @@ TEST_F(NetdBinderTest, PerProfileNetworkPermission) { EXPECT_EQ(0, setNetworkForProcess(ENTERPRISE_NETID_1)); } } +} + +namespace { + +class ScopedIfaceRouteOperation { + using IfaceCmd = std::tuple<int32_t, const std::string>; + using RouteCmd = std::tuple<int32_t, const std::string, const std::string, const std::string>; + + // Helper type for the visitor. + template <class... Ts> + struct overloaded : Ts... { + using Ts::operator()...; + }; + // Explicit deduction guide + template <class... Ts> + overloaded(Ts...) -> overloaded<Ts...>; + + public: + ScopedIfaceRouteOperation(sp<INetd> netd) : mNetd(netd) {} + + binder::Status addInterface(int32_t netId, const std::string& iface) { + const binder::Status status = mNetd->networkAddInterface(netId, iface); + if (status.isOk()) { + mCmds.push_back(std::make_tuple(netId, iface)); + } + return status; + } + + binder::Status addRoute(int32_t netId, const std::string& iface, const std::string& destination, + const std::string& nextHop) { + const binder::Status status = mNetd->networkAddRoute(netId, iface, destination, nextHop); + if (status.isOk()) { + mCmds.push_back(std::make_tuple(netId, iface, destination, nextHop)); + } + return status; + } + + ~ScopedIfaceRouteOperation() { + // Remove routes and interfaces in reverse order. + for (std::vector<std::variant<IfaceCmd, RouteCmd>>::reverse_iterator iter = mCmds.rbegin(); + iter != mCmds.rend(); iter++) { + // Do corresponding works according to the type of the command pointed by the iter. + std::visit(overloaded{ + [&](IfaceCmd& cmd) { + mNetd->networkRemoveInterface(std::get<0>(cmd), + std::get<1>(cmd)); + }, + [&](RouteCmd& cmd) { + mNetd->networkRemoveRoute(std::get<0>(cmd), std::get<1>(cmd), + std::get<2>(cmd), std::get<3>(cmd)); + }, + }, + *iter); + } + } + + private: + sp<INetd> mNetd; + std::vector<std::variant<IfaceCmd, RouteCmd>> mCmds; +}; + +std::optional<sockaddr_in6> getV6LinkLocalAddrFromIfIndex(const unsigned ifIndex) { + struct ifaddrs* ifAddrList = nullptr; + sockaddr_in6 linkLocalAddr{}; + + if (getifaddrs(&ifAddrList) == -1) return std::nullopt; + + for (struct ifaddrs* ifa = ifAddrList; ifa != nullptr; ifa = ifa->ifa_next) { + if (ifa->ifa_addr != nullptr && ifa->ifa_addr->sa_family == AF_INET6) { + struct sockaddr_in6* addr = reinterpret_cast<struct sockaddr_in6*>(ifa->ifa_addr); + if (addr->sin6_scope_id == ifIndex && IN6_IS_ADDR_LINKLOCAL(&(addr->sin6_addr))) { + linkLocalAddr = *addr; + freeifaddrs(ifAddrList); + return linkLocalAddr; + } + } + } + + freeifaddrs(ifAddrList); + return std::nullopt; +} + +int retry_bind(int sockfd, struct sockaddr* addr, socklen_t addrlen) { + int ret = 0; + + for (int retry = 0; retry < 10; retry++) { + ret = bind(sockfd, addr, addrlen); + if (ret == 0 || (ret == -1 && errno != EADDRNOTAVAIL)) { + break; + } + usleep(100 * 1000); + } + return ret; +} + +} // namespace + +TEST_F(NetdBinderTest, V6LinkLocalFwmark) { + createAndSetDefaultNetwork(TEST_NETID1, sTun.name()); + + // Add an interface and route for Local network. + ScopedIfaceRouteOperation scopedOperation(mNetd); + EXPECT_TRUE(scopedOperation.addInterface(NetworkController::LOCAL_NET_ID, sTun2.name()).isOk()); + EXPECT_TRUE( + scopedOperation.addRoute(NetworkController::LOCAL_NET_ID, sTun2.name(), "fe80::/64", "") + .isOk()); + + // Bind a listening socket to the auto assigned link-local address of the Local network. + std::optional<sockaddr_in6> v6LinkLocalAddr_1 = getV6LinkLocalAddrFromIfIndex(sTun2.ifindex()); + ASSERT_TRUE(v6LinkLocalAddr_1.has_value()) << "errno:" << errno; + socklen_t len = sizeof(v6LinkLocalAddr_1.value()); + unique_fd s1(socket(AF_INET6, SOCK_STREAM | SOCK_NONBLOCK, 0)); + ASSERT_EQ(0, bind(s1, reinterpret_cast<sockaddr*>(&v6LinkLocalAddr_1.value()), len)) + << "errno:" << errno; + ASSERT_EQ(0, getsockname(s1, reinterpret_cast<sockaddr*>(&v6LinkLocalAddr_1.value()), &len)) + << "errno:" << errno; + ASSERT_EQ(0, listen(s1, 10)) << "errno:" << errno; + + // Add another v6 link-local address. + const char* v6LinkLocalAddr_2 = "fe80::ace:d00d"; + EXPECT_TRUE(mNetd->interfaceAddAddress(sTun2.name(), v6LinkLocalAddr_2, 64).isOk()); + + // Bind a client socket on the new added link-local address and connect it to the listen socket. + // Have different src and dst addresses is needed because we want to test the behavior of fwmark + // and destroying sockets. The same src and dst addresses are treated as loopbacks and won't be + // destroyed in any way. + const struct addrinfo hints = { + .ai_family = AF_INET6, + .ai_socktype = SOCK_STREAM, + .ai_flags = AI_NUMERICHOST, + }; + struct addrinfo* addrinfoList = nullptr; + int ret = getaddrinfo(v6LinkLocalAddr_2, nullptr, &hints, &addrinfoList); + ScopedAddrinfo addrinfoCleanup(addrinfoList); + ASSERT_EQ(0, ret) << "errno:" << errno; + + len = addrinfoList[0].ai_addrlen; + sockaddr_in6 sin6 = *reinterpret_cast<sockaddr_in6*>(addrinfoList[0].ai_addr); + sin6.sin6_scope_id = sTun2.ifindex(); + + unique_fd c1(socket(AF_INET6, SOCK_STREAM, 0)); + // Retry in case the newly added address is not ready yet. + ASSERT_EQ(0, retry_bind(c1, reinterpret_cast<sockaddr*>(&sin6), len)) << "errno:" << errno; + ASSERT_EQ(0, getsockname(c1, reinterpret_cast<sockaddr*>(&sin6), &len)) << "errno:" << errno; + ASSERT_EQ(0, connect(c1, reinterpret_cast<sockaddr*>(&v6LinkLocalAddr_1.value()), len)) + << "errno:" << errno; + + // Verify netId in fwmark. + Fwmark fwmark; + socklen_t fwmarkLen = sizeof(fwmark.intValue); + EXPECT_EQ(0, getsockopt(c1, SOL_SOCKET, SO_MARK, &fwmark.intValue, &fwmarkLen)); + EXPECT_EQ((unsigned)NetworkController::LOCAL_NET_ID, fwmark.netId); + + unique_fd a1(accept(s1, nullptr, 0)); + ASSERT_NE(-1, a1) << "errno:" << errno; + EXPECT_EQ(0, getsockopt(a1, SOL_SOCKET, SO_MARK, &fwmark.intValue, &fwmarkLen)); + // TODO: Fix fwmark on the accept socket? + fwmark.netId = NetworkController::LOCAL_NET_ID; + EXPECT_EQ(0, setsockopt(a1, SOL_SOCKET, SO_MARK, &fwmark.intValue, sizeof(fwmark.intValue))); + + // Change permission on the default network. Client socket should not be destroyed. + EXPECT_TRUE( + mNetd->networkSetPermissionForNetwork(TEST_NETID1, INetd::PERMISSION_NETWORK).isOk()); + + char buf[1024] = {}; + EXPECT_EQ(3, write(a1, "foo", 3)) << "errno:" << errno; + EXPECT_EQ(3, read(c1, buf, sizeof(buf))) << "errno:" << errno; }
\ No newline at end of file |