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