1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.netty.util.internal;
18
19 import io.netty.util.NetUtil;
20 import io.netty.util.internal.logging.InternalLogger;
21 import io.netty.util.internal.logging.InternalLoggerFactory;
22
23 import java.net.InetAddress;
24 import java.net.NetworkInterface;
25 import java.net.SocketException;
26 import java.util.Arrays;
27 import java.util.Enumeration;
28 import java.util.LinkedHashMap;
29 import java.util.Map;
30 import java.util.Map.Entry;
31
32 import static io.netty.util.internal.EmptyArrays.EMPTY_BYTES;
33
34 public final class MacAddressUtil {
35 private static final InternalLogger logger = InternalLoggerFactory.getInstance(MacAddressUtil.class);
36
37 private static final int EUI64_MAC_ADDRESS_LENGTH = 8;
38 private static final int EUI48_MAC_ADDRESS_LENGTH = 6;
39
40
41
42
43
44
45
46
47 public static byte[] bestAvailableMac() {
48
49 byte[] bestMacAddr = EMPTY_BYTES;
50 InetAddress bestInetAddr = NetUtil.LOCALHOST4;
51
52
53 Map<NetworkInterface, InetAddress> ifaces = new LinkedHashMap<NetworkInterface, InetAddress>();
54 for (NetworkInterface iface: NetUtil.NETWORK_INTERFACES) {
55
56 Enumeration<InetAddress> addrs = SocketUtils.addressesFromNetworkInterface(iface);
57 if (addrs.hasMoreElements()) {
58 InetAddress a = addrs.nextElement();
59 if (!a.isLoopbackAddress()) {
60 ifaces.put(iface, a);
61 }
62 }
63 }
64
65 for (Entry<NetworkInterface, InetAddress> entry: ifaces.entrySet()) {
66 NetworkInterface iface = entry.getKey();
67 InetAddress inetAddr = entry.getValue();
68 if (iface.isVirtual()) {
69 continue;
70 }
71
72 byte[] macAddr;
73 try {
74 macAddr = SocketUtils.hardwareAddressFromNetworkInterface(iface);
75 } catch (SocketException e) {
76 logger.debug("Failed to get the hardware address of a network interface: {}", iface, e);
77 continue;
78 }
79
80 boolean replace = false;
81 int res = compareAddresses(bestMacAddr, macAddr);
82 if (res < 0) {
83
84 replace = true;
85 } else if (res == 0) {
86
87 res = compareAddresses(bestInetAddr, inetAddr);
88 if (res < 0) {
89
90 replace = true;
91 } else if (res == 0) {
92
93 if (bestMacAddr.length < macAddr.length) {
94 replace = true;
95 }
96 }
97 }
98
99 if (replace) {
100 bestMacAddr = macAddr;
101 bestInetAddr = inetAddr;
102 }
103 }
104
105 if (bestMacAddr == EMPTY_BYTES) {
106 return null;
107 }
108
109 if (bestMacAddr.length == EUI48_MAC_ADDRESS_LENGTH) {
110 byte[] newAddr = new byte[EUI64_MAC_ADDRESS_LENGTH];
111 System.arraycopy(bestMacAddr, 0, newAddr, 0, 3);
112 newAddr[3] = (byte) 0xFF;
113 newAddr[4] = (byte) 0xFE;
114 System.arraycopy(bestMacAddr, 3, newAddr, 5, 3);
115 bestMacAddr = newAddr;
116 } else {
117
118 bestMacAddr = Arrays.copyOf(bestMacAddr, EUI64_MAC_ADDRESS_LENGTH);
119 }
120
121 return bestMacAddr;
122 }
123
124
125
126
127
128 public static byte[] defaultMachineId() {
129 byte[] bestMacAddr = bestAvailableMac();
130 if (bestMacAddr == null) {
131 bestMacAddr = new byte[EUI64_MAC_ADDRESS_LENGTH];
132 PlatformDependent.threadLocalRandom().nextBytes(bestMacAddr);
133 logger.warn(
134 "Failed to find a usable hardware address from the network interfaces; using random bytes: {}",
135 formatAddress(bestMacAddr));
136 }
137 return bestMacAddr;
138 }
139
140
141
142
143
144
145 public static byte[] parseMAC(String value) {
146 final byte[] machineId;
147 final char separator;
148 switch (value.length()) {
149 case 17:
150 separator = value.charAt(2);
151 validateMacSeparator(separator);
152 machineId = new byte[EUI48_MAC_ADDRESS_LENGTH];
153 break;
154 case 23:
155 separator = value.charAt(2);
156 validateMacSeparator(separator);
157 machineId = new byte[EUI64_MAC_ADDRESS_LENGTH];
158 break;
159 default:
160 throw new IllegalArgumentException("value is not supported [MAC-48, EUI-48, EUI-64]");
161 }
162
163 final int end = machineId.length - 1;
164 int j = 0;
165 for (int i = 0; i < end; ++i, j += 3) {
166 final int sIndex = j + 2;
167 machineId[i] = StringUtil.decodeHexByte(value, j);
168 if (value.charAt(sIndex) != separator) {
169 throw new IllegalArgumentException("expected separator '" + separator + " but got '" +
170 value.charAt(sIndex) + "' at index: " + sIndex);
171 }
172 }
173
174 machineId[end] = StringUtil.decodeHexByte(value, j);
175
176 return machineId;
177 }
178
179 private static void validateMacSeparator(char separator) {
180 if (separator != ':' && separator != '-') {
181 throw new IllegalArgumentException("unsupported separator: " + separator + " (expected: [:-])");
182 }
183 }
184
185
186
187
188
189 public static String formatAddress(byte[] addr) {
190 StringBuilder buf = new StringBuilder(24);
191 for (byte b: addr) {
192 buf.append(String.format("%02x:", b & 0xff));
193 }
194 return buf.substring(0, buf.length() - 1);
195 }
196
197
198
199
200
201 static int compareAddresses(byte[] current, byte[] candidate) {
202 if (candidate == null || candidate.length < EUI48_MAC_ADDRESS_LENGTH) {
203 return 1;
204 }
205
206
207 boolean onlyZeroAndOne = true;
208 for (byte b: candidate) {
209 if (b != 0 && b != 1) {
210 onlyZeroAndOne = false;
211 break;
212 }
213 }
214
215 if (onlyZeroAndOne) {
216 return 1;
217 }
218
219
220 if ((candidate[0] & 1) != 0) {
221 return 1;
222 }
223
224
225 if ((candidate[0] & 2) == 0) {
226 if (current.length != 0 && (current[0] & 2) == 0) {
227
228 return 0;
229 } else {
230
231 return -1;
232 }
233 } else {
234 if (current.length != 0 && (current[0] & 2) == 0) {
235
236 return 1;
237 } else {
238
239 return 0;
240 }
241 }
242 }
243
244
245
246
247 private static int compareAddresses(InetAddress current, InetAddress candidate) {
248 return scoreAddress(current) - scoreAddress(candidate);
249 }
250
251 private static int scoreAddress(InetAddress addr) {
252 if (addr.isAnyLocalAddress() || addr.isLoopbackAddress()) {
253 return 0;
254 }
255 if (addr.isMulticastAddress()) {
256 return 1;
257 }
258 if (addr.isLinkLocalAddress()) {
259 return 2;
260 }
261 if (addr.isSiteLocalAddress()) {
262 return 3;
263 }
264
265 return 4;
266 }
267
268 private MacAddressUtil() { }
269 }