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