summaryrefslogtreecommitdiff
path: root/server/NetworkController.cpp
diff options
context:
space:
mode:
authorRubin Xu <rubinxu@google.com>2018-04-27 14:27:59 +0100
committerRubin Xu <rubinxu@google.com>2018-06-07 08:12:24 +0100
commit6c00b61656da32ce3223a2fc711f5649b6b7b5ac (patch)
tree3757ef63883c1e5916c0a635d329c9d77ab5280e /server/NetworkController.cpp
parent18a257aecc1b4a380f6245134c74640b753bba87 (diff)
Do not destroy socket when VPN interface address is still in use
Normally when an IP address is removed, all sockets associated with the addresses are destroyed. This patchset changes this behavior such that if the address in question is still being used by another interface that belongs to the same underlying virtual network, the destroy operation is skipped. This change is needed to support VPN seamless handover where the VPN app will establish a second TUN interface (with different config) before tearing down the existing interface. The intention is that during this handover existing socket connections should not be disturbed. There is a companion change in the framework side to make sure during such handover, the VPN netId remains unchanged so routing still works. Bug: 64692591 Test: cts-tradefed run commandAndExit cts-dev -m CtsHostsideNetworkTests -t com.android.cts.net.HostsideVpnTests Test: system/netd/tests/runtests.sh Change-Id: I02c6b0db5f15cd1aef3e3fa6f0c36e86b4f427fd Merged-In: I02c6b0db5f15cd1aef3e3fa6f0c36e86b4f427fd (cherry picked from commit acbb6b7bbea17c5653929ee5224bd4f8e16c0f69)
Diffstat (limited to 'server/NetworkController.cpp')
-rw-r--r--server/NetworkController.cpp90
1 files changed, 89 insertions, 1 deletions
diff --git a/server/NetworkController.cpp b/server/NetworkController.cpp
index 87ad1bd0..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"
@@ -345,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;
}
@@ -466,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;
@@ -484,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) {
@@ -583,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();
@@ -631,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();