NetworkInterface.java revision 10914811ea02b4acc7ab1dc5a0ada1b54cdf2203
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 for (int i = 0; i < interfaceNames.length; ++i) { 270 interfaces[i] = NetworkInterface.getByName(interfaceNames[i]); 271 } 272 273 List<NetworkInterface> result = new ArrayList<NetworkInterface>(); 274 boolean[] peeked = new boolean[interfaces.length]; 275 for (int counter = 0; counter < interfaces.length; counter++) { 276 // If this interface has been touched, continue. 277 if (peeked[counter]) { 278 continue; 279 } 280 int counter2 = counter; 281 // Checks whether the following interfaces are children. 282 for (; counter2 < interfaces.length; counter2++) { 283 if (peeked[counter2]) { 284 continue; 285 } 286 if (interfaces[counter2].name.startsWith(interfaces[counter].name + ":")) { 287 // Tagged as peeked 288 peeked[counter2] = true; 289 interfaces[counter].children.add(interfaces[counter2]); 290 interfaces[counter2].parent = interfaces[counter]; 291 interfaces[counter].addresses.addAll(interfaces[counter2].addresses); 292 } 293 } 294 // Tagged as peeked 295 result.add(interfaces[counter]); 296 peeked[counter] = true; 297 } 298 return result; 299 } 300 301 /** 302 * Compares the specified object to this {@code NetworkInterface} and 303 * returns whether they are equal or not. The object must be an instance of 304 * {@code NetworkInterface} with the same name, display name, and list 305 * of interface addresses. 306 * 307 * @param obj 308 * the object to compare with this instance. 309 * @return {@code true} if the specified object is equal to this {@code 310 * NetworkInterface}, {@code false} otherwise. 311 * @see #hashCode() 312 */ 313 @Override 314 public boolean equals(Object obj) { 315 if (obj == this) { 316 return true; 317 } 318 if (!(obj instanceof NetworkInterface)) { 319 return false; 320 } 321 NetworkInterface rhs = (NetworkInterface) obj; 322 // TODO: should the order of the addresses matter (we use List.equals)? 323 return interfaceIndex == rhs.interfaceIndex && 324 name.equals(rhs.name) && 325 addresses.equals(rhs.addresses); 326 } 327 328 /** 329 * Returns the hash code for this {@code NetworkInterface}. Since the 330 * name should be unique for each network interface the hash code is 331 * generated using the name. 332 */ 333 @Override public int hashCode() { 334 return name.hashCode(); 335 } 336 337 /** 338 * Returns a string containing details of this network interface. 339 * The exact format is deliberately unspecified. Callers that require a specific 340 * format should build a string themselves, using this class' accessor methods. 341 */ 342 @Override public String toString() { 343 StringBuilder sb = new StringBuilder(25); 344 sb.append("["); 345 sb.append(name); 346 sb.append("]["); 347 sb.append(interfaceIndex); 348 sb.append("]"); 349 for (InetAddress address : addresses) { 350 sb.append("["); 351 sb.append(address.toString()); 352 sb.append("]"); 353 } 354 return sb.toString(); 355 } 356 357 /** 358 * Returns a List of the InterfaceAddresses for this network interface. 359 * @since 1.6 360 */ 361 public List<InterfaceAddress> getInterfaceAddresses() { 362 return Collections.unmodifiableList(interfaceAddresses); 363 } 364 365 /** 366 * Returns an enumeration of all the sub-interfaces of this network interface. 367 * Sub-interfaces are also known as virtual interfaces. 368 * 369 * <p>For example, {@code eth0:1} would be a sub-interface of {@code eth0}. 370 * 371 * @return an Enumeration of all the sub-interfaces of this network interface 372 * @since 1.6 373 */ 374 public Enumeration<NetworkInterface> getSubInterfaces() { 375 return Collections.enumeration(children); 376 } 377 378 /** 379 * Returns the parent NetworkInterface of this interface if this is a 380 * sub-interface, or null if it's a physical (non virtual) interface. 381 * 382 * @return the NetworkInterface this interface is attached to. 383 * @since 1.6 384 */ 385 public NetworkInterface getParent() { 386 return parent; 387 } 388 389 /** 390 * Returns true if this network interface is up. 391 * 392 * @return true if the interface is up. 393 * @throws SocketException if an I/O error occurs. 394 * @since 1.6 395 */ 396 public boolean isUp() throws SocketException { 397 return hasFlag(IFF_UP); 398 } 399 400 /** 401 * Returns true if this network interface is a loopback interface. 402 * 403 * @return true if the interface is a loopback interface. 404 * @throws SocketException if an I/O error occurs. 405 * @since 1.6 406 */ 407 public boolean isLoopback() throws SocketException { 408 return hasFlag(IFF_LOOPBACK); 409 } 410 411 /** 412 * Returns true if this network interface is a point-to-point interface. 413 * (For example, a PPP connection using a modem.) 414 * 415 * @return true if the interface is point-to-point. 416 * @throws SocketException if an I/O error occurs. 417 * @since 1.6 418 */ 419 public boolean isPointToPoint() throws SocketException { 420 return hasFlag(IFF_POINTOPOINT); 421 } 422 423 /** 424 * Returns true if this network interface supports multicast. 425 * 426 * @throws SocketException if an I/O error occurs. 427 * @since 1.6 428 */ 429 public boolean supportsMulticast() throws SocketException { 430 return hasFlag(IFF_MULTICAST); 431 } 432 433 private boolean hasFlag(int mask) throws SocketException { 434 int flags = readIntFile("/sys/class/net/" + name + "/flags"); 435 return (flags & mask) != 0; 436 } 437 438 /** 439 * Returns the hardware address of the interface, if it has one, or null otherwise. 440 * 441 * @throws SocketException if an I/O error occurs. 442 * @since 1.6 443 */ 444 public byte[] getHardwareAddress() throws SocketException { 445 try { 446 // Parse colon-separated bytes with a trailing newline: "aa:bb:cc:dd:ee:ff\n". 447 String s = IoUtils.readFileAsString("/sys/class/net/" + name + "/address"); 448 byte[] result = new byte[s.length()/3]; 449 for (int i = 0; i < result.length; ++i) { 450 result[i] = (byte) Integer.parseInt(s.substring(3*i, 3*i + 2), 16); 451 } 452 // We only want to return non-zero hardware addresses. 453 for (int i = 0; i < result.length; ++i) { 454 if (result[i] != 0) { 455 return result; 456 } 457 } 458 return null; 459 } catch (Exception ex) { 460 throw rethrowAsSocketException(ex); 461 } 462 } 463 464 /** 465 * Returns the Maximum Transmission Unit (MTU) of this interface. 466 * 467 * @return the value of the MTU for the interface. 468 * @throws SocketException if an I/O error occurs. 469 * @since 1.6 470 */ 471 public int getMTU() throws SocketException { 472 return readIntFile("/sys/class/net/" + name + "/mtu"); 473 } 474 475 /** 476 * Returns true if this interface is a virtual interface (also called 477 * a sub-interface). Virtual interfaces are, on some systems, interfaces 478 * created as a child of a physical interface and given different settings 479 * (like address or MTU). Usually the name of the interface will the name of 480 * the parent followed by a colon (:) and a number identifying the child, 481 * since there can be several virtual interfaces attached to a single 482 * physical interface. 483 * 484 * @return true if this interface is a virtual interface. 485 * @since 1.6 486 */ 487 public boolean isVirtual() { 488 return parent != null; 489 } 490} 491