diff options
-rw-r--r-- | core/java/android/net/NetworkUtils.java | 46 | ||||
-rw-r--r-- | core/java/android/net/RouteInfo.java | 67 | ||||
-rw-r--r-- | services/java/com/android/server/ConnectivityService.java | 47 |
3 files changed, 132 insertions, 28 deletions
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java index 823d10f..fbe5379 100644 --- a/core/java/android/net/NetworkUtils.java +++ b/core/java/android/net/NetworkUtils.java @@ -225,4 +225,50 @@ public class NetworkUtils { } return addRoute(interfaceName, dstStr, prefixLength, gwStr) == 0; } + + /** + * Get InetAddress masked with prefixLength. Will never return null. + * @param IP address which will be masked with specified prefixLength + * @param prefixLength the prefixLength used to mask the IP + */ + public static InetAddress getNetworkPart(InetAddress address, int prefixLength) { + if (address == null) { + throw new RuntimeException("getNetworkPart doesn't accept null address"); + } + + byte[] array = address.getAddress(); + + if (prefixLength < 0 || prefixLength > array.length * 8) { + throw new RuntimeException("getNetworkPart - bad prefixLength"); + } + + int offset = prefixLength / 8; + int reminder = prefixLength % 8; + byte mask = (byte)(0xFF << (8 - reminder)); + + if (offset < array.length) array[offset] = (byte)(array[offset] & mask); + + offset++; + + for (; offset < array.length; offset++) { + array[offset] = 0; + } + + InetAddress netPart = null; + try { + netPart = InetAddress.getByAddress(array); + } catch (UnknownHostException e) { + throw new RuntimeException("getNetworkPart error - " + e.toString()); + } + return netPart; + } + + /** + * Check if IP address type is consistent between two InetAddress. + * @return true if both are the same type. False otherwise. + */ + public static boolean addressTypeMatches(InetAddress left, InetAddress right) { + return (((left instanceof Inet4Address) && (right instanceof Inet4Address)) || + ((left instanceof Inet6Address) && (right instanceof Inet6Address))); + } } diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java index 5b10531..9c4e48b 100644 --- a/core/java/android/net/RouteInfo.java +++ b/core/java/android/net/RouteInfo.java @@ -23,6 +23,9 @@ import java.net.UnknownHostException; import java.net.InetAddress; import java.net.Inet4Address; import java.net.Inet6Address; + +import java.util.Collection; + /** * A simple container for route information. * @@ -44,39 +47,30 @@ public class RouteInfo implements Parcelable { public RouteInfo(LinkAddress destination, InetAddress gateway) { if (destination == null) { try { - if ((gateway != null) && (gateway instanceof Inet4Address)) { - destination = new LinkAddress(InetAddress.getByName("0.0.0.0"), 32); + if ((gateway != null) || (gateway instanceof Inet4Address)) { + destination = new LinkAddress(Inet4Address.ANY, 0); } else { - destination = new LinkAddress(InetAddress.getByName("::0"), 128); + destination = new LinkAddress(Inet6Address.ANY, 0); } } catch (Exception e) {} } - mDestination = destination; + mDestination = new LinkAddress(NetworkUtils.getNetworkPart(destination.getAddress(), + destination.getNetworkPrefixLength()), destination.getNetworkPrefixLength()); mGateway = gateway; mIsDefault = isDefault(); } public RouteInfo(InetAddress gateway) { - LinkAddress destination = null; - try { - if ((gateway != null) && (gateway instanceof Inet4Address)) { - destination = new LinkAddress(InetAddress.getByName("0.0.0.0"), 32); - } else { - destination = new LinkAddress(InetAddress.getByName("::0"), 128); - } - } catch (Exception e) {} - mDestination = destination; - mGateway = gateway; - mIsDefault = isDefault(); + this(null, gateway); } private boolean isDefault() { boolean val = false; if (mGateway != null) { if (mGateway instanceof Inet4Address) { - val = (mDestination == null || mDestination.getNetworkPrefixLength() == 32); + val = (mDestination == null || mDestination.getNetworkPrefixLength() == 0); } else { - val = (mDestination == null || mDestination.getNetworkPrefixLength() == 128); + val = (mDestination == null || mDestination.getNetworkPrefixLength() == 0); } } return val; @@ -159,4 +153,43 @@ public class RouteInfo implements Parcelable { return new RouteInfo[size]; } }; + + private boolean matches(InetAddress destination) { + if (destination == null) return false; + + // if the destination is present and the route is default. + // return true + if (isDefault()) return true; + + // match the route destination and destination with prefix length + InetAddress dstNet = NetworkUtils.getNetworkPart(destination, + mDestination.getNetworkPrefixLength()); + + return mDestination.getAddress().equals(dstNet); + } + + /** + * Find the route from a Collection of routes that best matches a given address. + * May return null if no routes are applicable. + * @param routes a Collection of RouteInfos to chose from + * @param dest the InetAddress your trying to get to + * @return the RouteInfo from the Collection that best fits the given address + */ + public static RouteInfo selectBestRoute(Collection<RouteInfo> routes, InetAddress dest) { + if ((routes == null) || (dest == null)) return null; + + RouteInfo bestRoute = null; + // pick a longest prefix match under same address type + for (RouteInfo route : routes) { + if (NetworkUtils.addressTypeMatches(route.mDestination.getAddress(), dest)) { + if ((bestRoute != null) && + (bestRoute.mDestination.getNetworkPrefixLength() >= + route.mDestination.getNetworkPrefixLength())) { + continue; + } + if (route.matches(dest)) bestRoute = route; + } + } + return bestRoute; + } } diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 428c94f..9bc7a9f 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -81,6 +81,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { private static final String NETWORK_RESTORE_DELAY_PROP_NAME = "android.telephony.apn-restore"; + // used in recursive route setting to add gateways for the host for which + // a host route was requested. + private static final int MAX_HOSTROUTE_CYCLE_COUNT = 10; + private Tethering mTethering; private boolean mTetheringConfigValid = false; @@ -921,7 +925,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { } try { InetAddress addr = InetAddress.getByAddress(hostAddress); - return addHostRoute(tracker, addr); + return addHostRoute(tracker, addr, 0); } catch (UnknownHostException e) {} return false; } @@ -934,24 +938,45 @@ public class ConnectivityService extends IConnectivityManager.Stub { * TODO - deprecate * @return {@code true} on success, {@code false} on failure */ - private boolean addHostRoute(NetworkStateTracker nt, InetAddress hostAddress) { + private boolean addHostRoute(NetworkStateTracker nt, InetAddress hostAddress, int cycleCount) { if (nt.getNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI) { return false; } - LinkProperties p = nt.getLinkProperties(); - if (p == null) return false; - String interfaceName = p.getInterfaceName(); + LinkProperties lp = nt.getLinkProperties(); + if ((lp == null) || (hostAddress == null)) return false; + String interfaceName = lp.getInterfaceName(); if (DBG) { - log("Requested host route to " + hostAddress + "(" + interfaceName + ")"); + log("Requested host route to " + hostAddress + "(" + interfaceName + "), cycleCount=" + + cycleCount); } - if (interfaceName != null) { - return NetworkUtils.addHostRoute(interfaceName, hostAddress, null); - } else { + if (interfaceName == null) { if (DBG) loge("addHostRoute failed due to null interface name"); return false; } + + RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getRoutes(), hostAddress); + InetAddress gateway = null; + if (bestRoute != null) { + gateway = bestRoute.getGateway(); + // if the best route is ourself, don't relf-reference, just add the host route + if (hostAddress.equals(gateway)) gateway = null; + } + if (gateway != null) { + if (cycleCount > MAX_HOSTROUTE_CYCLE_COUNT) { + loge("Error adding hostroute - too much recursion"); + return false; + } + if (!addHostRoute(nt, gateway, cycleCount+1)) return false; + } + return NetworkUtils.addHostRoute(interfaceName, hostAddress, gateway); + } + + // TODO support the removal of single host routes. Keep a ref count of them so we + // aren't over-zealous + private boolean removeHostRoute(NetworkStateTracker nt, InetAddress hostAddress) { + return false; } /** @@ -1389,7 +1414,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { Collection<InetAddress> dnsList = p.getDnses(); for (InetAddress dns : dnsList) { if (DBG) log(" adding " + dns); - NetworkUtils.addHostRoute(interfaceName, dns, null); + addHostRoute(nt, dns, 0); } nt.privateDnsRouteSet(true); } @@ -1423,7 +1448,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { //TODO - handle non-default routes if (route.isDefaultRoute()) { InetAddress gateway = route.getGateway(); - if (NetworkUtils.addHostRoute(interfaceName, gateway, null) && + if (addHostRoute(nt, gateway, 0) && NetworkUtils.addDefaultRoute(interfaceName, gateway)) { if (DBG) { NetworkInfo networkInfo = nt.getNetworkInfo(); |