1 /*
2  * Copyright 2019 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 com.android.net.module.util;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.net.MacAddress;
22 
23 import java.security.SecureRandom;
24 import java.util.Arrays;
25 import java.util.Objects;
26 import java.util.Random;
27 
28 /**
29  * Collection of MAC address utilities.
30  * @hide
31  */
32 public final class MacAddressUtils {
33 
34     private static final long VALID_LONG_MASK = (1L << 48) - 1;
35     private static final long LOCALLY_ASSIGNED_MASK = longAddrFromByteAddr(
36             MacAddress.fromString("2:0:0:0:0:0").toByteArray());
37     private static final long MULTICAST_MASK = longAddrFromByteAddr(
38             MacAddress.fromString("1:0:0:0:0:0").toByteArray());
39     private static final long OUI_MASK = longAddrFromByteAddr(
40             MacAddress.fromString("ff:ff:ff:0:0:0").toByteArray());
41     private static final long NIC_MASK = longAddrFromByteAddr(
42             MacAddress.fromString("0:0:0:ff:ff:ff").toByteArray());
43     // Matches WifiInfo.DEFAULT_MAC_ADDRESS
44     private static final MacAddress DEFAULT_MAC_ADDRESS =
45             MacAddress.fromString("02:00:00:00:00:00");
46     private static final int ETHER_ADDR_LEN = 6;
47 
48     /**
49      * @return true if this MacAddress is a multicast address.
50      */
isMulticastAddress(@onNull MacAddress address)51     public static boolean isMulticastAddress(@NonNull MacAddress address) {
52         return (longAddrFromByteAddr(address.toByteArray()) & MULTICAST_MASK) != 0;
53     }
54 
55     /**
56      * Returns a generated MAC address whose 46 bits, excluding the locally assigned bit and the
57      * unicast bit, are randomly selected.
58      *
59      * The locally assigned bit is always set to 1. The multicast bit is always set to 0.
60      *
61      * @return a random locally assigned, unicast MacAddress.
62      */
createRandomUnicastAddress()63     public static @NonNull MacAddress createRandomUnicastAddress() {
64         return createRandomUnicastAddress(null, new SecureRandom());
65     }
66 
67     /**
68      * Returns a randomly generated MAC address using the given Random object and the same
69      * OUI values as the given MacAddress.
70      *
71      * The locally assigned bit is always set to 1. The multicast bit is always set to 0.
72      *
73      * @param base a base MacAddress whose OUI is used for generating the random address.
74      *             If base == null then the OUI will also be randomized.
75      * @param r a standard Java Random object used for generating the random address.
76      * @return a random locally assigned MacAddress.
77      */
createRandomUnicastAddress(@ullable MacAddress base, @NonNull Random r)78     public static @NonNull MacAddress createRandomUnicastAddress(@Nullable MacAddress base,
79             @NonNull Random r) {
80         long addr;
81 
82         if (base == null) {
83             addr = r.nextLong() & VALID_LONG_MASK;
84         } else {
85             addr = (longAddrFromByteAddr(base.toByteArray()) & OUI_MASK)
86                     | (NIC_MASK & r.nextLong());
87         }
88         addr |= LOCALLY_ASSIGNED_MASK;
89         addr &= ~MULTICAST_MASK;
90         MacAddress mac = MacAddress.fromBytes(byteAddrFromLongAddr(addr));
91         if (mac.equals(DEFAULT_MAC_ADDRESS)) {
92             return createRandomUnicastAddress(base, r);
93         }
94         return mac;
95     }
96 
97     /**
98      * Convert a byte address to long address.
99      */
longAddrFromByteAddr(byte[] addr)100     public static long longAddrFromByteAddr(byte[] addr) {
101         Objects.requireNonNull(addr);
102         if (!isMacAddress(addr)) {
103             throw new IllegalArgumentException(
104                     Arrays.toString(addr) + " was not a valid MAC address");
105         }
106         long longAddr = 0;
107         for (byte b : addr) {
108             final int uint8Byte = b & 0xff;
109             longAddr = (longAddr << 8) + uint8Byte;
110         }
111         return longAddr;
112     }
113 
114     /**
115      * Convert a long address to byte address.
116      */
byteAddrFromLongAddr(long addr)117     public static byte[] byteAddrFromLongAddr(long addr) {
118         byte[] bytes = new byte[ETHER_ADDR_LEN];
119         int index = ETHER_ADDR_LEN;
120         while (index-- > 0) {
121             bytes[index] = (byte) addr;
122             addr = addr >> 8;
123         }
124         return bytes;
125     }
126 
127     /**
128      * Returns true if the given byte array is a valid MAC address.
129      * A valid byte array representation for a MacAddress is a non-null array of length 6.
130      *
131      * @param addr a byte array.
132      * @return true if the given byte array is not null and has the length of a MAC address.
133      *
134      */
isMacAddress(byte[] addr)135     public static boolean isMacAddress(byte[] addr) {
136         return addr != null && addr.length == ETHER_ADDR_LEN;
137     }
138 }
139