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