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