/*
 * Copyright 2016 The Netty Project
 *
 * The Netty Project licenses this file to you 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 io.netty.util.internal;

import io.netty.util.NetUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;

import static io.netty.util.internal.EmptyArrays.EMPTY_BYTES;

public final class MacAddressUtil {
    private static final InternalLogger logger = InternalLoggerFactory.getInstance(MacAddressUtil.class);

    private static final int EUI64_MAC_ADDRESS_LENGTH = 8;
    private static final int EUI48_MAC_ADDRESS_LENGTH = 6;

    
Obtains the best MAC address found on local network interfaces. Generally speaking, an active network interface used on public networks is better than a local network interface.
Returns:byte array containing a MAC. null if no MAC can be found.
/** * Obtains the best MAC address found on local network interfaces. * Generally speaking, an active network interface used on public * networks is better than a local network interface. * * @return byte array containing a MAC. null if no MAC can be found. */
public static byte[] bestAvailableMac() { // Find the best MAC address available. byte[] bestMacAddr = EMPTY_BYTES; InetAddress bestInetAddr = NetUtil.LOCALHOST4; // Retrieve the list of available network interfaces. Map<NetworkInterface, InetAddress> ifaces = new LinkedHashMap<NetworkInterface, InetAddress>(); try { Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); if (interfaces != null) { while (interfaces.hasMoreElements()) { NetworkInterface iface = interfaces.nextElement(); // Use the interface with proper INET addresses only. Enumeration<InetAddress> addrs = SocketUtils.addressesFromNetworkInterface(iface); if (addrs.hasMoreElements()) { InetAddress a = addrs.nextElement(); if (!a.isLoopbackAddress()) { ifaces.put(iface, a); } } } } } catch (SocketException e) { logger.warn("Failed to retrieve the list of available network interfaces", e); } for (Entry<NetworkInterface, InetAddress> entry: ifaces.entrySet()) { NetworkInterface iface = entry.getKey(); InetAddress inetAddr = entry.getValue(); if (iface.isVirtual()) { continue; } byte[] macAddr; try { macAddr = SocketUtils.hardwareAddressFromNetworkInterface(iface); } catch (SocketException e) { logger.debug("Failed to get the hardware address of a network interface: {}", iface, e); continue; } boolean replace = false; int res = compareAddresses(bestMacAddr, macAddr); if (res < 0) { // Found a better MAC address. replace = true; } else if (res == 0) { // Two MAC addresses are of pretty much same quality. res = compareAddresses(bestInetAddr, inetAddr); if (res < 0) { // Found a MAC address with better INET address. replace = true; } else if (res == 0) { // Cannot tell the difference. Choose the longer one. if (bestMacAddr.length < macAddr.length) { replace = true; } } } if (replace) { bestMacAddr = macAddr; bestInetAddr = inetAddr; } } if (bestMacAddr == EMPTY_BYTES) { return null; } switch (bestMacAddr.length) { case EUI48_MAC_ADDRESS_LENGTH: // EUI-48 - convert to EUI-64 byte[] newAddr = new byte[EUI64_MAC_ADDRESS_LENGTH]; System.arraycopy(bestMacAddr, 0, newAddr, 0, 3); newAddr[3] = (byte) 0xFF; newAddr[4] = (byte) 0xFE; System.arraycopy(bestMacAddr, 3, newAddr, 5, 3); bestMacAddr = newAddr; break; default: // Unknown bestMacAddr = Arrays.copyOf(bestMacAddr, EUI64_MAC_ADDRESS_LENGTH); } return bestMacAddr; }
Returns the result of bestAvailableMac() if non-null otherwise returns a random EUI-64 MAC address.
/** * Returns the result of {@link #bestAvailableMac()} if non-{@code null} otherwise returns a random EUI-64 MAC * address. */
public static byte[] defaultMachineId() { byte[] bestMacAddr = bestAvailableMac(); if (bestMacAddr == null) { bestMacAddr = new byte[EUI64_MAC_ADDRESS_LENGTH]; PlatformDependent.threadLocalRandom().nextBytes(bestMacAddr); logger.warn( "Failed to find a usable hardware address from the network interfaces; using random bytes: {}", formatAddress(bestMacAddr)); } return bestMacAddr; }
Parse a EUI-48, MAC-48, or EUI-64 MAC address from a String and return it as a byte[].
Params:
  • value – The string representation of the MAC address.
Returns:The byte representation of the MAC address.
/** * Parse a EUI-48, MAC-48, or EUI-64 MAC address from a {@link String} and return it as a {@code byte[]}. * @param value The string representation of the MAC address. * @return The byte representation of the MAC address. */
public static byte[] parseMAC(String value) { final byte[] machineId; final char separator; switch (value.length()) { case 17: separator = value.charAt(2); validateMacSeparator(separator); machineId = new byte[EUI48_MAC_ADDRESS_LENGTH]; break; case 23: separator = value.charAt(2); validateMacSeparator(separator); machineId = new byte[EUI64_MAC_ADDRESS_LENGTH]; break; default: throw new IllegalArgumentException("value is not supported [MAC-48, EUI-48, EUI-64]"); } final int end = machineId.length - 1; int j = 0; for (int i = 0; i < end; ++i, j += 3) { final int sIndex = j + 2; machineId[i] = StringUtil.decodeHexByte(value, j); if (value.charAt(sIndex) != separator) { throw new IllegalArgumentException("expected separator '" + separator + " but got '" + value.charAt(sIndex) + "' at index: " + sIndex); } } machineId[end] = StringUtil.decodeHexByte(value, j); return machineId; } private static void validateMacSeparator(char separator) { if (separator != ':' && separator != '-') { throw new IllegalArgumentException("unsupported separator: " + separator + " (expected: [:-])"); } }
Params:
  • addr – byte array of a MAC address.
Returns:hex formatted MAC address.
/** * @param addr byte array of a MAC address. * @return hex formatted MAC address. */
public static String formatAddress(byte[] addr) { StringBuilder buf = new StringBuilder(24); for (byte b: addr) { buf.append(String.format("%02x:", b & 0xff)); } return buf.substring(0, buf.length() - 1); }
Returns:positive - current is better, 0 - cannot tell from MAC addr, negative - candidate is better.
/** * @return positive - current is better, 0 - cannot tell from MAC addr, negative - candidate is better. */
// visible for testing static int compareAddresses(byte[] current, byte[] candidate) { if (candidate == null || candidate.length < EUI48_MAC_ADDRESS_LENGTH) { return 1; } // Must not be filled with only 0 and 1. boolean onlyZeroAndOne = true; for (byte b: candidate) { if (b != 0 && b != 1) { onlyZeroAndOne = false; break; } } if (onlyZeroAndOne) { return 1; } // Must not be a multicast address if ((candidate[0] & 1) != 0) { return 1; } // Prefer globally unique address. if ((candidate[0] & 2) == 0) { if (current.length != 0 && (current[0] & 2) == 0) { // Both current and candidate are globally unique addresses. return 0; } else { // Only candidate is globally unique. return -1; } } else { if (current.length != 0 && (current[0] & 2) == 0) { // Only current is globally unique. return 1; } else { // Both current and candidate are non-unique. return 0; } } }
Returns:positive - current is better, 0 - cannot tell, negative - candidate is better
/** * @return positive - current is better, 0 - cannot tell, negative - candidate is better */
private static int compareAddresses(InetAddress current, InetAddress candidate) { return scoreAddress(current) - scoreAddress(candidate); } private static int scoreAddress(InetAddress addr) { if (addr.isAnyLocalAddress() || addr.isLoopbackAddress()) { return 0; } if (addr.isMulticastAddress()) { return 1; } if (addr.isLinkLocalAddress()) { return 2; } if (addr.isSiteLocalAddress()) { return 3; } return 4; } private MacAddressUtil() { } }