From 6307b35e1d3a8d577877be0b4c9cea1a909b6329 Mon Sep 17 00:00:00 2001 From: Sreeram Ramachandran Date: Tue, 3 Jun 2014 18:41:43 -0700 Subject: [PATCH] Add a new IpPrefix class and use it in RouteInfo. This change uses IpPrefix only in the public API and continues to use LinkAddress for everything else. It does not change the callers to use the new APIs, with the exception of changing all current uses of getDestination to getDestinationLinkAddress to make room for the new getDestination method that returns an IpPrefix. Based on Sreeram's earlier change: https://googleplex-android-review.git.corp.google.com/#/c/477874/ but a bit simplified and with a bit more documentation. Bug: 15142362 Bug: 13885501 Change-Id: Ib4cd96b22cbff4ea31bb26a7853989f50da8de4e (cherry picked from commit 7d3b4b9a3d4de9673119632da0ebd583e50126f7) --- api/current.txt | 16 +- core/java/android/net/IpPrefix.aidl | 20 +++ core/java/android/net/IpPrefix.java | 170 +++++++++++++++++++++ core/java/android/net/LinkProperties.java | 15 +- core/java/android/net/RouteInfo.java | 82 ++++++++-- .../coretests/src/android/net/RouteInfoTest.java | 6 +- .../com/android/server/ConnectivityService.java | 4 +- .../android/server/NetworkManagementService.java | 8 +- .../java/com/android/server/net/IpConfigStore.java | 2 +- 9 files changed, 286 insertions(+), 37 deletions(-) create mode 100644 core/java/android/net/IpPrefix.aidl create mode 100644 core/java/android/net/IpPrefix.java diff --git a/api/current.txt b/api/current.txt index 8b230b67f4ad..56c9c92a87b5 100644 --- a/api/current.txt +++ b/api/current.txt @@ -16017,6 +16017,14 @@ package android.net { field public int serverAddress; } + public class IpPrefix implements android.os.Parcelable { + method public int describeContents(); + method public java.net.InetAddress getAddress(); + method public int getPrefixLength(); + method public byte[] getRawAddress(); + method public void writeToParcel(android.os.Parcel, int); + } + public class LinkAddress implements android.os.Parcelable { method public int describeContents(); method public java.net.InetAddress getAddress(); @@ -16028,8 +16036,6 @@ package android.net { } public class LinkProperties implements android.os.Parcelable { - ctor public LinkProperties(); - ctor public LinkProperties(android.net.LinkProperties); method public int describeContents(); method public java.util.List getDnsServers(); method public java.lang.String getDomains(); @@ -16237,12 +16243,8 @@ package android.net { } public class RouteInfo implements android.os.Parcelable { - ctor public RouteInfo(android.net.LinkAddress, java.net.InetAddress, java.lang.String); - ctor public RouteInfo(android.net.LinkAddress, java.net.InetAddress); - ctor public RouteInfo(java.net.InetAddress); - ctor public RouteInfo(android.net.LinkAddress); method public int describeContents(); - method public android.net.LinkAddress getDestination(); + method public android.net.IpPrefix getDestination(); method public java.net.InetAddress getGateway(); method public java.lang.String getInterface(); method public boolean isDefaultRoute(); diff --git a/core/java/android/net/IpPrefix.aidl b/core/java/android/net/IpPrefix.aidl new file mode 100644 index 000000000000..9e552c72c64d --- /dev/null +++ b/core/java/android/net/IpPrefix.aidl @@ -0,0 +1,20 @@ +/** + * + * 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; + +parcelable IpPrefix; diff --git a/core/java/android/net/IpPrefix.java b/core/java/android/net/IpPrefix.java new file mode 100644 index 000000000000..a14d13f17650 --- /dev/null +++ b/core/java/android/net/IpPrefix.java @@ -0,0 +1,170 @@ +/* + * 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 android.os.Parcel; +import android.os.Parcelable; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; + +/** + * This class represents an IP prefix, i.e., a contiguous block of IP addresses aligned on a + * power of two boundary (also known as an "IP subnet"). A prefix is specified by two pieces of + * information: + * + * + * + * For example, the prefix 192.0.2.0/24 covers the 256 IPv4 addresses from + * 192.0.2.0 to 192.0.2.255, inclusive, and the prefix + * 2001:db8:1:2 covers the 2^64 IPv6 addresses from 2001:db8:1:2:: to + * 2001:db8:1:2:ffff:ffff:ffff:ffff, inclusive. + * + * Objects of this class are immutable. + */ +public final class IpPrefix implements Parcelable { + private final byte[] address; // network byte order + private final int prefixLength; + + /** + * Constructs a new {@code IpPrefix} from a byte array containing an IPv4 or IPv6 address in + * network byte order and a prefix length. + * + * @param address the IP address. Must be non-null and exactly 4 or 16 bytes long. + * @param prefixLength the prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6). + * + * @hide + */ + public IpPrefix(byte[] address, int prefixLength) { + if (address.length != 4 && address.length != 16) { + throw new IllegalArgumentException( + "IpPrefix has " + address.length + " bytes which is neither 4 nor 16"); + } + if (prefixLength < 0 || prefixLength > (address.length * 8)) { + throw new IllegalArgumentException("IpPrefix with " + address.length + + " bytes has invalid prefix length " + prefixLength); + } + this.address = address.clone(); + this.prefixLength = prefixLength; + // TODO: Validate that the non-prefix bits are zero + } + + /** + * @hide + */ + public IpPrefix(InetAddress address, int prefixLength) { + this(address.getAddress(), prefixLength); + } + + /** + * Compares this {@code IpPrefix} object against the specified object in {@code obj}. Two + * objects are equal if they have the same startAddress and prefixLength. + * + * @param obj the object to be tested for equality. + * @return {@code true} if both objects are equal, {@code false} otherwise. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof IpPrefix)) { + return false; + } + IpPrefix that = (IpPrefix) obj; + return Arrays.equals(this.address, that.address) && this.prefixLength == that.prefixLength; + } + + /** + * Gets the hashcode of the represented IP prefix. + * + * @return the appropriate hashcode value. + */ + @Override + public int hashCode() { + return Arrays.hashCode(address) + 11 * prefixLength; + } + + /** + * Returns a copy of the first IP address in the prefix. Modifying the returned object does not + * change this object's contents. + * + * @return the address in the form of a byte array. + */ + public InetAddress getAddress() { + try { + return InetAddress.getByAddress(address); + } catch (UnknownHostException e) { + // Cannot happen. InetAddress.getByAddress can only throw an exception if the byte + // array is the wrong length, but we check that in the constructor. + return null; + } + } + + /** + * Returns a copy of the IP address bytes in network order (the highest order byte is the zeroth + * element). Modifying the returned array does not change this object's contents. + * + * @return the address in the form of a byte array. + */ + public byte[] getRawAddress() { + return address.clone(); + } + + /** + * Returns the prefix length of this {@code IpAddress}. + * + * @return the prefix length. + */ + public int getPrefixLength() { + return prefixLength; + } + + /** + * Implement the Parcelable interface. + */ + public int describeContents() { + return 0; + } + + /** + * Implement the Parcelable interface. + */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeByteArray(address); + dest.writeInt(prefixLength); + } + + /** + * Implement the Parcelable interface. + */ + public static final Creator CREATOR = + new Creator() { + public IpPrefix createFromParcel(Parcel in) { + byte[] address = in.createByteArray(); + int prefixLength = in.readInt(); + return new IpPrefix(address, prefixLength); + } + + public IpPrefix[] newArray(int size) { + return new IpPrefix[size]; + } + }; +} diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index cff9025d74cd..8eefa0f55fab 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -44,7 +44,7 @@ import java.util.List; * does not affect live networks. * */ -public class LinkProperties implements Parcelable { +public final class LinkProperties implements Parcelable { // The interface described by the network link. private String mIfaceName; private ArrayList mLinkAddresses = new ArrayList(); @@ -77,9 +77,15 @@ public class LinkProperties implements Parcelable { } } + /** + * @hide + */ public LinkProperties() { } + /** + * @hide + */ public LinkProperties(LinkProperties source) { if (source != null) { mIfaceName = source.getInterfaceName(); @@ -267,7 +273,7 @@ public class LinkProperties implements Parcelable { } /** - * Returns all the {@link LinkAddress} for DNS servers on this link. + * Returns all the {@link InetAddress} for DNS servers on this link. * * @return An umodifiable {@link List} of {@link InetAddress} for DNS servers on * this link. @@ -457,7 +463,6 @@ public class LinkProperties implements Parcelable { /** * Implement the Parcelable interface - * @hide */ public int describeContents() { return 0; @@ -477,12 +482,12 @@ public class LinkProperties implements Parcelable { String domainName = "Domains: " + mDomains; - String mtu = "MTU: " + mMtu; + String mtu = " MTU: " + mMtu; String routes = " Routes: ["; for (RouteInfo route : mRoutes) routes += route.toString() + ","; routes += "] "; - String proxy = (mHttpProxy == null ? "" : "HttpProxy: " + mHttpProxy.toString() + " "); + String proxy = (mHttpProxy == null ? "" : " HttpProxy: " + mHttpProxy.toString() + " "); String stacked = ""; if (mStackedLinks.values().size() > 0) { diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java index ad8e4f7eec51..6d65d8f06901 100644 --- a/core/java/android/net/RouteInfo.java +++ b/core/java/android/net/RouteInfo.java @@ -35,10 +35,10 @@ import java.util.Objects; * * A route contains three pieces of information: *
    - *
  • a destination {@link LinkAddress} for directly-connected subnets. If this is - * {@code null} it indicates a default route of the address family (IPv4 or IPv6) + *
  • a destination {@link IpPrefix} specifying the network destinations covered by this route. + * If this is {@code null} it indicates a default route of the address family (IPv4 or IPv6) * implied by the gateway IP address. - *
  • a gateway {@link InetAddress} for default routes. If this is {@code null} it + *
  • a gateway {@link InetAddress} indicating the next hop to use. If this is {@code null} it * indicates a directly-connected route. *
  • an interface (which may be unspecified). *
@@ -46,9 +46,10 @@ import java.util.Objects; * destination and gateway are both specified, they must be of the same address family * (IPv4 or IPv6). */ -public class RouteInfo implements Parcelable { +public final class RouteInfo implements Parcelable { /** * The IP destination address for this route. + * TODO: Make this an IpPrefix. */ private final LinkAddress mDestination; @@ -80,6 +81,19 @@ public class RouteInfo implements Parcelable { * @param destination the destination prefix * @param gateway the IP address to route packets through * @param iface the interface name to send packets on + * + * TODO: Convert to use IpPrefix. + * + * @hide + */ + public RouteInfo(IpPrefix destination, InetAddress gateway, String iface) { + this(destination == null ? null : + new LinkAddress(destination.getAddress(), destination.getPrefixLength()), + gateway, iface); + } + + /** + * @hide */ public RouteInfo(LinkAddress destination, InetAddress gateway, String iface) { if (destination == null) { @@ -128,8 +142,17 @@ public class RouteInfo implements Parcelable { *

* Destination and gateway may not both be null. * - * @param destination the destination address and prefix in a {@link LinkAddress} + * @param destination the destination address and prefix in an {@link IpPrefix} * @param gateway the {@link InetAddress} to route packets through + * + * @hide + */ + public RouteInfo(IpPrefix destination, InetAddress gateway) { + this(destination, gateway, null); + } + + /** + * @hide */ public RouteInfo(LinkAddress destination, InetAddress gateway) { this(destination, gateway, null); @@ -139,16 +162,27 @@ public class RouteInfo implements Parcelable { * Constructs a default {@code RouteInfo} object. * * @param gateway the {@link InetAddress} to route packets through + * + * @hide */ public RouteInfo(InetAddress gateway) { - this(null, gateway, null); + this((LinkAddress) null, gateway, null); } /** * Constructs a {@code RouteInfo} object representing a direct connected subnet. * - * @param destination the {@link LinkAddress} describing the address and prefix + * @param destination the {@link IpPrefix} describing the address and prefix * length of the subnet. + * + * @hide + */ + public RouteInfo(IpPrefix destination) { + this(destination, null, null); + } + + /** + * @hide */ public RouteInfo(LinkAddress destination) { this(destination, null, null); @@ -194,11 +228,19 @@ public class RouteInfo implements Parcelable { } /** - * Retrieves the destination address and prefix length in the form of a {@link LinkAddress}. + * Retrieves the destination address and prefix length in the form of an {@link IpPrefix}. * - * @return {@link LinkAddress} specifying the destination. This is never {@code null}. + * @return {@link IpPrefix} specifying the destination. This is never {@code null}. + */ + public IpPrefix getDestination() { + return new IpPrefix(mDestination.getAddress(), mDestination.getPrefixLength()); + } + + /** + * TODO: Convert callers to use IpPrefix and then remove. + * @hide */ - public LinkAddress getDestination() { + public LinkAddress getDestinationLinkAddress() { return mDestination; } @@ -233,7 +275,8 @@ public class RouteInfo implements Parcelable { /** * Indicates if this route is a host route (ie, matches only a single host address). * - * @return {@code true} if the destination has a prefix length of 32/128 for v4/v6. + * @return {@code true} if the destination has a prefix length of 32 or 128 for IPv4 or IPv6, + * respectively. * @hide */ public boolean isHostRoute() { @@ -295,13 +338,22 @@ public class RouteInfo implements Parcelable { return bestRoute; } + /** + * Returns a human-readable description of this object. + */ public String toString() { String val = ""; if (mDestination != null) val = mDestination.toString(); - if (mGateway != null) val += " -> " + mGateway.getHostAddress(); + val += " ->"; + if (mGateway != null) val += " " + mGateway.getHostAddress(); + if (mInterface != null) val += " " + mInterface; return val; } + /** + * Compares this RouteInfo object against the specified object and indicates if they are equal. + * @return {@code true} if the objects are equal, {@code false} otherwise. + */ public boolean equals(Object obj) { if (this == obj) return true; @@ -314,6 +366,9 @@ public class RouteInfo implements Parcelable { Objects.equals(mInterface, target.getInterface()); } + /** + * Returns a hashcode for this RouteInfo object. + */ public int hashCode() { return (mDestination == null ? 0 : mDestination.hashCode() * 41) + (mGateway == null ? 0 :mGateway.hashCode() * 47) @@ -323,7 +378,6 @@ public class RouteInfo implements Parcelable { /** * Implement the Parcelable interface - * @hide */ public int describeContents() { return 0; @@ -331,7 +385,6 @@ public class RouteInfo implements Parcelable { /** * Implement the Parcelable interface - * @hide */ public void writeToParcel(Parcel dest, int flags) { if (mDestination == null) { @@ -354,7 +407,6 @@ public class RouteInfo implements Parcelable { /** * Implement the Parcelable interface. - * @hide */ public static final Creator CREATOR = new Creator() { diff --git a/core/tests/coretests/src/android/net/RouteInfoTest.java b/core/tests/coretests/src/android/net/RouteInfoTest.java index 55d6592d9a40..01283a65bab5 100644 --- a/core/tests/coretests/src/android/net/RouteInfoTest.java +++ b/core/tests/coretests/src/android/net/RouteInfoTest.java @@ -43,17 +43,17 @@ public class RouteInfoTest extends TestCase { // Invalid input. try { - r = new RouteInfo(null, null, "rmnet0"); + r = new RouteInfo((LinkAddress) null, null, "rmnet0"); fail("Expected RuntimeException: destination and gateway null"); } catch(RuntimeException e) {} // Null destination is default route. - r = new RouteInfo(null, Address("2001:db8::1"), null); + r = new RouteInfo((LinkAddress) null, Address("2001:db8::1"), null); assertEquals(Prefix("::/0"), r.getDestination()); assertEquals(Address("2001:db8::1"), r.getGateway()); assertNull(r.getInterface()); - r = new RouteInfo(null, Address("192.0.2.1"), "wlan0"); + r = new RouteInfo((LinkAddress) null, Address("192.0.2.1"), "wlan0"); assertEquals(Prefix("0.0.0.0/0"), r.getDestination()); assertEquals(Address("192.0.2.1"), r.getGateway()); assertEquals("wlan0", r.getInterface()); diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 70b9d4410c3a..5b36ec6139b7 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -1871,7 +1871,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { mNetd.addRoute(netId, r); } if (exempt) { - LinkAddress dest = r.getDestination(); + LinkAddress dest = r.getDestinationLinkAddress(); if (!mExemptAddresses.contains(dest)) { mNetd.setHostExemption(dest); mExemptAddresses.add(dest); @@ -1904,7 +1904,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { } else { mNetd.removeRoute(netId, r); } - LinkAddress dest = r.getDestination(); + LinkAddress dest = r.getDestinationLinkAddress(); if (mExemptAddresses.contains(dest)) { mNetd.clearHostExemption(dest); mExemptAddresses.remove(dest); diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index eefe8dad2235..3b748bd455ec 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -882,7 +882,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub final Command cmd = new Command("network", "route", action, netId); // create triplet: interface dest-ip-addr/prefixlength gateway-ip-addr - final LinkAddress la = route.getDestination(); + final LinkAddress la = route.getDestinationLinkAddress(); cmd.appendArg(route.getInterface()); cmd.appendArg(la.getAddress().getHostAddress() + "/" + la.getNetworkPrefixLength()); if (route.hasGateway()) { @@ -1697,7 +1697,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub public void setMarkedForwardingRoute(String iface, RouteInfo route) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { - LinkAddress dest = route.getDestination(); + LinkAddress dest = route.getDestinationLinkAddress(); mConnector.execute("interface", "fwmark", "route", "add", iface, dest.getAddress().getHostAddress(), dest.getNetworkPrefixLength()); } catch (NativeDaemonConnectorException e) { @@ -1709,7 +1709,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub public void clearMarkedForwardingRoute(String iface, RouteInfo route) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { - LinkAddress dest = route.getDestination(); + LinkAddress dest = route.getDestinationLinkAddress(); mConnector.execute("interface", "fwmark", "route", "remove", iface, dest.getAddress().getHostAddress(), dest.getNetworkPrefixLength()); } catch (NativeDaemonConnectorException e) { @@ -1998,7 +1998,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub final Command cmd = new Command("network", "route", "legacy", uid, action, netId); // create triplet: interface dest-ip-addr/prefixlength gateway-ip-addr - final LinkAddress la = routeInfo.getDestination(); + final LinkAddress la = routeInfo.getDestinationLinkAddress(); cmd.appendArg(routeInfo.getInterface()); cmd.appendArg(la.getAddress().getHostAddress() + "/" + la.getNetworkPrefixLength()); if (routeInfo.hasGateway()) { diff --git a/services/core/java/com/android/server/net/IpConfigStore.java b/services/core/java/com/android/server/net/IpConfigStore.java index bb0d5fe60884..35bc6ec4cd8f 100644 --- a/services/core/java/com/android/server/net/IpConfigStore.java +++ b/services/core/java/com/android/server/net/IpConfigStore.java @@ -85,7 +85,7 @@ public class IpConfigStore { } for (RouteInfo route : linkProperties.getRoutes()) { out.writeUTF(GATEWAY_KEY); - LinkAddress dest = route.getDestination(); + LinkAddress dest = route.getDestinationLinkAddress(); if (dest != null) { out.writeInt(1); out.writeUTF(dest.getAddress().getHostAddress()); -- 2.11.0