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