summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.txt2
-rw-r--r--core/java/android/net/IConnectivityManager.aidl2
-rw-r--r--core/java/android/net/NetworkAgent.java30
-rw-r--r--core/java/android/net/NetworkCapabilities.java19
-rw-r--r--core/java/android/net/NetworkUtils.java7
-rw-r--r--core/java/android/net/UidRange.aidl24
-rw-r--r--core/java/android/net/UidRange.java102
-rw-r--r--core/java/android/net/VpnService.java15
-rw-r--r--core/java/android/os/INetworkManagementService.aidl42
-rw-r--r--core/jni/android_net_NetUtils.cpp6
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java248
-rw-r--r--services/core/java/com/android/server/NetworkManagementService.java106
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkAgentInfo.java4
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkMonitor.java3
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java431
-rw-r--r--services/core/jni/com_android_server_connectivity_Vpn.cpp124
16 files changed, 565 insertions, 600 deletions
diff --git a/api/current.txt b/api/current.txt
index bc436c3..092061c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -16506,6 +16506,7 @@ package android.net {
field public static final int NET_CAPABILITY_MMS = 0; // 0x0
field public static final int NET_CAPABILITY_NOT_METERED = 11; // 0xb
field public static final int NET_CAPABILITY_NOT_RESTRICTED = 13; // 0xd
+ field public static final int NET_CAPABILITY_NOT_VPN = 15; // 0xf
field public static final int NET_CAPABILITY_RCS = 8; // 0x8
field public static final int NET_CAPABILITY_SUPL = 1; // 0x1
field public static final int NET_CAPABILITY_TRUSTED = 14; // 0xe
@@ -16514,6 +16515,7 @@ package android.net {
field public static final int TRANSPORT_BLUETOOTH = 2; // 0x2
field public static final int TRANSPORT_CELLULAR = 0; // 0x0
field public static final int TRANSPORT_ETHERNET = 3; // 0x3
+ field public static final int TRANSPORT_VPN = 4; // 0x4
field public static final int TRANSPORT_WIFI = 1; // 0x1
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index b76fc38..b9c6491 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -115,8 +115,6 @@ interface IConnectivityManager
void setDataDependency(int networkType, boolean met);
- boolean protectVpn(in ParcelFileDescriptor socket);
-
boolean prepareVpn(String oldPackage, String newPackage);
ParcelFileDescriptor establishVpn(in VpnConfig config);
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 3d0874b..41eab02 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -92,6 +92,20 @@ public abstract class NetworkAgent extends Handler {
*/
public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 4;
+ /**
+ * Sent by the NetworkAgent to ConnectivityService to add new UID ranges
+ * to be forced into this Network. For VPNs only.
+ * obj = UidRange[] to forward
+ */
+ public static final int EVENT_UID_RANGES_ADDED = BASE + 5;
+
+ /**
+ * Sent by the NetworkAgent to ConnectivityService to remove UID ranges
+ * from being forced into this Network. For VPNs only.
+ * obj = UidRange[] to stop forwarding
+ */
+ public static final int EVENT_UID_RANGES_REMOVED = BASE + 6;
+
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score) {
super(looper);
@@ -194,6 +208,22 @@ public abstract class NetworkAgent extends Handler {
}
/**
+ * Called by the VPN code when it wants to add ranges of UIDs to be routed
+ * through the VPN network.
+ */
+ public void addUidRanges(UidRange[] ranges) {
+ queueOrSendMessage(EVENT_UID_RANGES_ADDED, ranges);
+ }
+
+ /**
+ * Called by the VPN code when it wants to remove ranges of UIDs from being routed
+ * through the VPN network.
+ */
+ public void removeUidRanges(UidRange[] ranges) {
+ queueOrSendMessage(EVENT_UID_RANGES_REMOVED, ranges);
+ }
+
+ /**
* Called when ConnectivityService has indicated they no longer want this network.
* The parent factory should (previously) have received indication of the change
* as well, either canceling NetworkRequests or altering their score such that this
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 00200d0..239db86 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -64,7 +64,7 @@ public final class NetworkCapabilities implements Parcelable {
* by any Network that matches all of them.
*/
private long mNetworkCapabilities = (1 << NET_CAPABILITY_NOT_RESTRICTED) |
- (1 << NET_CAPABILITY_TRUSTED);
+ (1 << NET_CAPABILITY_TRUSTED) | (1 << NET_CAPABILITY_NOT_VPN);
/**
* Indicates this is a network that has the ability to reach the
@@ -158,9 +158,15 @@ public final class NetworkCapabilities implements Parcelable {
*/
public static final int NET_CAPABILITY_TRUSTED = 14;
+ /*
+ * Indicates that this network is not a VPN. This capability is set by default and should be
+ * explicitly cleared when creating VPN networks.
+ */
+ public static final int NET_CAPABILITY_NOT_VPN = 15;
+
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
- private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_TRUSTED;
+ private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_VPN;
/**
* Adds the given capability to this {@code NetworkCapability} instance.
@@ -271,8 +277,13 @@ public final class NetworkCapabilities implements Parcelable {
*/
public static final int TRANSPORT_ETHERNET = 3;
+ /**
+ * Indicates this network uses a VPN transport.
+ */
+ public static final int TRANSPORT_VPN = 4;
+
private static final int MIN_TRANSPORT = TRANSPORT_CELLULAR;
- private static final int MAX_TRANSPORT = TRANSPORT_ETHERNET;
+ private static final int MAX_TRANSPORT = TRANSPORT_VPN;
/**
* Adds the given transport type to this {@code NetworkCapability} instance.
@@ -500,6 +511,7 @@ public final class NetworkCapabilities implements Parcelable {
case TRANSPORT_WIFI: transports += "WIFI"; break;
case TRANSPORT_BLUETOOTH: transports += "BLUETOOTH"; break;
case TRANSPORT_ETHERNET: transports += "ETHERNET"; break;
+ case TRANSPORT_VPN: transports += "VPN"; break;
}
if (++i < types.length) transports += "|";
}
@@ -523,6 +535,7 @@ public final class NetworkCapabilities implements Parcelable {
case NET_CAPABILITY_INTERNET: capabilities += "INTERNET"; break;
case NET_CAPABILITY_NOT_RESTRICTED: capabilities += "NOT_RESTRICTED"; break;
case NET_CAPABILITY_TRUSTED: capabilities += "TRUSTED"; break;
+ case NET_CAPABILITY_NOT_VPN: capabilities += "NOT_VPN"; break;
}
if (++i < types.length) capabilities += "&";
}
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index c4b17b6..aa1e123 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -155,6 +155,13 @@ public class NetworkUtils {
public native static boolean bindSocketToNetwork(int socketfd, int netId);
/**
+ * Protect {@code socketfd} from VPN connections. After protecting, data sent through
+ * this socket will go directly to the underlying network, so its traffic will not be
+ * forwarded through the VPN.
+ */
+ public native static boolean protectFromVpn(int socketfd);
+
+ /**
* Convert a IPv4 address from an integer to an InetAddress.
* @param hostAddress an int corresponding to the IPv4 address in network byte order
*/
diff --git a/core/java/android/net/UidRange.aidl b/core/java/android/net/UidRange.aidl
new file mode 100644
index 0000000..f9be628
--- /dev/null
+++ b/core/java/android/net/UidRange.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+/**
+ * An inclusive range of UIDs.
+ *
+ * {@hide}
+ */
+parcelable UidRange;
diff --git a/core/java/android/net/UidRange.java b/core/java/android/net/UidRange.java
new file mode 100644
index 0000000..2e586b3
--- /dev/null
+++ b/core/java/android/net/UidRange.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static android.os.UserHandle.PER_USER_RANGE;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.IllegalArgumentException;
+
+/**
+ * An inclusive range of UIDs.
+ *
+ * @hide
+ */
+public final class UidRange implements Parcelable {
+ public final int start;
+ public final int stop;
+
+ public UidRange(int startUid, int stopUid) {
+ if (startUid < 0) throw new IllegalArgumentException("Invalid start UID.");
+ if (stopUid < 0) throw new IllegalArgumentException("Invalid stop UID.");
+ if (startUid > stopUid) throw new IllegalArgumentException("Invalid UID range.");
+ start = startUid;
+ stop = stopUid;
+ }
+
+ public static UidRange createForUser(int userId) {
+ return new UidRange(userId * PER_USER_RANGE, (userId + 1) * PER_USER_RANGE - 1);
+ }
+
+ public int getStartUser() {
+ return start / PER_USER_RANGE;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + start;
+ result = 31 * result + stop;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o instanceof UidRange) {
+ UidRange other = (UidRange) o;
+ return start == other.start && stop == other.stop;
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return start + "-" + stop;
+ }
+
+ // implement the Parcelable interface
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(start);
+ dest.writeInt(stop);
+ }
+
+ public static final Creator<UidRange> CREATOR =
+ new Creator<UidRange>() {
+ @Override
+ public UidRange createFromParcel(Parcel in) {
+ int start = in.readInt();
+ int stop = in.readInt();
+
+ return new UidRange(start, stop);
+ }
+ @Override
+ public UidRange[] newArray(int size) {
+ return new UidRange[size];
+ }
+ };
+}
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index 7c62bf6..1d89eae 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -24,6 +24,7 @@ import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
+import android.net.NetworkUtils;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
@@ -168,19 +169,7 @@ public class VpnService extends Service {
* @return {@code true} on success.
*/
public boolean protect(int socket) {
- ParcelFileDescriptor dup = null;
- try {
- dup = ParcelFileDescriptor.fromFd(socket);
- return getService().protectVpn(dup);
- } catch (Exception e) {
- return false;
- } finally {
- try {
- dup.close();
- } catch (Exception e) {
- // ignore
- }
- }
+ return NetworkUtils.protectFromVpn(socket);
}
/**
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index eb9ba13..d997e44 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -22,6 +22,7 @@ import android.net.INetworkManagementEventObserver;
import android.net.LinkAddress;
import android.net.NetworkStats;
import android.net.RouteInfo;
+import android.net.UidRange;
import android.net.wifi.WifiConfiguration;
import android.os.INetworkActivityListener;
@@ -325,28 +326,14 @@ interface INetworkManagementService
void setFirewallUidRule(int uid, boolean allow);
/**
- * Set all packets from users [uid_start,uid_end] to go through interface iface
- * iface must already be set for marked forwarding by {@link setMarkedForwarding}
+ * Set all packets from users in ranges to go through VPN specified by netId.
*/
- void setUidRangeRoute(String iface, int uid_start, int uid_end, boolean forward_dns);
+ void addVpnUidRanges(int netId, in UidRange[] ranges);
/**
- * Clears the special routing rules for users [uid_start,uid_end]
+ * Clears the special VPN rules for users in ranges and VPN specified by netId.
*/
- void clearUidRangeRoute(String iface, int uid_start, int uid_end);
-
- /**
- * Setup an interface for routing packets marked by {@link setUidRangeRoute}
- *
- * This sets up a dedicated routing table for packets marked for {@code iface} and adds
- * source-NAT rules so that the marked packets have the correct source address.
- */
- void setMarkedForwarding(String iface);
-
- /**
- * Removes marked forwarding for an interface
- */
- void clearMarkedForwarding(String iface);
+ void removeVpnUidRanges(int netId, in UidRange[] ranges);
/**
* Get the SO_MARK associated with routing packets for user {@code uid}
@@ -410,9 +397,14 @@ interface INetworkManagementService
boolean isNetworkActive();
/**
- * Setup a new network.
+ * Setup a new physical network.
+ */
+ void createPhysicalNetwork(int netId);
+
+ /**
+ * Setup a new VPN.
*/
- void createNetwork(int netId);
+ void createVirtualNetwork(int netId, boolean hasDNS);
/**
* Remove a network.
@@ -437,4 +429,14 @@ interface INetworkManagementService
void setPermission(boolean internal, boolean changeNetState, in int[] uids);
void clearPermission(in int[] uids);
+
+ /**
+ * Allow UID to call protect().
+ */
+ void allowProtect(int uid);
+
+ /**
+ * Deny UID from calling protect().
+ */
+ void denyProtect(int uid);
}
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 6f89800..a75d547 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -285,6 +285,11 @@ static jboolean android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz,
return (jboolean) !setNetworkForSocket(netId, socket);
}
+static jboolean android_net_utils_protectFromVpn(JNIEnv *env, jobject thiz, jint socket)
+{
+ return (jboolean) !protectFromVpn(socket);
+}
+
// ----------------------------------------------------------------------------
/*
@@ -308,6 +313,7 @@ static JNINativeMethod gNetworkUtilMethods[] = {
{ "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
{ "unbindProcessToNetworkForHostResolution", "()Z", (void*) android_net_utils_unbindProcessToNetworkForHostResolution },
{ "bindSocketToNetwork", "(II)Z", (void*) android_net_utils_bindSocketToNetwork },
+ { "protectFromVpn", "(I)Z", (void*)android_net_utils_protectFromVpn },
};
int register_android_net_NetworkUtils(JNIEnv* env)
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 6fc7c6b..ea05b98 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -87,6 +87,7 @@ import android.net.ProxyDataTracker;
import android.net.ProxyInfo;
import android.net.RouteInfo;
import android.net.SamplingDataTracker;
+import android.net.UidRange;
import android.net.Uri;
import android.net.wimax.WimaxManagerConstants;
import android.os.AsyncTask;
@@ -235,7 +236,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
@GuardedBy("mVpns")
private final SparseArray<Vpn> mVpns = new SparseArray<Vpn>();
- private VpnCallback mVpnCallback = new VpnCallback();
private boolean mLockdownEnabled;
private LockdownVpnTracker mLockdownTracker;
@@ -363,8 +363,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
*/
private static final int EVENT_SET_POLICY_DATA_ENABLE = 12;
- private static final int EVENT_VPN_STATE_CHANGED = 13;
-
/**
* Used internally to disable fail fast of mobile data
*/
@@ -3178,6 +3176,30 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (score != null) updateNetworkScore(nai, score.intValue());
break;
}
+ case NetworkAgent.EVENT_UID_RANGES_ADDED: {
+ NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+ if (nai == null) {
+ loge("EVENT_UID_RANGES_ADDED from unknown NetworkAgent");
+ break;
+ }
+ try {
+ mNetd.addVpnUidRanges(nai.network.netId, (UidRange[])msg.obj);
+ } catch (RemoteException e) {
+ }
+ break;
+ }
+ case NetworkAgent.EVENT_UID_RANGES_REMOVED: {
+ NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+ if (nai == null) {
+ loge("EVENT_UID_RANGES_REMOVED from unknown NetworkAgent");
+ break;
+ }
+ try {
+ mNetd.removeVpnUidRanges(nai.network.netId, (UidRange[])msg.obj);
+ } catch (RemoteException e) {
+ }
+ break;
+ }
case NetworkMonitor.EVENT_NETWORK_VALIDATED: {
NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
handleConnectionValidated(nai);
@@ -3459,12 +3481,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (affectedNetwork != null) {
// check if this network still has live requests - otherwise, tear down
// TODO - probably push this to the NF/NA
- boolean keep = false;
- for (int i = 0; i < affectedNetwork.networkRequests.size(); i++) {
+ boolean keep = affectedNetwork.isVPN();
+ for (int i = 0; i < affectedNetwork.networkRequests.size() && !keep; i++) {
NetworkRequest r = affectedNetwork.networkRequests.valueAt(i);
if (mNetworkRequests.get(r).isRequest) {
keep = true;
- break;
}
}
if (keep == false) {
@@ -3544,12 +3565,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
handleSetPolicyDataEnable(networkType, enabled);
break;
}
- case EVENT_VPN_STATE_CHANGED: {
- if (mLockdownTracker != null) {
- mLockdownTracker.onVpnStateChanged((NetworkInfo) msg.obj);
- }
- break;
- }
case EVENT_ENABLE_FAIL_FAST_MOBILE_DATA: {
int tag = mEnableFailFastMobileDataTag.get();
if (msg.arg1 == tag) {
@@ -4057,36 +4072,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
/**
- * Protect a socket from VPN routing rules. This method is used by
- * VpnBuilder and not available in ConnectivityManager. Permissions
- * are checked in Vpn class.
- * @hide
- */
- @Override
- public boolean protectVpn(ParcelFileDescriptor socket) {
- throwIfLockdownEnabled();
- try {
- int type = mActiveDefaultNetwork;
- int user = UserHandle.getUserId(Binder.getCallingUid());
- if (ConnectivityManager.isNetworkTypeValid(type) && mNetTrackers[type] != null) {
- synchronized(mVpns) {
- mVpns.get(user).protect(socket);
- }
- return true;
- }
- } catch (Exception e) {
- // ignore
- } finally {
- try {
- socket.close();
- } catch (Exception e) {
- // ignore
- }
- }
- return false;
- }
-
- /**
* Prepare for a VPN application. This method is used by VpnDialogs
* and not available in ConnectivityManager. Permissions are checked
* in Vpn class.
@@ -4180,144 +4165,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
- /**
- * Callback for VPN subsystem. Currently VPN is not adapted to the service
- * through NetworkStateTracker since it works differently. For example, it
- * needs to override DNS servers but never takes the default routes. It
- * relies on another data network, and it could keep existing connections
- * alive after reconnecting, switching between networks, or even resuming
- * from deep sleep. Calls from applications should be done synchronously
- * to avoid race conditions. As these are all hidden APIs, refactoring can
- * be done whenever a better abstraction is developed.
- */
- public class VpnCallback {
- private VpnCallback() {
- }
-
- public void onStateChanged(NetworkInfo info) {
- mHandler.obtainMessage(EVENT_VPN_STATE_CHANGED, info).sendToTarget();
- }
-
- public void override(String iface, List<String> dnsServers, List<String> searchDomains) {
- if (dnsServers == null) {
- restore();
- return;
- }
-
- // Convert DNS servers into addresses.
- List<InetAddress> addresses = new ArrayList<InetAddress>();
- for (String address : dnsServers) {
- // Double check the addresses and remove invalid ones.
- try {
- addresses.add(InetAddress.parseNumericAddress(address));
- } catch (Exception e) {
- // ignore
- }
- }
- if (addresses.isEmpty()) {
- restore();
- return;
- }
-
- // Concatenate search domains into a string.
- StringBuilder buffer = new StringBuilder();
- if (searchDomains != null) {
- for (String domain : searchDomains) {
- buffer.append(domain).append(' ');
- }
- }
- String domains = buffer.toString().trim();
-
- // Apply DNS changes.
- synchronized (mDnsLock) {
- // TODO: Re-enable this when the netId of the VPN is known.
- // updateDnsLocked("VPN", netId, addresses, domains);
- }
-
- // Temporarily disable the default proxy (not global).
- synchronized (mProxyLock) {
- mDefaultProxyDisabled = true;
- if (mGlobalProxy == null && mDefaultProxy != null) {
- sendProxyBroadcast(null);
- }
- }
-
- // TODO: support proxy per network.
- }
-
- public void restore() {
- synchronized (mProxyLock) {
- mDefaultProxyDisabled = false;
- if (mGlobalProxy == null && mDefaultProxy != null) {
- sendProxyBroadcast(mDefaultProxy);
- }
- }
- }
-
- public void protect(ParcelFileDescriptor socket) {
- try {
- final int mark = mNetd.getMarkForProtect();
- NetworkUtils.markSocket(socket.getFd(), mark);
- } catch (RemoteException e) {
- }
- }
-
- public void setRoutes(String interfaze, List<RouteInfo> routes) {
- for (RouteInfo route : routes) {
- try {
- mNetd.setMarkedForwardingRoute(interfaze, route);
- } catch (RemoteException e) {
- }
- }
- }
-
- public void setMarkedForwarding(String interfaze) {
- try {
- mNetd.setMarkedForwarding(interfaze);
- } catch (RemoteException e) {
- }
- }
-
- public void clearMarkedForwarding(String interfaze) {
- try {
- mNetd.clearMarkedForwarding(interfaze);
- } catch (RemoteException e) {
- }
- }
-
- public void addUserForwarding(String interfaze, int uid, boolean forwardDns) {
- int uidStart = uid * UserHandle.PER_USER_RANGE;
- int uidEnd = uidStart + UserHandle.PER_USER_RANGE - 1;
- addUidForwarding(interfaze, uidStart, uidEnd, forwardDns);
- }
-
- public void clearUserForwarding(String interfaze, int uid, boolean forwardDns) {
- int uidStart = uid * UserHandle.PER_USER_RANGE;
- int uidEnd = uidStart + UserHandle.PER_USER_RANGE - 1;
- clearUidForwarding(interfaze, uidStart, uidEnd, forwardDns);
- }
-
- public void addUidForwarding(String interfaze, int uidStart, int uidEnd,
- boolean forwardDns) {
- // TODO: Re-enable this when the netId of the VPN is known.
- // try {
- // mNetd.setUidRangeRoute(netId, uidStart, uidEnd, forwardDns);
- // } catch (RemoteException e) {
- // }
-
- }
-
- public void clearUidForwarding(String interfaze, int uidStart, int uidEnd,
- boolean forwardDns) {
- // TODO: Re-enable this when the netId of the VPN is known.
- // try {
- // mNetd.clearUidRangeRoute(interfaze, uidStart, uidEnd);
- // } catch (RemoteException e) {
- // }
-
- }
- }
-
@Override
public boolean updateLockdownVpn() {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
@@ -5361,9 +5208,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
loge("Starting user already has a VPN");
return;
}
- userVpn = new Vpn(mContext, mVpnCallback, mNetd, this, userId);
+ userVpn = new Vpn(mHandler.getLooper(), mContext, mNetd, this, userId);
mVpns.put(userId, userVpn);
- userVpn.startMonitoring(mContext, mTrackerHandler);
}
}
@@ -5885,7 +5731,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
loge("Unknown NetworkAgentInfo in handleConnectionValidated");
return;
}
- boolean keep = false;
+ boolean keep = newNetwork.isVPN();
boolean isNewDefault = false;
if (DBG) log("handleConnectionValidated for "+newNetwork.name());
// check if any NetworkRequest wants this NetworkAgent
@@ -5947,8 +5793,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
for (NetworkAgentInfo nai : affectedNetworks) {
- boolean teardown = true;
- for (int i = 0; i < nai.networkRequests.size(); i++) {
+ boolean teardown = !nai.isVPN();
+ for (int i = 0; i < nai.networkRequests.size() && teardown; i++) {
NetworkRequest nr = nai.networkRequests.valueAt(i);
try {
if (mNetworkRequests.get(nr).isRequest) {
@@ -6031,6 +5877,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
oldInfo = networkAgent.networkInfo;
networkAgent.networkInfo = newInfo;
}
+ if (networkAgent.isVPN() && mLockdownTracker != null) {
+ mLockdownTracker.onVpnStateChanged(newInfo);
+ }
if (oldInfo != null && oldInfo.getState() == state) {
if (VDBG) log("ignoring duplicate network state non-change");
@@ -6049,7 +5898,12 @@ public class ConnectivityService extends IConnectivityManager.Stub {
// CONNECTING and back (like wifi on DHCP renew).
// TODO: keep track of which networks we've created, or ask netd
// to tell us whether we've already created this network or not.
- mNetd.createNetwork(networkAgent.network.netId);
+ if (networkAgent.isVPN()) {
+ mNetd.createVirtualNetwork(networkAgent.network.netId,
+ !networkAgent.linkProperties.getDnsServers().isEmpty());
+ } else {
+ mNetd.createPhysicalNetwork(networkAgent.network.netId);
+ }
} catch (Exception e) {
loge("Error creating network " + networkAgent.network.netId + ": "
+ e.getMessage());
@@ -6059,9 +5913,31 @@ public class ConnectivityService extends IConnectivityManager.Stub {
updateLinkProperties(networkAgent, null);
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);
networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
+ if (networkAgent.isVPN()) {
+ // Temporarily disable the default proxy (not global).
+ synchronized (mProxyLock) {
+ if (!mDefaultProxyDisabled) {
+ mDefaultProxyDisabled = true;
+ if (mGlobalProxy == null && mDefaultProxy != null) {
+ sendProxyBroadcast(null);
+ }
+ }
+ }
+ // TODO: support proxy per network.
+ }
} else if (state == NetworkInfo.State.DISCONNECTED ||
state == NetworkInfo.State.SUSPENDED) {
networkAgent.asyncChannel.disconnect();
+ if (networkAgent.isVPN()) {
+ synchronized (mProxyLock) {
+ if (mDefaultProxyDisabled) {
+ mDefaultProxyDisabled = false;
+ if (mGlobalProxy == null && mDefaultProxy != null) {
+ sendProxyBroadcast(mDefaultProxy);
+ }
+ }
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index f9c7a78..c9f40cf 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -46,6 +46,7 @@ import android.net.LinkAddress;
import android.net.NetworkStats;
import android.net.NetworkUtils;
import android.net.RouteInfo;
+import android.net.UidRange;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.os.BatteryStats;
@@ -90,6 +91,7 @@ import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -116,6 +118,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub
private static final String DEFAULT = "default";
private static final String SECONDARY = "secondary";
+ private static final int MAX_UID_RANGES_PER_COMMAND = 10;
+
/**
* Name representing {@link #setGlobalAlert(long)} limit when delivered to
* {@link INetworkManagementEventObserver#limitReached(String, String)}.
@@ -1702,44 +1706,46 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
@Override
- public void setUidRangeRoute(String iface, int uid_start, int uid_end, boolean forward_dns) {
+ public void addVpnUidRanges(int netId, UidRange[] ranges) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- try {
- mConnector.execute("interface", "fwmark",
- "uid", "add", iface, uid_start, uid_end, forward_dns ? 1 : 0);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
- }
- }
-
- @Override
- public void clearUidRangeRoute(String iface, int uid_start, int uid_end) {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- try {
- mConnector.execute("interface", "fwmark",
- "uid", "remove", iface, uid_start, uid_end, 0);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
- }
- }
-
- @Override
- public void setMarkedForwarding(String iface) {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- try {
- mConnector.execute("interface", "fwmark", "rule", "add", iface);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ Object[] argv = new Object[3 + MAX_UID_RANGES_PER_COMMAND];
+ argv[0] = "users";
+ argv[1] = "add";
+ argv[2] = netId;
+ int argc = 3;
+ // Avoid overly long commands by limiting number of UID ranges per command.
+ for (int i = 0; i < ranges.length; i++) {
+ argv[argc++] = ranges[i].toString();
+ if (i == (ranges.length - 1) || argc == argv.length) {
+ try {
+ mConnector.execute("network", Arrays.copyOf(argv, argc));
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ argc = 3;
+ }
}
}
@Override
- public void clearMarkedForwarding(String iface) {
+ public void removeVpnUidRanges(int netId, UidRange[] ranges) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- try {
- mConnector.execute("interface", "fwmark", "rule", "remove", iface);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ Object[] argv = new Object[3 + MAX_UID_RANGES_PER_COMMAND];
+ argv[0] = "users";
+ argv[1] = "remove";
+ argv[2] = netId;
+ int argc = 3;
+ // Avoid overly long commands by limiting number of UID ranges per command.
+ for (int i = 0; i < ranges.length; i++) {
+ argv[argc++] = ranges[i].toString();
+ if (i == (ranges.length - 1) || argc == argv.length) {
+ try {
+ mConnector.execute("network", Arrays.copyOf(argv, argc));
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ argc = 3;
+ }
}
}
@@ -2015,7 +2021,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
@Override
- public void createNetwork(int netId) {
+ public void createPhysicalNetwork(int netId) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
@@ -2026,6 +2032,17 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
@Override
+ public void createVirtualNetwork(int netId, boolean hasDNS) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+
+ try {
+ mConnector.execute("network", "create", netId, "vpn", hasDNS ? "1" : "0");
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
public void removeNetwork(int netId) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
@@ -2143,4 +2160,27 @@ public class NetworkManagementService extends INetworkManagementService.Stub
throw e.rethrowAsParcelableException();
}
}
+
+ @Override
+ public void allowProtect(int uid) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+
+ try {
+ mConnector.execute("network", "protect", "allow", uid);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
+ public void denyProtect(int uid) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+
+ try {
+ mConnector.execute("network", "protect", "deny", uid);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 1332898..10bdba0 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -69,6 +69,10 @@ public class NetworkAgentInfo {
networkRequests.put(networkRequest.requestId, networkRequest);
}
+ public boolean isVPN() {
+ return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
+ }
+
public String toString() {
return "NetworkAgentInfo{ ni{" + networkInfo + "} network{" +
network + "} lp{" +
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 0d3b501..6fb8570 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -319,6 +319,9 @@ public class NetworkMonitor extends StateMachine {
case CMD_REEVALUATE:
if (message.arg1 != mReevaluateToken)
break;
+ if (mNetworkAgentInfo.isVPN()) {
+ transitionTo(mValidatedState);
+ }
// If network provides no internet connectivity adjust evaluation.
if (!mNetworkAgentInfo.networkCapabilities.hasCapability(
NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index df12995..d15254b 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -30,6 +30,7 @@ import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.graphics.Bitmap;
@@ -43,14 +44,18 @@ import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
+import android.net.NetworkAgent;
+import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
import android.net.NetworkUtils;
import android.net.RouteInfo;
-import android.net.NetworkInfo.DetailedState;
+import android.net.UidRange;
import android.os.Binder;
import android.os.FileUtils;
import android.os.IBinder;
import android.os.INetworkManagementService;
+import android.os.Looper;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -69,7 +74,6 @@ import com.android.internal.R;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
-import com.android.server.ConnectivityService.VpnCallback;
import com.android.server.net.BaseNetworkObserver;
import java.io.File;
@@ -78,7 +82,9 @@ import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Inet4Address;
import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import libcore.io.IoUtils;
@@ -86,16 +92,18 @@ import libcore.io.IoUtils;
/**
* @hide
*/
-public class Vpn extends BaseNetworkStateTracker {
+public class Vpn {
+ private static final String NETWORKTYPE = "VPN";
private static final String TAG = "Vpn";
private static final boolean LOGD = true;
-
+
// TODO: create separate trackers for each unique VPN to support
// automated reconnection
- private final VpnCallback mCallback;
-
- private String mPackage = VpnConfig.LEGACY_VPN;
+ private Context mContext;
+ private NetworkInfo mNetworkInfo;
+ private String mPackage;
+ private int mOwnerUID;
private String mInterface;
private Connection mConnection;
private LegacyVpnRunner mLegacyVpnRunner;
@@ -103,22 +111,29 @@ public class Vpn extends BaseNetworkStateTracker {
private volatile boolean mEnableNotif = true;
private volatile boolean mEnableTeardown = true;
private final IConnectivityManager mConnService;
+ private final INetworkManagementService mNetd;
private VpnConfig mConfig;
+ private NetworkAgent mNetworkAgent;
+ private final Looper mLooper;
+ private final NetworkCapabilities mNetworkCapabilities;
/* list of users using this VPN. */
@GuardedBy("this")
- private SparseBooleanArray mVpnUsers = null;
+ private List<UidRange> mVpnUsers = null;
private BroadcastReceiver mUserIntentReceiver = null;
private final int mUserId;
- public Vpn(Context context, VpnCallback callback, INetworkManagementService netService,
+ public Vpn(Looper looper, Context context, INetworkManagementService netService,
IConnectivityManager connService, int userId) {
- super(ConnectivityManager.TYPE_VPN);
mContext = context;
- mCallback = callback;
+ mNetd = netService;
mConnService = connService;
mUserId = userId;
+ mLooper = looper;
+
+ mPackage = VpnConfig.LEGACY_VPN;
+ mOwnerUID = getAppUid(mPackage);
try {
netService.registerObserver(mObserver);
@@ -149,6 +164,12 @@ public class Vpn extends BaseNetworkStateTracker {
mContext.registerReceiverAsUser(
mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
}
+
+ mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0, NETWORKTYPE, "");
+ // TODO: Copy metered attribute and bandwidths from physical transport, b/16207332
+ mNetworkCapabilities = new NetworkCapabilities();
+ mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
+ mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
}
/**
@@ -168,35 +189,15 @@ public class Vpn extends BaseNetworkStateTracker {
mEnableTeardown = enableTeardown;
}
- @Override
- protected void startMonitoringInternal() {
- // Ignored; events are sent through callbacks for now
- }
-
- @Override
- public boolean teardown() {
- // TODO: finish migration to unique tracker for each VPN
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean reconnect() {
- // TODO: finish migration to unique tracker for each VPN
- throw new UnsupportedOperationException();
- }
-
- @Override
- public String getTcpBufferSizesPropName() {
- return PROP_TCP_BUFFER_UNKNOWN;
- }
-
/**
* Update current state, dispaching event to listeners.
*/
private void updateState(DetailedState detailedState, String reason) {
if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason);
mNetworkInfo.setDetailedState(detailedState, reason, null);
- mCallback.onStateChanged(new NetworkInfo(mNetworkInfo));
+ if (mNetworkAgent != null) {
+ mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ }
}
/**
@@ -234,22 +235,10 @@ public class Vpn extends BaseNetworkStateTracker {
// Reset the interface and hide the notification.
if (mInterface != null) {
- final long token = Binder.clearCallingIdentity();
- try {
- mCallback.restore();
- final int size = mVpnUsers.size();
- final boolean forwardDns = (mConfig.dnsServers != null &&
- mConfig.dnsServers.size() != 0);
- for (int i = 0; i < size; i++) {
- int user = mVpnUsers.keyAt(i);
- mCallback.clearUserForwarding(mInterface, user, forwardDns);
- hideNotification(user);
- }
-
- mCallback.clearMarkedForwarding(mInterface);
- } finally {
- Binder.restoreCallingIdentity(token);
+ for (UidRange uidRange : mVpnUsers) {
+ hideNotification(uidRange.getStartUser());
}
+ agentDisconnect();
jniReset(mInterface);
mInterface = null;
mVpnUsers = null;
@@ -270,34 +259,125 @@ public class Vpn extends BaseNetworkStateTracker {
mLegacyVpnRunner = null;
}
+ long token = Binder.clearCallingIdentity();
+ try {
+ mNetd.denyProtect(mOwnerUID);
+ } catch (Exception e) {
+ Log.wtf(TAG, "Failed to disallow UID " + mOwnerUID + " to call protect() " + e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
mPackage = newPackage;
+ mOwnerUID = getAppUid(newPackage);
+ token = Binder.clearCallingIdentity();
+ try {
+ mNetd.allowProtect(mOwnerUID);
+ } catch (Exception e) {
+ Log.wtf(TAG, "Failed to allow UID " + mOwnerUID + " to call protect() " + e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
mConfig = null;
updateState(DetailedState.IDLE, "prepare");
return true;
}
- /**
- * Protect a socket from VPN rules by binding it to the main routing table.
- * The socket is NOT closed by this method.
- *
- * @param socket The socket to be bound.
- */
- public void protect(ParcelFileDescriptor socket) throws Exception {
-
+ private int getAppUid(String app) {
+ if (app == VpnConfig.LEGACY_VPN) {
+ return Process.myUid();
+ }
PackageManager pm = mContext.getPackageManager();
- int appUid = pm.getPackageUid(mPackage, mUserId);
- if (Binder.getCallingUid() != appUid) {
- throw new SecurityException("Unauthorized Caller");
+ int result;
+ try {
+ result = pm.getPackageUid(app, mUserId);
+ } catch (NameNotFoundException e) {
+ result = -1;
}
- // protect the socket from routing rules
- final long token = Binder.clearCallingIdentity();
+ return result;
+ }
+
+ public NetworkInfo getNetworkInfo() {
+ return mNetworkInfo;
+ }
+
+ private void agentConnect() {
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(mInterface);
+ boolean hasDefaultRoute = false;
+ for (RouteInfo route : mConfig.routes) {
+ lp.addRoute(route);
+ if (route.isDefaultRoute()) hasDefaultRoute = true;
+ }
+ if (hasDefaultRoute) {
+ mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ } else {
+ mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ }
+ if (mConfig.dnsServers != null) {
+ for (String dnsServer : mConfig.dnsServers) {
+ lp.addDnsServer(InetAddress.parseNumericAddress(dnsServer));
+ }
+ }
+ // Concatenate search domains into a string.
+ StringBuilder buffer = new StringBuilder();
+ if (mConfig.searchDomains != null) {
+ for (String domain : mConfig.searchDomains) {
+ buffer.append(domain).append(' ');
+ }
+ }
+ lp.setDomains(buffer.toString().trim());
+ mNetworkInfo.setIsAvailable(true);
+ mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
+ long token = Binder.clearCallingIdentity();
try {
- mCallback.protect(socket);
+ mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE,
+ mNetworkInfo, mNetworkCapabilities, lp, 0) {
+ public void unwanted() {
+ // We are user controlled, not driven by NetworkRequest.
+ };
+ };
} finally {
Binder.restoreCallingIdentity(token);
}
+ addVpnUserLocked(mUserId);
+ // If we are owner assign all Restricted Users to this VPN
+ if (mUserId == UserHandle.USER_OWNER) {
+ token = Binder.clearCallingIdentity();
+ List<UserInfo> users;
+ try {
+ users = UserManager.get(mContext).getUsers();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ for (UserInfo user : users) {
+ if (user.isRestricted()) {
+ addVpnUserLocked(user.id);
+ }
+ }
+ }
+ mNetworkAgent.addUidRanges(mVpnUsers.toArray(new UidRange[mVpnUsers.size()]));
+ }
+
+ private void agentDisconnect(NetworkInfo networkInfo, NetworkAgent networkAgent) {
+ networkInfo.setIsAvailable(false);
+ networkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
+ if (networkAgent != null) {
+ networkAgent.sendNetworkInfo(networkInfo);
+ }
+ }
+
+ private void agentDisconnect(NetworkAgent networkAgent) {
+ NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo);
+ agentDisconnect(networkInfo, networkAgent);
+ }
+ private void agentDisconnect() {
+ if (mNetworkInfo.isConnected()) {
+ agentDisconnect(mNetworkInfo, mNetworkAgent);
+ mNetworkAgent = null;
+ }
}
/**
@@ -311,14 +391,7 @@ public class Vpn extends BaseNetworkStateTracker {
public synchronized ParcelFileDescriptor establish(VpnConfig config) {
// Check if the caller is already prepared.
UserManager mgr = UserManager.get(mContext);
- PackageManager pm = mContext.getPackageManager();
- ApplicationInfo app = null;
- try {
- app = AppGlobals.getPackageManager().getApplicationInfo(mPackage, 0, mUserId);
- if (Binder.getCallingUid() != app.uid) {
- return null;
- }
- } catch (Exception e) {
+ if (Binder.getCallingUid() != mOwnerUID) {
return null;
}
// Check if the service is properly declared.
@@ -350,7 +423,9 @@ public class Vpn extends BaseNetworkStateTracker {
VpnConfig oldConfig = mConfig;
String oldInterface = mInterface;
Connection oldConnection = mConnection;
- SparseBooleanArray oldUsers = mVpnUsers;
+ NetworkAgent oldNetworkAgent = mNetworkAgent;
+ mNetworkAgent = null;
+ List<UidRange> oldUsers = mVpnUsers;
// Configure the interface. Abort if any of these steps fails.
ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
@@ -382,67 +457,27 @@ public class Vpn extends BaseNetworkStateTracker {
mConfig = config;
// Set up forwarding and DNS rules.
- mVpnUsers = new SparseBooleanArray();
- token = Binder.clearCallingIdentity();
- try {
- mCallback.setMarkedForwarding(mInterface);
- 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);
- }
+ mVpnUsers = new ArrayList<UidRange>();
+ agentConnect();
if (oldConnection != null) {
mContext.unbindService(oldConnection);
}
+ // 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.
+ agentDisconnect(oldNetworkAgent);
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);
- }
+ agentDisconnect();
// restore old state
mConfig = oldConfig;
mConnection = oldConnection;
mVpnUsers = oldUsers;
+ mNetworkAgent = oldNetworkAgent;
mInterface = oldInterface;
throw e;
}
@@ -469,29 +504,27 @@ public class Vpn extends BaseNetworkStateTracker {
return mVpnUsers != null;
}
+ // Note: This function adds to mVpnUsers but does not publish list to NetworkAgent.
private void addVpnUserLocked(int user) {
- enforceControlPermission();
-
if (!isRunningLocked()) {
throw new IllegalStateException("VPN is not active");
}
- final boolean forwardDns = (mConfig.dnsServers != null &&
- mConfig.dnsServers.size() != 0);
-
// add the user
- mCallback.addUserForwarding(mInterface, user, forwardDns);
- mVpnUsers.put(user, true);
+ mVpnUsers.add(UidRange.createForUser(user));
// show the notification
if (!mPackage.equals(VpnConfig.LEGACY_VPN)) {
// Load everything for the user's notification
PackageManager pm = mContext.getPackageManager();
ApplicationInfo app = null;
+ final long token = Binder.clearCallingIdentity();
try {
app = AppGlobals.getPackageManager().getApplicationInfo(mPackage, 0, mUserId);
} catch (RemoteException e) {
throw new IllegalStateException("Invalid application");
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
String label = app.loadLabel(pm).toString();
// Load the icon and convert it into a bitmap.
@@ -515,15 +548,14 @@ public class Vpn extends BaseNetworkStateTracker {
}
private void removeVpnUserLocked(int user) {
- enforceControlPermission();
-
if (!isRunningLocked()) {
throw new IllegalStateException("VPN is not active");
}
- final boolean forwardDns = (mConfig.dnsServers != null &&
- mConfig.dnsServers.size() != 0);
- mCallback.clearUserForwarding(mInterface, user, forwardDns);
- mVpnUsers.delete(user);
+ UidRange uidRange = UidRange.createForUser(user);
+ if (mNetworkAgent != null) {
+ mNetworkAgent.removeUidRanges(new UidRange[] { uidRange });
+ }
+ mVpnUsers.remove(uidRange);
hideNotification(user);
}
@@ -535,6 +567,10 @@ public class Vpn extends BaseNetworkStateTracker {
if (user.isRestricted()) {
try {
addVpnUserLocked(userId);
+ if (mNetworkAgent != null) {
+ UidRange uidRange = UidRange.createForUser(userId);
+ mNetworkAgent.addUidRanges(new UidRange[] { uidRange });
+ }
} catch (Exception e) {
Log.wtf(TAG, "Failed to add restricted user to owner", e);
}
@@ -588,28 +624,15 @@ public class Vpn extends BaseNetworkStateTracker {
public void interfaceRemoved(String interfaze) {
synchronized (Vpn.this) {
if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
- final long token = Binder.clearCallingIdentity();
- try {
- final int size = mVpnUsers.size();
- final boolean forwardDns = (mConfig.dnsServers != null &&
- mConfig.dnsServers.size() != 0);
- for (int i = 0; i < size; i++) {
- int user = mVpnUsers.keyAt(i);
- mCallback.clearUserForwarding(mInterface, user, forwardDns);
- hideNotification(user);
- }
- mVpnUsers = null;
- mCallback.clearMarkedForwarding(mInterface);
-
- mCallback.restore();
- } finally {
- Binder.restoreCallingIdentity(token);
+ for (UidRange uidRange : mVpnUsers) {
+ hideNotification(uidRange.getStartUser());
}
+ mVpnUsers = null;
mInterface = null;
if (mConnection != null) {
mContext.unbindService(mConnection);
mConnection = null;
- updateState(DetailedState.DISCONNECTED, "interfaceRemoved");
+ agentDisconnect();
} else if (mLegacyVpnRunner != null) {
mLegacyVpnRunner.exit();
mLegacyVpnRunner = null;
@@ -658,27 +681,32 @@ public class Vpn extends BaseNetworkStateTracker {
private void showNotification(String label, Bitmap icon, int user) {
if (!mEnableNotif) return;
- mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext);
-
- NotificationManager nm = (NotificationManager)
- mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-
- if (nm != null) {
- String title = (label == null) ? mContext.getString(R.string.vpn_title) :
- mContext.getString(R.string.vpn_title_long, label);
- String text = (mConfig.session == null) ? mContext.getString(R.string.vpn_text) :
- mContext.getString(R.string.vpn_text_long, mConfig.session);
-
- Notification notification = new Notification.Builder(mContext)
- .setSmallIcon(R.drawable.vpn_connected)
- .setLargeIcon(icon)
- .setContentTitle(title)
- .setContentText(text)
- .setContentIntent(mStatusIntent)
- .setDefaults(0)
- .setOngoing(true)
- .build();
- nm.notifyAsUser(null, R.drawable.vpn_connected, notification, new UserHandle(user));
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext);
+
+ NotificationManager nm = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+
+ if (nm != null) {
+ String title = (label == null) ? mContext.getString(R.string.vpn_title) :
+ mContext.getString(R.string.vpn_title_long, label);
+ String text = (mConfig.session == null) ? mContext.getString(R.string.vpn_text) :
+ mContext.getString(R.string.vpn_text_long, mConfig.session);
+
+ Notification notification = new Notification.Builder(mContext)
+ .setSmallIcon(R.drawable.vpn_connected)
+ .setLargeIcon(icon)
+ .setContentTitle(title)
+ .setContentText(text)
+ .setContentIntent(mStatusIntent)
+ .setDefaults(0)
+ .setOngoing(true)
+ .build();
+ nm.notifyAsUser(null, R.drawable.vpn_connected, notification, new UserHandle(user));
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
@@ -690,14 +718,18 @@ public class Vpn extends BaseNetworkStateTracker {
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
if (nm != null) {
- nm.cancelAsUser(null, R.drawable.vpn_connected, new UserHandle(user));
+ final long token = Binder.clearCallingIdentity();
+ try {
+ nm.cancelAsUser(null, R.drawable.vpn_connected, new UserHandle(user));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
}
private native int jniCreate(int mtu);
private native String jniGetName(int tun);
private native int jniSetAddresses(String interfaze, String addresses);
- private native int jniSetRoutes(String interfaze, String routes);
private native void jniReset(String interfaze);
private native int jniCheck(String interfaze);
@@ -959,7 +991,7 @@ public class Vpn extends BaseNetworkStateTracker {
for (LocalSocket socket : mSockets) {
IoUtils.closeQuietly(socket);
}
- updateState(DetailedState.DISCONNECTED, "exit");
+ agentDisconnect();
try {
mContext.unregisterReceiver(mBroadcastReceiver);
} catch (IllegalArgumentException e) {}
@@ -1018,7 +1050,7 @@ public class Vpn extends BaseNetworkStateTracker {
restart = restart || (arguments != null);
}
if (!restart) {
- updateState(DetailedState.DISCONNECTED, "execute");
+ agentDisconnect();
return;
}
updateState(DetailedState.CONNECTING, "execute");
@@ -1129,15 +1161,6 @@ public class Vpn extends BaseNetworkStateTracker {
}
}
- // Set the routes.
- long token = Binder.clearCallingIdentity();
- try {
- mCallback.setMarkedForwarding(mConfig.interfaze);
- mCallback.setRoutes(mConfig.interfaze, mConfig.routes);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
-
// Here is the last step and it must be done synchronously.
synchronized (Vpn.this) {
// Set the start time
@@ -1153,44 +1176,14 @@ public class Vpn extends BaseNetworkStateTracker {
// Now INetworkManagementEventObserver is watching our back.
mInterface = mConfig.interfaze;
- mVpnUsers = new SparseBooleanArray();
-
- token = Binder.clearCallingIdentity();
- try {
- mCallback.override(mInterface, mConfig.dnsServers, mConfig.searchDomains);
- addVpnUserLocked(mUserId);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ mVpnUsers = new ArrayList<UidRange>();
+
+ agentConnect();
- // Assign all restircted users to this VPN
- // (Legacy VPNs are Owner only)
- UserManager mgr = UserManager.get(mContext);
- 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);
- }
Log.i(TAG, "Connected!");
- updateState(DetailedState.CONNECTED, "execute");
}
} catch (Exception e) {
Log.i(TAG, "Aborting", e);
- // make sure the routing is cleared
- try {
- mCallback.clearMarkedForwarding(mConfig.interfaze);
- } catch (Exception ignored) {
- }
exit();
} finally {
// Kill the daemons if they fail to stop.
@@ -1202,7 +1195,7 @@ public class Vpn extends BaseNetworkStateTracker {
// Do not leave an unstable state.
if (!initFinished || mNetworkInfo.getDetailedState() == DetailedState.CONNECTING) {
- updateState(DetailedState.FAILED, "execute");
+ agentDisconnect();
}
}
}
@@ -1232,7 +1225,7 @@ public class Vpn extends BaseNetworkStateTracker {
SystemService.stop(daemon);
}
- updateState(DetailedState.DISCONNECTED, "babysit");
+ agentDisconnect();
}
}
}
diff --git a/services/core/jni/com_android_server_connectivity_Vpn.cpp b/services/core/jni/com_android_server_connectivity_Vpn.cpp
index bf34a74..6031906 100644
--- a/services/core/jni/com_android_server_connectivity_Vpn.cpp
+++ b/services/core/jni/com_android_server_connectivity_Vpn.cpp
@@ -187,96 +187,6 @@ static int set_addresses(const char *name, const char *addresses)
return count;
}
-static int set_routes(const char *name, const char *routes)
-{
- int index = get_interface_index(name);
- if (index < 0) {
- return index;
- }
-
- rtentry rt4;
- memset(&rt4, 0, sizeof(rt4));
- rt4.rt_dev = (char *)name;
- rt4.rt_flags = RTF_UP;
- rt4.rt_dst.sa_family = AF_INET;
- rt4.rt_genmask.sa_family = AF_INET;
-
- in6_rtmsg rt6;
- memset(&rt6, 0, sizeof(rt6));
- rt6.rtmsg_ifindex = index;
- rt6.rtmsg_flags = RTF_UP;
-
- char address[65];
- int prefix;
- int chars;
- int count = 0;
-
- while (sscanf(routes, " %64[^/]/%d %n", address, &prefix, &chars) == 2) {
- routes += chars;
-
- if (strchr(address, ':')) {
- // Add an IPv6 route.
- if (inet_pton(AF_INET6, address, &rt6.rtmsg_dst) != 1 ||
- prefix < 0 || prefix > 128) {
- count = BAD_ARGUMENT;
- break;
- }
-
- rt6.rtmsg_dst_len = prefix ? prefix : 1;
- if (ioctl(inet6, SIOCADDRT, &rt6) && errno != EEXIST) {
- count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
- break;
- }
-
- if (!prefix) {
- // Split the route instead of replacing the default route.
- rt6.rtmsg_dst.s6_addr[0] ^= 0x80;
- if (ioctl(inet6, SIOCADDRT, &rt6) && errno != EEXIST) {
- count = SYSTEM_ERROR;
- break;
- }
- }
- } else {
- // Add an IPv4 route.
- if (inet_pton(AF_INET, address, as_in_addr(&rt4.rt_dst)) != 1 ||
- prefix < 0 || prefix > 32) {
- count = BAD_ARGUMENT;
- break;
- }
-
- in_addr_t mask = prefix ? (~0 << (32 - prefix)) : 0x80000000;
- *as_in_addr(&rt4.rt_genmask) = htonl(mask);
- if (ioctl(inet4, SIOCADDRT, &rt4) && errno != EEXIST) {
- count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
- break;
- }
-
- if (!prefix) {
- // Split the route instead of replacing the default route.
- *as_in_addr(&rt4.rt_dst) ^= htonl(0x80000000);
- if (ioctl(inet4, SIOCADDRT, &rt4) && errno != EEXIST) {
- count = SYSTEM_ERROR;
- break;
- }
- }
- }
- ALOGD("Route added on %s: %s/%d", name, address, prefix);
- ++count;
- }
-
- if (count == BAD_ARGUMENT) {
- ALOGE("Invalid route: %s/%d", address, prefix);
- } else if (count == SYSTEM_ERROR) {
- ALOGE("Cannot add route: %s/%d: %s",
- address, prefix, strerror(errno));
- } else if (*routes) {
- ALOGE("Invalid route: %s", routes);
- count = BAD_ARGUMENT;
- }
-
- return count;
-}
-
static int reset_interface(const char *name)
{
ifreq ifr4;
@@ -366,39 +276,6 @@ error:
return count;
}
-static jint setRoutes(JNIEnv *env, jobject thiz, jstring jName,
- jstring jRoutes)
-{
- const char *name = NULL;
- const char *routes = NULL;
- int count = -1;
-
- name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
- if (!name) {
- jniThrowNullPointerException(env, "name");
- goto error;
- }
- routes = jRoutes ? env->GetStringUTFChars(jRoutes, NULL) : NULL;
- if (!routes) {
- jniThrowNullPointerException(env, "routes");
- goto error;
- }
- count = set_routes(name, routes);
- if (count < 0) {
- throwException(env, count, "Cannot set route");
- count = -1;
- }
-
-error:
- if (name) {
- env->ReleaseStringUTFChars(jName, name);
- }
- if (routes) {
- env->ReleaseStringUTFChars(jRoutes, routes);
- }
- return count;
-}
-
static void reset(JNIEnv *env, jobject thiz, jstring jName)
{
const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
@@ -430,7 +307,6 @@ static JNINativeMethod gMethods[] = {
{"jniCreate", "(I)I", (void *)create},
{"jniGetName", "(I)Ljava/lang/String;", (void *)getName},
{"jniSetAddresses", "(Ljava/lang/String;Ljava/lang/String;)I", (void *)setAddresses},
- {"jniSetRoutes", "(Ljava/lang/String;Ljava/lang/String;)I", (void *)setRoutes},
{"jniReset", "(Ljava/lang/String;)V", (void *)reset},
{"jniCheck", "(Ljava/lang/String;)I", (void *)check},
};