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)
diff --git a/server/NetlinkHandler.cpp b/server/NetlinkHandler.cpp
index d0aafde..348e488 100644
--- a/server/NetlinkHandler.cpp
+++ b/server/NetlinkHandler.cpp
@@ -55,6 +55,18 @@
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 @@
(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 @@
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);
}