Understanding JDK 1.6 on Ubuntu Linux IPv6 output handling

448 views Asked by At

I'm running Ubuntu 10.10 and my hosts configuration are as following:

root@maxim-desktop:~# cat /etc/hosts
192.168.20.20   maxim-desktop   # Added by NetworkManager
192.168.10.20   maxim-lp
127.0.0.1       localhost.localdomain   localhost
::1     maxim-desktop   localhost6.localdomain6 localhost6

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
root@maxim-desktop:~# cat /etc/resolv.conf 
# Generated by NetworkManager
nameserver 192.168.20.1
root@maxim-desktop:~# cat /etc/hostname 
maxim-desktop
root@maxim-desktop:~# hostname 
maxim-desktop
root@maxim-desktop:~#  hostname --fqdn
root@maxim-desktop:~# ifconfig 
eth0      Link encap:Ethernet  HWaddr 00:1c:c0:f2:ba:89  
          inet addr:192.168.20.20  Bcast:192.168.0.255  Mask:255.255.255.0
          inet6 addr: fe80::21c:c0ff:fef2:ba89/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:9023854 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8532803 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:4839992361 (4.8 GB)  TX bytes:1067998152 (1.0 GB)
          Interrupt:20 Memory:d3300000-d3320000 

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:12333251 errors:0 dropped:0 overruns:0 frame:0
          TX packets:12333251 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:3216352432 (3.2 GB)  TX bytes:3216352432 (3.2 GB)

I'm trying to reach the same result from within java code.

Sadly, the following code just doesn't seem to cut it through.

//Copyright (c) 2011, Maxim Veksler
//All rights reserved.
//
//Redistribution and use in source and binary forms, with or without
//modification, are permitted provided that the following conditions are met:
//    * Redistributions of source code must retain the above copyright
//      notice, this list of conditions and the following disclaimer.
//    * Redistributions in binary form must reproduce the above copyright
//      notice, this list of conditions and the following disclaimer in the
//      documentation and/or other materials provided with the distribution.
//    * Neither the name of the <organization> nor the
//      names of its contributors may be used to endorse or promote products
//      derived from this software without specific prior written permission.
//
//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
//WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
//DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
//DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
//(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
//ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
//(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Hostname and IP address info, based on JDK6 NetworkInterface
 * 
 * @author Maxim Veksler <[email protected]>
 */
public class NetworkUtil {
    private static Logger log = LoggerFactory.getLogger(NetworkUtil.class);

    public static void main(String[] args) {
        System.out.println("MAC: " + NetworkUtil.getMachineMac());
        System.out.println("HOSTNAME: " + NetworkUtil.getMachineHostname());
        System.out.println("IP: " + NetworkUtil.getMachineIPAddress());
        System.out.println("HOSTNAME ipv6: " + NetworkUtil.getMachineIPv6Hostname());
        System.out.println("IP ipv6: " + NetworkUtil.getMachineIPv6Address());
    }

    /**
     * Get the MAC address of the remote IP (if on local LAN). 
     * @param hostnameOrIP The target IP or Hostname (if you have DNS configured).
     * 
     * @return MAC address if IP in local LAN, null if not.
     */
    public static String getRemoteHostMac(String hostnameOrIP) {
        try {
            InetAddress address = InetAddress.getByName(hostnameOrIP);
            NetworkInterface networkInterface = NetworkInterface.getByInetAddress(address);
            return obtainMACFromAddress(networkInterface);
        } catch (Exception e) {
            if(log.isDebugEnabled()) {
                log.debug("Failed to obtain MAC address for address " + hostnameOrIP, e);
            }
        }

        // Means we had a failure.
        return null;
    }

    /**
     * Get the machine address of the machine we are currently running on.
     * @return something like 08-00-27-DC-4A-9E or null if can't obtain mac
     */
    public static String getMachineMac() {
        try {
            return obtainMACFromAddress(getNonLoopbackNetworkInterface());
        } catch (Exception e) {
            if(log.isDebugEnabled()) {
                log.debug("Failed to obtain MAC address for localhost", e);
            }
        }

        return null;
    }

    /**
     * Get machine hostname, based on IPv4 configurations.
     * 
     * @return String representing FQDN or null if can't find hostname
     */
    public static String getMachineHostname() {
        try {
            NetworkInterface networkInterface = getNonLoopbackNetworkInterface();
            Inet4Address address = getInet4Address(networkInterface);
            if(address != null)
                return address.getCanonicalHostName();
        } catch (Exception e) {
            if(log.isDebugEnabled()) {
                log.debug("Failed to obtain MachineHostname", e);
            }
        }

        return null;
    }

    /**
     * Get machine hostname, based on IPv6 configurations.
     * @return String representing FQDN or null if can't find hostname
     */
    public static String getMachineIPv6Hostname() {
        try {
            NetworkInterface networkInterface = getNonLoopbackNetworkInterface();
            Inet6Address address = getInet6Address(networkInterface);
            if(address != null)
                return address.getCanonicalHostName();
        } catch (Exception e) {
            if(log.isDebugEnabled()) {
                log.debug("Failed to obtain IPv6Hostname", e);
            }
        }

        return null;
    }


    /**
         * Get machine IP, based on IPv4 configurations.
     * 
     * @return String representing IP or null if can't find properly configured interface
     */
    public static String getMachineIPAddress() {
        try {
            NetworkInterface networkInterface = getNonLoopbackNetworkInterface();
            Inet4Address address = getInet4Address(networkInterface);
            if(address != null)
                return address.getHostAddress();
        } catch (Exception e) {
            if(log.isDebugEnabled()) {
                log.debug("Failed to obtain MachineIPAddress", e);
            }
        }

        return null;
    }

    /**
     * Get machine IP, based on  IPv6 configurations.
     * 
     * @return String representing IP or null if can't find properly configured interface
     */
    public static String getMachineIPv6Address() {
        try {
            NetworkInterface networkInterface = getNonLoopbackNetworkInterface();
            Inet6Address address = getInet6Address(networkInterface);
            if(address != null)
                return address.getHostAddress();
        } catch (Exception e) {
            if(log.isDebugEnabled()) {
                log.debug("Failed to obtain MachineIPv6Address", e);
            }
        }

        return null;
    }


    /*
     * ########################
     * Helper private functions
     */

    private static String obtainMACFromAddress(NetworkInterface networkInterface) throws SocketException {
        if(networkInterface != null) {
            byte[] mac = networkInterface.getHardwareAddress();
            if(mac == null) 
                throw new Error("Failed to obtain mac address from interface: " + networkInterface.getDisplayName());

            StringBuilder stringBuilder = new StringBuilder(17);
            /*
             * Extract each array of mac address and convert it to hexa with the
             * following format 08-00-27-DC-4A-9E.
             */
            for (int i = 0; i < mac.length; i++) {
                stringBuilder.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? "-" : ""));
            }

            return stringBuilder.toString();
        }

        return null;
    }

    private static Inet4Address getInet4Address(NetworkInterface networkInterface) {
        if(networkInterface != null) {
            Enumeration<InetAddress> NICAddresses = networkInterface.getInetAddresses();
            while(NICAddresses.hasMoreElements()) {
                InetAddress address = NICAddresses.nextElement();

                if(address instanceof Inet4Address)
                    return (Inet4Address)address;
            }
        }

        return null;
    }

    private static Inet6Address getInet6Address(NetworkInterface networkInterface) {
        if(networkInterface != null) {
            Enumeration<InetAddress> NICAddresses = networkInterface.getInetAddresses();
            while(NICAddresses.hasMoreElements()) {
                InetAddress address = NICAddresses.nextElement();

                if(address instanceof Inet6Address)
                    return (Inet6Address)address;
            }
        }

        return null;
    }

    private static NetworkInterface getNonLoopbackNetworkInterface() throws SocketException {
        // We need to iterate over all NIC's machine has because stupid ubuntu does not assign
        // MAC address to default loopback interface...
        Enumeration<NetworkInterface> b = NetworkInterface.getNetworkInterfaces();
        while(b.hasMoreElements()) {
            NetworkInterface networkInterface = b.nextElement();
            Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
            while(inetAddresses.hasMoreElements()) {
                InetAddress address = inetAddresses.nextElement();
                if(!address.isLoopbackAddress())
                    return networkInterface;
            }
        }

        // Means we haven't found any non loopback interfaces. Bummer, return empty handed.
        return null;
    }

}

The following is printed on my machine:

MAC: 00-1C-C0-F2-BA-89
HOSTNAME: maxim-desktop
IP: 192.168.20.20
HOSTNAME ipv6: fe80:0:0:0:21c:c0ff:fef2:ba89%2
IP ipv6: fe80:0:0:0:21c:c0ff:fef2:ba89%2
  • As you can see the hostname output for IPv4 & IPv6 are not the same, I wonder why should it be like this?
  • Also, what is the meaning of the %2 that get's attached to ipv6 related queries - Is it a bug in my code or a JVM / Linux kernel issue?

Thank you,
Maxim.

1

There are 1 answers

4
andri On BEST ANSWER
  1. If you look at the code, here's what it does:

    • find a non-loopback network interface (ie. in your case, eth0)
    • query for the IP address of that interface (in your case, 192.168.20.20 or fe80:0:0:0:21c:c0ff:fef2:ba89%2)
    • resolve the IP address. As you can see from your hosts file, 192.168.20.20 correctly resolves to maxim-desktop, but there is no hostname corresponding to your IPv6 address.
  2. IPv6 addresses beginning with fe80 (or, to be precise, addresses in the fe80::/10 network) are link-local addresses, and that %2 denotes which network interface the system should use.