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