Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IPv6Address with prefix length and zone to Inet6Address conversion issue #48

Closed
swimmesberger opened this issue Aug 31, 2020 · 6 comments

Comments

@swimmesberger
Copy link

swimmesberger commented Aug 31, 2020

When an ipv6 address with a zone and a associated prefix length is converted to an JRE Inet6Address null is returned because the JRE can't parse the subnet mask. E.g. when an address like this is constructed:
"fe80::%eth13/64"

Then an exception is thrown here because the zone is not numeric (eth13):
https://github.com/seancfoley/IPAddress/blob/master/IPAddress/src/inet.ipaddr/inet/ipaddr/ipv6/IPv6Address.java#L1781
and converted to a normalized string which is "fe80:0:0:0:0:0:0:0%eth13/64" which will result in an UnknownHostException from the JRE because of following error:
"java.net.UnknownHostException: no such interface eth13/64"

@seancfoley
Copy link
Owner

As you've indicated, InetAddress and Inet6Address do not accept addresses with prefix length aka subnet mask (the /64). I can add code to drop the prefix length first before attempting the conversion. As a workaround, you can do so as well, you can call withoutPrefixLength() first.

However, after dropping the prefix length, the address string that is used by the code at that line above has wildcards (fe80:0:0:0:*:*:*:* and not fe80:0:0:0:0:0:0:0). So that causes an exception too. So I will add code to handle that as well.

Thirdly, it seems the interface name must match an interface on the system (ie there must be an eth13). I assume it does in your case, but if not, that would also cause an exception.

So as a work-around, the following can work:

IPAddressString str = new IPAddressString("fe80::%eth13/64");
IPAddress addr = str.getAddress();
InetAddress inetAddr = addr.withoutPrefixLength().getLower().toInetAddress(); // drop /64 and take lower address
System.out.println(inetAddr);
		
inetAddr = addr.getLower().withoutPrefixLength().toInetAddress(); // or change order of calls
System.out.println(inetAddr);	

Output:

/fe80:0:0:0:0:0:0:0%eth13
/fe80:0:0:0:0:0:0:0%eth13

@swimmesberger
Copy link
Author

I have already found the workaround to drop the prefix length for the address that works fine now. I can't really say what I expected but the behavior seems to be different for ipv4 addresses with a prefix length therefore I wondered why the code worked for ipv4 addresses but not ipv6.

@seancfoley
Copy link
Owner

seancfoley commented Aug 31, 2020

Yes, well I will be fixing this because the method should not fail the conversion, it should use the lower address without the prefix length to generate the InetAddress, that is the intended and expected behaviour. That is what happens with IPv4 (there are no scopes or zones but it does drop the prefix length). For IPv4, since there are no scopes or zones, the code is a bit different and works correctly.

@swimmesberger
Copy link
Author

Just wanted to note that the conversion from IPAddress to InetAddress for ipv6 with zones is a "heavy" operation because the Inet6Address JRE class calls "initstr" in the constructor which will do a NetworkInterface.getByName call which can take a couple of cycles. In loops or performance critical areas it is therefore not a good idea to call "toInetAddress" very often.
In our use-case we have a simple facade for your "IPAddress" library to do some ip based calculations but the input and output is always a JRE InetAddress therefore we added a workaround for this case:

private static InetAddress toInetAddress(InetAddress origin, IPAddress libAddress) {
    InetAddress inetAddress;
    // the Inet6Address constructor used by the IPAddress library for IPv6 needs a native network interface lookup when a scroped interface is set which is a
    // pretty heavy operation
    // therefore we use our custom method for those cases
    if (origin instanceof Inet6Address) {
      Inet6Address inet6Address = (Inet6Address) origin;
      try {
        inetAddress = Inet6Address.getByAddress(null, libAddress.getBytes(), inet6Address.getScopedInterface());
      } catch (UnknownHostException e) {
        inetAddress = null;
      }
    } else {
      inetAddress = libAddress.toInetAddress();
    }
    return inetAddress;
  }

By calling the Inet6Address constructor which takes a NetworkInterface there is no lookup cost.

@seancfoley
Copy link
Owner

Thanks for that info. The network interface lookup is indeed excessively costly and I am thinking of better integration of IPv6Address with NetworkInterface to avoid this lookup when it is not necessary.

@seancfoley
Copy link
Owner

I've addressed these issues in the latest version 5.3.3. If you create addresses with zones from strings, then the library will look up the interface, just once for each interface. If you create addresses directly, you can supply an IPv6Zone object, which can be shared amongst addresses. May operations retain the zone, but if you use operations that do not (like merge or span operations), then you can add back the zone with setZone following the operation. Since zone objects are shared, and they hold NetworkInterface objects, that will ensure NetworkInterface.getByName is not repeatedly called.

Also, the original issue is fixed, the issue of converting an address with zone to Inet6Address.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants