Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Overview

In this article, we’ll focus on network interfaces and how to access them programmatically in Java.

Simply put, a network interface is the point of interconnection between a device and any of its network connections.

In everyday language, we refer to them by the term Network Interface Cards (NICs) – but they don’t all have to be of hardware form.

For example, the popular localhost IP 127.0.0.1, which we use a lot in testing web and network applications is the loopback interface – which is not a direct hardware interface.

Of course, systems often have multiple active network connections, such as wired ethernet, WIFI, Bluetooth, etc.

In Java, the main API we can use to interact directly with them is the java.net.NetworkInterface class. And so, to get started quickly, let’s import the full package:

import java.net.*;

2. Why Access Network Interfaces?

Most Java programs won’t probably interact with them directly; there are however special scenarios when we do need this kind of low-level access.

The most outstanding of these is where a system has multiple cards and you would like to have the freedom to choose a specific interface to use a socket with. In such a scenario, we usually know the name but not necessarily the IP address.

Normally, when we want to make a socket connection the to a specific server address:

Socket socket = new Socket();
socket.connect(new InetSocketAddress(address, port));

This way, the system will pick a suitable local address, bind to it and communicate to the server through its network interface. However, this approach does not allow us to choose our own.

We will make an assumption here; we don’t know the address but we know the name. Just for demonstration purposes, let’s assume we want the connection over the loopback interface, by convention, its name is lo, at least on Linux and Windows systems, on OSX it is lo0:

NetworkInterface nif = NetworkInterface.getByName("lo");
Enumeration<InetAddress> nifAddresses = nif.getInetAddresses();

Socket socket = new Socket();
socket.bind(new InetSocketAddress(nifAddresses.nextElement(), 0));
socket.connect(new InetSocketAddress(address, port));

So we retrieve the network interface attached to lo first, retrieve the addresses attached to it, create a socket, bind it to any of the enumerated addresses which we don’t even know at compile time and then connect.

A NetworkInterface object contains a name and a set of IP addresses assigned to it. So binding to any of these addresses will guarantee communication through this interface.

This does not really say anything special about the API. We know that if we want our local address to be localhost, the first snippet would suffice if we just added the binding code.

Additionally, we would never really have to go through all the several steps since localhost has one well-known address, 127.0.0.1 and we can easily bind the socket to it.

However, in your case, lo could perhaps have represented other interfaces like Bluetooth – net1, wireless network – net0 or ethernet – eth0. In such cases, you would not know the IP address at compile time.

3. Retrieving Network Interfaces

In this section, we will explore the other available APIs for retrieving the available interfaces. In the previous section, we saw just one of these approaches; the getByName() static method.

It’s worth noting that the NetworkInterface class does not have any public constructors, so we are of course not able to create a new instance. Instead, we’re going to use the available APIs to retrieve one.

The API we looked at so far is used to search a network interface by the specified name:

@Test
public void givenName_whenReturnsNetworkInterface_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("lo");

    assertNotNull(nif);
}

It returns null if none is for the name:

@Test
public void givenInExistentName_whenReturnsNull_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("inexistent_name");

    assertNull(nif);
}

The second API is getByInetAddress(), it also requires that we provide a known parameter, this time we can provide the IP address:

@Test
public void givenIP_whenReturnsNetworkInterface_thenCorrect() {
    byte[] ip = new byte[] { 127, 0, 0, 1 };

    NetworkInterface nif = NetworkInterface.getByInetAddress(
      InetAddress.getByAddress(ip));

    assertNotNull(nif);
}

Or name of the host:

@Test
public void givenHostName_whenReturnsNetworkInterface_thenCorrect()  {
    NetworkInterface nif = NetworkInterface.getByInetAddress(
      InetAddress.getByName("localhost"));

    assertNotNull(nif);
}

Or if you are specific about localhost:

@Test
public void givenLocalHost_whenReturnsNetworkInterface_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByInetAddress(
      InetAddress.getLocalHost());

    assertNotNull(nif);
}

Another alternative is also to explicitly use the loopback interface:

@Test
public void givenLoopBack_whenReturnsNetworkInterface_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByInetAddress(
      InetAddress.getLoopbackAddress());

    assertNotNull(nif);
}

The third approach which has only been available since Java 7 is to get a network interface by its index:

NetworkInterface nif = NetworkInterface.getByIndex(int index);

The final approach involves using the getNetworkInterfaces API. It returns an Enumeration of all available network interfaces in the system. It’s upon us to retrieve the returned objects in a loop, the standard idiom uses a List:

Enumeration<NetworkInterface> nets = NetworkInterface.getNetworkInterfaces();

for (NetworkInterface nif: Collections.list(nets)) {
    //do something with the network interface
}

4. Network Interface Parameters

There is a lot of valuable information we can get from one after retrieving its object. One of the most useful is the list of IP addresses assigned to it.

We can get IP addresses using two APIs. The first API is getInetAddresses(). It returns an Enumeration of InetAddress instances which we can process as we deem fit:

@Test
public void givenInterface_whenReturnsInetAddresses_thenCorrect()  {
    NetworkInterface nif = NetworkInterface.getByName("lo");
    Enumeration<InetAddress> addressEnum = nif.getInetAddresses();
    InetAddress address = addressEnum.nextElement();

    assertEquals("127.0.0.1", address.getHostAddress());
}

The second API is getInterfaceAddresses(). It returns a List of InterfaceAddress instances which are more powerful than InetAddress instances. For example, apart from the IP address, you may be interested in the broadcast address:

@Test
public void givenInterface_whenReturnsInterfaceAddresses_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("lo");
    List<InterfaceAddress> addressEnum = nif.getInterfaceAddresses();
    InterfaceAddress address = addressEnum.get(0);

    InetAddress localAddress=address.getAddress();
    InetAddress broadCastAddress = address.getBroadcast();

    assertEquals("127.0.0.1", localAddress.getHostAddress());
    assertEquals("127.255.255.255",broadCastAddress.getHostAddress());
}

We can access network parameters about an interface beyond the name and IP addresses assigned to it. To check if it is up and running:

@Test
public void givenInterface_whenChecksIfUp_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("lo");

    assertTrue(nif.isUp());
}

To check if it is a loopback interface:

@Test
public void givenInterface_whenChecksIfLoopback_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("lo");

    assertTrue(nif.isLoopback());
}

To check if it represents a point to point network connection:

@Test
public void givenInterface_whenChecksIfPointToPoint_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("lo");

    assertFalse(nif.isPointToPoint());
}

Or if it’s a virtual interface:

@Test
public void givenInterface_whenChecksIfVirtual_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("lo");
    assertFalse(nif.isVirtual());
}

To check if multicasting is supported:

@Test
public void givenInterface_whenChecksMulticastSupport_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("lo");

    assertTrue(nif.supportsMulticast());
}

Or to retrieve its physical address, usually called MAC address:

@Test
public void givenInterface_whenGetsMacAddress_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("lo");
    byte[] bytes = nif.getHardwareAddress();

    assertNotNull(bytes);
}

Another parameter is the Maximum Transmission Unit which defines the largest packet size that can be transmitted through this interface:

@Test
public void givenInterface_whenGetsMTU_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("net0");
    int mtu = nif.getMTU();

    assertEquals(1500, mtu);
}

5. Conclusion

In this article, we have shown network interfaces, how to access them programmatically and why we would need to access them.

The full source code and samples used in this article are available in the Github project.

Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE
res – REST with Spring (eBook) (everywhere)
Comments are closed on this article!