1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.net; 18 19 import android.os.Parcel; 20 import android.os.Parcelable; 21 import android.util.Pair; 22 23 import java.net.Inet4Address; 24 import java.net.Inet6Address; 25 import java.net.InetAddress; 26 import java.net.UnknownHostException; 27 import java.util.Arrays; 28 import java.util.Comparator; 29 30 /** 31 * This class represents an IP prefix, i.e., a contiguous block of IP addresses aligned on a 32 * power of two boundary (also known as an "IP subnet"). A prefix is specified by two pieces of 33 * information: 34 * 35 * <ul> 36 * <li>A starting IP address (IPv4 or IPv6). This is the first IP address of the prefix. 37 * <li>A prefix length. This specifies the length of the prefix by specifing the number of bits 38 * in the IP address, starting from the most significant bit in network byte order, that 39 * are constant for all addresses in the prefix. 40 * </ul> 41 * 42 * For example, the prefix <code>192.0.2.0/24</code> covers the 256 IPv4 addresses from 43 * <code>192.0.2.0</code> to <code>192.0.2.255</code>, inclusive, and the prefix 44 * <code>2001:db8:1:2</code> covers the 2^64 IPv6 addresses from <code>2001:db8:1:2::</code> to 45 * <code>2001:db8:1:2:ffff:ffff:ffff:ffff</code>, inclusive. 46 * 47 * Objects of this class are immutable. 48 */ 49 public final class IpPrefix implements Parcelable { 50 private final byte[] address; // network byte order 51 private final int prefixLength; 52 checkAndMaskAddressAndPrefixLength()53 private void checkAndMaskAddressAndPrefixLength() { 54 if (address.length != 4 && address.length != 16) { 55 throw new IllegalArgumentException( 56 "IpPrefix has " + address.length + " bytes which is neither 4 nor 16"); 57 } 58 NetworkUtils.maskRawAddress(address, prefixLength); 59 } 60 61 /** 62 * Constructs a new {@code IpPrefix} from a byte array containing an IPv4 or IPv6 address in 63 * network byte order and a prefix length. Silently truncates the address to the prefix length, 64 * so for example {@code 192.0.2.1/24} is silently converted to {@code 192.0.2.0/24}. 65 * 66 * @param address the IP address. Must be non-null and exactly 4 or 16 bytes long. 67 * @param prefixLength the prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6). 68 * 69 * @hide 70 */ IpPrefix(byte[] address, int prefixLength)71 public IpPrefix(byte[] address, int prefixLength) { 72 this.address = address.clone(); 73 this.prefixLength = prefixLength; 74 checkAndMaskAddressAndPrefixLength(); 75 } 76 77 /** 78 * Constructs a new {@code IpPrefix} from an IPv4 or IPv6 address and a prefix length. Silently 79 * truncates the address to the prefix length, so for example {@code 192.0.2.1/24} is silently 80 * converted to {@code 192.0.2.0/24}. 81 * 82 * @param address the IP address. Must be non-null. 83 * @param prefixLength the prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6). 84 * @hide 85 */ IpPrefix(InetAddress address, int prefixLength)86 public IpPrefix(InetAddress address, int prefixLength) { 87 // We don't reuse the (byte[], int) constructor because it calls clone() on the byte array, 88 // which is unnecessary because getAddress() already returns a clone. 89 this.address = address.getAddress(); 90 this.prefixLength = prefixLength; 91 checkAndMaskAddressAndPrefixLength(); 92 } 93 94 /** 95 * Constructs a new IpPrefix from a string such as "192.0.2.1/24" or "2001:db8::1/64". 96 * Silently truncates the address to the prefix length, so for example {@code 192.0.2.1/24} 97 * is silently converted to {@code 192.0.2.0/24}. 98 * 99 * @param prefix the prefix to parse 100 * 101 * @hide 102 */ IpPrefix(String prefix)103 public IpPrefix(String prefix) { 104 // We don't reuse the (InetAddress, int) constructor because "error: call to this must be 105 // first statement in constructor". We could factor out setting the member variables to an 106 // init() method, but if we did, then we'd have to make the members non-final, or "error: 107 // cannot assign a value to final variable address". So we just duplicate the code here. 108 Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(prefix); 109 this.address = ipAndMask.first.getAddress(); 110 this.prefixLength = ipAndMask.second; 111 checkAndMaskAddressAndPrefixLength(); 112 } 113 114 /** 115 * Compares this {@code IpPrefix} object against the specified object in {@code obj}. Two 116 * objects are equal if they have the same startAddress and prefixLength. 117 * 118 * @param obj the object to be tested for equality. 119 * @return {@code true} if both objects are equal, {@code false} otherwise. 120 */ 121 @Override equals(Object obj)122 public boolean equals(Object obj) { 123 if (!(obj instanceof IpPrefix)) { 124 return false; 125 } 126 IpPrefix that = (IpPrefix) obj; 127 return Arrays.equals(this.address, that.address) && this.prefixLength == that.prefixLength; 128 } 129 130 /** 131 * Gets the hashcode of the represented IP prefix. 132 * 133 * @return the appropriate hashcode value. 134 */ 135 @Override hashCode()136 public int hashCode() { 137 return Arrays.hashCode(address) + 11 * prefixLength; 138 } 139 140 /** 141 * Returns a copy of the first IP address in the prefix. Modifying the returned object does not 142 * change this object's contents. 143 * 144 * @return the address in the form of a byte array. 145 */ getAddress()146 public InetAddress getAddress() { 147 try { 148 return InetAddress.getByAddress(address); 149 } catch (UnknownHostException e) { 150 // Cannot happen. InetAddress.getByAddress can only throw an exception if the byte 151 // array is the wrong length, but we check that in the constructor. 152 return null; 153 } 154 } 155 156 /** 157 * Returns a copy of the IP address bytes in network order (the highest order byte is the zeroth 158 * element). Modifying the returned array does not change this object's contents. 159 * 160 * @return the address in the form of a byte array. 161 */ getRawAddress()162 public byte[] getRawAddress() { 163 return address.clone(); 164 } 165 166 /** 167 * Returns the prefix length of this {@code IpPrefix}. 168 * 169 * @return the prefix length. 170 */ getPrefixLength()171 public int getPrefixLength() { 172 return prefixLength; 173 } 174 175 /** 176 * Determines whether the prefix contains the specified address. 177 * 178 * @param address An {@link InetAddress} to test. 179 * @return {@code true} if the prefix covers the given address. 180 */ contains(InetAddress address)181 public boolean contains(InetAddress address) { 182 byte[] addrBytes = (address == null) ? null : address.getAddress(); 183 if (addrBytes == null || addrBytes.length != this.address.length) { 184 return false; 185 } 186 NetworkUtils.maskRawAddress(addrBytes, prefixLength); 187 return Arrays.equals(this.address, addrBytes); 188 } 189 190 /** 191 * Returns whether the specified prefix is entirely contained in this prefix. 192 * 193 * Note this is mathematical inclusion, so a prefix is always contained within itself. 194 * @param otherPrefix the prefix to test 195 * @hide 196 */ containsPrefix(IpPrefix otherPrefix)197 public boolean containsPrefix(IpPrefix otherPrefix) { 198 if (otherPrefix.getPrefixLength() < prefixLength) return false; 199 final byte[] otherAddress = otherPrefix.getRawAddress(); 200 NetworkUtils.maskRawAddress(otherAddress, prefixLength); 201 return Arrays.equals(otherAddress, address); 202 } 203 204 /** 205 * @hide 206 */ isIPv6()207 public boolean isIPv6() { 208 return getAddress() instanceof Inet6Address; 209 } 210 211 /** 212 * @hide 213 */ isIPv4()214 public boolean isIPv4() { 215 return getAddress() instanceof Inet4Address; 216 } 217 218 /** 219 * Returns a string representation of this {@code IpPrefix}. 220 * 221 * @return a string such as {@code "192.0.2.0/24"} or {@code "2001:db8:1:2::/64"}. 222 */ toString()223 public String toString() { 224 try { 225 return InetAddress.getByAddress(address).getHostAddress() + "/" + prefixLength; 226 } catch(UnknownHostException e) { 227 // Cosmic rays? 228 throw new IllegalStateException("IpPrefix with invalid address! Shouldn't happen.", e); 229 } 230 } 231 232 /** 233 * Implement the Parcelable interface. 234 */ describeContents()235 public int describeContents() { 236 return 0; 237 } 238 239 /** 240 * Implement the Parcelable interface. 241 */ writeToParcel(Parcel dest, int flags)242 public void writeToParcel(Parcel dest, int flags) { 243 dest.writeByteArray(address); 244 dest.writeInt(prefixLength); 245 } 246 247 /** 248 * Returns a comparator ordering IpPrefixes by length, shorter to longer. 249 * Contents of the address will break ties. 250 * @hide 251 */ lengthComparator()252 public static Comparator<IpPrefix> lengthComparator() { 253 return new Comparator<IpPrefix>() { 254 @Override 255 public int compare(IpPrefix prefix1, IpPrefix prefix2) { 256 if (prefix1.isIPv4()) { 257 if (prefix2.isIPv6()) return -1; 258 } else { 259 if (prefix2.isIPv4()) return 1; 260 } 261 final int p1len = prefix1.getPrefixLength(); 262 final int p2len = prefix2.getPrefixLength(); 263 if (p1len < p2len) return -1; 264 if (p2len < p1len) return 1; 265 final byte[] a1 = prefix1.address; 266 final byte[] a2 = prefix2.address; 267 final int len = a1.length < a2.length ? a1.length : a2.length; 268 for (int i = 0; i < len; ++i) { 269 if (a1[i] < a2[i]) return -1; 270 if (a1[i] > a2[i]) return 1; 271 } 272 if (a2.length < len) return 1; 273 if (a1.length < len) return -1; 274 return 0; 275 } 276 }; 277 } 278 279 /** 280 * Implement the Parcelable interface. 281 */ 282 public static final Creator<IpPrefix> CREATOR = 283 new Creator<IpPrefix>() { 284 public IpPrefix createFromParcel(Parcel in) { 285 byte[] address = in.createByteArray(); 286 int prefixLength = in.readInt(); 287 return new IpPrefix(address, prefixLength); 288 } 289 290 public IpPrefix[] newArray(int size) { 291 return new IpPrefix[size]; 292 } 293 }; 294 } 295