1 /* 2 * Copyright 2017 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.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.compat.annotation.UnsupportedAppUsage; 23 import android.net.util.MacAddressUtils; 24 import android.net.wifi.WifiInfo; 25 import android.os.Parcel; 26 import android.os.Parcelable; 27 28 import com.android.internal.util.Preconditions; 29 30 import java.lang.annotation.Retention; 31 import java.lang.annotation.RetentionPolicy; 32 import java.net.Inet6Address; 33 import java.net.UnknownHostException; 34 import java.security.SecureRandom; 35 import java.util.Arrays; 36 37 /** 38 * Representation of a MAC address. 39 * 40 * This class only supports 48 bits long addresses and does not support 64 bits long addresses. 41 * Instances of this class are immutable. This class provides implementations of hashCode() 42 * and equals() that make it suitable for use as keys in standard implementations of 43 * {@link java.util.Map}. 44 */ 45 public final class MacAddress implements Parcelable { 46 47 private static final int ETHER_ADDR_LEN = 6; 48 private static final byte[] ETHER_ADDR_BROADCAST = addr(0xff, 0xff, 0xff, 0xff, 0xff, 0xff); 49 50 /** 51 * The MacAddress representing the unique broadcast MAC address. 52 */ 53 public static final MacAddress BROADCAST_ADDRESS = MacAddress.fromBytes(ETHER_ADDR_BROADCAST); 54 55 /** 56 * The MacAddress zero MAC address. 57 * 58 * <p>Not publicly exposed or treated specially since the OUI 00:00:00 is registered. 59 * @hide 60 */ 61 @UnsupportedAppUsage 62 public static final MacAddress ALL_ZEROS_ADDRESS = new MacAddress(0); 63 64 /** @hide */ 65 @Retention(RetentionPolicy.SOURCE) 66 @IntDef(prefix = { "TYPE_" }, value = { 67 TYPE_UNKNOWN, 68 TYPE_UNICAST, 69 TYPE_MULTICAST, 70 TYPE_BROADCAST, 71 }) 72 public @interface MacAddressType { } 73 74 /** @hide Indicates a MAC address of unknown type. */ 75 public static final int TYPE_UNKNOWN = 0; 76 /** Indicates a MAC address is a unicast address. */ 77 public static final int TYPE_UNICAST = 1; 78 /** Indicates a MAC address is a multicast address. */ 79 public static final int TYPE_MULTICAST = 2; 80 /** Indicates a MAC address is the broadcast address. */ 81 public static final int TYPE_BROADCAST = 3; 82 83 private static final long VALID_LONG_MASK = (1L << 48) - 1; 84 private static final long LOCALLY_ASSIGNED_MASK = MacAddress.fromString("2:0:0:0:0:0").mAddr; 85 private static final long MULTICAST_MASK = MacAddress.fromString("1:0:0:0:0:0").mAddr; 86 private static final long OUI_MASK = MacAddress.fromString("ff:ff:ff:0:0:0").mAddr; 87 private static final long NIC_MASK = MacAddress.fromString("0:0:0:ff:ff:ff").mAddr; 88 private static final MacAddress BASE_GOOGLE_MAC = MacAddress.fromString("da:a1:19:0:0:0"); 89 /** Default wifi MAC address used for a special purpose **/ 90 private static final MacAddress DEFAULT_MAC_ADDRESS = 91 MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS); 92 93 // Internal representation of the MAC address as a single 8 byte long. 94 // The encoding scheme sets the two most significant bytes to 0. The 6 bytes of the 95 // MAC address are encoded in the 6 least significant bytes of the long, where the first 96 // byte of the array is mapped to the 3rd highest logical byte of the long, the second 97 // byte of the array is mapped to the 4th highest logical byte of the long, and so on. 98 private final long mAddr; 99 MacAddress(long addr)100 private MacAddress(long addr) { 101 mAddr = (VALID_LONG_MASK & addr); 102 } 103 104 /** 105 * Returns the type of this address. 106 * 107 * @return the int constant representing the MAC address type of this MacAddress. 108 */ getAddressType()109 public @MacAddressType int getAddressType() { 110 if (equals(BROADCAST_ADDRESS)) { 111 return TYPE_BROADCAST; 112 } 113 if ((mAddr & MULTICAST_MASK) != 0) { 114 return TYPE_MULTICAST; 115 } 116 return TYPE_UNICAST; 117 } 118 119 /** 120 * @return true if this MacAddress is a locally assigned address. 121 */ isLocallyAssigned()122 public boolean isLocallyAssigned() { 123 return (mAddr & LOCALLY_ASSIGNED_MASK) != 0; 124 } 125 126 /** 127 * Convert this MacAddress to a byte array. 128 * 129 * The returned array is in network order. For example, if this MacAddress is 1:2:3:4:5:6, 130 * the returned array is [1, 2, 3, 4, 5, 6]. 131 * 132 * @return a byte array representation of this MacAddress. 133 */ toByteArray()134 public @NonNull byte[] toByteArray() { 135 return byteAddrFromLongAddr(mAddr); 136 } 137 138 /** 139 * Returns a human-readable representation of this MacAddress. 140 * The exact format is implementation-dependent and should not be assumed to have any 141 * particular format. 142 */ 143 @Override toString()144 public @NonNull String toString() { 145 return stringAddrFromLongAddr(mAddr); 146 } 147 148 /** 149 * @return a String representation of the OUI part of this MacAddress made of 3 hexadecimal 150 * numbers in [0,ff] joined by ':' characters. 151 */ toOuiString()152 public @NonNull String toOuiString() { 153 return String.format( 154 "%02x:%02x:%02x", (mAddr >> 40) & 0xff, (mAddr >> 32) & 0xff, (mAddr >> 24) & 0xff); 155 } 156 157 @Override hashCode()158 public int hashCode() { 159 return (int) ((mAddr >> 32) ^ mAddr); 160 } 161 162 @Override equals(Object o)163 public boolean equals(Object o) { 164 return (o instanceof MacAddress) && ((MacAddress) o).mAddr == mAddr; 165 } 166 167 @Override writeToParcel(Parcel out, int flags)168 public void writeToParcel(Parcel out, int flags) { 169 out.writeLong(mAddr); 170 } 171 172 @Override describeContents()173 public int describeContents() { 174 return 0; 175 } 176 177 public static final @android.annotation.NonNull Parcelable.Creator<MacAddress> CREATOR = 178 new Parcelable.Creator<MacAddress>() { 179 public MacAddress createFromParcel(Parcel in) { 180 return new MacAddress(in.readLong()); 181 } 182 183 public MacAddress[] newArray(int size) { 184 return new MacAddress[size]; 185 } 186 }; 187 188 /** 189 * Returns true if the given byte array is an valid MAC address. 190 * A valid byte array representation for a MacAddress is a non-null array of length 6. 191 * 192 * @param addr a byte array. 193 * @return true if the given byte array is not null and has the length of a MAC address. 194 * 195 * @hide 196 */ isMacAddress(byte[] addr)197 public static boolean isMacAddress(byte[] addr) { 198 return MacAddressUtils.isMacAddress(addr); 199 } 200 201 /** 202 * Returns the MAC address type of the MAC address represented by the given byte array, 203 * or null if the given byte array does not represent a MAC address. 204 * A valid byte array representation for a MacAddress is a non-null array of length 6. 205 * 206 * @param addr a byte array representing a MAC address. 207 * @return the int constant representing the MAC address type of the MAC address represented 208 * by the given byte array, or type UNKNOWN if the byte array is not a valid MAC address. 209 * 210 * @hide 211 */ macAddressType(byte[] addr)212 public static int macAddressType(byte[] addr) { 213 if (!isMacAddress(addr)) { 214 return TYPE_UNKNOWN; 215 } 216 return MacAddress.fromBytes(addr).getAddressType(); 217 } 218 219 /** 220 * Converts a String representation of a MAC address to a byte array representation. 221 * A valid String representation for a MacAddress is a series of 6 values in the 222 * range [0,ff] printed in hexadecimal and joined by ':' characters. 223 * 224 * @param addr a String representation of a MAC address. 225 * @return the byte representation of the MAC address. 226 * @throws IllegalArgumentException if the given String is not a valid representation. 227 * 228 * @hide 229 */ byteAddrFromStringAddr(String addr)230 public static @NonNull byte[] byteAddrFromStringAddr(String addr) { 231 Preconditions.checkNotNull(addr); 232 String[] parts = addr.split(":"); 233 if (parts.length != ETHER_ADDR_LEN) { 234 throw new IllegalArgumentException(addr + " was not a valid MAC address"); 235 } 236 byte[] bytes = new byte[ETHER_ADDR_LEN]; 237 for (int i = 0; i < ETHER_ADDR_LEN; i++) { 238 int x = Integer.valueOf(parts[i], 16); 239 if (x < 0 || 0xff < x) { 240 throw new IllegalArgumentException(addr + "was not a valid MAC address"); 241 } 242 bytes[i] = (byte) x; 243 } 244 return bytes; 245 } 246 247 /** 248 * Converts a byte array representation of a MAC address to a String representation made 249 * of 6 hexadecimal numbers in [0,ff] joined by ':' characters. 250 * A valid byte array representation for a MacAddress is a non-null array of length 6. 251 * 252 * @param addr a byte array representation of a MAC address. 253 * @return the String representation of the MAC address. 254 * @throws IllegalArgumentException if the given byte array is not a valid representation. 255 * 256 * @hide 257 */ stringAddrFromByteAddr(byte[] addr)258 public static @NonNull String stringAddrFromByteAddr(byte[] addr) { 259 if (!isMacAddress(addr)) { 260 return null; 261 } 262 return String.format("%02x:%02x:%02x:%02x:%02x:%02x", 263 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); 264 } 265 byteAddrFromLongAddr(long addr)266 private static byte[] byteAddrFromLongAddr(long addr) { 267 return MacAddressUtils.byteAddrFromLongAddr(addr); 268 } 269 longAddrFromByteAddr(byte[] addr)270 private static long longAddrFromByteAddr(byte[] addr) { 271 return MacAddressUtils.longAddrFromByteAddr(addr); 272 } 273 274 // Internal conversion function equivalent to longAddrFromByteAddr(byteAddrFromStringAddr(addr)) 275 // that avoids the allocation of an intermediary byte[]. longAddrFromStringAddr(String addr)276 private static long longAddrFromStringAddr(String addr) { 277 Preconditions.checkNotNull(addr); 278 String[] parts = addr.split(":"); 279 if (parts.length != ETHER_ADDR_LEN) { 280 throw new IllegalArgumentException(addr + " was not a valid MAC address"); 281 } 282 long longAddr = 0; 283 for (int i = 0; i < parts.length; i++) { 284 int x = Integer.valueOf(parts[i], 16); 285 if (x < 0 || 0xff < x) { 286 throw new IllegalArgumentException(addr + "was not a valid MAC address"); 287 } 288 longAddr = x + (longAddr << 8); 289 } 290 return longAddr; 291 } 292 293 // Internal conversion function equivalent to stringAddrFromByteAddr(byteAddrFromLongAddr(addr)) 294 // that avoids the allocation of an intermediary byte[]. stringAddrFromLongAddr(long addr)295 private static @NonNull String stringAddrFromLongAddr(long addr) { 296 return String.format("%02x:%02x:%02x:%02x:%02x:%02x", 297 (addr >> 40) & 0xff, 298 (addr >> 32) & 0xff, 299 (addr >> 24) & 0xff, 300 (addr >> 16) & 0xff, 301 (addr >> 8) & 0xff, 302 addr & 0xff); 303 } 304 305 /** 306 * Creates a MacAddress from the given String representation. A valid String representation 307 * for a MacAddress is a series of 6 values in the range [0,ff] printed in hexadecimal 308 * and joined by ':' characters. 309 * 310 * @param addr a String representation of a MAC address. 311 * @return the MacAddress corresponding to the given String representation. 312 * @throws IllegalArgumentException if the given String is not a valid representation. 313 */ fromString(@onNull String addr)314 public static @NonNull MacAddress fromString(@NonNull String addr) { 315 return new MacAddress(longAddrFromStringAddr(addr)); 316 } 317 318 /** 319 * Creates a MacAddress from the given byte array representation. 320 * A valid byte array representation for a MacAddress is a non-null array of length 6. 321 * 322 * @param addr a byte array representation of a MAC address. 323 * @return the MacAddress corresponding to the given byte array representation. 324 * @throws IllegalArgumentException if the given byte array is not a valid representation. 325 */ fromBytes(@onNull byte[] addr)326 public static @NonNull MacAddress fromBytes(@NonNull byte[] addr) { 327 return new MacAddress(longAddrFromByteAddr(addr)); 328 } 329 330 /** 331 * Returns a generated MAC address whose 24 least significant bits constituting the 332 * NIC part of the address are randomly selected and has Google OUI base. 333 * 334 * The locally assigned bit is always set to 1. The multicast bit is always set to 0. 335 * 336 * @return a random locally assigned, unicast MacAddress with Google OUI. 337 * 338 * @hide 339 */ createRandomUnicastAddressWithGoogleBase()340 public static @NonNull MacAddress createRandomUnicastAddressWithGoogleBase() { 341 return MacAddressUtils.createRandomUnicastAddress(BASE_GOOGLE_MAC, new SecureRandom()); 342 } 343 344 // Convenience function for working around the lack of byte literals. addr(int... in)345 private static byte[] addr(int... in) { 346 if (in.length != ETHER_ADDR_LEN) { 347 throw new IllegalArgumentException(Arrays.toString(in) 348 + " was not an array with length equal to " + ETHER_ADDR_LEN); 349 } 350 byte[] out = new byte[ETHER_ADDR_LEN]; 351 for (int i = 0; i < ETHER_ADDR_LEN; i++) { 352 out[i] = (byte) in[i]; 353 } 354 return out; 355 } 356 357 /** 358 * Checks if this MAC Address matches the provided range. 359 * 360 * @param baseAddress MacAddress representing the base address to compare with. 361 * @param mask MacAddress representing the mask to use during comparison. 362 * @return true if this MAC Address matches the given range. 363 * 364 */ matches(@onNull MacAddress baseAddress, @NonNull MacAddress mask)365 public boolean matches(@NonNull MacAddress baseAddress, @NonNull MacAddress mask) { 366 Preconditions.checkNotNull(baseAddress); 367 Preconditions.checkNotNull(mask); 368 return (mAddr & mask.mAddr) == (baseAddress.mAddr & mask.mAddr); 369 } 370 371 /** 372 * Create a link-local Inet6Address from the MAC address. The EUI-48 MAC address is converted 373 * to an EUI-64 MAC address per RFC 4291. The resulting EUI-64 is used to construct a link-local 374 * IPv6 address per RFC 4862. 375 * 376 * @return A link-local Inet6Address constructed from the MAC address. 377 */ getLinkLocalIpv6FromEui48Mac()378 public @Nullable Inet6Address getLinkLocalIpv6FromEui48Mac() { 379 byte[] macEui48Bytes = toByteArray(); 380 byte[] addr = new byte[16]; 381 382 addr[0] = (byte) 0xfe; 383 addr[1] = (byte) 0x80; 384 addr[8] = (byte) (macEui48Bytes[0] ^ (byte) 0x02); // flip the link-local bit 385 addr[9] = macEui48Bytes[1]; 386 addr[10] = macEui48Bytes[2]; 387 addr[11] = (byte) 0xff; 388 addr[12] = (byte) 0xfe; 389 addr[13] = macEui48Bytes[3]; 390 addr[14] = macEui48Bytes[4]; 391 addr[15] = macEui48Bytes[5]; 392 393 try { 394 return Inet6Address.getByAddress(null, addr, 0); 395 } catch (UnknownHostException e) { 396 return null; 397 } 398 } 399 } 400