0

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 <maxim@vekslers.org>
 */
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.

Maxim Veksler
  • 29,272
  • 38
  • 131
  • 151

1 Answers1

1
  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.

andri
  • 11,171
  • 2
  • 38
  • 49
  • The %2 is new to me, any idea why it was added in ipv6? - ipv4 does not contain this notation. Side note: I've looked at this code quite a bit, I'm the implementor of this utility class :) – Maxim Veksler Feb 17 '11 at 21:24
  • The %2 was added as fe80:: is a unicast address, and could theoretically be sent via any network interface. You can force an interface with that notation, as it's likely that if you want to, say, access the computer next to you over link-local IPv6 then you don't want the packet to be sent via the loopback interface. – andri Feb 17 '11 at 21:29
  • Also, I'm not really sure what you want to achieve with your code, but it mostly seems to assume that the machine has only one non-local network interface. You most certainly shouldn't assume that. – andri Feb 17 '11 at 21:31
  • Yes, this code handles the simplistic case of 1 external NIC per machine. This works well for us as the workstations are of this configuration and the ec2 instances are of this configuration as well. The purpose of this utility is to be able to find (currently with very basic huristics, which I would love to extend) the most likely IP & Hostname that connect the machine to the outside world. If there are several of these then the first one. – Maxim Veksler Feb 18 '11 at 09:53