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