/common/src/main/java/io/netty/util/internal/MacAddressUtil.java

https://gitlab.com/taichu/netty · Java · 221 lines · 144 code · 27 blank · 50 comment · 44 complexity · 3d9d438ba0f5ee76e0801d38feb309d6 MD5 · raw file

  1. /*
  2. * Copyright 2016 The Netty Project
  3. *
  4. * The Netty Project licenses this file to you under the Apache License,
  5. * version 2.0 (the "License"); you may not use this file except in compliance
  6. * with the License. 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, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations
  14. * under the License.
  15. */
  16. package io.netty.util.internal;
  17. import java.net.InetAddress;
  18. import java.net.NetworkInterface;
  19. import java.net.SocketException;
  20. import java.util.Arrays;
  21. import java.util.Enumeration;
  22. import java.util.LinkedHashMap;
  23. import java.util.Map;
  24. import java.util.Map.Entry;
  25. import io.netty.util.NetUtil;
  26. import io.netty.util.internal.logging.InternalLogger;
  27. import io.netty.util.internal.logging.InternalLoggerFactory;
  28. public final class MacAddressUtil {
  29. /**
  30. * Length of a valid MAC address.
  31. */
  32. public static final int MAC_ADDRESS_LENGTH = 8;
  33. private static final byte[] NOT_FOUND = { -1 };
  34. private static final InternalLogger logger = InternalLoggerFactory.getInstance(MacAddressUtil.class);
  35. /**
  36. * Obtains the best MAC address found on local network interfaces.
  37. * Generally speaking, an active network interface used on public
  38. * networks is better than a local network interface.
  39. *
  40. * @return byte array containing a MAC. null if no MAC can be found.
  41. */
  42. public static byte[] bestAvailableMac() {
  43. // Find the best MAC address available.
  44. byte[] bestMacAddr = NOT_FOUND;
  45. InetAddress bestInetAddr = NetUtil.LOCALHOST4;
  46. // Retrieve the list of available network interfaces.
  47. Map<NetworkInterface, InetAddress> ifaces = new LinkedHashMap<NetworkInterface, InetAddress>();
  48. try {
  49. for (Enumeration<NetworkInterface> i = NetworkInterface.getNetworkInterfaces(); i.hasMoreElements();) {
  50. NetworkInterface iface = i.nextElement();
  51. // Use the interface with proper INET addresses only.
  52. Enumeration<InetAddress> addrs = iface.getInetAddresses();
  53. if (addrs.hasMoreElements()) {
  54. InetAddress a = addrs.nextElement();
  55. if (!a.isLoopbackAddress()) {
  56. ifaces.put(iface, a);
  57. }
  58. }
  59. }
  60. } catch (SocketException e) {
  61. logger.warn("Failed to retrieve the list of available network interfaces", e);
  62. }
  63. for (Entry<NetworkInterface, InetAddress> entry: ifaces.entrySet()) {
  64. NetworkInterface iface = entry.getKey();
  65. InetAddress inetAddr = entry.getValue();
  66. if (iface.isVirtual()) {
  67. continue;
  68. }
  69. byte[] macAddr;
  70. try {
  71. macAddr = iface.getHardwareAddress();
  72. } catch (SocketException e) {
  73. logger.debug("Failed to get the hardware address of a network interface: {}", iface, e);
  74. continue;
  75. }
  76. boolean replace = false;
  77. int res = compareAddresses(bestMacAddr, macAddr);
  78. if (res < 0) {
  79. // Found a better MAC address.
  80. replace = true;
  81. } else if (res == 0) {
  82. // Two MAC addresses are of pretty much same quality.
  83. res = compareAddresses(bestInetAddr, inetAddr);
  84. if (res < 0) {
  85. // Found a MAC address with better INET address.
  86. replace = true;
  87. } else if (res == 0) {
  88. // Cannot tell the difference. Choose the longer one.
  89. if (bestMacAddr.length < macAddr.length) {
  90. replace = true;
  91. }
  92. }
  93. }
  94. if (replace) {
  95. bestMacAddr = macAddr;
  96. bestInetAddr = inetAddr;
  97. }
  98. }
  99. if (bestMacAddr == NOT_FOUND) {
  100. return null;
  101. }
  102. switch (bestMacAddr.length) {
  103. case 6: // EUI-48 - convert to EUI-64
  104. byte[] newAddr = new byte[MAC_ADDRESS_LENGTH];
  105. System.arraycopy(bestMacAddr, 0, newAddr, 0, 3);
  106. newAddr[3] = (byte) 0xFF;
  107. newAddr[4] = (byte) 0xFE;
  108. System.arraycopy(bestMacAddr, 3, newAddr, 5, 3);
  109. bestMacAddr = newAddr;
  110. break;
  111. default: // Unknown
  112. bestMacAddr = Arrays.copyOf(bestMacAddr, MAC_ADDRESS_LENGTH);
  113. }
  114. return bestMacAddr;
  115. }
  116. /**
  117. * @param addr byte array of a MAC address.
  118. * @return hex formatted MAC address.
  119. */
  120. public static String formatAddress(byte[] addr) {
  121. StringBuilder buf = new StringBuilder(24);
  122. for (byte b: addr) {
  123. buf.append(String.format("%02x:", b & 0xff));
  124. }
  125. return buf.substring(0, buf.length() - 1);
  126. }
  127. /**
  128. * @return positive - current is better, 0 - cannot tell from MAC addr, negative - candidate is better.
  129. */
  130. private static int compareAddresses(byte[] current, byte[] candidate) {
  131. if (candidate == null) {
  132. return 1;
  133. }
  134. // Must be EUI-48 or longer.
  135. if (candidate.length < 6) {
  136. return 1;
  137. }
  138. // Must not be filled with only 0 and 1.
  139. boolean onlyZeroAndOne = true;
  140. for (byte b: candidate) {
  141. if (b != 0 && b != 1) {
  142. onlyZeroAndOne = false;
  143. break;
  144. }
  145. }
  146. if (onlyZeroAndOne) {
  147. return 1;
  148. }
  149. // Must not be a multicast address
  150. if ((candidate[0] & 1) != 0) {
  151. return 1;
  152. }
  153. // Prefer globally unique address.
  154. if ((current[0] & 2) == 0) {
  155. if ((candidate[0] & 2) == 0) {
  156. // Both current and candidate are globally unique addresses.
  157. return 0;
  158. } else {
  159. // Only current is globally unique.
  160. return 1;
  161. }
  162. } else {
  163. if ((candidate[0] & 2) == 0) {
  164. // Only candidate is globally unique.
  165. return -1;
  166. } else {
  167. // Both current and candidate are non-unique.
  168. return 0;
  169. }
  170. }
  171. }
  172. /**
  173. * @return positive - current is better, 0 - cannot tell, negative - candidate is better
  174. */
  175. private static int compareAddresses(InetAddress current, InetAddress candidate) {
  176. return scoreAddress(current) - scoreAddress(candidate);
  177. }
  178. private static int scoreAddress(InetAddress addr) {
  179. if (addr.isAnyLocalAddress() || addr.isLoopbackAddress()) {
  180. return 0;
  181. }
  182. if (addr.isMulticastAddress()) {
  183. return 1;
  184. }
  185. if (addr.isLinkLocalAddress()) {
  186. return 2;
  187. }
  188. if (addr.isSiteLocalAddress()) {
  189. return 3;
  190. }
  191. return 4;
  192. }
  193. private MacAddressUtil() { }
  194. }