Prohibit address families by default unless a VPN explicitly allows them.
Bug: 15972465
Change-Id: I3278d94536fefacc86390c1ba4231680f7be8589
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index a365af0..22da90e 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -106,6 +106,20 @@
*/
public static final int EVENT_UID_RANGES_REMOVED = BASE + 6;
+ /**
+ * Sent by the NetworkAgent to ConnectivityService to block all routes for a certain address
+ * family (AF_INET or AF_INET6) on this Network. For VPNs only.
+ * obj = Integer representing the family (AF_INET or AF_INET6)
+ */
+ public static final int EVENT_BLOCK_ADDRESS_FAMILY = BASE + 7;
+
+ /**
+ * Sent by the NetworkAgent to ConnectivityService to unblock routes for a certain address
+ * family (AF_INET or AF_INET6) on this Network. For VPNs only.
+ * obj = Integer representing the family (AF_INET or AF_INET6)
+ */
+ public static final int EVENT_UNBLOCK_ADDRESS_FAMILY = BASE + 8;
+
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score) {
this(looper, context, logTag, ni, nc, lp, score, null);
@@ -229,6 +243,21 @@
}
/**
+ * Called by the VPN code when it wants to block an address family from being routed, typically
+ * because the VPN network doesn't support that family.
+ */
+ public void blockAddressFamily(int family) {
+ queueOrSendMessage(EVENT_BLOCK_ADDRESS_FAMILY, family);
+ }
+
+ /**
+ * Called by the VPN code when it wants to unblock an address family from being routed.
+ */
+ public void unblockAddressFamily(int family) {
+ queueOrSendMessage(EVENT_UNBLOCK_ADDRESS_FAMILY, family);
+ }
+
+ /**
* 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/VpnService.java b/core/java/android/net/VpnService.java
index 4b07e3f..9b66997 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -373,6 +373,7 @@
throw new IllegalArgumentException("Bad address");
}
mAddresses.add(new LinkAddress(address, prefixLength));
+ mConfig.updateAllowedFamilies(address);
return this;
}
@@ -413,6 +414,7 @@
}
}
mRoutes.add(new RouteInfo(new LinkAddress(address, prefixLength), null));
+ mConfig.updateAllowedFamilies(address);
return this;
}
@@ -497,7 +499,14 @@
* @return this {@link Builder} object to facilitate chaining of method calls.
*/
public Builder allowFamily(int family) {
- // TODO
+ if (family == AF_INET) {
+ mConfig.allowIPv4 = true;
+ } else if (family == AF_INET6) {
+ mConfig.allowIPv6 = true;
+ } else {
+ throw new IllegalArgumentException(family + " is neither " + AF_INET + " nor " +
+ AF_INET6);
+ }
return this;
}
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index c467d4a..d6ee8e0 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -404,4 +404,7 @@
void addInterfaceToLocalNetwork(String iface, in List<RouteInfo> routes);
void removeInterfaceFromLocalNetwork(String iface);
+
+ void blockAddressFamily(int family, int netId, String iface);
+ void unblockAddressFamily(int family, int netId, String iface);
}
diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java
index dac59f9..0099269 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -27,6 +27,7 @@
import android.net.RouteInfo;
import android.net.LinkAddress;
+import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.List;
import java.util.ArrayList;
@@ -75,6 +76,16 @@
public boolean legacy;
public boolean blocking;
public boolean allowBypass;
+ public boolean allowIPv4;
+ public boolean allowIPv6;
+
+ public void updateAllowedFamilies(InetAddress address) {
+ if (address instanceof Inet4Address) {
+ allowIPv4 = true;
+ } else {
+ allowIPv6 = true;
+ }
+ }
public void addLegacyRoutes(String routesStr) {
if (routesStr.trim().equals("")) {
@@ -87,6 +98,7 @@
RouteInfo info = new RouteInfo(new LinkAddress
(InetAddress.parseNumericAddress(split[0]), Integer.parseInt(split[1])), null);
this.routes.add(info);
+ updateAllowedFamilies(info.getDestination().getAddress());
}
}
@@ -101,6 +113,7 @@
LinkAddress addr = new LinkAddress(InetAddress.parseNumericAddress(split[0]),
Integer.parseInt(split[1]));
this.addresses.add(addr);
+ updateAllowedFamilies(addr.getAddress());
}
}
@@ -124,6 +137,8 @@
out.writeInt(legacy ? 1 : 0);
out.writeInt(blocking ? 1 : 0);
out.writeInt(allowBypass ? 1 : 0);
+ out.writeInt(allowIPv4 ? 1 : 0);
+ out.writeInt(allowIPv6 ? 1 : 0);
}
public static final Parcelable.Creator<VpnConfig> CREATOR =
@@ -144,6 +159,8 @@
config.legacy = in.readInt() != 0;
config.blocking = in.readInt() != 0;
config.allowBypass = in.readInt() != 0;
+ config.allowIPv4 = in.readInt() != 0;
+ config.allowIPv6 = in.readInt() != 0;
return config;
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 56265e1..aa7501b 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2940,7 +2940,9 @@
}
try {
mNetd.addVpnUidRanges(nai.network.netId, (UidRange[])msg.obj);
- } catch (RemoteException e) {
+ } catch (Exception e) {
+ // Never crash!
+ loge("Exception in addVpnUidRanges: " + e);
}
break;
}
@@ -2952,7 +2954,39 @@
}
try {
mNetd.removeVpnUidRanges(nai.network.netId, (UidRange[])msg.obj);
- } catch (RemoteException e) {
+ } catch (Exception e) {
+ // Never crash!
+ loge("Exception in removeVpnUidRanges: " + e);
+ }
+ break;
+ }
+ case NetworkAgent.EVENT_BLOCK_ADDRESS_FAMILY: {
+ NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+ if (nai == null) {
+ loge("EVENT_BLOCK_ADDRESS_FAMILY from unknown NetworkAgent");
+ break;
+ }
+ try {
+ mNetd.blockAddressFamily((Integer) msg.obj, nai.network.netId,
+ nai.linkProperties.getInterfaceName());
+ } catch (Exception e) {
+ // Never crash!
+ loge("Exception in blockAddressFamily: " + e);
+ }
+ break;
+ }
+ case NetworkAgent.EVENT_UNBLOCK_ADDRESS_FAMILY: {
+ NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+ if (nai == null) {
+ loge("EVENT_UNBLOCK_ADDRESS_FAMILY from unknown NetworkAgent");
+ break;
+ }
+ try {
+ mNetd.unblockAddressFamily((Integer) msg.obj, nai.network.netId,
+ nai.linkProperties.getInterfaceName());
+ } catch (Exception e) {
+ // Never crash!
+ loge("Exception in blockAddressFamily: " + e);
}
break;
}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 75a7878..362a745 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -25,6 +25,8 @@
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.TrafficStats.UID_TETHERING;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
import static com.android.server.NetworkManagementService.NetdResponseCode.ClatdStatusResult;
import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceGetCfgResult;
import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceListResult;
@@ -85,6 +87,7 @@
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
@@ -2102,4 +2105,36 @@
public void removeInterfaceFromLocalNetwork(String iface) {
modifyInterfaceInNetwork("remove", "local", iface);
}
+
+ @Override
+ public void blockAddressFamily(int family, int netId, String iface) {
+ modifyAddressFamily("add", family, netId, iface);
+ }
+
+ @Override
+ public void unblockAddressFamily(int family, int netId, String iface) {
+ modifyAddressFamily("remove", family, netId, iface);
+ }
+
+ private void modifyAddressFamily(String action, int family, int netId, String iface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+
+ final Command cmd = new Command("network", "route", action, netId, iface);
+
+ if (family == AF_INET) {
+ cmd.appendArg(Inet4Address.ANY.getHostAddress() + "/0");
+ } else if (family == AF_INET6) {
+ cmd.appendArg(Inet6Address.ANY.getHostAddress() + "/0");
+ } else {
+ throw new IllegalStateException(family + " is neither " + AF_INET + " nor " + AF_INET6);
+ }
+
+ cmd.appendArg("unreachable");
+
+ try {
+ mConnector.execute(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 0305424..0a8ca6a 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -17,6 +17,8 @@
package com.android.server.connectivity;
import static android.Manifest.permission.BIND_VPN_SERVICE;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
import android.app.AppGlobals;
import android.app.Notification;
@@ -107,6 +109,8 @@
private String mPackage;
private int mOwnerUID;
private String mInterface;
+ private boolean mAllowIPv4;
+ private boolean mAllowIPv6;
private Connection mConnection;
private LegacyVpnRunner mLegacyVpnRunner;
private PendingIntent mStatusIntent;
@@ -307,6 +311,7 @@
private void agentConnect() {
LinkProperties lp = new LinkProperties();
lp.setInterfaceName(mInterface);
+
boolean hasDefaultRoute = false;
for (RouteInfo route : mConfig.routes) {
lp.addRoute(route);
@@ -317,11 +322,19 @@
} else {
mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
}
+
if (mConfig.dnsServers != null) {
for (String dnsServer : mConfig.dnsServers) {
- lp.addDnsServer(InetAddress.parseNumericAddress(dnsServer));
+ InetAddress address = InetAddress.parseNumericAddress(dnsServer);
+ lp.addDnsServer(address);
+ if (address instanceof Inet4Address) {
+ mAllowIPv4 = true;
+ } else {
+ mAllowIPv6 = true;
+ }
}
}
+
// Concatenate search domains into a string.
StringBuilder buffer = new StringBuilder();
if (mConfig.searchDomains != null) {
@@ -330,12 +343,13 @@
}
}
lp.setDomains(buffer.toString().trim());
+
mNetworkInfo.setIsAvailable(true);
mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
+
NetworkMisc networkMisc = new NetworkMisc();
- if (mConfig.allowBypass) {
- networkMisc.allowBypass = true;
- }
+ networkMisc.allowBypass = mConfig.allowBypass;
+
long token = Binder.clearCallingIdentity();
try {
mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE,
@@ -347,6 +361,14 @@
} finally {
Binder.restoreCallingIdentity(token);
}
+
+ if (!mAllowIPv4) {
+ mNetworkAgent.blockAddressFamily(AF_INET);
+ }
+ if (!mAllowIPv6) {
+ mNetworkAgent.blockAddressFamily(AF_INET6);
+ }
+
addVpnUserLocked(mUserId);
// If we are owner assign all Restricted Users to this VPN
if (mUserId == UserHandle.USER_OWNER) {
@@ -432,6 +454,8 @@
NetworkAgent oldNetworkAgent = mNetworkAgent;
mNetworkAgent = null;
List<UidRange> oldUsers = mVpnUsers;
+ boolean oldAllowIPv4 = mAllowIPv4;
+ boolean oldAllowIPv6 = mAllowIPv6;
// Configure the interface. Abort if any of these steps fails.
ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
@@ -464,6 +488,9 @@
// Set up forwarding and DNS rules.
mVpnUsers = new ArrayList<UidRange>();
+ mAllowIPv4 = mConfig.allowIPv4;
+ mAllowIPv6 = mConfig.allowIPv6;
+
agentConnect();
if (oldConnection != null) {
@@ -492,6 +519,8 @@
mVpnUsers = oldUsers;
mNetworkAgent = oldNetworkAgent;
mInterface = oldInterface;
+ mAllowIPv4 = oldAllowIPv4;
+ mAllowIPv6 = oldAllowIPv6;
throw e;
}
Log.i(TAG, "Established by " + config.user + " on " + mInterface);
@@ -1175,6 +1204,8 @@
// Now INetworkManagementEventObserver is watching our back.
mInterface = mConfig.interfaze;
mVpnUsers = new ArrayList<UidRange>();
+ mAllowIPv4 = mConfig.allowIPv4;
+ mAllowIPv6 = mConfig.allowIPv6;
agentConnect();