JDK-8303477 : Modernize Windows native code for NetworkInterface
  • Type: CSR
  • Component: core-libs
  • Sub-Component: java.net
  • Priority: P4
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 21
  • Submitted: 2023-03-01
  • Updated: 2023-03-13
  • Resolved: 2023-03-13
Related Reports
CSR :  
Description
Summary
-------

Refresh the native parts of NetworkInterface class on Windows

Problem
-------

There are currently 2 distinct functional implementations. One is based on pre-WinXP APIs, the other on is based on WinXP APIs. The former is used when user defines system property `java.net.preferIPv4Stack=true` and can't deal with IPv6, the latter is slow and hard to use. None of them provides persistent interface names; names are generated based on interface position in iteration order, and can change when the application is running.

Solution
--------

Reimplement NetworkInterface using the new APIs introduced in Windows Vista, and use persistent interface names offered by the system.

The current implementation assigns names based on interface type and position in iteration order:
```
        switch (ifrowP->dwType) {
            case MIB_IF_TYPE_ETHERNET:
                _snprintf_s(dev_name, 8, _TRUNCATE, "eth%d", eth++);
                break;

            case MIB_IF_TYPE_TOKENRING:
                _snprintf_s(dev_name, 8, _TRUNCATE, "tr%d", tr++);
                break;
            ...
```
This produces names like `eth0`, `ppp0` or `wlan0`. All loopback interfaces were merged under the name `lo`, which was a mixed blessing, see [JDK-6798979](https://bugs.openjdk.org/browse/JDK-6798979).

The new implementation assigns names based on system-provided interface identifier (LUID):
```
    // set the NetworkInterface's name
    apiRetVal = ConvertInterfaceLuidToNameW(
            &(ifRow->InterfaceLuid), ifName, NDIS_IF_MAX_BUFFER_SIZE);
```
This produces names like `ethernet_32768`, `ppp_32768`, `wireless_32768` or `loopback_0`. There's no overlap between the old and the new namespace.

The existing naming scheme is not predictable (except for the `lo` interface), so it's unlikely that existing code stores interface names and reuses them. The more common use case is that the code enumerates all interfaces and picks instances out based on other characteristics like the interface address. That use case won't be affected by this change.

The interface names are used in the following places:

- Method [NetworkInterface.getByName](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/net/NetworkInterface.html#getByName(java.lang.String)) accepts the interface name and returns a NetworkInterface. It returns null if there's no interface with the specified name.
- Methods [InetAddress.getByName](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/net/InetAddress.html#getByName(java.lang.String)) and [getAllByName](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/net/InetAddress.html#getAllByName(java.lang.String)) resolve interface names when passed a [scoped IPv6 address](https://www.rfc-editor.org/rfc/rfc4007) like "[fe80:0:0:0:ece8:3c63:68f9:a95b%wireless_32768]". The methods will throw when they receive an unrecognized interface name.
- Deserialization of Inet6Address will reset the scope to default if the serialized scope refers to an interface that cannot be resolved.

Other user-visible changes include:

- System interfaces are enumerated in a different order.
- Certain NetworkInterface methods like `isUp`, `isPointToPoint`, `isLoopback`, `getHardwareAddress`, `getMTU` and `supportsMulticast` may return different values on some interfaces. In the current XP implementation if you call these methods on a filter interface, you get bogus results because the code can't find the interface. The new return values reflect the interface state more adequately.

Specification
-------------

This is a behavioral change only; there is no update to the specification.


Comments
I see a release note is already planned; moving to Approved.
13-03-2023

A corpus search of 30411656 classes in 130997 artifacts found 399 usages. Some of these are in different versions of the same library so it's actually only 127 libraries (unique groupID:artifactID).
03-03-2023

NIF::supportsMulticast() is only false when multicasting is administratively disabled on an interface, and true otherwise. If supportsMulticast is false, the interface will not be usable, but if supportsMulticast is true, other conditions still have to be met. Looking at the available information sources we were not able to determine if it's possible to disable multicasting on selected interfaces on Windows.
02-03-2023

"Enabling/disabling multicast seems less accessible on Windows and the proposed change to have supportsMulticast return true on all interface seems okay and could be re-examined in the future if it turns out to be problematic." Currently the way to find multicast support NetworkInterface is, by using NIF::supportsMulticast(). From the above statement the applications relying on this would break when supportsMulticast() returns true but practically which is not.
02-03-2023

NetworkInterface was added in Java 1.4 to model network interfaces, their IP addresses and other configuration. The main use for NetworkInterface is multicasting applications where a NetworkInterface is needed when joining a multicast group or when specifying the network for outgoing multicast datagrams. A secondary use for NetworkInterface is for informational purpose as its API can be used to enumerate network configuration. There is a bit of an impedance mismatch with NetworkInterface on Windows. On Unix systems, developers and admins can use programs such as "ifconfig" to show or change network interface configuration. Network interfaces on Unix/Linux/macOS systems have names such as "lo" or "lo0" for the loopback interface, "ens3" or "en0" for wired or wireless interfaces. Developers and admins on Windows are more likely to use programs such as "Control Panel" to look at Network Connections where they show up as with a description like "XXX Ethernet Adapter". They are command line programs such as "netsh" that reveal interface names but nothing, to my knowledge, that exposes the interface name that the Windows APIs reveal via its APIs to map a Locally Unique IDentifier (LUID) to a name. NetworkInterface defined a getName method to get the network interface name, and a static getByName method to find a network interface by name. This is very useful for Unix/Linux/macOS systems where you can use getByName("lo") or getByname("en3") as the names show up with ifconfig. The experience isn't great on Windows. NetworkInterface was introduced before Windows provided APIs for network interface names. For this reason, the original implementation of NetworkInterface on Windows synthesized names for network interfaces. This scheme evolved over time as the NetworkInterface implementation was updated to make use of newer "IP helper" libraries and IPv6 was support was introduced. The synthesized names were never guaranteed to be stable and could potentially change after reboot or even run-to-run. To my knowledge, the synthesized names haven't caused any issues but it may be that multicasting applications deployed on Windows doesn't have persistent configuration that includes the network interface name. The change proposed in this CSR stems from a re-implementation of the Windows implementation of NetworkInterface. It makes use of Windows APIs that did not exist when NetworkInterface was originally introduced. The new implementation gets the network interface name from Windows (LUID to interface name). The names will look different to the synthesized names although their "display name" (NetworkInterface::getDisplayName, essentially their description) will be the same as before. It's possible there are deployments with persistent configuration containing the synthesized names. This may be true for simple configurations that have IPv6 enabled and where the synthesized names are likely to be "lo" for the loopback interface and "eth1" for the wired connection. Anything beyond that is probably unstable as multiple interfaces might change the names due to ordering. So there is a behavior change that will need to be documented in a release note. There are a few other behavior changes that will also need to be documented. One is that network interfaces without plumbed IP addresses may show up when they didn't previously show up. This could potentially confuse tests that enumerate all network interfaces and assume that the first (or all) have an IP address configured. there are other subtle differences to previous releases. The NetworkInterface::supportsMulticast method is used to test if the network interface supports multicast. On Unix systems, it is possible to enable/disable multicast on specific interfaces. Enabling/disabling multicast seems less accessible on Windows and the proposed change to have supportsMulticast return true on all interface seems okay and could be re-examined in the future if it turns out to be problematic. My overall assessment on this change is that it's good for the JDK. It's possible that a synthesized name such as "lo" or "eth1" is hardcoded into programs, tests, or in some configuration but such usages are not portable and very fragile. A clear/well-written release note will be required to document the change.
02-03-2023

Moving to Provisional, not Approved. Before this request is Finalized, I'd like to see more discussion on what user-visible impacts, if any, this change is expected to have.
01-03-2023