NetworkInterface.java revision 8ffa0b68c9fd3f722bee2bcd94b1d38151a0791d
1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package java.net; 19 20import java.io.BufferedReader; 21import java.io.File; 22import java.io.FileDescriptor; 23import java.io.FileReader; 24import java.io.IOException; 25import java.util.ArrayList; 26import java.util.Arrays; 27import java.util.Collections; 28import java.util.Enumeration; 29import java.util.LinkedList; 30import java.util.List; 31import java.util.Map; 32import libcore.io.ErrnoException; 33import libcore.io.IoUtils; 34import libcore.io.Libcore; 35import static libcore.io.OsConstants.*; 36 37/** 38 * This class is used to represent a network interface of the local device. An 39 * interface is defined by its address and a platform dependent name. The class 40 * provides methods to get all information about the available interfaces of the 41 * system or to identify the local interface of a joined multicast group. 42 */ 43public final class NetworkInterface extends Object { 44 private final String name; 45 private final int interfaceIndex; 46 private final List<InterfaceAddress> interfaceAddresses; 47 private final List<InetAddress> addresses; 48 49 private final List<NetworkInterface> children = new LinkedList<NetworkInterface>(); 50 51 private NetworkInterface parent = null; 52 53 private NetworkInterface(String name, int interfaceIndex, 54 List<InetAddress> addresses, List<InterfaceAddress> interfaceAddresses) { 55 this.name = name; 56 this.interfaceIndex = interfaceIndex; 57 this.addresses = addresses; 58 this.interfaceAddresses = interfaceAddresses; 59 } 60 61 static NetworkInterface forUnboundMulticastSocket() { 62 // This is what the RI returns for a MulticastSocket that hasn't been constrained 63 // to a specific interface. 64 return new NetworkInterface(null, -1, 65 Arrays.asList(Inet6Address.ANY), Collections.<InterfaceAddress>emptyList()); 66 } 67 68 /** 69 * Returns the index for the network interface, or -1 if unknown. 70 * @since 1.7 71 */ 72 public int getIndex() { 73 return interfaceIndex; 74 } 75 76 /** 77 * Returns the name of this network interface (such as "eth0" or "lo"). 78 */ 79 public String getName() { 80 return name; 81 } 82 83 /** 84 * Returns an enumeration of the addresses bound to this network interface. 85 */ 86 public Enumeration<InetAddress> getInetAddresses() { 87 return Collections.enumeration(addresses); 88 } 89 90 /** 91 * Returns a human-readable name for this network interface. On Android, this is the same 92 * string as returned by {@link #getName}. 93 */ 94 public String getDisplayName() { 95 return name; 96 } 97 98 /** 99 * Returns the {@code NetworkInterface} corresponding to the named network interface, or null 100 * if no interface has this name. 101 * 102 * @throws SocketException if an error occurs. 103 * @throws NullPointerException if {@code interfaceName == null}. 104 */ 105 public static NetworkInterface getByName(String interfaceName) throws SocketException { 106 if (interfaceName == null) { 107 throw new NullPointerException("interfaceName == null"); 108 } 109 if (!isValidInterfaceName(interfaceName)) { 110 return null; 111 } 112 113 int interfaceIndex = readIntFile("/sys/class/net/" + interfaceName + "/ifindex"); 114 List<InetAddress> addresses = new ArrayList<InetAddress>(); 115 List<InterfaceAddress> interfaceAddresses = new ArrayList<InterfaceAddress>(); 116 collectIpv6Addresses(interfaceName, interfaceIndex, addresses, interfaceAddresses); 117 collectIpv4Address(interfaceName, addresses, interfaceAddresses); 118 119 return new NetworkInterface(interfaceName, interfaceIndex, addresses, interfaceAddresses); 120 } 121 122 private static void collectIpv6Addresses(String interfaceName, int interfaceIndex, 123 List<InetAddress> addresses, List<InterfaceAddress> interfaceAddresses) throws SocketException { 124 // Format of /proc/net/if_inet6. 125 // All numeric fields are implicit hex, 126 // but not necessarily two-digit (http://code.google.com/p/android/issues/detail?id=34022). 127 // 1. IPv6 address 128 // 2. interface index 129 // 3. prefix length 130 // 4. scope 131 // 5. flags 132 // 6. interface name 133 // "00000000000000000000000000000001 01 80 10 80 lo" 134 // "fe800000000000000000000000000000 407 40 20 80 wlan0" 135 BufferedReader in = null; 136 try { 137 in = new BufferedReader(new FileReader("/proc/net/if_inet6")); 138 String suffix = " " + interfaceName; 139 String line; 140 while ((line = in.readLine()) != null) { 141 if (!line.endsWith(suffix)) { 142 continue; 143 } 144 145 // Extract the IPv6 address. 146 byte[] addressBytes = new byte[16]; 147 for (int i = 0; i < addressBytes.length; ++i) { 148 addressBytes[i] = (byte) Integer.parseInt(line.substring(2*i, 2*i + 2), 16); 149 } 150 151 // Extract the prefix length. 152 // Skip the IPv6 address and its trailing space. 153 int prefixLengthStart = 32 + 1; 154 // Skip the interface index and its trailing space. 155 prefixLengthStart = line.indexOf(' ', prefixLengthStart) + 1; 156 int prefixLengthEnd = line.indexOf(' ', prefixLengthStart); 157 short prefixLength = Short.parseShort(line.substring(prefixLengthStart, prefixLengthEnd), 16); 158 159 Inet6Address inet6Address = new Inet6Address(addressBytes, null, interfaceIndex); 160 addresses.add(inet6Address); 161 interfaceAddresses.add(new InterfaceAddress(inet6Address, prefixLength)); 162 } 163 } catch (Exception ex) { 164 throw rethrowAsSocketException(ex); 165 } finally { 166 IoUtils.closeQuietly(in); 167 } 168 } 169 170 private static void collectIpv4Address(String interfaceName, List<InetAddress> addresses, 171 List<InterfaceAddress> interfaceAddresses) throws SocketException { 172 FileDescriptor fd = null; 173 try { 174 fd = Libcore.os.socket(AF_INET, SOCK_DGRAM, 0); 175 InetAddress address = Libcore.os.ioctlInetAddress(fd, SIOCGIFADDR, interfaceName); 176 InetAddress broadcast = Libcore.os.ioctlInetAddress(fd, SIOCGIFBRDADDR, interfaceName); 177 InetAddress netmask = Libcore.os.ioctlInetAddress(fd, SIOCGIFNETMASK, interfaceName); 178 if (broadcast.equals(Inet4Address.ANY)) { 179 broadcast = null; 180 } 181 182 addresses.add(address); 183 interfaceAddresses.add(new InterfaceAddress((Inet4Address) address, 184 (Inet4Address) broadcast, (Inet4Address) netmask)); 185 } catch (ErrnoException errnoException) { 186 if (errnoException.errno != EADDRNOTAVAIL) { 187 // EADDRNOTAVAIL just means no IPv4 address for this interface. 188 // Anything else is a real error. 189 throw rethrowAsSocketException(errnoException); 190 } 191 } catch (Exception ex) { 192 throw rethrowAsSocketException(ex); 193 } finally { 194 IoUtils.closeQuietly(fd); 195 } 196 } 197 198 @FindBugsSuppressWarnings("DMI_HARDCODED_ABSOLUTE_FILENAME") 199 private static boolean isValidInterfaceName(String interfaceName) { 200 // Don't just stat because a crafty user might have / or .. in the supposed interface name. 201 for (String validName : new File("/sys/class/net").list()) { 202 if (interfaceName.equals(validName)) { 203 return true; 204 } 205 } 206 return false; 207 } 208 209 private static int readIntFile(String path) throws SocketException { 210 try { 211 String s = IoUtils.readFileAsString(path).trim(); 212 if (s.startsWith("0x")) { 213 return Integer.parseInt(s.substring(2), 16); 214 } else { 215 return Integer.parseInt(s); 216 } 217 } catch (Exception ex) { 218 throw rethrowAsSocketException(ex); 219 } 220 } 221 222 private static SocketException rethrowAsSocketException(Exception ex) throws SocketException { 223 SocketException result = new SocketException(); 224 result.initCause(ex); 225 throw result; 226 } 227 228 /** 229 * Returns the {@code NetworkInterface} corresponding to the given address, or null if no 230 * interface has this address. 231 * 232 * @throws SocketException if an error occurs. 233 * @throws NullPointerException if {@code address == null}. 234 */ 235 public static NetworkInterface getByInetAddress(InetAddress address) throws SocketException { 236 if (address == null) { 237 throw new NullPointerException("address == null"); 238 } 239 for (NetworkInterface networkInterface : getNetworkInterfacesList()) { 240 if (networkInterface.addresses.contains(address)) { 241 return networkInterface; 242 } 243 } 244 return null; 245 } 246 247 /** 248 * Returns the NetworkInterface corresponding to the given interface index, or null if no 249 * interface has this index. 250 * 251 * @throws SocketException if an error occurs. 252 * @since 1.7 253 */ 254 public static NetworkInterface getByIndex(int index) throws SocketException { 255 String name = Libcore.os.if_indextoname(index); 256 if (name == null) { 257 return null; 258 } 259 return NetworkInterface.getByName(name); 260 } 261 262 /** 263 * Gets a list of all network interfaces available on the local system or 264 * {@code null} if no interface is available. 265 * 266 * @return the list of {@code NetworkInterface} instances representing the 267 * available interfaces. 268 * @throws SocketException 269 * if an error occurs while getting the network interface 270 * information. 271 */ 272 public static Enumeration<NetworkInterface> getNetworkInterfaces() throws SocketException { 273 return Collections.enumeration(getNetworkInterfacesList()); 274 } 275 276 @FindBugsSuppressWarnings("DMI_HARDCODED_ABSOLUTE_FILENAME") 277 private static List<NetworkInterface> getNetworkInterfacesList() throws SocketException { 278 String[] interfaceNames = new File("/sys/class/net").list(); 279 NetworkInterface[] interfaces = new NetworkInterface[interfaceNames.length]; 280 boolean[] done = new boolean[interfaces.length]; 281 for (int i = 0; i < interfaceNames.length; ++i) { 282 interfaces[i] = NetworkInterface.getByName(interfaceNames[i]); 283 // http://b/5833739: getByName can return null if the interface went away between our 284 // readdir(2) and our stat(2), so mark interfaces that disappeared as 'done'. 285 if (interfaces[i] == null) { 286 done[i] = true; 287 } 288 } 289 290 List<NetworkInterface> result = new ArrayList<NetworkInterface>(); 291 for (int counter = 0; counter < interfaces.length; counter++) { 292 // If this interface has been dealt with already, continue. 293 if (done[counter]) { 294 continue; 295 } 296 int counter2 = counter; 297 // Checks whether the following interfaces are children. 298 for (; counter2 < interfaces.length; counter2++) { 299 if (done[counter2]) { 300 continue; 301 } 302 if (interfaces[counter2].name.startsWith(interfaces[counter].name + ":")) { 303 interfaces[counter].children.add(interfaces[counter2]); 304 interfaces[counter2].parent = interfaces[counter]; 305 interfaces[counter].addresses.addAll(interfaces[counter2].addresses); 306 done[counter2] = true; 307 } 308 } 309 result.add(interfaces[counter]); 310 done[counter] = true; 311 } 312 return result; 313 } 314 315 /** 316 * Compares the specified object to this {@code NetworkInterface} and 317 * returns whether they are equal or not. The object must be an instance of 318 * {@code NetworkInterface} with the same name, display name, and list 319 * of interface addresses. 320 * 321 * @param obj 322 * the object to compare with this instance. 323 * @return {@code true} if the specified object is equal to this {@code 324 * NetworkInterface}, {@code false} otherwise. 325 * @see #hashCode() 326 */ 327 @Override 328 public boolean equals(Object obj) { 329 if (obj == this) { 330 return true; 331 } 332 if (!(obj instanceof NetworkInterface)) { 333 return false; 334 } 335 NetworkInterface rhs = (NetworkInterface) obj; 336 // TODO: should the order of the addresses matter (we use List.equals)? 337 return interfaceIndex == rhs.interfaceIndex && 338 name.equals(rhs.name) && 339 addresses.equals(rhs.addresses); 340 } 341 342 /** 343 * Returns the hash code for this {@code NetworkInterface}. Since the 344 * name should be unique for each network interface the hash code is 345 * generated using the name. 346 */ 347 @Override public int hashCode() { 348 return name.hashCode(); 349 } 350 351 /** 352 * Returns a string containing details of this network interface. 353 * The exact format is deliberately unspecified. Callers that require a specific 354 * format should build a string themselves, using this class' accessor methods. 355 */ 356 @Override public String toString() { 357 StringBuilder sb = new StringBuilder(25); 358 sb.append("["); 359 sb.append(name); 360 sb.append("]["); 361 sb.append(interfaceIndex); 362 sb.append("]"); 363 for (InetAddress address : addresses) { 364 sb.append("["); 365 sb.append(address.toString()); 366 sb.append("]"); 367 } 368 return sb.toString(); 369 } 370 371 /** 372 * Returns a List of the InterfaceAddresses for this network interface. 373 * @since 1.6 374 */ 375 public List<InterfaceAddress> getInterfaceAddresses() { 376 return Collections.unmodifiableList(interfaceAddresses); 377 } 378 379 /** 380 * Returns an enumeration of all the sub-interfaces of this network interface. 381 * Sub-interfaces are also known as virtual interfaces. 382 * 383 * <p>For example, {@code eth0:1} would be a sub-interface of {@code eth0}. 384 * 385 * @return an Enumeration of all the sub-interfaces of this network interface 386 * @since 1.6 387 */ 388 public Enumeration<NetworkInterface> getSubInterfaces() { 389 return Collections.enumeration(children); 390 } 391 392 /** 393 * Returns the parent NetworkInterface of this interface if this is a 394 * sub-interface, or null if it's a physical (non virtual) interface. 395 * 396 * @return the NetworkInterface this interface is attached to. 397 * @since 1.6 398 */ 399 public NetworkInterface getParent() { 400 return parent; 401 } 402 403 /** 404 * Returns true if this network interface is up. 405 * 406 * @return true if the interface is up. 407 * @throws SocketException if an I/O error occurs. 408 * @since 1.6 409 */ 410 public boolean isUp() throws SocketException { 411 return hasFlag(IFF_UP); 412 } 413 414 /** 415 * Returns true if this network interface is a loopback interface. 416 * 417 * @return true if the interface is a loopback interface. 418 * @throws SocketException if an I/O error occurs. 419 * @since 1.6 420 */ 421 public boolean isLoopback() throws SocketException { 422 return hasFlag(IFF_LOOPBACK); 423 } 424 425 /** 426 * Returns true if this network interface is a point-to-point interface. 427 * (For example, a PPP connection using a modem.) 428 * 429 * @return true if the interface is point-to-point. 430 * @throws SocketException if an I/O error occurs. 431 * @since 1.6 432 */ 433 public boolean isPointToPoint() throws SocketException { 434 return hasFlag(IFF_POINTOPOINT); 435 } 436 437 /** 438 * Returns true if this network interface supports multicast. 439 * 440 * @throws SocketException if an I/O error occurs. 441 * @since 1.6 442 */ 443 public boolean supportsMulticast() throws SocketException { 444 return hasFlag(IFF_MULTICAST); 445 } 446 447 private boolean hasFlag(int mask) throws SocketException { 448 int flags = readIntFile("/sys/class/net/" + name + "/flags"); 449 return (flags & mask) != 0; 450 } 451 452 /** 453 * Returns the hardware address of the interface, if it has one, or null otherwise. 454 * 455 * @throws SocketException if an I/O error occurs. 456 * @since 1.6 457 */ 458 public byte[] getHardwareAddress() throws SocketException { 459 try { 460 // Parse colon-separated bytes with a trailing newline: "aa:bb:cc:dd:ee:ff\n". 461 String s = IoUtils.readFileAsString("/sys/class/net/" + name + "/address"); 462 byte[] result = new byte[s.length()/3]; 463 for (int i = 0; i < result.length; ++i) { 464 result[i] = (byte) Integer.parseInt(s.substring(3*i, 3*i + 2), 16); 465 } 466 // We only want to return non-zero hardware addresses. 467 for (int i = 0; i < result.length; ++i) { 468 if (result[i] != 0) { 469 return result; 470 } 471 } 472 return null; 473 } catch (Exception ex) { 474 throw rethrowAsSocketException(ex); 475 } 476 } 477 478 /** 479 * Returns the Maximum Transmission Unit (MTU) of this interface. 480 * 481 * @return the value of the MTU for the interface. 482 * @throws SocketException if an I/O error occurs. 483 * @since 1.6 484 */ 485 public int getMTU() throws SocketException { 486 return readIntFile("/sys/class/net/" + name + "/mtu"); 487 } 488 489 /** 490 * Returns true if this interface is a virtual interface (also called 491 * a sub-interface). Virtual interfaces are, on some systems, interfaces 492 * created as a child of a physical interface and given different settings 493 * (like address or MTU). Usually the name of the interface will the name of 494 * the parent followed by a colon (:) and a number identifying the child, 495 * since there can be several virtual interfaces attached to a single 496 * physical interface. 497 * 498 * @return true if this interface is a virtual interface. 499 * @since 1.6 500 */ 501 public boolean isVirtual() { 502 return parent != null; 503 } 504} 505