• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 java.io.BufferedReader;
21 import java.io.File;
22 import java.io.FileDescriptor;
23 import java.io.FileReader;
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Collections;
28 import java.util.Enumeration;
29 import java.util.LinkedList;
30 import java.util.List;
31 import java.util.Map;
32 import libcore.io.ErrnoException;
33 import libcore.io.IoUtils;
34 import libcore.io.Libcore;
35 import 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  */
43 public 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 
NetworkInterface(String name, int interfaceIndex, List<InetAddress> addresses, List<InterfaceAddress> interfaceAddresses)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 
forUnboundMulticastSocket()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      */
getIndex()73     public int getIndex() {
74         return interfaceIndex;
75     }
76 
77     /**
78      * Returns the name of this network interface (such as "eth0" or "lo").
79      */
getName()80     public String getName() {
81         return name;
82     }
83 
84     /**
85      * Returns an enumeration of the addresses bound to this network interface.
86      */
getInetAddresses()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      */
getDisplayName()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      */
getByName(String interfaceName)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 
collectIpv6Addresses(String interfaceName, int interfaceIndex, List<InetAddress> addresses, List<InterfaceAddress> interfaceAddresses)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 
collectIpv4Address(String interfaceName, List<InetAddress> addresses, List<InterfaceAddress> interfaceAddresses)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")
isValidInterfaceName(String interfaceName)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 
readIntFile(String path)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 
rethrowAsSocketException(Exception ex)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      */
getByInetAddress(InetAddress address)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      */
getByIndex(int index)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      */
getNetworkInterfaces()273     public static Enumeration<NetworkInterface> getNetworkInterfaces() throws SocketException {
274         return Collections.enumeration(getNetworkInterfacesList());
275     }
276 
277     @FindBugsSuppressWarnings("DMI_HARDCODED_ABSOLUTE_FILENAME")
getNetworkInterfacesList()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
equals(Object obj)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      */
hashCode()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      */
toString()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      */
getInterfaceAddresses()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      */
getSubInterfaces()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      */
getParent()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      */
isUp()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      */
isLoopback()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      */
isPointToPoint()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      */
supportsMulticast()444     public boolean supportsMulticast() throws SocketException {
445         return hasFlag(IFF_MULTICAST);
446     }
447 
hasFlag(int mask)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      */
getHardwareAddress()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      */
getMTU()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      */
isVirtual()502     public boolean isVirtual() {
503         return parent != null;
504     }
505 }
506