Switch to use netd to add/remove routes.
Also adds support for v6 routes and for removing single routes.
Change-Id: I1c4f08c7938371090944d8d6f603e1e0d6d70c01
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index f88b188..b923afc 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -26,6 +26,7 @@
import android.net.DummyDataStateTracker;
import android.net.EthernetDataTracker;
import android.net.IConnectivityManager;
+import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.MobileDataStateTracker;
import android.net.NetworkConfig;
@@ -41,6 +42,7 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
@@ -60,6 +62,7 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.net.InetAddress;
+import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
@@ -124,6 +127,8 @@
private AtomicBoolean mBackgroundDataEnabled = new AtomicBoolean(true);
+ private INetworkManagementService mNetd;
+
private static final int ENABLED = 1;
private static final int DISABLED = 0;
@@ -939,10 +944,6 @@
* @return {@code true} on success, {@code false} on failure
*/
private boolean addHostRoute(NetworkStateTracker nt, InetAddress hostAddress, int cycleCount) {
- if (nt.getNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI) {
- return false;
- }
-
LinkProperties lp = nt.getLinkProperties();
if ((lp == null) || (hostAddress == null)) return false;
@@ -957,20 +958,28 @@
}
RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getRoutes(), hostAddress);
- InetAddress gateway = null;
+ InetAddress gatewayAddress = null;
if (bestRoute != null) {
- gateway = bestRoute.getGateway();
+ gatewayAddress = bestRoute.getGateway();
// if the best route is ourself, don't relf-reference, just add the host route
- if (hostAddress.equals(gateway)) gateway = null;
+ if (hostAddress.equals(gatewayAddress)) gatewayAddress = null;
}
- if (gateway != null) {
+ if (gatewayAddress != null) {
if (cycleCount > MAX_HOSTROUTE_CYCLE_COUNT) {
loge("Error adding hostroute - too much recursion");
return false;
}
- if (!addHostRoute(nt, gateway, cycleCount+1)) return false;
+ if (!addHostRoute(nt, gatewayAddress, cycleCount+1)) return false;
}
- return NetworkUtils.addHostRoute(interfaceName, hostAddress, gateway);
+
+ RouteInfo route = RouteInfo.makeHostRoute(hostAddress, gatewayAddress);
+
+ try {
+ mNetd.addRoute(interfaceName, route);
+ return true;
+ } catch (Exception ex) {
+ return false;
+ }
}
// TODO support the removal of single host routes. Keep a ref count of them so we
@@ -1297,6 +1306,9 @@
}
void systemReady() {
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ mNetd = INetworkManagementService.Stub.asInterface(b);
+
synchronized(this) {
mSystemReady = true;
if (mInitialBroadcast != null) {
@@ -1419,7 +1431,6 @@
if (interfaceName != null && !privateDnsRouteSet) {
Collection<InetAddress> dnsList = p.getDnses();
for (InetAddress dns : dnsList) {
- if (DBG) log(" adding " + dns);
addHostRoute(nt, dns, 0);
}
nt.privateDnsRouteSet(true);
@@ -1427,8 +1438,6 @@
}
private void removePrivateDnsRoutes(NetworkStateTracker nt) {
- // TODO - we should do this explicitly but the NetUtils api doesnt
- // support this yet - must remove all. No worse than before
LinkProperties p = nt.getLinkProperties();
if (p == null) return;
String interfaceName = p.getInterfaceName();
@@ -1438,7 +1447,17 @@
log("removePrivateDnsRoutes for " + nt.getNetworkInfo().getTypeName() +
" (" + interfaceName + ")");
}
- NetworkUtils.removeHostRoutes(interfaceName);
+
+ Collection<InetAddress> dnsList = p.getDnses();
+ for (InetAddress dns : dnsList) {
+ if (DBG) log(" removing " + dns);
+ RouteInfo route = RouteInfo.makeHostRoute(dns);
+ try {
+ mNetd.removeRoute(interfaceName, route);
+ } catch (Exception ex) {
+ loge("error (" + ex + ") removing dns route " + route);
+ }
+ }
nt.privateDnsRouteSet(false);
}
}
@@ -1449,19 +1468,27 @@
if (p == null) return;
String interfaceName = p.getInterfaceName();
if (TextUtils.isEmpty(interfaceName)) return;
- for (RouteInfo route : p.getRoutes()) {
+ for (RouteInfo route : p.getRoutes()) {
//TODO - handle non-default routes
if (route.isDefaultRoute()) {
+ if (DBG) log("adding default route " + route);
InetAddress gateway = route.getGateway();
- if (addHostRoute(nt, gateway, 0) &&
- NetworkUtils.addDefaultRoute(interfaceName, gateway)) {
+ if (addHostRoute(nt, gateway, 0)) {
+ try {
+ mNetd.addRoute(interfaceName, route);
+ } catch (Exception e) {
+ loge("error adding default route " + route);
+ continue;
+ }
if (DBG) {
NetworkInfo networkInfo = nt.getNetworkInfo();
log("addDefaultRoute for " + networkInfo.getTypeName() +
" (" + interfaceName + "), GatewayAddr=" +
gateway.getHostAddress());
}
+ } else {
+ loge("error adding host route for default route " + route);
}
}
}
@@ -1473,8 +1500,17 @@
if (p == null) return;
String interfaceName = p.getInterfaceName();
- if (interfaceName != null) {
- if (NetworkUtils.removeDefaultRoute(interfaceName) >= 0) {
+ if (interfaceName == null) return;
+
+ for (RouteInfo route : p.getRoutes()) {
+ //TODO - handle non-default routes
+ if (route.isDefaultRoute()) {
+ try {
+ mNetd.removeRoute(interfaceName, route);
+ } catch (Exception ex) {
+ loge("error (" + ex + ") removing default route " + route);
+ continue;
+ }
if (DBG) {
NetworkInfo networkInfo = nt.getNetworkInfo();
log("removeDefaultRoute for " + networkInfo.getTypeName() + " (" +
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 44f5df2..0b4b958 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -28,6 +28,7 @@
import android.net.INetworkManagementEventObserver;
import android.net.LinkAddress;
import android.net.NetworkUtils;
+import android.net.RouteInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.os.INetworkManagementService;
@@ -43,11 +44,16 @@
import android.content.ContentResolver;
import android.database.ContentObserver;
+import java.io.BufferedReader;
+import java.io.DataInputStream;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileReader;
+import java.io.InputStreamReader;
+import java.io.IOException;
import java.lang.IllegalStateException;
-
import java.net.InetAddress;
+import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.util.concurrent.CountDownLatch;
@@ -60,6 +66,9 @@
private static final boolean DBG = false;
private static final String NETD_TAG = "NetdConnector";
+ private static final int ADD = 1;
+ private static final int REMOVE = 2;
+
class NetdResponseCode {
public static final int InterfaceListResult = 110;
public static final int TetherInterfaceListResult = 111;
@@ -309,6 +318,164 @@
}
}
+ public void addRoute(String interfaceName, RouteInfo route) {
+ modifyRoute(interfaceName, ADD, route);
+ }
+
+ public void removeRoute(String interfaceName, RouteInfo route) {
+ modifyRoute(interfaceName, REMOVE, route);
+ }
+
+ private void modifyRoute(String interfaceName, int action, RouteInfo route) {
+ ArrayList<String> rsp;
+
+ StringBuilder cmd;
+
+ switch (action) {
+ case ADD:
+ {
+ cmd = new StringBuilder("interface route add " + interfaceName);
+ break;
+ }
+ case REMOVE:
+ {
+ cmd = new StringBuilder("interface route remove " + interfaceName);
+ break;
+ }
+ default:
+ throw new IllegalStateException("Unknown action type " + action);
+ }
+
+ // create triplet: dest-ip-addr prefixlength gateway-ip-addr
+ LinkAddress la = route.getDestination();
+ cmd.append(' ');
+ cmd.append(la.getAddress().getHostAddress());
+ cmd.append(' ');
+ cmd.append(la.getNetworkPrefixLength());
+ cmd.append(' ');
+ if (route.getGateway() == null) {
+ if (la.getAddress() instanceof Inet4Address) {
+ cmd.append("0.0.0.0");
+ } else {
+ cmd.append ("::0");
+ }
+ } else {
+ cmd.append(route.getGateway().getHostAddress());
+ }
+ try {
+ rsp = mConnector.doCommand(cmd.toString());
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate with native dameon to add routes - "
+ + e);
+ }
+
+ for (String line : rsp) {
+ Log.v(TAG, "add route response is " + line);
+ }
+ }
+
+ private ArrayList<String> readRouteList(String filename) {
+ FileInputStream fstream = null;
+ ArrayList<String> list = new ArrayList<String>();
+
+ try {
+ fstream = new FileInputStream(filename);
+ DataInputStream in = new DataInputStream(fstream);
+ BufferedReader br = new BufferedReader(new InputStreamReader(in));
+ String s;
+
+ // throw away the title line
+
+ while (((s = br.readLine()) != null) && (s.length() != 0)) {
+ list.add(s);
+ }
+ } catch (IOException ex) {
+ // return current list, possibly empty
+ } finally {
+ if (fstream != null) {
+ try {
+ fstream.close();
+ } catch (IOException ex) {}
+ }
+ }
+
+ return list;
+ }
+
+ public RouteInfo[] getRoutes(String interfaceName) {
+ ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>();
+
+ // v4 routes listed as:
+ // iface dest-addr gateway-addr flags refcnt use metric netmask mtu window IRTT
+ for (String s : readRouteList("/proc/net/route")) {
+ String[] fields = s.split("\t");
+
+ if (fields.length > 7) {
+ String iface = fields[0];
+
+ if (interfaceName.equals(iface)) {
+ String dest = fields[1];
+ String gate = fields[2];
+ String flags = fields[3]; // future use?
+ String mask = fields[7];
+ try {
+ // address stored as a hex string, ex: 0014A8C0
+ InetAddress destAddr =
+ NetworkUtils.intToInetAddress((int)Long.parseLong(dest, 16));
+ int prefixLength =
+ NetworkUtils.netmaskIntToPrefixLength(
+ (int)Long.parseLong(mask, 16));
+ LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
+
+ // address stored as a hex string, ex 0014A8C0
+ InetAddress gatewayAddr =
+ NetworkUtils.intToInetAddress((int)Long.parseLong(gate, 16));
+
+ RouteInfo route = new RouteInfo(linkAddress, gatewayAddr);
+ routes.add(route);
+ } catch (Exception e) {
+ Log.e(TAG, "Error parsing route " + s + " : " + e);
+ continue;
+ }
+ }
+ }
+ }
+
+ // v6 routes listed as:
+ // dest-addr prefixlength ?? ?? gateway-addr ?? ?? ?? ?? iface
+ for (String s : readRouteList("/proc/net/ipv6_route")) {
+ String[]fields = s.split("\\s+");
+ if (fields.length > 9) {
+ String iface = fields[9].trim();
+ if (interfaceName.equals(iface)) {
+ String dest = fields[0];
+ String prefix = fields[1];
+ String gate = fields[4];
+
+ try {
+ // prefix length stored as a hex string, ex 40
+ int prefixLength = Integer.parseInt(prefix, 16);
+
+ // address stored as a 32 char hex string
+ // ex fe800000000000000000000000000000
+ InetAddress destAddr = NetworkUtils.hexToInet6Address(dest);
+ LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
+
+ InetAddress gateAddr = NetworkUtils.hexToInet6Address(gate);
+
+ RouteInfo route = new RouteInfo(linkAddress, gateAddr);
+ routes.add(route);
+ } catch (Exception e) {
+ Log.e(TAG, "Error parsing route " + s + " : " + e);
+ continue;
+ }
+ }
+ }
+ }
+ return (RouteInfo[]) routes.toArray(new RouteInfo[0]);
+ }
+
public void shutdown() {
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.SHUTDOWN)