Enable IPv6 address privacy for all interfaces and potentially try to force reload. - Android Network

Android examples for Network:IPv6

Description

Enable IPv6 address privacy for all interfaces and potentially try to force reload.

Demo Code

/*****************************************************************************
 *  Project: Android IPv6Config//  w ww  .j a  v  a2 s.co m
 *  Description: Android application to change IPv6 kernel configuration
 *  Author: Ren? Mayrhofer
 *  Copyright: Ren? Mayrhofer, 2011-2014
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 
 * as published by the Free Software Foundation.
 *****************************************************************************/
import java.io.File;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Main{
    /** Our logger for this class. */
    private final static Logger logger = java.util.logging.Logger
            .getLogger(Constants.LOG_TAG);
    /** Identifies the gateway of a route. */
    private final static String ROUTE_GATEWAY = "via";
    /** Identifies the device of a route. */
    private final static String ROUTE_DEVICE = "dev";
    /** Search for the "ip" and "busybox" binaries at these locations. */
    public final static String[] LINUX_BINARY_LOCATIONS = { "/sbin/",
            "/bin/", "/system/bin/", "/system/xbin/" };
    /** Preferred is to use the "ip" binary directly. 
     * @see BUSYBOX_BINARY_PREFIX
     */
    public final static String IP_BINARY = "ip";
    /** But if no (working) ip binary can be found, then try to use busybox with an "ip" applet.
     * @see IP_BINARY
     */
    public final static String BUSYBOX_BINARY = "busybox";
    /** Command to get and set network interface addresses and options under modern Linux systems. */
    private final static String ADDRESSES_COMMAND = " addr";
    /** Command to get and set routes under modern Linux systems. */
    private final static String ROUTES_COMMAND = " route";
    /** Option to select only IPv6 addresses/routes. */
    private final static String OPTION_IPv6_ONLY = " -6 ";
    /** Simply the shell command (and if necessary path) to execute a standard POSIX shell. */
    public final static String SH_COMMAND = "sh";
    /** Path for the IPv6 configuration kernel options. */
    public final static String IPV6_CONFIG_TREE = "/proc/sys/net/ipv6/conf/";
    /** First part of the command to enable IPv6 address privacy (before interface name). */
    private final static String ENABLE_ADDRESS_PRIVACY_PART1 = "echo 2 > "
            + IPV6_CONFIG_TREE;
    /** First part of the command to disable IPv6 address privacy (before interface name). */
    private final static String DISABLE_ADDRESS_PRIVACY_PART1 = "echo 0 > "
            + IPV6_CONFIG_TREE;
    /** Second part of the command to enable/disable IPv6 address privacy (after interface name). */
    private final static String ADDRESS_PRIVACY_PART2 = "/use_tempaddr";
    /** Interface "name" to denote all network interface for kernel configuration options. */
    private final static String CONF_INTERFACES_ALL = "all";
    /** Interface "name" to denote the default kernel configuration options for new (hotplug enabled) network interfaces. */
    private final static String CONF_INTERFACES_DEFAULT = "default";
    /** Command to get and set network interface status under modern Linux systems (up/down mostly). */
    private final static String SET_INTERFACE = " link set ";
    /** Option to set network interface up.  */
    private final static String UP = " up";
    /** Option to set network interface down. */
    private final static String DOWN = " down";
    /** Option to add network interface / addresses / routes. */
    private final static String ADD = " add ";
    /** Delay between setting an interface down and up to force its IPv6 address to be reset (in milliseconds). */
    public final static int INTERFACE_DOWN_UP_DELAY = 100;
    /** Static initializer: find out where to call the "ip" binary from and remember for future use. */
    private static String ipBinaryLocation = null;
    private static String ipBinaryTriedPaths = null;
    /** Enable address privacy for all interfaces and potentially try to force reload.
     * 
     * @param enablePrivacy If true, enable privacy. If false, disable address privacy. 
     * @param forceAddressReload If set to true, each interface will also be 
     *        reset by calling forceAddressReload.
     * @return false if address privacy could not be set on any of the interfaces,
     *         true if all of them could be set.
     */
    public static boolean enableIPv6AddressPrivacy(boolean enablePrivacy,
            boolean forceAddressReload) {
        logger.fine((enablePrivacy ? "Enabling" : "Disabling")
                + " IPv6 address privacy"
                + (forceAddressReload ? " and forcing reload of interfaces"
                        : ""));

        boolean ret = true;
        LinkedList<String> allIfaces = new LinkedList<String>();
        LinkedList<String> modifiedIfacesToReload = new LinkedList<String>();

        // include the special "default" and "all" trees
        allIfaces.add(CONF_INTERFACES_ALL);
        allIfaces.add(CONF_INTERFACES_DEFAULT);

        // for now, use static interface names
        // TODO: take all other interfaces with IPv6 addresses as well
        allIfaces.add("eth0"); // WLAN interface on HTC Desire, Desire HD and Google Nexus S (and probably others)
        allIfaces.add("rmnet0"); // GPRS/UMTS interface
        allIfaces.add("rmnet1");
        allIfaces.add("rmnet2");
        allIfaces.add("ip6tnl0"); // IPv6 in/over IPv4 tunnel
        allIfaces.add("tiwlan0"); // WLAN interface on Motorola Milestone

        /* query IPv6 default route so that we only need to force reload on
         * those interfaces that are actually used for IPv6 outgoing traffic
         */
        LinkedList<String> ifacesWithIPv6Route = getIfacesWithIPv6DefaultRoute();

        for (String iface : allIfaces) {
            File configDir = new File(IPV6_CONFIG_TREE + iface);
            // only try to enable if this is indeed known as an IPv6-capable interface to the kernel
            if (configDir.isDirectory())
                if (enableIPv6AddressPrivacy(iface, enablePrivacy)) {
                    if (ifacesWithIPv6Route.contains(iface))
                        modifiedIfacesToReload.add(iface);
                } else
                    ret = false;
        }

        if (forceAddressReload)
            forceAddressReload(modifiedIfacesToReload);

        return ret;
    }
    /** Enable address privacy for a specific interface. This sets the 
     * "use_tempaddr" kernel option to "2" for the given interface.
     * 
     * @param enablePrivacy If true, enable privacy. If false, disable address privacy. 
     * @return true if the kernel option could be set, false otherwise. 
     */
    public static boolean enableIPv6AddressPrivacy(String iface,
            boolean enablePrivacy) {
        try {
            if (Command.executeCommand(SH_COMMAND, true,
                    (enablePrivacy ? ENABLE_ADDRESS_PRIVACY_PART1
                            : DISABLE_ADDRESS_PRIVACY_PART1)
                            + iface
                            + ADDRESS_PRIVACY_PART2, null, null) == 0) {
                logger.finer("Enabled address privacy on interface "
                        + iface);
                return true;
            } else {
                return false;
            }
        } catch (IOException e) {
            logger.severe("Unable to execute system command, address privacy may not be enabled (access privileges missing?) "
                    + e);
            return false;
        } catch (InterruptedException e) {
            return false;
        }
    }
    /** Determine which interfaces have an IPv6 default route set.
     * 
     * @return the list of interfaces with an IPv6 default route.
     */
    public static LinkedList<String> getIfacesWithIPv6DefaultRoute() {
        LinkedList<String> ifaces = new LinkedList<String>();
        LinkedList<RouteDetail> routes;
        try {
            routes = LinuxIPCommandHelper.getRouteOutput(true);
            for (RouteDetail route : routes) {
                if (route.target.equalsIgnoreCase("default")
                        || route.target.equals("::/0")
                        || route.target.equals("2000::/3") // with IPv6, a route prefix of 2000::/3 is currently enough as a default route
                ) {
                    // ok, default route found
                    logger.info("Found default IPv6 route " + route.target
                            + " pointing to gateway '" + route.gateway
                            + "' on interface '" + route.iface + "'");
                    ifaces.add(route.iface);
                }
            }

            if (ifaces.size() == 0)
                logger.info("Unable to find any IPv6 default route");
        } catch (IOException e) {
            logger.warning("Unable to query Linux IPv4 main routing table"
                    + e);
        }
        return ifaces;
    }
    /** Tries to force the interface to reset its addresses by setting it down and then up. */
    public static boolean forceAddressReload(String iface) {
        String cmd = getIPCommandLocation() + SET_INTERFACE + iface + " ";

        try {
            if (Command.executeCommand(SH_COMMAND, true, cmd + DOWN, null,
                    null) == 0) {
                // wait just a little for the interface to properly go down
                Thread.sleep(INTERFACE_DOWN_UP_DELAY);
                if (Command.executeCommand(SH_COMMAND, true, cmd + UP,
                        null, null) == 0) {
                    logger.finer("Reset interface " + iface
                            + " to force address reload");
                    return true;
                } else {
                    logger.warning("Set interface " + iface
                            + " down but was unable to set it up again");
                    return false;
                }
            } else {
                logger.warning("Unable to set interface " + iface + " down");
                return false;
            }
        } catch (IOException e) {
            logger.severe("Unable to execute system command, new addresses may not have been set (access privileges missing?) "
                    + e);
            return false;
        } catch (InterruptedException e) {
            return false;
        }
    }
    /** Tries to force all specified interfaces to reset their addresses by setting them down and then up. */
    public static boolean forceAddressReload(List<String> ifaces) {
        boolean ret = true;
        LinkedList<String> downedIfaces = new LinkedList<String>();

        String cmd = getIPCommandLocation() + SET_INTERFACE;

        // remember the default route so that we can restore it later on
        String currentDefaultRoute = getIPv4DefaultRouteSpecification();

        try {
            // first set all interfaces down
            for (String iface : ifaces) {
                // only try to enable if this is indeed known as an IPv6-capable interface to the kernel
                File configDir = new File(IPV6_CONFIG_TREE + iface);
                if (configDir.isDirectory()
                        && !iface.equals(CONF_INTERFACES_ALL)
                        && !iface.equals(CONF_INTERFACES_DEFAULT)) {
                    if (Command.executeCommand(SH_COMMAND, true, cmd
                            + iface + DOWN, null, null) == 0)
                        downedIfaces.add(iface);
                    else {
                        logger.warning("Unable to set interface " + iface
                                + " down, will not try to set it up again");
                        ret = false;
                    }
                }
            }

            // then wait just a little for the interfaces to properly go down
            Thread.sleep(INTERFACE_DOWN_UP_DELAY);

            // and start all those again that were set down
            for (String iface : downedIfaces) {
                if (Command.executeCommand(SH_COMMAND, true, cmd + iface
                        + UP, null, null) == 0)
                    logger.finer("Reset interface " + iface
                            + " to force address reload");
                else {
                    logger.warning("Set interface " + iface
                            + " down but was unable to set it up again");
                    ret = false;
                }
            }

            // if we had one, restore old default route
            if (currentDefaultRoute != null
                    && currentDefaultRoute.length() > 0) {
                if (Command.executeCommand(SH_COMMAND, true,
                        getIPCommandLocation() + ROUTES_COMMAND + ADD
                                + currentDefaultRoute, null, null) == 0)
                    logger.fine("Reloaded default route '"
                            + currentDefaultRoute + "'");
                else {
                    logger.warning("Unable to reload default route '"
                            + currentDefaultRoute
                            + "', connectivity may be broken until next network interface change!");
                }
            }

            return ret;
        } catch (IOException e) {
            logger.severe("Unable to execute system command, new addresses may not have been set (access privileges missing?) "
                    + e);
            return false;
        } catch (InterruptedException e) {
            return false;
        }
    }
    /** Returns the list of routes in the main routing table. 
     *  
     * @param queryIPv6 If true, then IPv6 routes are queried. If false, then IPv4 routes are queried.
     */
    public static LinkedList<RouteDetail> getRouteOutput(boolean queryIPv6)
            throws IOException {
        String cmd = getIPCommandLocation()
                + (queryIPv6 ? OPTION_IPv6_ONLY : "") + ROUTES_COMMAND;
        StringTokenizer lines = null;
        LinkedList<RouteDetail> list = new LinkedList<RouteDetail>();

        logger.warning("Acquiring route details with command '" + cmd + "'");

        try {
            lines = new StringTokenizer(Command.executeCommand(cmd, false,
                    false, null), "\n");
        } catch (Exception e) {
            logger.log(Level.WARNING,
                    "Tried to parse routes, but could not", e);
        }

        RouteDetail cur = null;
        while (lines != null && lines.hasMoreTokens()) {
            String line = lines.nextToken();
            logger.finest("getRouteOutput: parsing line '" + line + "'");

            StringTokenizer fields = new StringTokenizer(line, " \t");

            // the first field is always the target
            cur = new RouteDetail();
            cur.fullRouteLine = line;
            cur.target = fields.nextToken();

            // then we get options defined by "dev" or "via" (and others that we ignore)
            while (fields.hasMoreTokens()) {
                String opt = fields.nextToken().trim();
                logger.finest("getRouteOutput: trying to parse option '"
                        + opt + "'");

                if (opt.equals(ROUTE_GATEWAY) && fields.hasMoreTokens()) {
                    cur.gateway = InetAddress.getByName(fields.nextToken()
                            .trim());
                    logger.finest("getRouteOutput: found gateway "
                            + cur.gateway + " for target " + cur.target);
                } else if (opt.equals(ROUTE_DEVICE)
                        && fields.hasMoreTokens()) {
                    cur.iface = fields.nextToken().trim();
                    logger.finest("getRouteOutput: found interface "
                            + cur.iface + " for target " + cur.target);
                } else {
                    logger.finest("getRouteOutput: ignoring unknown option '"
                            + opt
                            + "' or no further field in string. Cannot parse.");
                }
            }

            // line finished, add route to list
            list.add(cur);
        }

        return list;
    }
    /** Helper to locate a usable "ip" command or null if none is found. */
    public static String getIPCommandLocation() {
        if (ipBinaryLocation == null) {
            ipBinaryTriedPaths = "";

            if (!tryIPBinaries(LINUX_BINARY_LOCATIONS, IP_BINARY, null)
                    && !tryIPBinaries(LINUX_BINARY_LOCATIONS,
                            BUSYBOX_BINARY, IP_BINARY))
                logger.severe("Could not find ip binary in"
                        + ipBinaryTriedPaths
                        + ", will be unable to read network interface details");
        }
        return ipBinaryLocation;
    }
    /** Returns the IPv4 default route specification (the full line of 
     * "ip route" output) for restoring it later on (e.g. after an interface
     * reload) or null if no default route is known.
     */
    public static String getIPv4DefaultRouteSpecification() {
        LinkedList<RouteDetail> routes;
        try {
            routes = LinuxIPCommandHelper.getRouteOutput(false);
            for (RouteDetail route : routes) {
                if (route.target.equalsIgnoreCase("default")
                        || route.target.equals("0.0.0.0/0")) {
                    // ok, default route found
                    logger.info("Found default IPv4 route pointing to gateway '"
                            + route.gateway
                            + "' on interface '"
                            + route.iface + "'");
                    return route.fullRouteLine;
                }
            }
        } catch (IOException e) {
            logger.warning("Unable to query Linux IPv4 main routing table"
                    + e);
        }
        return null;
    }
    /** Helper function to try a list of paths with a command to verify if "ip addr" can be executed correctly.
     * 
     * @return true if a working "ip addr" call could be made, false otherwise. If true is returned,
     *          the working full binary path is stored in ipBinaryLocation.
     * @see ipBinaryLocation 
     */
    private static boolean tryIPBinaries(String[] paths, String cmd,
            String cmd2) {
        for (String path : paths) {
            String binary = path + cmd;
            // sanity check: can we actually execute our command?
            logger.finer("Checking for availibility of command '" + binary
                    + "'");
            if (new File(binary).canRead()) {
                if (cmd2 != null)
                    binary = binary + " " + cmd2;
                /* second sanity check: does this binary work?
                 * (E.g. on the Samsung Galaxy S2, there actually is a binary under 
                 * /system/bin/ip that claims to work, but doesn't).
                 */
                try {
                    logger.fine("Trying to execute cmd '" + binary
                            + ADDRESSES_COMMAND + "'");
                    Command.executeCommand(binary + ADDRESSES_COMMAND,
                            false, false, null);
                    logger.fine("Found working ip binary in " + binary);
                    ipBinaryLocation = binary;
                    return true;
                } catch (Exception e) {
                    logger.warning("Found ip binary in "
                            + binary
                            + ", but does not behave as expected. Trying next location.");
                }
            }
            ipBinaryTriedPaths = ipBinaryTriedPaths + " '" + binary + "'";
        }
        return false;
    }
}

Related Tutorials