Changeset 5a63926 for core


Ignore:
Timestamp:
Mar 3, 2018 12:52:07 PM (2 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
3bc9053
Parents:
67e07d2
Message:

Address utils: Skip IPs of down interfaces on Windows
Improve identification of temporary IPv6 addresses
CLI improvements

File:
1 edited

Legend:

Unmodified
Added
Removed
  • core/java/src/net/i2p/util/Addresses.java

    r67e07d2 r5a63926  
    1717import java.net.UnknownHostException;
    1818import java.util.ArrayList;
     19import java.util.Arrays;
    1920import java.util.Collections;
    2021import java.util.Enumeration;
    2122import java.util.HashMap;
     23import java.util.HashSet;
    2224import java.util.List;
    2325import java.util.Map;
     
    4951    private static long _ifCacheTime;
    5052    private static final Map<Inet6Address, Inet6Addr> _ifCache = INET6_CACHE_ENABLED ? new HashMap<Inet6Address, Inet6Addr>(8) : null;
     53    /** 12 char hex lower case */
     54    private static final Set<String> _macCache = new HashSet<String>();
    5155
    5256    /**
    5357     *  Do we have any non-loop, non-wildcard IPv4 address at all?
     58     *  Warning, very slow on Windows, appx. 200ms + 50ms/interface
     59     *
    5460     *  @since 0.9.4
    5561     */
     
    6167    /**
    6268     *  Do we have any non-loop, non-wildcard IPv6 address at all?
     69     *  Warning, very slow on Windows, appx. 200ms + 50ms/interface
     70     *
    6371     *  @since 0.9.29
    6472     */
     
    7280    }
    7381
    74     /** @return the first non-local address IPv4 address it finds, or null */
     82    /**
     83     *  Warning, very slow on Windows, appx. 200ms + 50ms/interface
     84     *
     85     * @return the first non-local address IPv4 address it finds, or null
     86     */
    7587    public static String getAnyAddress() {
    7688        SortedSet<String> a = getAddresses();
     
    8193
    8294    /**
     95     *  Warning, very slow on Windows, appx. 200ms + 50ms/interface
     96     *
    8397     *  @return a sorted set of all addresses, excluding
    8498     *  IPv6, local, broadcast, multicast, etc.
     
    89103
    90104    /**
     105     *  Warning, very slow on Windows, appx. 200ms + 50ms/interface
     106     *
    91107     *  @return a sorted set of all addresses, excluding
    92108     *  only link local and multicast
     
    104120     *  See e.g. TransportUtil.isPubliclyRoutable().
    105121     *
     122     *  Warning, very slow on Windows, appx. 200ms + 50ms/interface
     123     *
    106124     *  @return a sorted set of all addresses including wildcard
    107125     *  @param includeLocal whether to include local
     
    120138     *  are included with IPv6 results. Additional validation is recommended.
    121139     *  See e.g. TransportUtil.isPubliclyRoutable().
     140     *
     141     *  Warning, very slow on Windows, appx. 200ms + 50ms/interface
    122142     *
    123143     *  @return a sorted set of all addresses
     
    160180            Enumeration<NetworkInterface> ifcs = NetworkInterface.getNetworkInterfaces();
    161181            if (ifcs != null) {
     182                Set<String> newMacs = new HashSet<String>(8);
    162183                while (ifcs.hasMoreElements()) {
    163184                    NetworkInterface ifc = ifcs.nextElement();
     185                    if (!ifc.isUp()) {
     186                        // This is super-important on Windows which has 40+ down interfaces
     187                        // and will also deliver IP addresses for down interfaces,
     188                        // for example recently disconnected wifi.
     189                        //System.out.println("Skipping, down: " + ifc.getDisplayName());
     190                        continue;
     191                    }
     192                    try {
     193                        byte[] mac = ifc.getHardwareAddress();
     194                        if (mac != null && mac.length == 6) {
     195                            newMacs.add(DataHelper.toString(mac));
     196                        }
     197                    } catch (SocketException ioe) {}
    164198                    for(Enumeration<InetAddress> addrs =  ifc.getInetAddresses(); addrs.hasMoreElements();) {
    165199                        InetAddress addr = addrs.nextElement();
     
    177211                            rv.add(stripScope(addr.getHostAddress()));
    178212                        }
     213                    }
     214                }
     215                if (!newMacs.isEmpty()) {
     216                    synchronized(_macCache) {
     217                        _macCache.clear();
     218                        _macCache.addAll(newMacs);
    179219                    }
    180220                }
     
    551591            in = new BufferedReader(new InputStreamReader(new FileInputStream(IF_INET6_FILE), "ISO-8859-1"), 1024);
    552592            String line = null;
     593            StringBuilder buf = new StringBuilder(40);
    553594            while ( (line = in.readLine()) != null) {
    554595                // http://tldp.org/HOWTO/html_single/Linux+IPv6-HOWTO/#PROC-NET
     
    560601                if (as.length() != 32)
    561602                    continue;
    562                 StringBuilder buf = new StringBuilder(40);
     603                buf.setLength(0);
    563604                int i = 0;
    564605                while(true) {
     
    591632    /**
    592633     *  Is this address dynamic?
    593      *  Returns false if unknown.
     634     *  Should be reliable on Linux.
     635     *  Returns best guess on Windows, Mac, and BSD, only valid if global scope.
     636     *  @param addr an address of a local interface, as returned from e.g. getAddresses()
    594637     *  @since 0.9.28
    595638     */
    596639    public static boolean isDynamic(Inet6Address addr) {
    597640        if (!INET6_CACHE_ENABLED)
    598             return false;
     641            return isTemporary(addr);
    599642        Inet6Addr a;
    600643        synchronized(_ifCache) {
     
    609652    /**
    610653     *  Is this address deprecated?
    611      *  Returns false if unknown.
     654     *  Should be reliable on Linux.
     655     *  Returns false on Windows, Mac, and BSD.
     656     *  @param addr an address of a local interface, as returned from e.g. getAddresses()
    612657     *  @since 0.9.28
    613658     */
     
    627672    /**
    628673     *  Is this address temporary?
    629      *  Returns false if unknown.
     674     *  Should be reliable on Linux.
     675     *  Returns best guess on Windows, Mac, and BSD, only valid if global scope.
     676     *  @param addr an address of a local interface, as returned from e.g. getAddresses()
    630677     *  @since 0.9.28
    631678     */
    632679    public static boolean isTemporary(Inet6Address addr) {
    633         if (!INET6_CACHE_ENABLED)
    634             return false;
     680        if (!INET6_CACHE_ENABLED) {
     681            // do some guessing for Win, Mac, and BSD
     682            byte[] b = addr.getAddress();
     683            if (b.length != 16)
     684                return false;
     685            if (b[8] == 0 && b[9] == 0 && b[10] == 0 && b[11] == 0)
     686                return false;
     687            String last3 = DataHelper.toHexString(Arrays.copyOfRange(b, 13, 16));
     688            synchronized(_macCache) {
     689                for (String m : _macCache) {
     690                    if (m.endsWith(last3))
     691                        return false;
     692                }
     693            }
     694            // RFC 7136 Section 3 says the 'U' bit (byte[8] & 0x02) can't be relied on
     695            // See also RFC 4291 Appendix A
     696            // Windows doesn't do this but BSD seems to
     697            if (!SystemVersion.isWindows())
     698                return (b[8] & 0x02) == 0;
     699            // This will often be wrong for Windows, which follows
     700            // RFC 4941 Section 3.2.1, the 'U' bit is always 0.
     701            // No apparent way to tell.
     702            return true;
     703        }
    635704        Inet6Addr a;
    636705        synchronized(_ifCache) {
     
    660729                _ifCacheTime = 0;
    661730            }
     731        }
     732        synchronized(_macCache) {
     733            _macCache.clear();
    662734        }
    663735    }
     
    680752        print(a);
    681753        System.out.println("\nAll addresses:");
     754        long time = System.currentTimeMillis();
    682755        a = getAddresses(true, true, true);
     756        time = System.currentTimeMillis() - time;
    683757        print(a);
    684758        System.out.println("\nIPv6 address flags:");
     
    699773                else if (addr.isLoopbackAddress())
    700774                    buf.append(" loopback");
    701                 else
     775                else {
    702776                    buf.append(" global");
    703                 if (isTemporary(addr))
    704                     buf.append(" temporary");
    705                 if (isDeprecated(addr))
    706                     buf.append(" deprecated");
    707                 if (isDynamic(addr))
    708                     buf.append(" dynamic");
     777                    if (isTemporary(addr))
     778                        buf.append(" temporary");
     779                    if (isDynamic(addr))
     780                        buf.append(" dynamic");
     781                    if (isDeprecated(addr))
     782                        buf.append(" deprecated");
     783                }
    709784            } catch (UnknownHostException uhe) {}
    710785            System.out.println(buf.toString());
    711786        }
     787
     788        System.out.println("\nMac addresses:");
     789        Set<String> macs = new TreeSet<String>();
     790        StringBuilder buf = new StringBuilder(17);
     791        for (String m : _macCache) {
     792            buf.setLength(0);
     793            int i = 0;
     794            while(true) {
     795                buf.append(m.substring(i, i+2));
     796                i += 2;
     797                if (i >= 12)
     798                    break;
     799                buf.append(':');
     800            }
     801            macs.add(buf.toString());
     802        }
     803        print(macs);
    712804        System.out.println("\nIs connected? " + isConnected() +
    713805                           "\nIs conn IPv6? " + isConnectedIPv6());
    714806        System.out.println("Has v6 flags? " + INET6_CACHE_ENABLED);
    715     }
    716 
     807        System.out.println("Uses v6 temp? " + getPrivacyStatus());
     808        // Windows 8.1 Java 1.8.0_66 netbook appx. 200ms + 50ms/interface
     809        System.out.println("scan time:    " + DataHelper.formatDuration(time));
     810    }
     811
     812    /** @since 0.9.34 */
    717813    private static void print(Set<String> a) {
    718814        if (a.isEmpty()) {
     
    724820        }
    725821    }
     822
     823    /**
     824     * RFC 4941
     825     * @since 0.9.34
     826     */
     827    private static String getPrivacyStatus() {
     828        // Windows: netsh interface ipv6 show privacy
     829        // Mac: sysctl net.inet6.ip6.use_tempaddr (1 is enabled)
     830        if (SystemVersion.isMac() || SystemVersion.isWindows())
     831            return "unknown";
     832        long t = getLong("/proc/sys/net/ipv6/conf/all/use_tempaddr");
     833        if (t < 0)
     834            return "unknown";
     835        String rv;
     836        if (t == 0)
     837            rv = "false";
     838        else if (t == 2)
     839            rv = "true";
     840        else
     841            rv = "unknown";
     842        if (t == 2) {
     843            long pref = getLong("/proc/sys/net/ipv6/conf/all/temp_prefered_lft");
     844            if (pref > 0)
     845                rv += ", preferred lifetime " + DataHelper.formatDuration(pref * 1000);
     846            long valid = getLong("/proc/sys/net/ipv6/conf/all/temp_valid_lft");
     847            if (pref > 0)
     848                rv += ", valid lifetime " + DataHelper.formatDuration(valid * 1000);
     849        }
     850        return rv;
     851    }
     852
     853
     854    /**
     855     * Return first line in a file as a long
     856     * @return -1 on failure
     857     * @since 0.9.34
     858     */
     859    private static long getLong(String s) {
     860        File f = new File(s);
     861        long rv = -1;
     862        if (f.exists()) {
     863            BufferedReader in = null;
     864            try {
     865                in = new BufferedReader(new InputStreamReader(new FileInputStream(f), "ISO-8859-1"), 64);
     866                String line = in.readLine();
     867                if (line != null) {
     868                    try {
     869                        rv = Long.parseLong(line.trim());
     870                    } catch (NumberFormatException nfe) {}
     871                }
     872            } catch (IOException ioe) {
     873            } finally {
     874                if (in != null) try { in.close(); } catch (IOException ioe) {}
     875            }
     876        }
     877        return rv;
     878    }
    726879}
Note: See TracChangeset for help on using the changeset viewer.