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