diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2018-06-07 03:01:58 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2018-06-07 03:01:58 +0000 |
commit | 4aa84ab2111f8ba3bc6f6e2d3e0cd176ba3de903 (patch) | |
tree | 0a4d286e98ca1efd883636473129406214d4785e | |
parent | d6178afc766582d9e379925ddc78fbfd874e48bb (diff) | |
parent | 33ef503e865d634a10f4a788f9713160141f6dd4 (diff) | |
download | netd-4aa84ab2111f8ba3bc6f6e2d3e0cd176ba3de903.tar.gz |
Snap for 4826407 from 33ef503e865d634a10f4a788f9713160141f6dd4 to pi-dr1-release
Change-Id: I99218ba406d1019a6ad8fe4a4b1018b82953858a
-rw-r--r-- | server/NetlinkHandler.cpp | 66 | ||||
-rw-r--r-- | server/NetworkController.cpp | 106 | ||||
-rw-r--r-- | server/NetworkController.h | 26 | ||||
-rw-r--r-- | server/RouteController.cpp | 12 | ||||
-rw-r--r-- | server/RouteController.h | 9 | ||||
-rw-r--r-- | server/XfrmController.cpp | 31 |
6 files changed, 210 insertions, 40 deletions
diff --git a/server/NetlinkHandler.cpp b/server/NetlinkHandler.cpp index 9b033ff7..2bc9c279 100644 --- a/server/NetlinkHandler.cpp +++ b/server/NetlinkHandler.cpp @@ -55,6 +55,18 @@ int NetlinkHandler::stop() { return this->stopListener(); } +static long parseIfIndex(const char* ifIndex) { + if (ifIndex == nullptr) { + return 0; + } + long ifaceIndex = strtol(ifIndex, NULL, 10); + // strtol returns 0 on error, which is fine because 0 is not a valid ifindex. + if (errno == ERANGE && (ifaceIndex == LONG_MAX || ifaceIndex == LONG_MIN)) { + return 0; + } + return ifaceIndex; +} + void NetlinkHandler::onEvent(NetlinkEvent *evt) { const char *subsys = evt->getSubsystem(); if (!subsys) { @@ -69,15 +81,11 @@ void NetlinkHandler::onEvent(NetlinkEvent *evt) { (action == NetlinkEvent::Action::kLinkUp) || (action == NetlinkEvent::Action::kLinkDown)) { const char *ifIndex = evt->findParam("IFINDEX"); - if (ifIndex) { - // strtol returns 0 on error, which is fine because 0 is not a valid ifindex. - long ifaceIndex = strtol(ifIndex, NULL, 10); - if (ifaceIndex == 0 || - (errno == ERANGE && (ifaceIndex == LONG_MAX || ifaceIndex == LONG_MIN))) { - ALOGE("invalid interface index: %s(%s)", iface, ifIndex); - } else { - gCtls->trafficCtrl.addInterface(iface, ifaceIndex); - } + long ifaceIndex = parseIfIndex(ifIndex); + if (ifaceIndex) { + gCtls->trafficCtrl.addInterface(iface, ifaceIndex); + } else { + ALOGE("invalid interface index: %s(%s)", iface, ifIndex); } } @@ -97,25 +105,35 @@ void NetlinkHandler::onEvent(NetlinkEvent *evt) { const char *address = evt->findParam("ADDRESS"); const char *flags = evt->findParam("FLAGS"); const char *scope = evt->findParam("SCOPE"); - if (action == NetlinkEvent::Action::kAddressRemoved && iface && address) { - // Note: if this interface was deleted, iface is "" and we don't notify. - SockDiag sd; - if (sd.open()) { - char addrstr[INET6_ADDRSTRLEN]; - strncpy(addrstr, address, sizeof(addrstr)); - char *slash = strchr(addrstr, '/'); - if (slash) { - *slash = '\0'; - } + const char *ifIndex = evt->findParam("IFINDEX"); + char addrstr[INET6_ADDRSTRLEN + strlen("/128")]; + strlcpy(addrstr, address, sizeof(addrstr)); + char *slash = strchr(addrstr, '/'); + if (slash) { + *slash = '\0'; + } - int ret = sd.destroySockets(addrstr); - if (ret < 0) { - ALOGE("Error destroying sockets: %s", strerror(ret)); + long ifaceIndex = parseIfIndex(ifIndex); + if (!ifaceIndex) { + ALOGE("invalid interface index: %s(%s)", iface, ifIndex); + } + if (action == NetlinkEvent::Action::kAddressUpdated) { + gCtls->netCtrl.addInterfaceAddress(ifaceIndex, address); + } else { // action == NetlinkEvent::Action::kAddressRemoved + bool shouldDestroy = gCtls->netCtrl.removeInterfaceAddress(ifaceIndex, address); + if (shouldDestroy) { + SockDiag sd; + if (sd.open()) { + int ret = sd.destroySockets(addrstr); + if (ret < 0) { + ALOGE("Error destroying sockets: %s", strerror(-ret)); + } + } else { + ALOGE("Error opening NETLINK_SOCK_DIAG socket: %s", strerror(errno)); } - } else { - ALOGE("Error opening NETLINK_SOCK_DIAG socket: %s", strerror(errno)); } } + // Note: if this interface was deleted, iface is "" and we don't notify. if (iface && iface[0] && address && flags && scope) { notifyAddressChanged(action, address, iface, flags, scope); } diff --git a/server/NetworkController.cpp b/server/NetworkController.cpp index 5b2cd892..5bbfe3f2 100644 --- a/server/NetworkController.cpp +++ b/server/NetworkController.cpp @@ -28,6 +28,8 @@ #define LOG_TAG "Netd" #include "log/log.h" +#include <android-base/strings.h> + #include "cutils/misc.h" #include "resolv_netid.h" @@ -192,6 +194,19 @@ uint32_t NetworkController::getNetworkForDnsLocked(unsigned* netId, uid_t uid) c Fwmark fwmark; fwmark.protectedFromVpn = true; fwmark.permission = PERMISSION_SYSTEM; + + // Common case: there is no VPN that applies to the user, and the query did not specify a netId. + // Therefore, it is safe to set the explicit bit on this query and skip all the complex logic + // below. While this looks like a special case, it is actually the one that handles the vast + // majority of DNS queries. + // TODO: untangle this code. + if (*netId == NETID_UNSET && getVirtualNetworkForUserLocked(uid) == nullptr) { + *netId = mDefaultNetId; + fwmark.netId = *netId; + fwmark.explicitlySelected = true; + return fwmark.intValue; + } + if (checkUserNetworkAccessLocked(uid, *netId) == 0) { // If a non-zero NetId was explicitly specified, and the user has permission for that // network, use that network's DNS servers. Do not fall through to the default network even @@ -210,7 +225,8 @@ uint32_t NetworkController::getNetworkForDnsLocked(unsigned* netId, uid_t uid) c } else { // If the user is subject to a VPN and the VPN provides DNS servers, use those servers // (possibly falling through to the default network if the VPN doesn't provide a route to - // them). Otherwise, use the default network's DNS servers. + // them). Otherwise, use the default network's DNS servers. We cannot set the explicit bit + // because we need to be able to fall through a split tunnel to the default network. VirtualNetwork* virtualNetwork = getVirtualNetworkForUserLocked(uid); if (virtualNetwork && virtualNetwork->getHasDns()) { *netId = virtualNetwork->getNetId(); @@ -331,6 +347,10 @@ unsigned NetworkController::getNetworkForInterface(const char* interface) const bool NetworkController::isVirtualNetwork(unsigned netId) const { android::RWLock::AutoRLock lock(mRWLock); + return isVirtualNetworkLocked(netId); +} + +bool NetworkController::isVirtualNetworkLocked(unsigned netId) const { Network* network = getNetworkLocked(netId); return network && network->getType() == Network::VIRTUAL; } @@ -452,6 +472,14 @@ int NetworkController::destroyNetwork(unsigned netId) { delete network; _resolv_delete_cache_for_net(netId); + for (auto iter = mIfindexToLastNetId.begin(); iter != mIfindexToLastNetId.end();) { + if (iter->second == netId) { + iter = mIfindexToLastNetId.erase(iter); + } else { + ++iter; + } + } + updateTcpSocketMonitorPolling(); return ret; @@ -470,8 +498,18 @@ int NetworkController::addInterfaceToNetwork(unsigned netId, const char* interfa ALOGE("interface %s already assigned to netId %u", interface, existingNetId); return -EBUSY; } + if (int ret = getNetworkLocked(netId)->addInterface(interface)) { + return ret; + } - return getNetworkLocked(netId)->addInterface(interface); + int ifIndex = RouteController::getIfIndex(interface); + if (ifIndex) { + mIfindexToLastNetId[ifIndex] = netId; + } else { + // Cannot happen, since addInterface() above will have failed. + ALOGE("inconceivable! added interface %s with no index", interface); + } + return 0; } int NetworkController::removeInterfaceFromNetwork(unsigned netId, const char* interface) { @@ -569,6 +607,53 @@ int NetworkController::removeRoute(unsigned netId, const char* interface, const return modifyRoute(netId, interface, destination, nexthop, false, legacy, uid); } +void NetworkController::addInterfaceAddress(unsigned ifIndex, const char* address) { + android::RWLock::AutoWLock lock(mRWLock); + + if (ifIndex == 0) { + ALOGE("Attempting to add address %s without ifindex", address); + return; + } + mAddressToIfindices[address].insert(ifIndex); +} + +// Returns whether we should call SOCK_DESTROY on the removed address. +bool NetworkController::removeInterfaceAddress(unsigned ifindex, const char* address) { + android::RWLock::AutoWLock lock(mRWLock); + // First, update mAddressToIfindices map + auto ifindicesIter = mAddressToIfindices.find(address); + if (ifindicesIter == mAddressToIfindices.end()) { + ALOGE("Removing unknown address %s from ifindex %u", address, ifindex); + return true; + } + std::unordered_set<unsigned>& ifindices = ifindicesIter->second; + if (ifindices.erase(ifindex) > 0) { + if (ifindices.size() == 0) { + mAddressToIfindices.erase(ifindicesIter); + } + } else { + ALOGE("No record of address %s on interface %u", address, ifindex); + return true; + } + // Then, check for VPN handover condition + if (mIfindexToLastNetId.find(ifindex) == mIfindexToLastNetId.end()) { + ALOGE("Interface index %u was never in a currently-connected netId", ifindex); + return true; + } + unsigned lastNetId = mIfindexToLastNetId[ifindex]; + for (unsigned idx : ifindices) { + unsigned activeNetId = mIfindexToLastNetId[idx]; + // If this IP address is still assigned to another interface in the same network, + // then we don't need to destroy sockets on it because they are likely still valid. + // For now we do this only on VPNs. + // TODO: evaluate extending this to all network types. + if (lastNetId == activeNetId && isVirtualNetworkLocked(activeNetId)) { + return false; + } + } + return true; +} + bool NetworkController::canProtectLocked(uid_t uid) const { return ((getPermissionForUserLocked(uid) & PERMISSION_SYSTEM) == PERMISSION_SYSTEM) || mProtectableUsers.find(uid) != mProtectableUsers.end(); @@ -617,6 +702,23 @@ void NetworkController::dump(DumpWriter& dw) { } dw.decIndent(); + dw.blankline(); + dw.println("Interface <-> last network map:"); + dw.incIndent(); + for (const auto& i : mIfindexToLastNetId) { + dw.println("Ifindex: %u NetId: %u", i.first, i.second); + } + dw.decIndent(); + + dw.blankline(); + dw.println("Interface addresses:"); + dw.incIndent(); + for (const auto& i : mAddressToIfindices) { + dw.println("address: %s ifindices: [%s]", i.first.c_str(), + android::base::Join(i.second, ", ").c_str()); + } + dw.decIndent(); + dw.decIndent(); dw.decIndent(); diff --git a/server/NetworkController.h b/server/NetworkController.h index 627e44da..5e7af809 100644 --- a/server/NetworkController.h +++ b/server/NetworkController.h @@ -27,6 +27,8 @@ #include <map> #include <set> #include <sys/types.h> +#include <unordered_map> +#include <unordered_set> #include <vector> struct android_net_context; @@ -123,6 +125,12 @@ public: int removeRoute(unsigned netId, const char* interface, const char* destination, const char* nexthop, bool legacy, uid_t uid) WARN_UNUSED_RESULT; + // Notes that the specified address has appeared on the specified interface. + void addInterfaceAddress(unsigned ifIndex, const char* address); + // Notes that the specified address has been removed from the specified interface. + // Returns true if we should destroy sockets on this address. + bool removeInterfaceAddress(unsigned ifIndex, const char* address); + bool canProtect(uid_t uid) const; void allowProtect(const std::vector<uid_t>& uids); void denyProtect(const std::vector<uid_t>& uids); @@ -137,6 +145,7 @@ private: unsigned getNetworkForConnectLocked(uid_t uid) const; unsigned getNetworkForInterfaceLocked(const char* interface) const; bool canProtectLocked(uid_t uid) const; + bool isVirtualNetworkLocked(unsigned netId) const; VirtualNetwork* getVirtualNetworkForUserLocked(uid_t uid) const; Permission getPermissionForUserLocked(uid_t uid) const; @@ -151,12 +160,27 @@ private: class DelegateImpl; DelegateImpl* const mDelegateImpl; - // mRWLock guards all accesses to mDefaultNetId, mNetworks, mUsers and mProtectableUsers. + // mRWLock guards all accesses to mDefaultNetId, mNetworks, mUsers, mProtectableUsers, + // mIfindexToLastNetId and mAddressToIfindices. mutable android::RWLock mRWLock; unsigned mDefaultNetId; std::map<unsigned, Network*> mNetworks; // Map keys are NetIds. std::map<uid_t, Permission> mUsers; std::set<uid_t> mProtectableUsers; + // Map interface (ifIndex) to its current NetId, or the last NetId if the interface was removed + // from the network and not added to another network. This state facilitates the interface to + // NetId lookup during RTM_DELADDR (NetworkController::removeInterfaceAddress), when the + // interface in question might already have been removed from the network. + // An interface is added to this map when it is added to a network and removed from this map + // when its network is destroyed. + std::unordered_map<unsigned, unsigned> mIfindexToLastNetId; + // Map IP address to the list of active interfaces (ifIndex) that have that address. + // Also contains IP addresses configured on interfaces that have not been added to any network. + // TODO: Does not track IP addresses present when netd is started or restarts after a crash. + // This is not a problem for its intended use (tracking IP addresses on VPN interfaces), but + // we should fix it. + std::unordered_map<std::string, std::unordered_set<unsigned>> mAddressToIfindices; + }; } // namespace net diff --git a/server/RouteController.cpp b/server/RouteController.cpp index b1aa5aca..c78854d8 100644 --- a/server/RouteController.cpp +++ b/server/RouteController.cpp @@ -154,6 +154,18 @@ uint32_t RouteController::getRouteTableForInterfaceLocked(const char* interface) return iter->second; } +uint32_t RouteController::getIfIndex(const char* interface) { + android::RWLock::AutoRLock lock(sInterfaceToTableLock); + + auto iter = sInterfaceToTable.find(interface); + if (iter == sInterfaceToTable.end()) { + ALOGE("getIfIndex: cannot find interface %s", interface); + return 0; + } + + return iter->second - ROUTE_TABLE_OFFSET_FROM_INDEX; +} + uint32_t RouteController::getRouteTableForInterface(const char* interface) { android::RWLock::AutoRLock lock(sInterfaceToTableLock); return getRouteTableForInterfaceLocked(interface); diff --git a/server/RouteController.h b/server/RouteController.h index de79b617..6e10ccea 100644 --- a/server/RouteController.h +++ b/server/RouteController.h @@ -45,6 +45,15 @@ public: static int Init(unsigned localNetId) WARN_UNUSED_RESULT; + // Returns an ifindex given the interface name, by looking up in sInterfaceToTable. + // This is currently only used by NetworkController::addInterfaceToNetwork + // and should probabaly be changed to passing the ifindex into RouteController instead. + // We do this instead of calling if_nametoindex because the same interface name can + // correspond to different interface indices over time. This way, even if the interface + // index has changed, we can still free any map entries indexed by the ifindex that was + // used to add them. + static uint32_t getIfIndex(const char* interface); + static int addInterfaceToLocalNetwork(unsigned netId, const char* interface) WARN_UNUSED_RESULT; static int removeInterfaceFromLocalNetwork(unsigned netId, const char* interface) WARN_UNUSED_RESULT; diff --git a/server/XfrmController.cpp b/server/XfrmController.cpp index 8a891eb7..b9a5a415 100644 --- a/server/XfrmController.cpp +++ b/server/XfrmController.cpp @@ -44,11 +44,6 @@ #include <linux/xfrm.h> #define LOG_TAG "XfrmController" -#include "android-base/stringprintf.h" -#include "android-base/strings.h" -#include "android-base/unique_fd.h" -#include <android/net/INetd.h> -#include <log/log_properties.h> #include "InterfaceController.h" #include "NetdConstants.h" #include "NetlinkCommands.h" @@ -57,6 +52,11 @@ #include "netdutils/Fd.h" #include "netdutils/Slice.h" #include "netdutils/Syscalls.h" +#include <android-base/properties.h> +#include <android-base/stringprintf.h> +#include <android-base/strings.h> +#include <android-base/unique_fd.h> +#include <android/net/INetd.h> #include <cutils/log.h> #include <cutils/properties.h> #include <logwrap/logwrap.h> @@ -87,6 +87,11 @@ constexpr uint32_t RAND_SPI_MAX = 0xFFFFFFFE; constexpr uint32_t INVALID_SPI = 0; +static inline bool isEngBuild() { + static const std::string sBuildType = android::base::GetProperty("ro.build.type", "user"); + return sBuildType == "eng"; +} + #define XFRM_MSG_TRANS(x) \ case x: \ return #x; @@ -126,18 +131,18 @@ uint8_t kPadBytesArray[] = {0, 0, 0}; void* kPadBytes = static_cast<void*>(kPadBytesArray); #define LOG_HEX(__desc16__, __buf__, __len__) \ - if (__android_log_is_debuggable()) { \ - do { \ + do { \ + if (isEngBuild()) { \ logHex(__desc16__, __buf__, __len__); \ - } while (0); \ - } + } \ + } while (0) #define LOG_IOV(__iov__) \ - if (__android_log_is_debuggable()) { \ - do { \ + do { \ + if (isEngBuild()) { \ logIov(__iov__); \ - } while (0); \ - } + } \ + } while (0) void logHex(const char* desc16, const char* buf, size_t len) { char* printBuf = new char[len * 2 + 1 + 26]; // len->ascii, +newline, +prefix strlen |