diff options
author | Chad Brubaker <cbrubaker@google.com> | 2014-02-21 11:04:45 -0800 |
---|---|---|
committer | Chad Brubaker <cbrubaker@google.com> | 2014-03-15 15:32:39 -0700 |
commit | 4c5c33e5e6fb37f85977c70a0baba4e1ed51fe0d (patch) | |
tree | 8da9334f19609a3bfd47f0850df701c4320a6a03 /services/java | |
parent | 1b66923148130dec7139175a3bf4c4d534cabac4 (diff) | |
download | frameworks_base-4c5c33e5e6fb37f85977c70a0baba4e1ed51fe0d.zip frameworks_base-4c5c33e5e6fb37f85977c70a0baba4e1ed51fe0d.tar.gz frameworks_base-4c5c33e5e6fb37f85977c70a0baba4e1ed51fe0d.tar.bz2 |
Fix support for simultaneous VPN tuns
A VPN can once again bring up a new tun interface while the old tun is
running. Once the new tun is set up the routing rules will be removed from the
old tun. It is up to the application to drain the old tun of traffic and
close it.
If the new tun fails to come up the old tun will remain untouched and
can still be used.
To prevent leakage the new rules are added before the old tun is
shutdown. Netd/Dns has been changed to allow multiple rules to exist
at once with the most recently added rule taking priority.
Bug: 12134439
Change-Id: I7e00c7c68cc339d81f09b3d2a33276ffc76985f5
Diffstat (limited to 'services/java')
-rw-r--r-- | services/java/com/android/server/connectivity/Vpn.java | 77 |
1 files changed, 51 insertions, 26 deletions
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java index 03405e7..8f25860 100644 --- a/services/java/com/android/server/connectivity/Vpn.java +++ b/services/java/com/android/server/connectivity/Vpn.java @@ -352,6 +352,12 @@ public class Vpn extends BaseNetworkStateTracker { Binder.restoreCallingIdentity(token); } + // Save the old config in case we need to go back. + VpnConfig oldConfig = mConfig; + String oldInterface = mInterface; + Connection oldConnection = mConnection; + SparseBooleanArray oldUsers = mVpnUsers; + // Configure the interface. Abort if any of these steps fails. ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu)); try { @@ -371,12 +377,7 @@ public class Vpn extends BaseNetworkStateTracker { new UserHandle(mUserId))) { throw new IllegalStateException("Cannot bind " + config.user); } - if (mConnection != null) { - mContext.unbindService(mConnection); - } - if (mInterface != null && !mInterface.equals(interfaze)) { - jniReset(mInterface); - } + mConnection = connection; mInterface = interfaze; @@ -385,50 +386,74 @@ public class Vpn extends BaseNetworkStateTracker { config.interfaze = mInterface; config.startTime = SystemClock.elapsedRealtime(); mConfig = config; + // Set up forwarding and DNS rules. mVpnUsers = new SparseBooleanArray(); token = Binder.clearCallingIdentity(); try { mCallback.setMarkedForwarding(mInterface); - mCallback.setRoutes(interfaze, config.routes); + mCallback.setRoutes(mInterface, config.routes); mCallback.override(mInterface, config.dnsServers, config.searchDomains); addVpnUserLocked(mUserId); - + // If we are owner assign all Restricted Users to this VPN + if (mUserId == UserHandle.USER_OWNER) { + for (UserInfo user : mgr.getUsers()) { + if (user.isRestricted()) { + try { + addVpnUserLocked(user.id); + } catch (Exception e) { + Log.wtf(TAG, "Failed to add user " + user.id + " to owner's VPN"); + } + } + } + } } finally { Binder.restoreCallingIdentity(token); } + if (oldConnection != null) { + mContext.unbindService(oldConnection); + } + if (oldInterface != null && !oldInterface.equals(interfaze)) { + // Remove the old tun's user forwarding rules + // The new tun's user rules have already been added so they will take over + // as rules are deleted. This prevents data leakage as the rules are moved over. + token = Binder.clearCallingIdentity(); + try { + final int size = oldUsers.size(); + final boolean forwardDns = (oldConfig.dnsServers != null && + oldConfig.dnsServers.size() != 0); + for (int i = 0; i < size; i++) { + int user = oldUsers.keyAt(i); + mCallback.clearUserForwarding(oldInterface, user, forwardDns); + } + mCallback.clearMarkedForwarding(oldInterface); + } finally { + Binder.restoreCallingIdentity(token); + } + jniReset(oldInterface); + } } catch (RuntimeException e) { updateState(DetailedState.FAILED, "establish"); IoUtils.closeQuietly(tun); // make sure marked forwarding is cleared if it was set + token = Binder.clearCallingIdentity(); try { mCallback.clearMarkedForwarding(mInterface); } catch (Exception ingored) { // ignored + } finally { + Binder.restoreCallingIdentity(token); } + // restore old state + mConfig = oldConfig; + mConnection = oldConnection; + mVpnUsers = oldUsers; + mInterface = oldInterface; throw e; } Log.i(TAG, "Established by " + config.user + " on " + mInterface); - - // If we are owner assign all Restricted Users to this VPN - if (mUserId == UserHandle.USER_OWNER) { - token = Binder.clearCallingIdentity(); - try { - for (UserInfo user : mgr.getUsers()) { - if (user.isRestricted()) { - try { - addVpnUserLocked(user.id); - } catch (Exception e) { - Log.wtf(TAG, "Failed to add user " + user.id + " to owner's VPN"); - } - } - } - } finally { - Binder.restoreCallingIdentity(token); - } - } // TODO: ensure that contract class eventually marks as connected updateState(DetailedState.AUTHENTICATING, "establish"); return tun; |