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