Wednesday, October 12, 2011

A Java library for IPv6

A while ago, I wrote a library to work with IPv4 concepts (IPv4 addresses, networks, network masks, prefix lengths, ranges of addresses etc) in Scala. Along those lines, I recently created a similar library to work with IPv6 concepts. This time though, I used good old Java.

If you simply need to make IPv6 connections, java.net.Inet6Address will be sufficient. However when doing things a little bit more advanced with the addresses themselves, I find java.net.InetAddress (both Inet4Address and Inet6Address) lacking a lot of functionality.

Here is a short overview of what can be done with my java-ipv6 library.

IPv6Address


The class IPv6Address represents an IPv6 address.

final IPv6Address iPv6Address = IPv6Address.fromString("fe80::226:2dff:fefa:cd1f");
Internally, the IPv6Address class uses two long values to store the IPv6 address. This makes for an optimized implementation, and a lot of bit twiddling fun for me while writing it...

IPv6Address can be used to make simple calculations on IPv6 addresses, such as addition and subtraction.

final IPv6Address iPv6Address = IPv6Address.fromString("fe80::226:2dff:fefa:cd1f");
final IPv6Address next = iPv6Address.add(1);
final IPv6Address previous = iPv6Address.subtract(1);
System.out.println(next.toString()); // prints fe80::226:2dff:fefa:cd20
System.out.println(previous.toString()); // prints fe80::226:2dff:fefa:cd1e

IPv6AddressRange


The class IPv6AddressRange represents a continuous range of consecutive IPv6 addresses.

final IPv6AddressRange range = new IPv6AddressRange(IPv6Address.fromString("fe80::226:2dff:fefa:cd1f"),
                                                    IPv6Address.fromString("fe80::226:2dff:fefa:ffff"));
System.out.println(range.contains(IPv6Address.fromString("fe80::226:2dff:fefa:dcba"))); // prints true
IPv6AddressRange contains methods to iterate over all the addresses in the range. Ranges can be compared with other ranges by checking if they overlap or if one range contains the other range.

IPv6Network


An IPv6Network is a range (extends IPv6AddressRange) that can be expressed as a network address and a prefix length.

final IPv6Network range = new IPv6Network(IPv6Address.fromString("fe80::226:2dff:fefa:0"),
                                          IPv6Address.fromString("fe80::226:2dff:fefa:ffff"));
final IPv6Network network = IPv6Network.fromString("fe80::226:2dff:fefa:0/112");
System.out.println(range.equals(network)); // prints true
Note that every IPv6Network is also an IPv6AddressRange, but not all IPv6AddressRanges are valid IPv6Networks. That is why, when constructing an IPv6Network from a range in between a first address and a last address, the smallest possible IPv6Network (i.e. the one with the longest prefix length) will be constructed.

IPv6AddressPool


An IPv6AddressPool is like a range (extends IPv6AddressRange) of which certain subnets are "allocated" and other are "free".

final IPv6AddressPool pool = new IPv6AddressPool(IPv6Address.fromString("fe80::226:2dff:fefa:0"),
                                                 IPv6Address.fromString("fe80::226:2dff:fefa:ffff"), 120);
System.out.println(pool.isFree(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"))); // prints true
final IPv6AddressPool newPool = pool.allocate(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"));
System.out.println(newPool.isFree(IPv6Network.fromString("fe80::226:2dff:fefa:5ff/120"))); // prints false

This was only a short introduction. Much more can be done with these types. I invite you to have a look at the javadoc and of course the actual source code.


Immutable


I decided to make all types immutable. For things like IPv6Address and IPv6Network, this obviously makes sense because they represent immutable concepts. For IPv6AddressPool, I was in doubt whether immutability was the right choice. When allocating an address in a pool, immutability means I have to return a new IPv6AddressPool instance. Internally the IPv6AddressPool maintains a SortedSet of all ranges of addresses that are still available in the pool. This SortedSet (TreeSet) thus has to be copied each time a new IPv6AddressPool is to be created. It is not a deep copy (the IPv6AddressRanges in the SortedSet are immutable themselves), but still constructing new TreeSet instances each time seems sub optimal. It would be nice to investigate if I can improve the situation using some kind of persistent data structure to replace the TreeSet. I had a quick look at pcollections, but it doesn't seem to provide an alternative to TreeSet with SortedSet semantics. Other suggestions are much appreciated!


17 comments:

Mikasch said...

I tried to run your code under java version "1.6.0_25" but I got:

Exception in thread "main" java.lang.UnsupportedClassVersionError: be/jvb/ipv6/IPv6AddressRange : Unsupported major.minor version 51.0


Any suggestions?

Jan Van Besien said...

The library relies on JDK7 and is compiled with JDK7. This means you can only run it with JDK7.

I'm using some methods which are only available since JDK7.

nilesh said...

Hi Jan,

I think the lastallocated reference variable in IPAddressPool shouldn't be final, because it will remain null if it is final variable.

Also we need to update the lastallocated when we do the allocation.

Please correct me if I am wrong.

Thanks.

Jan Van Besien said...

Hi nilesh,

Thanks for your interest in the library.

All types in the java-ipv6 library are immutable (on purpose). This implies that if you allocate an address on an IPv6AddressPool, you will get a new instance of the IPv6AddressPool class in which the lastAllocated variable will be filled in correctly (thanks to a private constructor in the IPv6AddressPool class). It is indeed not modifying the lastAllocated variable within the instance on which you do the allocation.

perezagua said...

First of all, I would like to thank you for the work you've done.
You've saved me a lot of time on a project I'm working on.

I'm testing deeply IPv6AddressRange class and specially a compare method, and I believe there is something wrong.

Let's imagine the following IPv6 ranges:

1º ==> aaaa:ffff:ffff:ffff:1:1:1:1 - cccc:ffff:ffff:ffff:5:5:5:5
2º ==> aaaa:ffff:ffff:ffff:1:1:1:1 - bbbb:ffff:ffff:ffff:5:5:5:5

Where the first IP of both ranges is the same.

If I'm not wrong the first ip range is greater than second range, so if I compare these two ranges the result would be (-1).


Here is the source code of compareTo method.


@Override
public int compareTo(IPv6AddressRange that)
{
if (this.first != that.first)
return this.first.compareTo(that.first);
else
return this.last.compareTo(that.last);
}

Is this sentence "if (this.first != that.first)" correct?.

You don't believe that we should use the compareTo() method of IPv6Address class

In this case the first IPv6 Address in both range are equals, but the method execute "return this.first.compareTo(that.first)"

Please Could you tell me if what I say is correct?

Thank you so much
Angel Martin

Jan Van Besien said...

Hi,

Thanks for you interest in the project.

(this.first != that.first) is indeed wrong, it should be (!this.first.equals(that.first)) in stead... Stupid mistake actually, I will fix it. I'm tracking the issue here: http://code.google.com/p/java-ipv6/issues/detail?id=2

Note however that the compareTo method is comparing ranges in order to order them by first address, and by last address if the first is equal. The compareTo method doesn't order them by "size" or "number of addresses in the range". That would also be potentially useful of course.

perezagua said...

OK. I understand. I'm working with geolocation and I must find the country code with a given IP.

What I do is extend the class IPv6AddressRange adding the country code associated with a range. Later my application receives an IPv6 and it have to look at which country belongs.

For this reason I have to generate a IPv6AddressRange ordered list.

Thanks

Jan Van Besien said...

For your use case, the current (fixed, I released version 0.8 of the library) implementation of compareTo is OK I think. Certainly if you do not have overlapping ranges (which I assume).

I do something very similar in the IPv6AddressPool class, in which I use a SortedSet of IPv6AddressRanges.

João Belém said...

Nice library! Are there also plans to add CIDR notation support?

Jan Van Besien said...

Unless I misunderstood your question, I think CIDR notation is already supported.

You can use it to create networks and ranges:

IPv6Network network = IPv6Network.fromString("fe80::226:2dff:fefa:0/112");

toString of a network will use CIDR notation by default.

sp said...

Hi Jan,

Does this library support converting ipv6 address ranges to IPv6 CIDR addresses.I tried a lot but could not get it using this library. Thanks a lot for your help.

Jan Van Besien said...

Have a look at the Examples.java class (http://code.google.com/p/java-ipv6/source/browse/trunk/src/test/java/com/googlecode/ipv6/examples/Examples.java). Both address/prefixlength and address/addressmask notations are supported when parsing. toString always prints address/prefixlength but you can get address/addressmask with a little workaround. I could add an explicit method for that, if that is what you are after.

sp said...

Hi Jan,

I was able print all the hosts in a range, but could not able to find the CIDR addresses from the range. It would be really great, if you could publish the explicit method to know the CIDR addresses from range.

Thanks,
sp

Jan Van Besien said...

Ah, you mean calculating the set of subnets that together compose a range of ip addresses. Like http://ip2cidr.com/ does for IPv4?

That is currently not possible but sounds like a useful thing to add. I will certainly look into it. I'll track progress here https://code.google.com/p/java-ipv6/issues/detail?id=9

The only related feature that is currently already supported is to create a single IPv6 network from the two addresses within the network. This will construct the smallest possible network ("longest prefix length") which contains both addresses.

sp said...

Yes! you are spot on! I could find lot of help for IPv4 but not for IPv6. Do you have any suggestion how to achieve this?

Jan Van Besien said...

I released version 0.15 today. It contains a long awaited new feature: the ability to calculate the set of subnets that together compose a range of ip addresses. It is like http://ip2cidr.com/ does for IPv4.

Given a random IPv6AddressRange between two addresses, you can now get an iterator that iterates over the minimal set of non overlapping, consecutive subnets that define that range.

Anonymous said...

Good Morning.

Master Jan Van Bessien.

Help-me,help-me please...or go killme and my friend and my family..

Need your LIB in version JRE 6.

Have one possibilition..??