IPAddress

Java and Go libraries for handling IP addresses and subnets, both IPv4 and IPv6

by Sean C Foley

Contents

Contents

Benefits of this Library

Java versus Golang Feature Matrix

Code Examples

Supported IP Address Parsing Formats

Core Types

Parse String Representation of IP Address or Host Name

Addresses from Numeric Values

Golang Address Keys

Golang Zero Values

Networks

Prefix Length Handling

Address Sections

IP Address Ranges

Address Tries

IP Address Operations

Parse String Representations of MAC Address

MAC Address Operations

IPv6 – MAC Address Integration

Address Framework

Conversion to String Representation of Address

Containment and Subnet Membership

DNS Resolution and URLs

Sorting and Comparisons

Make your IPv4 App work with IPv6

Benefits of this Library

The IPAddress library was intended to satisfy the following primary goals:

Java Versus Golang Feature Matrix

The basic goals remain the same for both Java and Go libraries. This matrix is a summary of the feature differences.

Feature Java Golang
Core types (IP/MAC strings, IPv4/v6/MAC addresses, sequential ranges, host names, base types for the like)
Core types are immutable
Rich type system for addresses, address sections, address segments, address divisions, address division groupings, and address ranges
Comparison of instances of all such types
Subnet containment checks
Sequential range containment checks
Merging subnets
Merging sequential ranges
Spanning subnets with CIDR blocks
Spanning sequential ranges with CIDR blocks
Spanning subnets with sequential blocks
Spanning sequential ranges with sequential blocks
Conversion to/from MAC from/to IPv6 Addresses
Conversion to/from IPv4 from/to IPv6 Addresses
Subnet iterators
Sequential range iterators
Subnet prefix iterators
Sequential range prefix iterators
Spliterator and stream iterator alternatives  
Parsing many address and subnet formats
String generation of many address and subnet formats
String collections  
IPv4/v6/MAC address increment/decrement
Masking, reversing, subtracting, intersecting, joining operations
Prefix length operations
Framework of address interfaces for polymorphic code
Address tries
Associative address tries
Integration with standard library maps and collections
Parse IP strings directly to sequential ranges
UNC Host, DNS, & IPv6 Base 85 string parsing and generation
Parse IP strings directly to division groupings Future
Prefix Block Allocator
Serialization  

Note that this document is being expanded to include more Go code examples and references.

Code Examples

This document provides in-depth and extensive documentation for the library, and includes some code snippets. However, for common use-cases, you may wish to go straight to the Java code examples or Go code examples which cover a wide breadth of common use-cases. The code examples are focused more on covering common use-cases and operations, while this document is focused more on covering all areas in more detail with smaller code snippets. This document focuses more on one area at a time, while the examples are useful in showing how to combine the functionality to achieve various end results.

Supported IP Address Parsing Formats

This includes, those supported by the well-known routines inet_aton and inet_pton, the subnet formats listed above, all combinations of the above, and others:

For a more detailed list or formats parsed, some examples are below, or see the javadoc or godoc for IPAddressString.

Subnet formats

For a more detailed list of formats parsed, some examples are below, or see the javadoc or the godoc for the type IPAddressString.

Core Types

The core types are HostName, IPAddressString, and MACAddressString along with the Address base type and its subtypes IPAddress, IPv4Address, IPv6Address, and MACAddress, as well as the sequential address types IPAddressSeqRange, IPv4AddressSeqRange and IPv6AddressSeqRange. In Go, the sequential address types are aliases derived from the same generic type.

If you have a textual representation of an IP address, then start with HostName or IPAddressString. If you have numeric bytes or integers, then start with IPv4Address, IPv6Address or MACAddress. Note that address instances can represent either a single address or a subnet. If you have either an address or host name, or you have something with a port or service name, then use HostName.

Parse String Representation of IP Address or Host Name

IPAddressString is used to convert.

With the Java library, you can use one of getAddress or toAddress, the difference being whether parsing errors are handled by exception or not.

IPAddress address = new IPAddressString("1.2.3.4").getAddress();
if(address != null) {
  //use address here
}

or

String str = "1.2.3.4";
try {
  IPAddress address = new IPAddressString(str).toAddress();
  //use address here
} catch (AddressStringException e) {
  String msg = e.getMessage();//detailed message indicating issue
}

If you have either a host name or an address, you can use HostName:

checkHost(new HostName("[::1]"));
checkHost(new HostName("*"));
checkHost(new HostName("a.b.com"));

static void checkHost(HostName host) {
  if(host.isAddress()) {
    System.out.println("address: " +
      host.asAddress().toCanonicalString());
  } else if(host.isAddressString()) {
    System.out.println("address string with ambiguous address: " +
      host.asAddressString());
  } else {
    System.out.println("host name with labels: " +
      Arrays.asList(host.getNormalizedLabels()));
  }
}

Output:

address: ::1
address string with ambiguous address: *
host name with labels: [a, b, com]

Similarly, with the Go library, you can use one of GetAddress or ToAddress, the difference being whether parsing errors are detected by checking for nil or checking an error.

address := ipaddr.NewIPAddressString("1.2.3.4").GetAddress()
if address != nil {
	//use address here
}

or

str := "1.2.3.4"
address, err := ipaddr.NewIPAddressString(str).ToAddress()
if err != nil {
	msg := err.Error() //detailed message indicating issue
} else {
	//use address here
}

If you have either a host name or an address, you can use HostName:

checkHost(ipaddr.NewHostName("[::1]"))
checkHost(ipaddr.NewHostName("*"))
checkHost(ipaddr.NewHostName("a.b.com"))

func checkHost(host *ipaddr.HostName) {
	if host.IsAddress() {
		fmt.Println("address:",
			host.AsAddress().ToCanonicalString())
	} else if host.IsAddressString() {
		fmt.Println("address string with ambiguous address:",
			host.AsAddressString())
	} else {
		fmt.Println("host name with labels:",
			host.GetNormalizedLabels())
	}
}

Output:

address: ::1
address string with ambiguous address: *
host name with labels: [a b com]

Format Examples

Many formats are supported. For instance, the address 1:2:3:0:0:6:: can be represented many ways as shown with this Java code:

static void parse(String formats[]) {
  for(String format : formats) {
    System.out.println(new IPAddressString(format).getAddress());
  }
}

static void parseHost(String formats[]) {
  for(String format : formats) {
    System.out.println(new HostName(format).getAddress());
  }
}

String formats[] = {
  "1:2:3:0:0:6::",
  "1:2:3:0:0:6:0:0",
  "1:2:3::6:0:0",
  "0001:0002:0003:0000:0000:0006:0000:0000",
  "1:2:3::6:0.0.0.0",
  "1:2:3:0:0:6::",
  "0b0000000000000001:0b0000000000000010:0b0000000000000011:0:0:0b0000000000000110::",
  "008JQWOV7O(=61h*;$LC",
  "0x00010002000300000000000600000000"
};
parse(formats);

String hostFormats[] = {
  "[1:2:3:0:0:6::]",
  "[1:2:3:0:0:6:0:0]",
  "[1:2:3::6:0:0]",
  "[0001:0002:0003:0000:0000:0006:0000:0000]",
  "[1:2:3::6:0.0.0.0]",
  "[1:2:3:0:0:6::]",
  "[0b0000000000000001:0b0000000000000010:0b0000000000000011:0:0:0b0000000000000110::]",
  "[008JQWOV7O(=61h*;$LC]",
  "[0x00010002000300000000000600000000]",
  "0.0.0.0.0.0.0.0.6.0.0.0.0.0.0.0.0.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.ip6.arpa",
  "1-2-3-0-0-6-0-0.ipv6-literal.net"
};
parseHost(hostFormats);

or this equivalent Go code:

func parse(formats []string) {
	for _, format := range formats {
		fmt.Println(ipaddr.NewIPAddressString(format).GetAddress())
	}
}

func parseHost(formats []string) {
	for _, format := range formats {
		fmt.Println(ipaddr.NewHostName(format).GetAddress())
	}
}

formats := []string{
	"1:2:3:0:0:6::",
	"1:2:3:0:0:6:0:0",
	"1:2:3::6:0:0",
	"0001:0002:0003:0000:0000:0006:0000:0000",
	"1:2:3::6:0.0.0.0",
	"1:2:3:0:0:6::",
	"0b0000000000000001:0b0000000000000010:0b0000000000000011:0:0:0b0000000000000110::",
	"008JQWOV7O(=61h*;$LC",
	"0x00010002000300000000000600000000",
}
parse(formats)

hostFormats := []string{
	"[1:2:3:0:0:6::]",
	"[1:2:3:0:0:6:0:0]",
	"[1:2:3::6:0:0]",
	"[0001:0002:0003:0000:0000:0006:0000:0000]",
	"[1:2:3::6:0.0.0.0]",
	"[1:2:3:0:0:6::]",
	"[0b0000000000000001:0b0000000000000010:0b0000000000000011:0:0:0b0000000000000110::]",
	"[008JQWOV7O(=61h*;$LC]",
	"[0x00010002000300000000000600000000]",
	"0.0.0.0.0.0.0.0.6.0.0.0.0.0.0.0.0.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.ip6.arpa",
	"1-2-3-0-0-6-0-0.ipv6-literal.net",
}
parseHost(hostFormats)

Output from both the Java and Go code:

1:2:3::6:0:0
1:2:3::6:0:0
1:2:3::6:0:0
1:2:3::6:0:0
1:2:3::6:0:0
1:2:3::6:0:0
1:2:3::6:0:0
1:2:3::6:0:0
1:2:3::6:0:0
1:2:3::6:0:0
1:2:3::6:0:0
1:2:3::6:0:0
1:2:3::6:0:0
1:2:3::6:0:0
1:2:3::6:0:0
1:2:3::6:0:0

Subnet strings are supported as well, using CIDR prefix notation or characters indicating range (‘-‘ for a specific range or ‘*’ for full-range segments).

For instance, the subnet ffff::/104 can be represented many ways.

Here is Java code parsing some of those representations:

static void parseSubnet(String formats[]) {
  for(String format : formats) {
    System.out.println(new
    IPAddressString(format).getAddress().assignPrefixForSingleBlock());
  }
}

static void parseHostSubnet(String formats[]) {
  for(String format : formats) {
    System.out.println(new
      HostName(format).getAddress().assignPrefixForSingleBlock());
    }
}


String prefixedFormats[] = {
  "ffff::/104",
  "ffff:0:0:0:0:0:0:0/104",
  "ffff:0000:0000:0000:0000:0000:0000:0000/104",
  "ffff::/104",
  "ffff::0.0.0.0/104",
  "0b1111111111111111::/104",
  "=q{+M|w0(OeO5^EGP660/104"
};

String rangeFormats[] = {
  "ffff:0:0:0:0:0:0-ff:*",
  "ffff::0-ff:*",
  "0xffff0000000000000000000000000000-0xffff0000000000000000000000ffffff"
};

parseSubnet(prefixedFormats);

parseSubnet(rangeFormats);

String hostFormats[] = {
  "[ffff::]/104",
  "[ffff:0:0:0:0:0:0:0]/104",
  "[ffff:0000:0000:0000:0000:0000:0000:0000]/104",
  "[ffff::]/104",
  "[ffff::0.0.0.0]/104",
  "[0b1111111111111111::]/104",
  "[=q{+M|w0(OeO5^EGP660]/104",
  "[ffff:0:0:0:0:0:0-ff:*]",
  "[ffff::0-ff:*]",
  "[0xffff0000000000000000000000000000-0xffff0000000000000000000000ffffff]",
  "*.*.*.*.*.*.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.f.f.f.f.ip6.arpa",
  "ffff-0-0-0-0-0-0-0.ipv6-literal.net/104"
};

parseHostSubnet(hostFormats);

Here is Go code parsing the same string representations:

func parseSubnet(formats []string) {
	for _, format := range formats {
		fmt.Println(ipaddr.NewIPAddressString(format).GetAddress().AssignPrefixForSingleBlock())
	}
}

func parseHostSubnet(formats []string) {
	for _, format := range formats {
		fmt.Println(ipaddr.NewHostName(format).GetAddress().AssignPrefixForSingleBlock())
	}
}

prefixedFormats := []string{
	"ffff::/104",
	"ffff:0:0:0:0:0:0:0/104",
	"ffff:0000:0000:0000:0000:0000:0000:0000/104",
	"ffff::/104",
	"ffff::0.0.0.0/104",
	"0b1111111111111111::/104",
	"=q{+M|w0(OeO5^EGP660/104",
}

rangeFormats := []string{
	"ffff:0:0:0:0:0:0-ff:*",
	"ffff::0-ff:*",
	"0xffff0000000000000000000000000000-0xffff0000000000000000000000ffffff",
}

parseSubnet(prefixedFormats)

parseSubnet(rangeFormats)

hostFormats := []string{
	"[ffff::]/104",
	"[ffff:0:0:0:0:0:0:0]/104",
	"[ffff:0000:0000:0000:0000:0000:0000:0000]/104",
	"[ffff::]/104",
	"[ffff::0.0.0.0]/104",
	"[0b1111111111111111::]/104",
	"[=q{+M|w0(OeO5^EGP660]/104",
	"[ffff:0:0:0:0:0:0-ff:*]",
	"[ffff::0-ff:*]",
	"[0xffff0000000000000000000000000000-0xffff0000000000000000000000ffffff]",
	"*.*.*.*.*.*.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.f.f.f.f.ip6.arpa",
	"ffff-0-0-0-0-0-0-0.ipv6-literal.net/104",
}

parseHostSubnet(hostFormats)

Note that the parsing code is the same for subnets as addresses. The additional call to assign the prefix corresponding to a single block (assignPrefixForSingleBlock) simply ensures a consistent prefix length in the final result.

Output from both the Java and Go code:

ffff::/104
ffff::/104
ffff::/104
ffff::/104
ffff::/104
ffff::/104
ffff::/104
ffff::/104
ffff::/104
ffff::/104
ffff::/104
ffff::/104
ffff::/104
ffff::/104
ffff::/104
ffff::/104
ffff::/104
ffff::/104
ffff::/104
ffff::/104
ffff::/104
ffff::/104

Delimited Segments

The subnet formats allow you to specify ranges of values. However, if you wish to parse addresses in which values are delimited, then you can use the methods parseDelimitedSegments and countDelimitedAddresses of IPAddressString. The former method will provide an iterator to traverse through the individual addresses, while the latter will provide the number of iterated elements.

For example, parsing the delimited segments of "1,2.3.4,5.6" will iterate through "1.3.4.6", "1.3.5.6", "2.3.4.6" and "2.3.5.6". You can construct IPAddressString instances from each individual string.

Address or Host Name Validation Options

Validation parameters allow you to restrict the permitted string formats, whether you wish to support just IPv4 or IPv6, or whether you wish to support just single addresses, or whether you wish to allow different address or subnet variants.

For IP addresses you can use IPAddressStringParameters with IPAddressString, and for host names you can use HostNameParameters with HostName.

Builders are used to construct. The following example Java code host options along with nested address options within:

HostNameParameters HOST_OPTIONS_EXAMPLE = new HostNameParameters.Builder().
	allowEmpty(false).
	setNormalizeToLowercase(true).
	allowBracketedIPv6(true).
	allowBracketedIPv4(true).
  getAddressOptionsBuilder().
    allowPrefix(true).
    allowMask(true).
    setRangeOptions(RangeParameters.WILDCARD_AND_RANGE).
    allow_inet_aton(true).
    allowEmpty(false).
    allowAll(false).
    allowPrefixOnly(false).
    getIPv4AddressParametersBuilder().
      allowPrefixLengthLeadingZeros(true).
      allowPrefixesBeyondAddressSize(false).
      allowWildcardedSeparator(true).
      getParentBuilder().
    getParentBuilder().
  toParams();

This is the equivalent code for Go. The parameters code for Go exists in a sub-package, the addrstrparam package.

var hostOptionsExample = new(addrstrparam.HostNameParamsBuilder).
	AllowEmpty(false).
	NormalizeToLowercase(true).
	AllowBracketedIPv6(true).
	AllowBracketedIPv4(true).
	GetIPAddressParamsBuilder().
		AllowPrefix(true).
		AllowMask(true).
		SetRangeParams(addrstrparam.WildcardAndRange).
		Allow_inet_aton(true).
		AllowEmpty(false).
		AllowAll(false).
		GetIPv4AddressParamsBuilder().
			AllowPrefixLenLeadingZeros(true).
			AllowPrefixesBeyondAddressSize(false).
			AllowWildcardedSeparator(true).
			GetParentBuilder().
		GetParentBuilder().
	ToParams()

The default options used by the library are permissive and not restrictive.

Host Name or Address with Port or Service Name

For an address or host with port or service name, use HostName. IPv6 addresses with ports should appear as [ipv6Address]:port to resolve the ambiguity of the colon separator, consistent with RFC 2732, 3986, 4038 and other RFCs. However, this library will parse IPv6 addresses without the brackets. You can use the “expectPort” setting of HostNameParameters to resolve ambiguities when the brackets are absent.

Java example code:

HostName hostName = new HostName("[::1]:80");
System.out.println("host: " + hostName.getHost() + " address: " +
  hostName.getAddress() + " port: " + hostName.getPort());

hostName = new HostName("localhost:80");
System.out.println("host: " + hostName.getHost() + " port: " +
  hostName.getPort());

hostName = new HostName("127.0.0.1:80");  
System.out.println("host: " + hostName.getHost() + " address: "
  + hostName.getAddress() + " port: " + hostName.getPort());

Go example code:

hostName := ipaddr.NewHostName("[::1]:80")
fmt.Println("host:", hostName.GetHost(), "address:",
	hostName.GetAddress(), "port:", hostName.GetPort())

hostName = ipaddr.NewHostName("localhost:80")
fmt.Println("host:", hostName.GetHost(), "port:",
	hostName.GetPort())

hostName = ipaddr.NewHostName("127.0.0.1:80")
fmt.Println("host:", hostName.GetHost(), "address:",
	hostName.GetAddress(), "port:", hostName.GetPort())

Output from both the Java and Go code:

host: ::1 address: ::1 port: 80
host: localhost port: 80  
host: 127.0.0.1 address: 127.0.0.1 port: 80

IP Version Determination and IPv4/v6 Conversion

With an IPAddress or IPAddressString object, you can check the version with isIPv4() and isIPv6(). With an IPAddress, you can obtain the more specific type corresponding to the IP version, either IPv4Address or IPv6Address, by calling toIPv4() or toIPv6(), which will return the more specific type if the original address was constructed as that type. Java code:

IPv6Address addr6 = new IPAddressString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").getAddress().toIPv6();

Go code:

var addr6 *ipaddr.IPv6Address
addr6 = ipaddr.NewIPAddressString("2001:0db8:85a3:0000:0000:8a2e:0370:7334").GetAddress().ToIPv6()

In Java, you can make use of isIPv4Convertible() and isIPv6Convertible() to do further conversions if the address is IPv4-mapped.

IPAddressString str = new IPAddressString("::ffff:1.2.3.4");
if(str.isIPv6()) {
  IPv6Address ipv6Address = str.getAddress().toIPv6();
  System.out.println(ipv6Address.toMixedString());
  if(ipv6Address.isIPv4Convertible()) {
    IPv4Address ipv4Address = ipv6Address.toIPv4();
    System.out.println(ipv4Address.toNormalizedString());
  }
}

Output:

::ffff:1.2.3.4
1.2.3.4

In Java, should you wish to change the default IPv4/IPv6 conversions from IPv4 mapped to something else, you can override the pair of methods toIPv4() and isIPv4Convertible() in your own IPv6Address subclass and/or the pair of methods toIPv6() and isIPv6Convertible() in your own IPv4Address subclass.

The Go library does not allow for implicit and automatic conversion. Much like the Go language in general, the code must be more explicit.

In both Go and Java, you can use your own instance of IPAddressConverter for some other suitable conversion, such as IPv4-translated, 6to4, 6over4, IPv4-compatible, or other. The wiki code examples provide example code for Java conversions and Go conversions for all of those IPv4/v6 conversion protocols.

Parsing Addresses with Prefix Length

This library will parse CIDR prefix IP addresses such as 10.1.2.3/24. That string can be interpreted both as an individual address, or as a prefix block with prefix 10.1.2. With other libraries, the ambiguity is resolved by the method, function, or type used for parsing. However, this library uses the same types and methods for both subnets and individual addresses, which can result in much cleaner and more flexible code, but also requires that such ambiguities are resolved differently.

Network addresses (addresses with a host that is zero) like 10.1.2.0/24 and a:​b:c:d::/64 are parsed as the full block of addresses with the indicated prefix - the network prefix remains constant while the host spans all values. This subnet is called the prefix block. If the host is not zero, such as with 10.1.2.3/24, then the string is parsed as a single individual address, but with an associated prefix length. This convention applies to both IPv4 and IPv6.

The same rule applies to strings in which the address is a subnet in which the subnet lower and upper boundaries have zero hosts, like 10.1.2-3.0/24 or 10.1.2.2-6/31. Both of those examples will become CIDR prefix blocks when parsed or constructed, the first with 512 addresses, and the second with 10 addresses.

When parsing an IPAddress string, you can ignore the presence of a prefix length or mask in the string with getHostAddress or toHostAddress. You will get the host address 10.1.2.3 when parsing 10.1.2.3/24. The prefix length remains available by calling getNetworkPrefixLength in Java, or GetNetworkPrefixLen in Go, on the IPAddressString instance.

The example code below shows some of these methods in use. Take note of the count for each parsed string, indicating whether it represents a subnet or address.

static void printPrefixedAddresses(String addressStr) {
  IPAddressString ipAddressString = new IPAddressString(addressStr);
  IPAddress address = ipAddressString.getAddress();
  System.out.println("count: " + address.getCount());
  IPAddress hostAddress = ipAddressString.getHostAddress();
  IPAddress prefixBlock = address.toPrefixBlock();
  Integer prefixLength = ipAddressString.getNetworkPrefixLength();  
  System.out.println(address);
  System.out.println(address.toCanonicalWildcardString());
  System.out.println(hostAddress);
  System.out.println(prefixLength);
  System.out.println(prefixBlock);
  System.out.println();
}

printPrefixedAddresses("10.1.2.3/24"); // individual address
printPrefixedAddresses("10.1.2.0/24"); // network

The equivalent code for Go is:

func printPrefixedAddresses(addressStr string) {
	ipAddressString := ipaddr.NewIPAddressString(addressStr)
	address := ipAddressString.GetAddress()
	fmt.Println("count:", address.GetCount())
	hostAddress := ipAddressString.GetHostAddress()
	prefixBlock := address.ToPrefixBlock()
	prefixLength := ipAddressString.GetNetworkPrefixLen()
	fmt.Println(address)
	fmt.Println(address.ToCanonicalWildcardString())
	fmt.Println(hostAddress)
	fmt.Println(prefixLength)
	fmt.Println(prefixBlock)
	fmt.Println()
}

printPrefixedAddresses("10.1.2.3/24") // individual address
printPrefixedAddresses("10.1.2.0/24") // network

Output from both the Java and Go code:

count: 1
10.1.2.3/24
10.1.2.3
10.1.2.3
24
10.1.2.0/24

count: 256
10.1.2.0/24
10.1.2.*
10.1.2.0
24
10.1.2.0/24

Parse Non-Segmented Addresses – Hex, Octal, IPv6 Base 85, Binary

Typically, the segments or other punctuation identify a string as a host name, as an IPv4 address, or as an IPv6 address. The parser also parses single segment values, or a range of single segment values.

With non-segmented addresses, ambiguity between IPv4 and IPv6 is resolved by the number of digits in the string. The number of digits is 32 for IPv6 hexadecimal, 20 for IPv6 base 85 (see RFC 1924), and 11 or less for IPv4, which can be octal, hexadecimal, or decimal. For IPv4, digits are presumed decimal unless preceded by 0x for hexadecimal or 0 for octal, as is consistent with the inet_aton routine. For IPv6, 32 digits are considered hexadecimal and a preceding 0x is optional.

Here is some Java code parsing single-segment addresses:

IPAddressString ipAddressString = new IPAddressString("4)+k&C#VzJ4br>0wv%Yp"); // base 85
IPAddress address = ipAddressString.getAddress();
System.out.println(address);

ipAddressString = new IPAddressString("108000000000000000080800200c417a"); // hex IPv6
address = ipAddressString.getAddress();
System.out.println(address);

ipAddressString = new IPAddressString("0b00010000100000000000000000000000000000000000000000000000000000000000000000001000000010000000000000100000000011000100000101111010"); // binary IPv6
address = ipAddressString.getAddress();
System.out.println(address);

ipAddressString = new IPAddressString("0x01020304"); // hex IPv4
address = ipAddressString.getAddress();
System.out.println(address);

ipAddressString = new IPAddressString("000100401404"); // octal IPv4
address = ipAddressString.getAddress();
System.out.println(address);

ipAddressString = new IPAddressString("0b00000001000000100000001100000100"); // binary IPv4
address = ipAddressString.getAddress();
System.out.println(address);

Here is equivalent Go code parsing the same single-segment addresses:

ipAddressString := ipaddr.NewIPAddressString("4)+k&C#VzJ4br>0wv%Yp") // base 85
address := ipAddressString.GetAddress()
fmt.Println(address)

ipAddressString = ipaddr.NewIPAddressString("108000000000000000080800200c417a") // hex IPv6
address = ipAddressString.GetAddress()
fmt.Println(address)

ipAddressString = ipaddr.NewIPAddressString("0b00010000100000000000000000000000000000000000000000000000000000000000000000001000000010000000000000100000000011000100000101111010") // binary IPv6
address = ipAddressString.GetAddress()
fmt.Println(address)

ipAddressString = ipaddr.NewIPAddressString("0x01020304") // hex IPv4
address = ipAddressString.GetAddress()
fmt.Println(address)

ipAddressString = ipaddr.NewIPAddressString("000100401404") // octal IPv4
address = ipAddressString.GetAddress()
fmt.Println(address)

ipAddressString = ipaddr.NewIPAddressString("0b00000001000000100000001100000100") // binary IPv4
address = ipAddressString.GetAddress()
fmt.Println(address)

Output from both the Java and Go code:

1080::8:800:200c:417a
1080::8:800:200c:417a
1080::8:800:200c:417a
1.2.3.4
1.2.3.4
1.2.3.4

When parsing a range of single-segment values, it might not be possible to represent the range as a series of segments of range values, which is what is needed to be represented by an IPv6Address of 8 segment ranges, or an IPv4Address of 4 segment ranges.

However, the string can still be parsed. In Java, the parsed result can be obtained using toDivisionGrouping, getDivisionGrouping, providing an exact representation of the string divisions. In both Java and Go, you can call the method getSequentialRange or toSequentialRange providing an IPAddressSeqRange instance with the range of addresses from the lower to the upper value of the range expressed by the string. From the range, a series of IPAddress instances can be obtained using spanWithPrefixBlocks or spanWithSequentialBlocks.

Parse Special Host Names – Reverse DNS Host Name, IPv6 Literal UNC Host Name

A couple of standardized host formats are recognized, namely the reverse DNS host format, and the UNC IPv6 literal host format.

Here is a Java code example parsing such strings:

HostName hostName = new HostName("a.7.1.4.c.0.0.2.0.0.8.0.8.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.0.1.ip6.arpa");
System.out.println(hostName.asAddress());

hostName = new HostName("4.3.2.1.in-addr.arpa");
System.out.println(hostName.asAddress());

hostName = new HostName("1080-0-0-0-8-800-200c-417a.ipv6-literal.net");  
System.out.println(hostName.asAddress());

Here is the equivalent Go code:

hostName := ipaddr.NewHostName("a.7.1.4.c.0.0.2.0.0.8.0.8.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.0.1.ip6.arpa")
fmt.Println(hostName.AsAddress())

hostName = ipaddr.NewHostName("4.3.2.1.in-addr.arpa")
fmt.Println(hostName.AsAddress())

hostName = ipaddr.NewHostName("1080-0-0-0-8-800-200c-417a.ipv6-literal.net")
fmt.Println(hostName.AsAddress())

Output from both the Java and Go code:

1080::8:800:200c:417a
1.2.3.4  
1080::8:800:200c:417a

A couple of methods in HostName are available to indicate such strings:

public boolean isUNCIPv6Literal()  
public boolean isReverseDNS()
func (host *HostName) IsUncIPv6Literal() bool
func (host *HostName) IsReverseDNS() bool

Parse IPv6 Zone or Scope ID

The IPv6 zone or scope ID is recognized, denoted by the ‘%’ character. It can be retrieved by the method getZone in IPv6Address. Here is sample Java code:

IPAddress addr = new IPAddressString("::%eth0").getAddress();
if(addr.isIPv6()) {
  System.out.println(addr.toIPv6().getZone());
}

Here is sample Go code to do the same:

addr := ipaddr.NewIPAddressString("::%eth0").GetAddress()
if addr.IsIPv6() {
	fmt.Println(addr.ToIPv6().GetZone())
}

Output from both the Java and Go code:

eth0

Addresses from Numeric Values

In addition to the range of string formats that can be parsed to produce IPAddress instances, you can also obtain IPAddress instances from a large number of numeric formats. You an obtain instances of IPAddress from byte arrays in Java and from byte slices in Go. You can obtain instances of IPAddress from java.net.InetAddress or java.net.InterfaceAddress in Java and any one of net.IP, net.IPAddr, net.IPMask, net.IPNet, netip.Addr, or netip.Prefix in Go. You can obtain instances of IPAddress from arrays of address segments in Java, or from slices of address segments in Go. You can obtain instances of IPAddress from individual integer segment values using the SegmentValueProvider interface. All of these options are generic to either IPv4 or IPv6 unless you specifically choose the IPv4 or IPv6-specific constructors. See the godoc or javadoc for the full list.

For IPv4 you have the additional option of constructing an address from a 32-bit integer. See the godoc or javadoc.

For IPv6, you have the additional options of constructing from MAC address instances, from a java.math.BigInteger in Java, or from a math/big.Int or a pair of 64-bit unsigned integers in Go. See the godoc or javadoc.

Once you have an IPAddress instance, there are methods to convert to bytes, to sections, to subnets or network prefixes, to masks, to all the same standard-library types from which you can construct an IPAddress instance, to different string representations, and so on.

When constructing IP addresses or sections, you can supply a prefix length, and when you do, the same rules regarding zero-hosts applies as when parsing from strings. Network addresses (addresses with host that is zero) like 10.1.2.0/24 and a:​b:c:d::/64 are constructed as the prefix block of addresses with the indicated prefix. If the host is not zero, like 10.1.2.3/24, then it is constructed as an individual address, an address with an associated prefix length. This applies to both IPv4 and IPv6.

The same rule applies to subnets where the lower and upper values have zero hosts, like 10.1.2-3.0/24 or 10.1.2.2-6/31. Both of those examples will become CIDR prefix blocks when constructed.

Should you wish to get the individual address or section with a zero host, you can construct without the prefix length and then apply the prefix length afterwards, or you can use getLower or toZeroHost after construction.

Golang Address Keys

The Go language has the concept of comparable types, those types that can be compared with comparison operators. The core types of this library are not comparable in that manner, although they are all comparable with each other using their Compare methods, or using one of the library’s comparator instances.

Each of the address and range core types provides an associated key type, a value type, that is comparable with comparison operators and usable as keys for the Go built-in map type. Use the ToKey methods to obtain the corresponding key, and use each key’s ToAddress method to get back the corresponding address.

You can see an example using address keys in the example wiki.

Golang Zero Values

The following Go code reveals the zero values for the address and sequential range core types, as well as some other related types:

strip := func(s string) string {
	return strings.ReplaceAll(strings.ReplaceAll(s, "ipaddr.", ""),
		"github.com/seancfoley/ipaddress-go/", "")
}

typeName := func(i any) string {
	return strip(reflect.ValueOf(i).Elem().Type().Name())
}

interfaceTypeName := func(i any) string {
	return strip(reflect.TypeOf(i).String())
}

truncateIndent := func(s, indent string) string {
	if boundary := len(indent) - (len(s) >> 3); boundary >= 0 {
		return indent[:boundary] + "\t" // every 8 chars eliminates a tab
	}
	return ""
}

baseIndent := "\t\t\t"
title := "Address item zero values"
fmt.Printf("%s%sint\tbits\tcount\tstring\n", title, truncateIndent(title, baseIndent))
vars := []ipaddr.AddressItem{
	&ipaddr.Address{}, &ipaddr.IPAddress{},
	&ipaddr.IPv4Address{}, &ipaddr.IPv6Address{}, &ipaddr.MACAddress{},

	&ipaddr.AddressSection{}, &ipaddr.IPAddressSection{},
	&ipaddr.IPv4AddressSection{}, &ipaddr.IPv6AddressSection{}, &ipaddr.MACAddressSection{},
	&ipaddr.EmbeddedIPv6AddressSection{},
	&ipaddr.AddressDivisionGrouping{}, &ipaddr.IPAddressLargeDivisionGrouping{},
	&ipaddr.IPv6v4MixedAddressGrouping{},

	&ipaddr.AddressSegment{}, &ipaddr.IPAddressSegment{},
	&ipaddr.IPv4AddressSegment{}, &ipaddr.IPv6AddressSegment{}, &ipaddr.MACAddressSegment{},
	&ipaddr.AddressDivision{}, &ipaddr.IPAddressLargeDivision{},

	&ipaddr.IPAddressSeqRange{}, &ipaddr.IPv4AddressSeqRange{}, &ipaddr.IPv6AddressSeqRange{},
}
for _, v := range vars {
	name := typeName(v) + "{}"
	indent := truncateIndent(name, baseIndent)
	fmt.Printf("%s%s%v\t%v\t%v\t\"%v\"\n", name, indent, v.GetValue(), v.GetBitCount(), v.GetCount(), v)
}

Zero values for versioned address types, like those for IPv4 and IPv6, are the respective zero-valued addresses.

For other addresses, sections and groupings, those with no specific address type, version, and length, the zero values have no segments nor divisions. They have a total of zero bits.

Regardless of bit-size, all zero-values have a corresponding integer value of zero.

The zero values for sequential ranges correspond to ranges with both boundaries as the corresponding zero valued address.

Output:

Address item zero values        int     bits    count   string
Address{}                       0       0       1       ""
IPAddress{}                     0       0       1       ""
IPv4Address{}                   0       32      1       "0.0.0.0"
IPv6Address{}                   0       128     1       "::"
MACAddress{}                    0       48      1       "00:00:00:00:00:00"
AddressSection{}                0       0       1       ""
IPAddressSection{}              0       0       1       ""
IPv4AddressSection{}            0       0       1       ""
IPv6AddressSection{}            0       0       1       ""
MACAddressSection{}             0       0       1       ""
EmbeddedIPv6AddressSection{}    0       0       1       ""
AddressDivisionGrouping{}       0       0       1       ""
IPAddressLargeDivisionGrouping{}0       0       1       ""
IPv6v4MixedAddressGrouping{}    0       0       1       ""
AddressSegment{}                0       0       1       "0x0"
IPAddressSegment{}              0       0       1       "0x0"
IPv4AddressSegment{}            0       8       1       "0"
IPv6AddressSegment{}            0       16      1       "0x0"
MACAddressSegment{}             0       8       1       "0x0"
AddressDivision{}               0       0       1       "0x0"
IPAddressLargeDivision{}        0       0       1       "0x0"
SequentialRange[*IPAddress]{}   0       0       1       " -> "
SequentialRange[*IPv4Address]{} 0       32      1       "0.0.0.0 -> 0.0.0.0"
SequentialRange[*IPv6Address]{} 0       128     1       ":: -> ::"

Address items allow for producing strings and counts from nil pointers. Most other methods, methods that require analysis of the internals of those struct types, will panic on nil pointers. But the String and GetCount methods will return a string indicating nil and a count of zero.

title = "Address item nil pointers"
fmt.Printf("\n%s%scount\tstring\n", title, truncateIndent(title, baseIndent+"\t\t"))
nilPtrItems := []ipaddr.AddressItem{
	(*ipaddr.Address)(nil), (*ipaddr.IPAddress)(nil),
	(*ipaddr.IPv4Address)(nil), (*ipaddr.IPv6Address)(nil), (*ipaddr.MACAddress)(nil),

	(*ipaddr.AddressSection)(nil), (*ipaddr.IPAddressSection)(nil),
	(*ipaddr.IPv4AddressSection)(nil), (*ipaddr.IPv6AddressSection)(nil), (*ipaddr.MACAddressSection)(nil),

	(*ipaddr.AddressSegment)(nil), (*ipaddr.IPAddressSegment)(nil),
	(*ipaddr.IPv4AddressSegment)(nil), (*ipaddr.IPv6AddressSegment)(nil), (*ipaddr.MACAddressSegment)(nil),

	(*ipaddr.IPAddressSeqRange)(nil), (*ipaddr.IPv4AddressSeqRange)(nil), (*ipaddr.IPv6AddressSeqRange)(nil),
}
for _, v := range nilPtrItems {
	name := "(" + interfaceTypeName(v) + ")(nil)"
	indent := truncateIndent(name, baseIndent+"\t\t")
	fmt.Printf("%s%s%v\t\"%v\"\n", name, indent, v.GetCount(), v)
}

Output:

Address item nil pointers                       count   string
(*Address)(nil)                                 0       "<nil>"
(*IPAddress)(nil)                               0       "<nil>"
(*IPv4Address)(nil)                             0       "<nil>"
(*IPv6Address)(nil)                             0       "<nil>"
(*MACAddress)(nil)                              0       "<nil>"
(*AddressSection)(nil)                          0       "<nil>"
(*IPAddressSection)(nil)                        0       "<nil>"
(*IPv4AddressSection)(nil)                      0       "<nil>"
(*IPv6AddressSection)(nil)                      0       "<nil>"
(*MACAddressSection)(nil)                       0       "<nil>"
(*AddressSegment)(nil)                          0       "<nil>"
(*IPAddressSegment)(nil)                        0       "<nil>"
(*IPv4AddressSegment)(nil)                      0       "<nil>"
(*IPv6AddressSegment)(nil)                      0       "<nil>"
(*MACAddressSegment)(nil)                       0       "<nil>"
(*SequentialRange[*IPAddress])(nil)             0       "<nil>"
(*SequentialRange[*IPv4Address])(nil)           0       "<nil>"
(*SequentialRange[*IPv6Address])(nil)           0       "<nil>"

The address key value types have zero values matching the zero values of their corresponding address and range types.

title = "Address key zero values"
fmt.Printf("\n%s%sstring\n", title, truncateIndent(title, baseIndent+"\t\t\t"))
keys := []fmt.Stringer{
	&ipaddr.AddressKey{}, &ipaddr.IPAddressKey{},
	&ipaddr.IPv4AddressKey{}, &ipaddr.IPv6AddressKey{}, &ipaddr.MACAddressKey{},
	&ipaddr.IPAddressSeqRangeKey{}, &ipaddr.IPv4AddressSeqRangeKey{}, &ipaddr.IPv6AddressSeqRangeKey{},
}
for _, k := range keys {
	name := typeName(k) + "{}"
	indent := truncateIndent(name, baseIndent+"\t\t\t")
	fmt.Printf("%s%s\"%v\"\n", name, indent, k)
}

Output:

Address key zero values                                 string
Key[*Address]{}                                         ""
Key[*IPAddress]{}                                       ""
IPv4AddressKey{}                                        "0.0.0.0"
IPv6AddressKey{}                                        "::"
MACAddressKey{}                                         "00:00:00:00:00:00"
SequentialRangeKey[*IPAddress]{}                        " -> "
SequentialRangeKey[*IPv4Address]{}                      "0.0.0.0 -> 0.0.0.0"
SequentialRangeKey[*IPv6Address]{}                      ":: -> ::"

The zero values of host identifier strings are empty strings.

title = "Host id zero values"
fmt.Printf("\n%s%sstring\n", title, truncateIndent(title, baseIndent+"\t\t\t"))
hostids := []ipaddr.HostIdentifierString{
  &ipaddr.HostName{}, &ipaddr.IPAddressString{}, &ipaddr.MACAddressString{},
}
for _, k := range hostids {
  name := typeName(k) + "{}"
  indent := truncateIndent(name, baseIndent+"\t\t\t")
  fmt.Printf("%s%s\"%v\"\n", name, indent, k)
}

Output:

Host id zero values                                     string
HostName{}                                              ""
IPAddressString{}                                       ""
MACAddressString{}                                      ""

Like addresses, host identifier strings allow for producing strings from nil pointers. Most other methods of these types will panic on nil pointers, but the String methods will return a string indicating nil.

title = "Host id nil pointers"
fmt.Printf("\n%s%sstring\n", title, truncateIndent(title, baseIndent+"\t\t\t"))
nilPtrIds := []ipaddr.HostIdentifierString{
  (*ipaddr.HostName)(nil), (*ipaddr.IPAddressString)(nil), (*ipaddr.MACAddressString)(nil),
}
for _, v := range nilPtrIds {
  name := "(" + interfaceTypeName(v) + ")(nil)"
  indent := truncateIndent(name, baseIndent+"\t\t\t")
  fmt.Printf("%s%s\"%v\"\n", name, indent, v)
}

Output:

Host id nil pointers                                    string
(*HostName)(nil)                                        "<nil>"
(*IPAddressString)(nil)                                 "<nil>"
(*MACAddressString)(nil)                                "<nil>"

Networks

Each of the IP address versions have an associated singleton network object. The network objects are used for caching, for configuration, or for obtaining masks and loopbacks.

Each of the IP address versions also have an associated “creator” type that can be used to create addresses, sections, and segments, and instances of those types may perform caching of address components for efficient memory usage and performance.

In the Java library, the methods defaultIpv6Network and defaultIpv4Network in Address provide access to the respective network objects. There is also a counterpart for MAC, available from the defaultMACNetwork method. Each network has an associated creator object available from the getAddressCreator method.

In the Go library, the network objects are the package-level variables ipaddr.IPv4Network and ipaddr.IPv6Network. Use the type IPAddressCreator for creator instances.

Prefix Length Handling

Prefix lengths in strings are parsed, as indicated in the section above on parsing, or can be supplied when directly constructing addresses or sections. Addresses and sections store their prefix lengths and the prefix length is incorporated in numerous address operations as well as when producing strings. For instance, an address will provide the network section, based upon the prefix length, with calls to the IPAddress method getNetworkSection in Java or GetNetworkSection in Go, and will supply the prefix length when calling getNetworkPrefixLength in Java or GetNetworkPrefixLen in Go.

Given an address with no prefix length, you can convert to an address with prefix length using the methods assignPrefixForSingleBlock / AssignPrefixForSingleBlock or assignMinPrefixForBlock / AssignMinPrefixForBlock, or any of the methods that allow you to set a prefix length directly such as setPrefixLength / SetPrefixLen or adjustPrefixLength / AdjustPrefixLen in Java / Go.

Anytime you have an individual address or a subnet with prefix length, you can get the address representing the entire block for that prefix using the method toPrefixBlock / ToPrefixBlock in in Java / Go. This type of subnet can be called a CIDR prefix block, a subnet which spans all the hosts for a specific CIDR prefix. In the reverse direction, given a CIDR prefix block, you can get the IPv4 network address or the IPv6 anycast address by calling getLower / GetLower or by calling toZeroHost / ToZeroHost.

Prefix Length and Equality

In this library, the subnet with prefix length 10.1.2.0/24, which can also be written as 10.1.2.*/24, is equivalent the non-prefixed address 10.1.2.*, since they both contain the same set of numeric addresses. In other words, when it comes to equality or comparison, the prefix length has no effect. Equality and comparison is entirely based on numeric values.

Address Sections

Addresses can be broken up into sections, and reconstituted from sections. A section is a series of segments. You can get the section for the full address by calling getSection, or you can get subsections by calling one of the variants, either getSection(int) or getSection(int, int) in Java. In Go you would use GetSubSection or GetTrailingSection. These methods return a subsection spanning the given indices. You can also get the segments in an address by calling getSegment or one of the variants of getSegments in Java, or one of GetSegments, CopySegments or CopySubSegments in Go, either on the address or on a section of the address.

You can also reconstitute an address from a section or array of segments using the appropriate address constructor, if your section or array of segments has the correct number of segments for the address type.

Host and Network Sections of IP Address

Use getHostSection() and getNetworkSection() to get the host and network sections of an IP address as indicated by prefix length, as shown by this Java code:

IPAddress address = new IPAddressString("1.2.3.4").getAddress();  
IPAddressSection network = address.getNetworkSection(16, true);  
IPAddressSection host = address.getHostSection(16);  
System.out.println(network.toCanonicalString());  
System.out.println(host.toCanonicalString());

This is the equivalent Go code:

address := ipaddr.NewIPAddressString("1.2.3.4").GetAddress()
network := address.GetNetworkSectionLen(16)
host := address.GetHostSectionLen(16).WithoutPrefixLen()
fmt.Println(network.ToCanonicalString())
fmt.Println(host.ToCanonicalString())

Output from both the Java and Go code:

1.2/16
3.4

Once you have a section of an address, most of the same methods are available as those available with addresses themselves.

IP Address Ranges

IPAddress Internal Format

An IPAddress or IPAddressString instance can represent any individual address or any range of addresses in which each segment specifies a range of sequential values. Such a range can be called a segment block, a block of values within the segment’s range of possible values. A subnet that contains segment blocks for any of its segments can be called a segment block subnet. An individual address is one in which each segment is a segment block of size one, just a single value.

Any CIDR prefix block can be specified as segment blocks. However, a prefix block is a more specific type of subnet, a subnet that contains the full range of values for its prefix. So any segment outside the prefix is a segment block containing all possible values for the segment.

An IPAddress instance has the canonical number of segments for its address version or type, which is 4 segments for IPv4, 8 for IPv6, and either 6 or 8 for MAC.

An IPAddressString can represent any such address string, as well as those that do not use the canonical number of segments. However, for ranges that do not have the canonical number of segments, converting to an IPAddress instance is not always possible (for example the IPv4 subnet 1.2-3.0-1000 cannot be expressed with 4 segments).

Sequential Blocks

Not all IPAddress subnets are sequential. For instance, 1-2.3.4-5.6 is not sequential, since the address 1.3.4.6 is followed in the block by 1.3.5.6 while the next sequential address 1.3.4.7 is not part of the block. A sequential block is an IPAddress subnet in which the range is sequential. For a block to be sequential, the first segment with a range of values must be followed only by segments that cover all values. For instance, 1.2.3-4.* is a sequential block, as well as 1:a-f:*:*:*:*:*:*, also writeable as 1:a-f:*. Any prefix block is sequential. A prefix block can also be expressed without the prefix length. For instance, the prefix block 1:2:3:4::/64 can be written without the prefix length as 1:2:3:4:*.

You can convert a non-sequential block to a collection of sequential blocks using the sequentialBlockIterator / SequentialBlockIterator method of IPAddress. If you wish to get the count of sequential blocks, use the method getSequentialBlockCount / GetSequentialBlockCount.

This Java and Go code demonstrates the use of the sequential block iterator.

convertNonSequentialBlock("a:b:c:d:1:2-4:3-5:4-6");

static void convertNonSequentialBlock(String string) {
  IPAddressString addrString = new IPAddressString(string);
  IPAddress addr = addrString.getAddress();
  System.out.println("Initial range block is " + addr);
  BigInteger sequentialCount = addr.getSequentialBlockCount();
  System.out.println("Sequential range block count is " + sequentialCount);

  Iterator<? extends IPAddress> iterator = addr.sequentialBlockIterator();
  while(iterator.hasNext()) {
    System.out.println(iterator.next());
  }
}
convertNonSequentialBlock("a:b:c:d:1:2-4:3-5:4-6")

func convertNonSequentialBlock(str string) {
	addrString := ipaddr.NewIPAddressString(str)
	addr := addrString.GetAddress()
	fmt.Println("Initial range block is", addr)
	sequentialCount := addr.GetSequentialBlockCount()
	fmt.Println("Sequential range block count is", sequentialCount)

	iterator := addr.SequentialBlockIterator()
	for iterator.HasNext() {
		fmt.Println(iterator.Next())
	}
}

Output from both the Java and Go code:

Initial range block is a:b:c:d:1:2-4:3-5:4-6
Sequential range block count is 9
a:b:c:d:1:2:3:4-6
a:b:c:d:1:2:4:4-6
a:b:c:d:1:2:5:4-6
a:b:c:d:1:3:3:4-6
a:b:c:d:1:3:4:4-6
a:b:c:d:1:3:5:4-6
a:b:c:d:1:4:3:4-6
a:b:c:d:1:4:4:4-6
a:b:c:d:1:4:5:4-6

Sequential Ranges

Not all sequential address ranges can be described by an instance of IPAddress or IPAddressString. One such example is the range of two IPv4 addresses from 1.2.3.255 to 1.2.4.0. One option is to represent the address range with just a single large segment covering the section of the address that has a range of values, such as in an instance of IPAddressLargeDivisionGrouping.

The more common option is to use an IPAddressSeqRange instance, which provides a more general representation of address ranges and their associated operations. You can represent any sequential range of addresses with an IPAddressSeqRange instance. In Go, IPAddressSeqRange is an alias for SequentialRange[*IPAddress].

You can also specify more specific ranges using IPv4AddressSeqRange and IPv6AddressSeqRange if the polymorphism of IPAddressSeqRange is not required. An IPAddressSeqRange represents either an IPv4 or IPv6 range, but it cannot represent a mix of IPv4 and IPv6 addresses.

Both IPAddress and IPAddressSeqRange implement the IPAddressRange interface. IPAddressSeqRange instances cover all ranges of addresses, while IPAddress instances cover all ranges of segments within addresses.
Individual addresses and CIDR prefix blocks can be represented by either type, although IPAddress instances are the standard for expressing individual addresses and CIDR prefix blocks.

Any IPAddressSeqRange instance can be converted to the minimal list of IPAddress sequential blocks or prefix blocks that cover the exact same range of addresses, providing a couple of different ways to go from IPAddressSeqRange to IPAddress instances. To go in the reverse direction, from IPAddress to IPAddressSeqRange, you can start with a sequential block iterator to convert any IPAddress instance to a series of IPAddress sequential blocks. Then you can convert each IPAddress sequential block to an IPAddressSeqRange with the method toSequentialRange. Finally, to eliminate overlap and obtain a minimal list of IPAddressSeqRange instances, you can use the join method of IPAddressSeqRange.

Here we show code examples of a round-trip from a sequential range to a list of sequential or prefix blocks, and then back again, merging the list of blocks back to the original sequential range.

First we show the process in Java code:

String address1 = "2:3:ffff:5::", address2 = "2:4:1:5::";
IPAddressString string1 = new IPAddressString(address1), string2 = new IPAddressString(address2);
IPAddress addr1 = string1.getAddress(), addr2 = string2.getAddress();
IPAddressSeqRange range = addr1.spanWithRange(addr2);
System.out.println("Original sequential range of " +
	range.getCount() + " addresses: " + range);

spanAndMergeSequentialBlocks(range);
spanAndMergePrefixBlocks(range);

static void spanAndMergePrefixBlocks(IPAddressSeqRange range) {
	IPAddress result[] = range.spanWithPrefixBlocks();
	System.out.println("Prefix blocks: " + Arrays.asList(result));
	List<IPAddressSeqRange> rangeList = new ArrayList<>();
	for(IPAddress a : result) {
		rangeList.add(a.toSequentialRange());
	}
	mergeBack(rangeList);
}

static void spanAndMergeSequentialBlocks(IPAddressSeqRange range) {
	IPAddress result[] = range.spanWithSequentialBlocks();
	System.out.println("Sequential blocks: " + Arrays.asList(result));
	List<IPAddressSeqRange> rangeList = new ArrayList<>();
	for(IPAddress a : result) {
		rangeList.add(a.toSequentialRange());
	}
	mergeBack(rangeList);
}

static void mergeBack(List<IPAddressSeqRange> rangeList) {
	IPAddressSeqRange joined[] = IPAddressSeqRange.join(
		rangeList.toArray(new IPAddressSeqRange[rangeList.size()]));
	System.out.println("Merged back again: " + Arrays.asList(joined));
}

Now we show the process in Go code:

address1, address2 := "2:3:ffff:5::", "2:4:1:5::"
string1, string2 := ipaddr.NewIPAddressString(address1), ipaddr.NewIPAddressString(address2)
addr1, addr2 := string1.GetAddress(), string2.GetAddress()
rng := addr1.SpanWithRange(addr2)
fmt.Println("Original sequential range of", rng.GetCount(), "addresses:", rng)

spanAndMergeSequentialBlocks(rng)
spanAndMergePrefixBlocks(rng)

func spanAndMergePrefixBlocks(rng *ipaddr.IPAddressSeqRange) {
	result := rng.SpanWithPrefixBlocks()
	fmt.Println("Prefix blocks:", commaDelimit(result))
	var rangeList []*ipaddr.IPAddressSeqRange
	for _, a := range result {
		rangeList = append(rangeList, a.ToSequentialRange())
	}
	mergeBack(rangeList)
}

func spanAndMergeSequentialBlocks(rng *ipaddr.IPAddressSeqRange) {
	result := rng.SpanWithSequentialBlocks()
	fmt.Println("Sequential blocks:", commaDelimit(result))
	var rangeList []*ipaddr.IPAddressSeqRange
	for _, a := range result {
		rangeList = append(rangeList, a.ToSequentialRange())
	}
	mergeBack(rangeList)
}

func mergeBack(rangeList []*ipaddr.IPAddressSeqRange) {
	var rng *ipaddr.IPAddressSeqRange
	joined := rng.Join(rangeList...) // can handle nil args, including the receiver
	fmt.Println("Merged back again:", joined)
}

func commaDelimit(slice any) string {
	return strings.ReplaceAll(fmt.Sprint(slice), " ", ", ")
}

Output from both the Java and Go code:

Original range of size 2417851639229258349412353: 2:3:ffff:5:: -> 2:4:1:5::
Sequential blocks: [2:3:ffff:5-ffff:*:*:*:*, 2:4:0:*:*:*:*:*, 2:4:1:0-4:*:*:*:*, 2:4:1:5::]
Merged back again: [2:3:ffff:5:: -> 2:4:1:5::]
Prefix blocks: [2:3:ffff:5::/64, 2:3:ffff:6::/63, 2:3:ffff:8::/61, 2:3:ffff:10::/60, 2:3:ffff:20::/59, 2:3:ffff:40::/58, 2:3:ffff:80::/57, 2:3:ffff:100::/56, 2:3:ffff:200::/55, 2:3:ffff:400::/54, 2:3:ffff:800::/53, 2:3:ffff:1000::/52, 2:3:ffff:2000::/51, 2:3:ffff:4000::/50, 2:3:ffff:8000::/49, 2:4::/48, 2:4:1::/62, 2:4:1:4::/64, 2:4:1:5::/128]
Merged back again: [2:3:ffff:5:: -> 2:4:1:5::]

As you can see in the example above, you can generally describe a range with fewer sequential blocks than prefix blocks.

Address Tries

The trie data structure is particularly useful when working with addresses. For that reason this library includes compact binary address tries (aka compact binary prefix tree or binary radix trie, amongst other names). Tries provide efficient retrieval operations, hence the name trie, but what makes them additionally useful for addresses is the fact that prefix tries are organized by the bits in the prefix of each key, the keys being addresses in this case, which mirrors the way that CIDR subnets and addresses are organized by prefix. So you can use tries for efficient subnet containment checks on many addresses or subnets at once in constant time (such as with a routing table). A trie is useful for efficient lookups, for efficiently dividing and subdividing subnets, for sorting addresses, and for traversing through subnets in different ways.

By associating each trie node with a value, tries can also be used for value lookups in which the keys are addresses.

Tries can also be used as the backing data structures for maps and sets.

When handling large numbers of addresses or CIDR prefix blocks, it can be much more efficient to use the trie data structure for common operations on those addresses and blocks, the trie constructed in linear time proportional to the number of addresses, and then offering constant time containment and retrieval operations on all of the contained addresses or subnets at once.

Here is an IPv6 trie constructed and then converted to a string, first in Java:

<T extends AddressTrie<A>, A extends Address> T populateTree(
			T trie, String addrStrs[], Function<String, A> creator) {
	for(String addrStr : addrStrs) {
		A addr = creator.apply(addrStr);
		trie.add(addr);
	}
	return trie;
}

String ipv6Addresses[] = {
	"1::ffff:2:3:5",
	"1::ffff:2:3:4",
	"1::ffff:2:3:6",
	"1::ffff:2:3:12",
	"1::ffff:aa:3:4",
	"1::ff:aa:3:4",
	"1::ff:aa:3:12",
	"bb::ffff:2:3:6",
	"bb::ffff:2:3:12",
	"bb::ffff:2:3:22",
	"bb::ffff:2:3:32",
	"bb::ffff:2:3:42",
	"bb::ffff:2:3:43",
};
IPv6AddressTrie ipv6Trie = populateTree(
	new IPv6AddressTrie(),
	ipv6Addresses,
	str -> new IPAddressString(str).getAddress().toIPv6());
System.out.println(ipv6Trie);

and now here is the code in Go:

ipv6Addresses := []string{
  "1::ffff:2:3:5",
  "1::ffff:2:3:4",
  "1::ffff:2:3:6",
  "1::ffff:2:3:12",
  "1::ffff:aa:3:4",
  "1::ff:aa:3:4",
  "1::ff:aa:3:12",
  "bb::ffff:2:3:6",
  "bb::ffff:2:3:12",
  "bb::ffff:2:3:22",
  "bb::ffff:2:3:32",
  "bb::ffff:2:3:42",
  "bb::ffff:2:3:43",
}
ipv6Trie := populateTree(ipv6Addresses)
fmt.Println(ipv6Trie)

func populateTree(addrStrs []string) ipaddr.Trie[*ipaddr.IPAddress] {
	trie := ipaddr.Trie[*ipaddr.IPAddress]{}
	for _, addrStr := range addrStrs {
  	addr := ipaddr.NewIPAddressString(addrStr).GetAddress()
  	trie.Add(addr)
	}
	return trie
}

Output from both the Java and Go code:

○ ::/0 (13)
└─○ ::/8 (13)
  ├─○ 1::/64 (7)
  │ ├─○ 1::ff:aa:3:0/123 (2)
  │ │ ├─● 1::ff:aa:3:4 (1)
  │ │ └─● 1::ff:aa:3:12 (1)
  │ └─○ 1::ffff:0:0:0/88 (5)
  │   ├─○ 1::ffff:2:3:0/123 (4)
  │   │ ├─○ 1::ffff:2:3:4/126 (3)
  │   │ │ ├─○ 1::ffff:2:3:4/127 (2)
  │   │ │ │ ├─● 1::ffff:2:3:4 (1)
  │   │ │ │ └─● 1::ffff:2:3:5 (1)
  │   │ │ └─● 1::ffff:2:3:6 (1)
  │   │ └─● 1::ffff:2:3:12 (1)
  │   └─● 1::ffff:aa:3:4 (1)
  └─○ bb::ffff:2:3:0/121 (6)
    ├─○ bb::ffff:2:3:0/122 (4)
    │ ├─○ bb::ffff:2:3:0/123 (2)
    │ │ ├─● bb::ffff:2:3:6 (1)
    │ │ └─● bb::ffff:2:3:12 (1)
    │ └─○ bb::ffff:2:3:20/123 (2)
    │   ├─● bb::ffff:2:3:22 (1)
    │   └─● bb::ffff:2:3:32 (1)
    └─○ bb::ffff:2:3:42/127 (2)
      ├─● bb::ffff:2:3:42 (1)
      └─● bb::ffff:2:3:43 (1)

An IPv4 trie constructed and then converted to a string using the same polymorphic populateTree method, first in Java:

String ipv4Addresses[] = {
	"1.2.3.4",
	"1.2.3.5",
	"1.2.3.6",
	"1.2.3.3",
	"1.2.3.255",
	"2.2.3.5",
	"2.2.3.128",
	"2.2.3.0/24",
	"2.2.4.0/24",
	"2.2.7.0/24",
	"2.2.4.3",
};
IPv4AddressTrie ipv4Trie = populateTree(
		new IPv4AddressTrie(),
		ipv4Addresses,
		str -> new IPAddressString(str).getAddress().toIPv4());
System.out.println(ipv4Trie);

and also in Go:

ipv4Addresses := []string{
	"1.2.3.4",
	"1.2.3.5",
	"1.2.3.6",
	"1.2.3.3",
	"1.2.3.255",
	"2.2.3.5",
	"2.2.3.128",
	"2.2.3.0/24",
	"2.2.4.0/24",
	"2.2.7.0/24",
	"2.2.4.3",
}
ipv4Trie := populateTree(ipv4Addresses)
fmt.Println(ipv4Trie)

Output from both the Java and Go code:

○ 0.0.0.0/0 (11)
└─○ 0.0.0.0/6 (11)
  ├─○ 1.2.3.0/24 (5)
  │ ├─○ 1.2.3.0/29 (4)
  │ │ ├─● 1.2.3.3 (1)
  │ │ └─○ 1.2.3.4/30 (3)
  │ │   ├─○ 1.2.3.4/31 (2)
  │ │   │ ├─● 1.2.3.4 (1)
  │ │   │ └─● 1.2.3.5 (1)
  │ │   └─● 1.2.3.6 (1)
  │ └─● 1.2.3.255 (1)
  └─○ 2.2.0.0/21 (6)
    ├─● 2.2.3.0/24 (3)
    │ ├─● 2.2.3.5 (1)
    │ └─● 2.2.3.128 (1)
    └─○ 2.2.4.0/22 (3)
      ├─● 2.2.4.0/24 (2)
      │ └─● 2.2.4.3 (1)
      └─● 2.2.7.0/24 (1)

A more compact non-binary representation of the same IPv4 trie, both Java and Go, respectively:

System.out.println(ipv4Trie.toAddedNodesTreeString());
fmt.Println(ipv4Trie.AddedNodesTreeString())

Output from both the Java and Go code:

○ 0.0.0.0/0
├─● 1.2.3.3
├─● 1.2.3.4
├─● 1.2.3.5
├─● 1.2.3.6
├─● 1.2.3.255
├─● 2.2.3.0/24
│ ├─● 2.2.3.5
│ └─● 2.2.3.128
├─● 2.2.4.0/24
│ └─● 2.2.4.3
└─● 2.2.7.0/24

Partitioning Subnets

An address trie stores individual addresses or CIDR prefix blocks subnets. There are IPAddress instances that cannot be added to a trie as-is, subnets that are not CIDR prefix blocks. Such subnets can be subdivided or partitioned to addresses or CIDR prefix blocks in various ways.

The Partition type encapsulates a partition of a subnet. It also provides a couple of methods that subdivide any subnet into individual addresses or prefix block subnets, which can then be inserted into a trie. Much like an iterator, a partition can be used only once. Simply create another whenever that may be necessary.

The two partition methods provided partition differently. partitionWithSingleBlockSize finds a maximal prefix block size and then iterates through a series of prefix blocks of that size. partitionWithSpanningBlocks uses any number of different prefix block sizes, which frequently results in a smaller total number of blocks.

Here we partition an IPv4 subnet with partitionWithSingleBlockSize and then check for the partitioned elements in the trie, first in Java:

String addrs = "1.2.1-7.*";
IPv4AddressTrie trie = new IPv4AddressTrie();
IPv4Address subnet = new IPAddressString(addrs).getAddress().toIPv4();
Partition.partitionWithSingleBlockSize(subnet).predicateForEach(trie::add);
boolean foundThemAll = Partition.partitionWithSingleBlockSize(subnet).predicateForEach(trie::contains);
System.out.println("all inserted: " + foundThemAll);
System.out.println(trie);

Here is the equivalent Go code:

addrs := "1.2.1-7.*"
trie := ipaddr.Trie[*ipaddr.IPAddress]{}
subnet := ipaddr.NewIPAddressString(addrs).GetAddress()
ipaddr.PartitionWithSingleBlockSize(subnet).PredicateForEach(trie.Add)
foundThemAll := ipaddr.PartitionWithSingleBlockSize(subnet).PredicateForEach(trie.Contains)
fmt.Println("all inserted:", foundThemAll)
fmt.Println(trie)

Output from both the Java and Go code:

all inserted: true

○ 0.0.0.0/0 (7)
└─○ 1.2.0.0/21 (7)
  ├─○ 1.2.0.0/22 (3)
  │ ├─● 1.2.1.0/24 (1)
  │ └─○ 1.2.2.0/23 (2)
  │   ├─● 1.2.2.0/24 (1)
  │   └─● 1.2.3.0/24 (1)
  └─○ 1.2.4.0/22 (4)
    ├─○ 1.2.4.0/23 (2)
    │ ├─● 1.2.4.0/24 (1)
    │ └─● 1.2.5.0/24 (1)
    └─○ 1.2.6.0/23 (2)
      ├─● 1.2.6.0/24 (1)
      └─● 1.2.7.0/24 (1)

Following that code, we try the other partition method partitionWithSpanningBlocks on the same subnet, with Java code:

trie = new IPv4AddressTrie();
Partition.partitionWithSpanningBlocks(subnet).predicateForEach(trie::add);
foundThemAll = Partition.partitionWithSpanningBlocks(subnet).predicateForEach(trie::contains);
System.out.println("all inserted: " + foundThemAll);
System.out.println(trie);

Here we try the other partition method PartitionWithSpanningBlocks on the same subnet, with Go code:

trie = ipaddr.Trie[*ipaddr.IPAddress]{}
ipaddr.PartitionWithSpanningBlocks(subnet).PredicateForEach(trie.Add)
foundThemAll = ipaddr.PartitionWithSpanningBlocks(subnet).PredicateForEach(trie.Contains)
fmt.Println("all inserted:", foundThemAll)
fmt.Println(trie)

Output from both the Java and Go code:

all inserted: true

○ 0.0.0.0/0 (3)
└─○ 1.2.0.0/21 (3)
  ├─○ 1.2.0.0/22 (2)
  │ ├─● 1.2.1.0/24 (1)
  │ └─● 1.2.2.0/23 (1)
  └─● 1.2.4.0/22 (1)

The two tries illustrate how the two partitions differ.

IP Address Operations

Here we summarize the operations on IP Addresses, Subnets, and Sequential Ranges. Many of these operations are also available on other address items, such as sections and segments.

We start with the more general operations, those that do not involve prefixes, prefix blocks, or segment blocks, followed by operations involving prefixes and prefix blocks, and the operations involving segment blocks. Segment block subnets are those subnets which have value ranges by segment. Prefix blocks are subnets that have a value range corresponding to a specified prefix length, the subnet containing the full block of addresses according to that prefix.

General Operations on Subnets, Addresses and Sequential Ranges

Many of these operations are available on address sections as well. You can obtain an address section by either constructing one directly or by getting the address section from a subnet or address.

Java Go Description
equals Equal Returns whether two address items or sequential ranges include the exact same individual values
contains Contains, ContainsRange Returns whether an address item or sequential range includes all the individual values of another address item or sequential range
isMultiple IsMultiple Returns whether an address item or sequential range includes multiple values within its range.
getCount GetCount Returns the number of individual values contained with the address item or sequential range, the number of values within its range of values.
getLower GetLower Returns the lowest single-valued individual address item contained within an address item or sequential range.
getUpper GetUpper Returns the highest single-valued individual address item contained within an address item or sequential range
includesMax IncludesMax Returns whether the range of values in the address item or sequential range includes the largest possible individual value.
includesZero IncludesZero Returns whether the range of values in the address item or sequential range includes the smallest possible individual value, namely the value of zero.
isMax IsMax Returns whether the the address item or sequential range includes just a single value, which is the largest possible individual value.
isZero IsZero Returns whether the the address item or sequential range includes just a single value, which is the smallest possible individual value, namely the value of zero.
isFullRange IsFullRange Returns whether the range of values in the address item or sequential range includes all possible values, from zero to the max value.
isSequential IsSequential Returns whether the range of values within the address item are a sequence of consecutive values. In other words, it is sequential if the number of disjoint value ranges in the address item is one. This really only applies to address items that are not sequential ranges since, by definition, a sequential range is always sequential, representing the sequence of addresses between a pair of addresses.
iterator, spliterator, stream Iterator Traverses through the individual address items (or sections thereof) comprising the range of values within the original address item or sequential range. Use getCount / GetCount to get the traversed count.
subtract Subtract Computes the difference, the set of addresses in the receiver address, subnet, or sequential range, but not in the argument address, subnet, or sequential range. Returns an array of subnets or or sequential ranges containing the result.
intersect Intersect Computes the conjunction of the address, subnet or sequential range arguments. The conjunction is the set of addresses in all of them. Returns the address, subnet or sequential range representing that set of addresses.

Other General Operations on Subnets and Addresses

Many of these operations are available on address sections as well. You can obtain an address section by either constructing one directly or by getting the address section from a subnet or address.

Java Go Description
increment Increment If the incremented address item is a subnet, provides the individual address that is the given increment into the sequence of individual addresses within the subnet range. An increment exceeding the subnet count being simply added to the final address in the subnet. If the address is an individual address, simply adds the given increment to the address value to produce a new address. The increment value can be a positive or negative integer.
incrementBoundary IncrementBoundary Returns the address that is the given increment from one of the range boundaries of the subnet or address, with positive increments added to the upper bound of the range, and negative increments being added to the lower bound. An increment of zero returns the original. If the address is an individual address, simply adds the given increment to the address value to produce a new address.
reverseBits, reverseBytes, reverseBytesPerSegment, reverseSegments ReverseBits, ReverseBytes, ReverseSegments Reverses the bits of segments, bytes, or the entire address or subnet. Each individual value is reversed and included in the result. Note that some subnets cannot have bits reversed due to a reversed segment not being expressible as a single range of segment values. Reverse operations can be useful for handling endianness (network byte order sometimes requires bytes be reversed), or DNS lookup.
toIPv4, toIPv6 ToIPv4, ToIPv6 Provides the same address represented with the more specific type, so that IP-version specific method calls can be made. If the address was originally constructed as the more specific type, then an instance of that type is returned. The address itself remains the same.
segmentsIterator, segmentsSpliterator, segmentsStream   Traverses through all address items, similar to iterator/spliterator/stream, but using only segment arrays. Use getCount to get the count.
toSequentialRange ToSequentialRange Returns the associated sequential range ranging from the lowest to highest value of the address or subnet. You can use the isSequential / IsSequential method of the address to know if the resulting sequential range represents the same set of addresses as the original address or subnet.
spanWithRange SpanWithRange Returns a sequential range that spans from the subnet to the given subnet. The resulting range qill include all addresses in both subnets as well as all addresses in between.

Other General Operations on Sequential Ranges

Java Go Description
overlaps Overlaps Returns whether the given sequential range overlaps with the sequential range.
extend Extend Extend extends the sequential range to include all address in the given sequential range, as well as all address in-between the two sequential ranges.
join Join, JoinTo Given a list of address ranges, merges them into the minimal list of address ranges.

Operations Involving Prefixes or Prefix Blocks

The following methods allow you to query whether something is a prefix block, change prefix lengths, mask addresses, or other prefix-related operations, which are particularly integral to CIDR addressing and routing.

Java Go Description
prefixEquals PrefixEqual Returns whether the prefix of the address item matches the same bits in the given address or subnet.
prefixContains PrefixContains Returns whether the prefix of the address item contains all the values of the same bits in the given address or subnet.
isPrefixBlock IsPrefixBlock Returns whether the address item has a prefix length and contains the prefix block for that prefix. A prefix block subnet, such as the /64 block a:​b:c:d::/64 or the /16 block 1.2.0.0/16, is a subnet or collection of address sections that contains all the addresses or address sections with the same prefix, with a prefix length identifying the prefix.
isSinglePrefixBlock IsSinglePrefixBlock Returns whether the address item has a prefix length, it has a single prefix of that prefix length, and contains the prefix block for that prefix.
containsSinglePrefixBlock ContainsSinglePrefixBlock Returns whether the subnet or sequential range contains the prefix block for the given prefix length, regardless of the assigned prefix length.
containsPrefixBlock ContainsPrefixBlock Returns whether the subnet or sequential range contains the prefix block for the given prefix length and has a single prefix of that prefix length, regardless of the assigned prefix length.
toPrefixBlock ToPrefixBlock, ToPrefixBlockLen Returns the subnet comprising the entire prefix block (all addresses with the same prefix), with either the existing or a given prefix length.
getMinPrefixLengthForBlock GetMinPrefixLenForBlock Returns the smallest prefix length for which the range matches the block of addresses for that prefix.
assignMinPrefixForBlock AssignMinPrefixForBlock Converts to an equivalent subnet (or section thereof) with the smallest prefix length, so that the result is a prefix block for that prefix length. The result will span the same range of values as the original.
getPrefixLengthForSingleBlock GetPrefixLenForSingleBlock Returns a prefix length for which the range matches exactly the prefix block for a single-valued prefix, if such a prefix length exists.
assignPrefixForSingleBlock AssignPrefixForSingleBlock Provides the equivalent subnet or address with a prefix length, the prefix length being the prefix length that makes the returned subnet a single prefix block. Such a subnet or address might not exist. The resulting subnet or address, if it exists, will span the same range of values as the original, and will thus remain equal to the original.
getBlockMaskPrefixLength GetBlockMaskPrefixLen Returns the prefix length of a network or host mask, if the address is a mask.
withoutPrefixLength WithoutPrefixLen Convert to the same but with no prefix length
setPrefixLength SetPrefixLen, SetPrefixLenZeroed Set the prefix length to the indicated value in the result. If there was an existing prefix length, then the “zeroed” variants control whether the bits to change sides of the prefix are changed to zeros, or whether the bits retain their values from when they were on the other side of the prefix boundary. Other bits retain their value. Java only: another method variant determines if the result can be re-interpreted as a prefix block when the host is all zeros, like when parsing strings with zero hosts or creating addresses with zero hosts.
adjustPrefixLength AdjustPrefixLen, AdjustPrefixLenZeroed Adjust the prefix length by the indicated value to a new prefix length in the result. The “zeroed” variants control whether the bits to change sides of the prefix are changed to zeros, or whether the bits retain their values from when they were on the other side of the prefix boundary. Other bits retain their value. Java only: another method variant determines if the result can be re-interpreted as a prefix block when the host is all zeros, like when parsing strings with zero hosts or creating addresses with zero hosts.
adjustPrefixBySegment   Adjust the prefix length to the next segment boundary in the result. The “zeroed” variants control whether the bits to change sides of the prefix are changed to zeros, or whether the bits retain their values from when they were on the other side of the prefix boundary. Other bits retain their value.
mask, maskNetwork Mask Applies a mask to a subnet or address, the result being the bitwise conjunction. For subnets, the mask is applied to all individual addresses to produce a single result, if possible. Java only: the “network” variant allows you to mask just the network and apply a prefix length.
bitwiseOr, bitwiseOrNetwork BitwiseOr Produces the bitwise disjunction of the original with the given mask. For subnets, the mask is applied to all individual addresses to produce a single result, if possible. Java only: the “network” variant allows you to mask just the network and apply a prefix length.
matchesWithMask MatchesWithMask Applies a mask to an address item and then compares the result with another address item, returning true if they match, false otherwise.
includesZeroHost IncludesZeroHost IncludesZeroHostLen Returns whether the host part of the address item, the bits following the prefix, includes the value of zero within its range.
includesMaxHost IncludesMaxHost IncludesMaxHostLen Returns whether the host part of the address item, the bits following the prefix, includes the maximum possible value within its range, the value in which all host bits are ones.
isZeroHost IsZeroHost, IsZeroHostLen Returns whether the host part of the address item, the sequence of bits following the prefix, is single-valued, that value being zero.
  IsMaxHost, IsMaxHostLen Returns whether the host part of the address item, the sequence of bits following the prefix, is single-valued and is the maximum possible value within its range, the value in which all host bits are ones.
toZeroHost ToZeroHost, ToZeroHostLen Produces the address item with prefix having the same range of values as the original and with the host, the sequence of bits beyond the prefix, having the value of zero.
toZeroNetwork ToZeroNetwork Produces the address item with the prefix, the bits within the prefix length, having the value of zero, and the host the same range of values as the original.
toMaxHost ToMaxHost, ToMaxHostLen Produces the address item with the same prefix as the original and with a host, the sequence of bits beyond the prefix, having the maximum possible value, the value where all host bits are ones.
prefixBlockIterator, prefixBlockSpliterator, prefixBlockStream PrefixBlockIterator If the subnet (or section thereof) has a prefix length, then this traverses through the prefix blocks subnets for that prefix length, with each traversed item being a prefix block. If no prefix length, then it traverses through all individual addresses (or section thereof). Use getPrefixCount / GetPrefixCount for the traversed count. For sequential range variants, the prefix length for the traversal is supplied.
prefixIterator, prefixSpliterator, prefixStream PrefixIterator If the subnet (or section thereof) has a prefix length, then traverses through the prefixes, with each traversed item including all those items from the original which have the same prefix. All except possibly the boundary iterations (first and last) will be a prefix block. If no prefix length, then it traverses through all individual addresses (or sections thereof). Use getPrefixCount / GetPrefixCount for the traversed count. For sequential range variants, the prefix length for the traversal is supplied.
nonZeroHostIterator   Traverses through the individual address items (or sections thereof) comprising the range of values within the original address item, but skipping those values with a zero host, if the original has a prefix length. Values with a zero host typically represent the entire block with the same prefix, rather than an individual address. Use getNonZeroHostCount to get the traversed count.
getPrefixCount GetPrefixCount, GetPrefixCountLen Returns the number of prefixes contained with the address item or sequential range, for either the existing prefix (which is the entire address item if no existing prefix length) or the given prefix length. For sequential range variants, the prefix length is given.
spanWithPrefixBlocks SpanWithPrefixBlocks, SpanWithPrefixBlocksTo Given a pair of addresses or subnets (or sections thereof), or a sequential range, finds the minimal list of prefix block subnets that span all addresses within.
mergeToPrefixBlocks MergeToPrefixBlocks Given a list of addresses or subnets (or sections thereof), merges them into the minimal list of prefix blocks.
coverWithPrefixBlock CoverWithPrefixBlock, CoverWithPrefixBlockTo Given a pair of addresses or subnets (or sections thereof), or a sequential range, returns the minimal-size prefix block that includes them both

Operations involving Segment Blocks

The following operations involve segment blocks. Note that a sequential block is a segment block segment that is sequential, such that for any two addresses in the range, all addresses in-between are also in the range. To be sequential, any multi-valued segment must be followed only by segments comprising all segment values.

Java Go Description
blockIterator, blockSpliterator, blockStream BlockIterator Traverses through the segment values of the higher segments up until a given segment index, with each traversed item including all those items from the original which have the same initial single-valued segments. If the segment index is 0, produces just a single value, the original address. If the segment index matches the segment count, traverses through all individual addresses (or sections thereof). Use getBlockCount / GetBlockCount to get the traversed count.
getBlockCount GetBlockCount Returns the count of distinct individual values in the given number of initial (more significant) segments.
sequentialBlockIterator, sequentialBlockSpliterator, sequentialBlockStream SequentialBlockIterator Traverses through the segment values of the higher segments up until a given segment index, with each traversed item including all those from the original which have the same initial single-valued segments. The segment index is chosen to be the largest segment index for which all iterated blocks will be sequential. Use getSequentialBlockCount / GetSequentialBlockCount to get the traversed count.
getSequentialBlockCount GetSequentialBlockCount Returns the count of maximal-length sequential value ranges in the address item. In other words, it is the number of disjoint value ranges in the address item. If isSequential / IsSequential returns true, the count will be one.
spanWithSequentialBlocks SpanWithSequentialBlocks, SpanWithSequentialBlocksTo Given a pair of addresses or subnets (or sections thereof), or a sequential range, finds the minimal list of sequential block subnets that span all addresses within.
mergeToSequentialBlocks MergeToSequentialBlocks Given a list of addresses or subnets (or sections thereof), merges them into the minimal list of sequential block subnets
append, prepend, replace Replace, ReplaceLen Adds or replaces segments in the subnet, address, or the section of a subnet or address. With subnets and addresses you must always maintain the correct number of segments, so only the replace operation is provided.

Example of Mask and Prefix Length Operations

The following code demonstrates how to get the lowest address in a prefixed subnet using any one of three methods: getting the lowest address in the subnet, masking, or converting the host to zero.

String addr = "1.2.3.4";
int prefixLength = 16;

IPAddress address = new IPAddressString(addr).getAddress();
IPAddress mask = address.getNetwork().getNetworkMask(prefixLength, false);
System.out.println("mask " + mask);


// create the prefix block subnet

IPAddress subnet = address.setPrefixLength(prefixLength).toPrefixBlock();
System.out.println("subnet " + subnet + " no prefix is " +
		subnet.withoutPrefixLength());


// mask

IPAddress maskedSubnet = subnet.mask(mask);
System.out.println("subnet " + subnet + " masked is " + maskedSubnet);
IPAddress maskedAddress = address.mask(mask);
System.out.println("address " + address + " masked is " + maskedAddress);
System.out.println("masked address " + maskedAddress +
		" equals masked subnet " + maskedSubnet + " is " +
		maskedAddress.equals(maskedSubnet));

// get the lower address

IPAddress lowestAddrInSubnet = subnet.getLower();
System.out.println("lowest in subnet is " + lowestAddrInSubnet);
System.out.println("lowest in subnet no prefix is " +
		lowestAddrInSubnet.withoutPrefixLength());
System.out.println("masked address " + maskedAddress +
		" equals lowest address in subnet " + lowestAddrInSubnet +  " is " +
		maskedAddress.equals(lowestAddrInSubnet));

// get the zero host

IPAddress zeroHost = subnet.toZeroHost();
System.out.println("zero host is " + zeroHost);
System.out.println("zero host no prefix is " +
		zeroHost.withoutPrefixLength());
System.out.println("masked address " + maskedAddress +
		" equals zero host " + zeroHost +  " is " +
		maskedAddress.equals(zeroHost));

Here is the equivalent Go code:

addr := "1.2.3.4"
prefixLength := 16

address := ipaddr.NewIPAddressString(addr).GetAddress()
mask := address.GetNetwork().GetNetworkMask(prefixLength)
fmt.Println("mask", mask)

// create the prefix block subnet

subnet := address.SetPrefixLen(prefixLength).ToPrefixBlock()
fmt.Println("subnet", subnet, "no prefix is", subnet.WithoutPrefixLen())

// mask

maskedSubnet, _ := subnet.Mask(mask)
maskedSubnet = maskedSubnet.WithoutPrefixLen()
fmt.Println("subnet", subnet, "masked is", maskedSubnet)
maskedAddress, _ := address.Mask(mask)
fmt.Println("address", address, "masked is", maskedAddress)
fmt.Println("masked address", maskedAddress,
	"equals masked subnet", maskedSubnet,
	"is", maskedAddress.Equal(maskedSubnet))

// get the lower address

lowestAddrInSubnet := subnet.GetLower()
fmt.Println("lowest in subnet is", lowestAddrInSubnet)
fmt.Println("lowest in subnet no prefix is",
  lowestAddrInSubnet.WithoutPrefixLen())
fmt.Println("masked address", maskedAddress,
	"equals lowest address in subnet", lowestAddrInSubnet,
	"is", maskedAddress.Equal(lowestAddrInSubnet))

	// get the zero host

zeroHost, _ := subnet.ToZeroHost()
fmt.Println("zero host is", zeroHost)
fmt.Println("zero host no prefix is", zeroHost.WithoutPrefixLen())
fmt.Println("masked address", maskedAddress,
	"equals zero host", zeroHost,
	"is", maskedAddress.Equal(zeroHost))

Here is the output from either the Java or Go code:

mask 255.255.0.0
subnet 1.2.0.0/16 no prefix is 1.2.*.*
subnet 1.2.0.0/16 masked is 1.2.0.0
address 1.2.3.4 masked is 1.2.0.0
masked address 1.2.0.0 equals masked subnet 1.2.0.0 is true
lowest in subnet is 1.2.0.0/16
lowest in subnet no prefix is 1.2.0.0
masked address 1.2.0.0 equals lowest address in subnet 1.2.0.0/16 is true
zero host is 1.2.0.0/16
zero host no prefix is 1.2.0.0
masked address 1.2.0.0 equals zero host 1.2.0.0/16 is true

Polymorphism

Simply change the string “1.2.3.4” in the code above to an IPv6 address like “a:ffff:​b:c:d::f” and the code works all the same.

Output:

mask ffff::
subnet a::/16 no prefix is a:*:*:*:*:*:*:*
subnet a::/16 masked is a::
address a:ffff:b:c:d::f masked is a::
masked address a:: equals masked subnet a:: is true
lowest in subnet is a::/16
lowest in subnet no prefix is a::
masked address a:: equals lowest address in subnet a::/16 is true
zero host is a::/16
zero host no prefix is a::
masked address a:: equals zero host a::/16 is true

Masking Subnets

When applying an operation to a subnet, the operation is applied to every member of the subnet. In such cases, the result must be something representable with sequential segment ranges, otherwise IncompatibleAddressException will be thrown in Java, or an IncompatibleAddressError returned in Go. In other words, each resulting segment must be representable by a single range of values.

For instance, masking the subnet block of 255 addresses 0.0.0.0/24 with the mask 0.0.0.128 results in the two addresses 0.0.0.0 and 0.0.0.128, which is not a sequential range of values.

Such exceptions or errors will not happen when using standard masking and subnetting techniques typical with IPv4 and IPv6 routing.

Subnetting

Subnetting can be accomplished using various address manipulation methods. Given a prefixed IP address, you can extend the prefix length and insert bits for an extended prefix and new subnet.

int originalPrefix = 18, adjustment = 4;
IPAddress address = new IPAddressString("207.0.64.0").getAddress();
IPAddress subnet1 = address.toPrefixBlock(originalPrefix);
System.out.println(subnet1 + " of size " + subnet1.getCount());
IPAddress subnet2 = subnet1.adjustPrefixLength(adjustment); // extend the prefix length
System.out.println(subnet2);
IPAddress prefixExtension = new IPAddressString("0.0.4.0").getAddress();
IPAddress subnet3 =
		subnet2.bitwiseOrNetwork(prefixExtension,
		  originalPrefix + adjustment); // adjust the extended prefix  
System.out.println(subnet3 + " of size " + subnet3.getCount());

The equivalent Go code is:

originalPrefix, adjustment := 18, 4
address := ipaddr.NewIPAddressString("207.0.64.0").GetAddress()
subnet1 := address.ToPrefixBlockLen(originalPrefix)
fmt.Println(subnet1, "of size", subnet1.GetCount())
subnet2, _ := subnet1.AdjustPrefixLenZeroed(adjustment) // extend the prefix length
fmt.Println(subnet2)
prefixExtension := ipaddr.NewIPAddressString("0.0.4.0").GetAddress()
subnet3, _ := subnet2.BitwiseOr(prefixExtension) // adjust the extended prefix
fmt.Println(subnet3, "of size", subnet3.GetCount())

Output for both the Java and Go code:

207.0.64.0/18 of size 16384
207.0.64.0/22
207.0.68.0/22 of size 1024

Here is the same subnetting operation using segment replacement.

IPv4Address address = new IPAddressString("207.0.64.0/18").getAddress().toIPv4();
IPv4AddressSection replacementSection =
		new IPAddressString("0.0.68.0/22").getAddress().toIPv4().getSection(2);
IPAddress subnet = new IPv4Address(address.getSection().replace(2, replacementSection));  
System.out.println(subnet + " of size " + subnet.getCount());
address := ipaddr.NewIPAddressString("207.0.64.0/18").GetAddress().ToIPv4()
replacementSection :=
	ipaddr.NewIPAddressString("0.0.68.0/22").GetAddress().ToIPv4().GetTrailingSection(2)
subnet, _ := ipaddr.NewIPv4Address(address.GetSection().Replace(2, replacementSection))
fmt.Println(subnet, "of size", subnet.GetCount())

Output for both the Java and Go code:

207.0.68.0/22 of size 1024

Alternatively, you can use the prefix block iterator to get a list of subnets when adjusting the prefix:

IPAddress subnet = new IPAddressString("192.168.0.0/28").getAddress();
IPAddress newSubnets = subnet.setPrefixLength(subnet.getPrefixLength() + 2, false);
System.out.println(newSubnets);

Iterator<? extends IPAddress> iterator = newSubnets.prefixBlockIterator();
if (iterator.hasNext()) {
	System.out.print(iterator.next());
	while (iterator.hasNext()) {
		System.out.print(", ");
		System.out.print(iterator.next());
	}
}
subnet := ipaddr.NewIPAddressString("192.168.0.0/28").GetAddress()
newSubnets := subnet.SetPrefixLen(subnet.GetPrefixLen().Len() + 2)
fmt.Println(newSubnets)

iterator := newSubnets.PrefixBlockIterator()
if iterator.HasNext() {
	fmt.Print(iterator.Next())
	for iterator.HasNext() {
		fmt.Print(", ", iterator.Next())
	}
}

Output for both the Java and Go code:

192.168.0.0-12/30
192.168.0.0/30, 192.168.0.4/30, 192.168.0.8/30, 192.168.0.12/30

Another option is to let the library do the work for you. A PrefixBlockAllocator instance can do CIDR subnetting using a standard variable-length subnetting algorithm.

The Java wiki and Go wiki provide subnetting code examples as well as examples demonstrating how to create or derive CIDR subnets.

Trie Operations

Most of these operations are methods that operate directly on AddressTrie or AssociativeAddressTrie instances, but some are available from their associated AddressTrieSet or AddressTrieMap instances available from asSet or asMap, in which case the methods would still be operating on the same backing trie.

xxx Entry? xxxx

Trie Iterators, Spliterators, and Streams

Most of these traversal methods traverse the nodes, while some traverse the keys/addresses of the nodes, including those iterators and spliterators within AddressTrieSet or AddressTrieMap.

Of those that traverse the nodes, some traverse only the added nodes and elements, while others traverse all the nodes. The added nodes are those explicitly added to the trie, the non-added nodes are those auto-generated for the binary trie structure. A non-added node can become added, and vice-versa.

Those that traverse the keys/addresses traverse only the added keys/addresses.

See the javadoc for TreeOps for more details on the orderings for the various traversals.

Parse String Representations of MAC Address

Parsing is like that for IP address. MACAddressString is used to convert. You can use one of getAddress or toAddress, the difference being whether parsing errors are handled by exception or not.

MACAddress address = new
MACAddressString("01:02:03:04:0a:0b").getAddress();
if(address != null) {
  //use address
}

or

try {
  MACAddress address = new MACAddressString("01:02:03:04:0a:0b").toAddress();
  //use address
} catch (AddressStringException e) {
  String msg = e.getMessage(); // detailed message indicating issue
}

Various Formats of MAC Addresses

MAC Addresses are expected to be in hexadecimal. However, there is a number of accepted formats for MAC addresses:

aa:bb:cc:dd:ee:ff
aa-bb-cc-dd-ee-ff
aa bb cc dd ee ff
aabb.ccdd.eeff
aabbccddeeff
aabbcc-ddeeff

For the non-segmented format (aabbccddeeff), all 12 digits are required to avoid ambiguity.

MAC addresses can be either 48 or 64 bits, and so for each 48-bit format there is a 64-bit equivalent:

aa:bb:cc:dd:ee:ff:11:22
aa-bb-cc-dd-ee-ff-11-22
aa bb cc dd ee ff 11 22
aabb.ccdd.eeff.1122
aabbccddeeff1122
aabbcc-ddeeff1122

For the non-segmented 64-bit format (aabbccddeeff1122), all 16 digits are required to avoid ambiguity.

As with IP addresses, you can specify ranges using ‘*’ and ‘-’ like aa-bb:*:*:cc:dd:ee. The range character for addresses that use the dash ‘-’ character as a separator is ‘|’, like aa|bb-*-*-cc-dd-ee.

Format Examples

For instance, the address 0a:0b:0c:0d:0e:0f can be represented many ways:

static void parseMAC(String formats[]) {
  for(String format : formats) {
    System.out.println(new MACAddressString(format).getAddress());
  }
}

public static void main(String[] args) {
  String formats[] = {
    "a:b:c:d:e:f",
    "0a:0b:0c:0d:0e:0f",
    "a:b:c:d:e:f",
    "0a-0b-0c-0d-0e-0f",
    "0a0b0c-0d0e0f",
    "0a0b.0c0d.0e0f",
    "0a 0b 0c 0d 0e 0f",
    "0a0b0c0d0e0f"
  };
  parseMAC(formats);
}

Output:

0a:0b:0c:0d:0e:0f
0a:0b:0c:0d:0e:0f
0a:0b:0c:0d:0e:0f
0a:0b:0c:0d:0e:0f
0a:0b:0c:0d:0e:0f
0a:0b:0c:0d:0e:0f
0a:0b:0c:0d:0e:0f
0a:0b:0c:0d:0e:0f

Ranges can be specified in the same way as for IP addresses (‘-’ for a specific range or ‘*’ for full range segments).

For instance, the address range ff:0f:aa-ff:00-ff:00-ff:00-ff can be represented many ways:

static void parseMAC(String formats[]) {
  for(String format : formats) {
    System.out.println(new MACAddressString(format).getAddress());
  }
}

public static void main(String[] args) {
  String formats[] = {
    "ff:f:aa-ff:00-ff:00-ff:00-ff",
    "ff:f:aa-ff:*:*:*",
    "ff:0f:aa-ff:*:*:*",
    "ff-0f-aa|ff-*-*-*",
    "ff0faa|ff0fff-*",
    "ff0f.aa00-ffff.*",
    "ff 0f aa-ff * * *",
    "ff0faa000000-ff0fffffffff"
  };
  parseMAC(formats);

}

Output:

ff:0f:aa-ff:*:*:*
ff:0f:aa-ff:*:*:*
ff:0f:aa-ff:*:*:*
ff:0f:aa-ff:*:*:*
ff:0f:aa-ff:*:*:*
ff:0f:aa-ff:*:*:*
ff:0f:aa-ff:*:*:*  
ff:0f:aa-ff:*:*:*

Delimited Segments

The range formats allow you to specify ranges of values. However, if you wish to parse addresses in which values are delimited, then you can use the methods parseDelimitedSegments(String) and countDelimitedAddresses(String) in MACAddressString. parseDelimitedSegments will provide an iterator to traverse through the individual addresses.

For example, given “1,2:3:4,5:6:7:8”, countDelimitedAddresses will return 4 for the possible combinations: “1:3:4:6:7:8”, “1:3:5:6:7:8”, “2:3:4:6:7:8” and “2:3:5:6:7:8”. With each string obtained from parseDelimitedSegments you can construct a MACAddressString instance.

MAC Address Validation Options

Validation options allow you to restrict the allowed formats.

You have the class MACAddressStringParameters which can be passed into MACAddressString

You can restrict validation options using the appropriate builder classes that are nested classes in the options classes. Here is an example:

MACAddressStringParameters MAC_ADDRESS_OPTIONS_EXAMPLE =
  new MACAddressStringParameters.Builder().
  allowEmpty(false).
  allowAll(false).
  getFormatBuilder().
  setRangeOptions(RangeParameters.*NO\_RANGE*).
  allowLeadingZeros(true).
  allowUnlimitedLeadingZeros(false).
  allowWildcardedSeparator(false).
  allowShortSegments(true).
  getParentBuilder().
  toParams();

The default options used by the library are permissive and not restrictive.

MAC Address Prefix Lengths

A ‘prefix length’ in this library is defined generally as the bit-length of the foremost portion of the address that is not specific to an individual address but common amongst a group of addresses. For IP addresses, this is explicitly defined as part of the address using the ‘/’ character. For IP addresses, the prefix is potentially variable and depends on how subnets have been allocated.

MAC Addresses don’t have the exact same concept of prefix length as IP addresses. But concept of prefix can be applied in an implicit sense. For instance, a MAC address is typically divided into an OUI (Organizationally Unique Identifier) and ODI (Organizational Defined Identifier), so you might consider the OUI bits as the prefix. There are other ways of assigning MAC address blocks as well, such as IAB (individual address block), or MA-S/MA-M/MA-L (MAC Address block small, medium, and large), in which a certain number of higher bits are provided as an identifier to organizations from which they can create various extended identifiers using the lower bits. There is generally a pre-defined set of high bits that can be considered a prefix. This prefix is not variable and was typically assigned by the IEEE. However, there is no explicit way to represent a MAC address string with an associated prefix, as there is with CIDR IP addresses, so a prefix with MAC addresses is implicit rather than explicit.

Within this library, the prefix for a MAC address is defined as the largest number of high bits for which an address represents all addresses with the same set of higher bits.

For instance, the prefix length of aa:*:*:*:*:* is 8 because the address represents all addresses that start with the same 8 bits “aa”. The prefix length of aa:*:cc:*:*:* is 24 because the address represents all addresses that start with the same 16 bits aa:*:cc. The address aa:bb:cc:dd:ee:ff does not have a prefix or prefix length as it represents just a single address.

Once a MAC address as an associated prefix length, that prefix length remains the same while any operations are applied to the address (with the exception of operations that explicitly change the prefix, like setPrefixLength).

In summary, on the MAC side, the prefix length is implicit and based upon the address itself, while on the IP address side, the prefix length is explicitly defined.

MAC Address Operations

Many of the same address operations available for IP addresses are available for MAC addresses, including the prefix operations, the section and segment access methods, iterators, containment, and reversal of bits, bytes and segments.

The reverse operations may be useful for for “MSB format”, “IBM format”, “Token-Ring format”, and “non-canonical form”, where the bits are reversed in each byte of a MAC address.

IPv6 – MAC Address Integration

There is a standardized procedure for converting MAC addresses to IPv6 given an IPv6 64-bit prefix, as described in IETF RFC 2464, RFC 3513, and RFC 4944.

It details how to combine a 48 bit MAC address with a 64-bit IPv6 network prefix to produce an associated 128-bit IPv6 address. This is done by first constructing a 64-bit extended unique IPv6 interface identifier (EUI-64) from the MAC address. This library has implemented the same MAC / IPv6 integration.

Starting with a MAC address or section and with the IPv6 prefix, you can construct the associated IPv6 address with one of these constructors:

public IPv6Address(IPv6Address prefix, MACAddress eui)

public IPv6Address(IPv6AddressSection section, MACAddress eui)

public IPv6Address(IPv6AddressSection section, MACAddressSection eui)

public IPv6Address(IPv6AddressSection section, MACAddressSection eui, CharSequence zone)

There are equivalent methods in the class MACAddress for producing the link local address which has a pre-defined prefix, or for producing the host (interface identifier) address section of an IPv6 address.

public IPv6Address toLinkLocalIPv6()  
public IPv6AddressSection toEUI64IPv6()

There is a similar method in MACAddressSection

public IPv6AddressSection toEUI64IPv6()

A MAC address is either 48 or 64 bits. To be converted to IPv6, the 48 bit address has segments inserted (two 1 byte MAC segments with value 0xfffe) to extend the address to 64 bits. For a 64 bit MAC address to be convertible, those same two segments must match the expected value of 0xffe. The following methods in MACAddress check will 64 bit addresses to ensure that are compatible with IPv6 by checking that the value of those two segments matches 0xfffe. Note that the asMAC argument allows you to extend using 0xffff rather than 0xfffe which is another manner by which a 48 bit MAC address can be extended to 64 bits. However, for purposes of extending to an IPv6 address the argument should be false so that the EUI-64 format is used.

public boolean isEUI64(boolean asMAC)  
public MACAddress toEUI64(boolean asMAC)

There are similar methods in MACAddressSection

public boolean isEUI64(boolean asMAC)

public boolean isEUI64(boolean asMAC, boolean partial)

public MACAddressSection toEUI64(boolean asMAC)

Given an existing EUI-64 section you can use the prepend and append methods to create a full IPv6 address section containing all segments:

public IPv6AddressSection prepend(IPv6AddressSection other)

public IPv6AddressSection append(IPv6AddressSection other)

and from there you just construct the address:

public IPv6Address(IPv6AddressSection section)

To go the reverse direction IPv6 to MAC, there is a method in IPv6Address to produce a MAC address:

public MACAddress toEUI(boolean extended)

and another in IPv6AddressSection that uses whatever part of the interface identifier is included in the section to produce a MAC address section:

public MACAddressSection toEUI(boolean extended)

For instance, if the IPV6 address section is the 8 bytes corresponding to the network prefix of an IPV6 address section, then the resulting MAC address section will be 0 bytes. If the IPV6 address section is the 8 bytes corresponding to the interface identifier, and that identifier has the required 0xfffe values in the 5th and 6th bytes, then the MAC address section will be the full 8 bytes of an EUI-64 MAC address.

The following code is an example of constructing IPv6 addresses from a MAC address:

public static void main(String args[]) {
  MACAddressString macStr = new MACAddressString("aa:bb:cc:dd:ee:ff");
  MACAddress macAddress = macStr.getAddress();
  IPv6Address linkLocal = macAddress.toLinkLocalIPv6();
  System.out.println(linkLocal);

  IPAddressString ipv6Str = new
  IPAddressString("1111:2222:3333:4444::/64");
  IPv6Address ipv6Address = ipv6Str.getAddress().toIPv6();
  IPv6Address macIpv6 = new IPv6Address(ipv6Address, macAddress);
  System.out.println(macIpv6);
}

Output:

fe80::a8bb:ccff:fedd:eeff  
1111:2222:3333:4444:a8bb:ccff:fedd:eeff/64

Address Framework

Much like there is a Java collections framework, there is an address framework to the IPAddress library. It is a unified set of inter-related interfaces, abstract implementations, and algorithms for all addresses and address components. It allows you to manipulate these items independently of implementation details, and provides standard interfaces to addresses and address components for code reuse and polymorphism. It represents the common structure of addresses, sections, segments, and so on.

You might wish to manipulate address components of different shapes and sizes transparently, or you may wish to manipulate different types of addresses or different address versions transparently. One element of the interface is the ability to convert any address component to bytes, whether division, segment, section, or address.

There is a hierarchy for the standard Address and Address Component data structures, which are addresses, sections of addresses, and segments of equal byte size inside those address sections.

There is a more diversified hierarchy for non-standard address structures, in which addresses or address sections might be divided into divisions of unequal length, or of non-integer byte-size.

The address hierarchy of interfaces (purple) and classes (green) is shown:

Most of the full class hierarchy for address structure showing addresses, sections, division groupings, segments and divisions is shown here, separated into the three primary categories shown above. The dashed lines indicate there are a few less-prominent classes in the library not shown in the diagram.

Conversion to String Representation of Address

Here is a list of string methods, in no specific order.

Note that, for a given address, the string produced by one of these methods might match those of other methods. They are not necessarily distinct from each other for all addresses.

All address types, HostName and IPAddressString:

All address types:

IP addresses and HostName:

IP addresses only:

IPv6 only:

MAC address only:

More Details and Examples

AddressSegmentSeries objects (such as Address and AddressSection) have methods that produce strings in various formats: toCanonicalString, toNormalizedString, toCompressedString, and toHexString.

public static void main(String[] args) {
  printStrings(new MACAddressString("a:bb:c:dd:e:ff").getAddress());
  printStrings(new MACAddressString("a:bb:c:*").getAddress());
  printStrings(new IPAddressString("a:bb:c::/64").getAddress());
  printStrings(new IPAddressString("1.2.3.4").getAddress());
  printStrings(new IPAddressString("1.2.0.0/16").getAddress());
  printStrings(new IPAddressString("a:bb:c::dd:e:ff").getAddress());
}

static void printStrings(AddressSegmentSeries series) {
  System.out.println(series.toCanonicalString());
  System.out.println(series.toNormalizedString());
  System.out.println(series.toCompressedString());
  System.out.println(series.toHexString(true));
  System.out.print("lower: " + series.getLower() + " bytes:");
  System.out.println(Arrays.toString(series.getBytes()));
  System.out.print("upper: " + series.getUpper() + " bytes:");
  System.out.println(Arrays.toString(series.getUpperBytes()));
  System.out.println();
}

Output:

0a-bb-0c-dd-0e-ff
0a:bb:0c:dd:0e:ff
a:bb:c:dd:e:ff
0x0abb0cdd0eff
lower: 0a:bb:0c:dd:0e:ff bytes:[10, -69, 12, -35, 14, -1]
upper: 0a:bb:0c:dd:0e:ff bytes:[10, -69, 12, -35, 14, -1]

0a-bb-0c-*-*-*
0a:bb:0c:*:*:*
a:bb:c:*:*:*
0x0abb0c000000-0x0abb0cffffff
lower: 0a:bb:0c:00:00:00 bytes:[10, -69, 12, 0, 0, 0]
upper: 0a:bb:0c:ff:ff:ff bytes:[10, -69, 12, -1, -1, -1]

a:bb:c::/64
a:bb:c:0:0:0:0:0/64
a:bb:c::/64
0x000a00bb000c00000000000000000000-0x000a00bb000c0000ffffffffffffffff
lower: a:bb:c:0:0:0:0:0 bytes:[0, 10, 0, -69, 0, 12, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0]
upper: a:bb:c:0:ffff:ffff:ffff:ffff bytes:[0, 10, 0, -69, 0, 12, 0, 0,
-1, -1, -1, -1, -1, -1, -1, -1]

1.2.3.4
1.2.3.4
1.2.3.4
0x01020304
lower: 1.2.3.4 bytes:[1, 2, 3, 4]
upper: 1.2.3.4 bytes:[1, 2, 3, 4]

1.2.0.0/16
1.2.0.0/16
1.2.0.0/16
0x01020000-0x0102ffff
lower: 1.2.0.0 bytes:[1, 2, 0, 0]
upper: 1.2.255.255 bytes:[1, 2, -1, -1]

a:bb:c::dd:e:ff
a:bb:c:0:0:dd:e:ff
a:bb:c::dd:e:ff
0x000a00bb000c0000000000dd000e00ff
lower: a:bb:c:0:0:dd:e:ff bytes:[0, 10, 0, -69, 0, 12, 0, 0, 0, 0, 0,
-35, 0, 14, 0, -1]  
upper: a:bb:c:0:0:dd:e:ff bytes:[0, 10, 0, -69, 0, 12, 0, 0, 0, 0, 0,
-35, 0, 14, 0, -1]

The IPAddress and IPAddressSection classes and their version-specific subclasses have additional methods to produce specific strings representing the address:

public static void main(String[] args) {
  IPAddress address = new IPAddressString("a:b:c::e:f").getAddress();
  print(address);
  address = new IPAddressString("a:b:c::").getAddress();
  print(address);

  //a:b:c:\*:: cannot be represented as a range of two single values
  //so this throws exception in toBase85String(), toBinaryString(), and toHexString()
  address = new IPAddressString("a:b:c:*::").getAddress();
  print(address);
}

static void print(IPAddress address) {
  System.out.println(address.toCanonicalString());
  System.out.println(address.toFullString());
  System.out.println(address.toNormalizedString());
  System.out.println(address.toSQLWildcardString());
  System.out.println(address.toSubnetString());
  try {
    if(address.isIPv6()) {
      System.out.println(address.toIPv6().toMixedString());
      System.out.println(address.toIPv6().toBase85String());
    }
    System.out.println(address.toBinaryString());
    System.out.println(address.toHexString(true));
  } catch(IncompatibleAddressException e) {}
  System.out.println();
}

Output:

a:b:c::e:f
000a:000b:000c:0000:0000:0000:000e:000f
a:b:c:0:0:0:e:f
a:b:c:0:0:0:e:f
a:b:c::e:f
a:b:c::0.14.0.15
00|N0s0$N0-%*(tF74+!
00000000000010100000000000001011000000000000110000000000000000000000000000000000000000000000000000000000000011100000000000001111
0x000a000b000c000000000000000e000f

a:b:c::
000a:000b:000c:0000:0000:0000:0000:0000
a:b:c:0:0:0:0:0
a:b:c:0:0:0:0:0
a:b:c::
a:b:c::
00|N0s0$N0-%*(tF5l-X
00000000000010100000000000001011000000000000110000000000000000000000000000000000000000000000000000000000000000000000000000000000
0x000a000b000c00000000000000000000

a:b:c:*::
000a:000b:000c:0000-ffff:0000:0000:0000:0000
a:b:c:*:0:0:0:0
a:b:c:%:0:0:0:0
a:b:c:*::
a:b:c:*::

UNC Strings

The method toUNCHostName() produces the UNC IP-literal string.

IPAddressString ipAddressString = new IPAddressString("2001:db8::1");
IPAddress address = ipAddressString.getAddress();
System.out.println(address.toUNCHostName());

ipAddressString = new IPAddressString("1.2.3.4");
address = ipAddressString.getAddress();
System.out.println(address.toUNCHostName());

Output:

2001-db8-0-0-0-0-0-1.ipv6-literal.net
1.2.3.4

DNS Lookup Strings

The method toReverseDNSLookupString() will produce a string for DNS lookup. If you wish to do a DNS lookup for a subnet rather than a full address, you can use getNetworkSection() to provide the network section you wish to lookup. You can specify the prefix length in either the string itself or the call to get the network section.

IPAddressString ipAddressString = new IPAddressString("2001:db8::1");
IPAddress address = ipAddressString.getAddress();
System.out.println(address.toReverseDNSLookupString());
IPAddressSection addressSection = address.getNetworkSection(64);
System.out.println(addressSection.toReverseDNSLookupString());

//same with prefix
ipAddressString = new IPAddressString("2001:db8::1/64");
address = ipAddressString.getAddress();
System.out.println(address.toReverseDNSLookupString());
addressSection = address.getNetworkSection();
System.out.println(addressSection.toReverseDNSLookupString());

//prefix block
ipAddressString = new IPAddressString("2001:db8::/64");
address = ipAddressString.getAddress();
System.out.println(address.toReverseDNSLookupString());
addressSection = address.getNetworkSection();
System.out.println(addressSection.toReverseDNSLookupString());

Output:

1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa
0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa
1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa
0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa
*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa
0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa

General String Methods

The methods toCanonicalString and toCompressedString are available for any address or address section.

The methods toHexString and toNormalizedString are available for any address component, including segments and sections.

Prefix Length Indicator in Strings

Typically prefix lengths will be added to IP strings. To choose to print wildcards * and range characters - as opposed to using prefix length, there are additional methods:

public static void main(String[] args) {  
  IPAddress address = new IPAddressString("a:b:c::/64").getAddress();  
  print(address);  
  address = new IPAddressString("a:b:c:*::/64").getAddress();  
  print(address);
}

static void print(IPAddress address) {
  System.out.println(address.toCanonicalString());
  System.out.println(address.toCanonicalWildcardString());
  System.out.println(address.toFullString());
  System.out.println(address.toNormalizedString());
  System.out.println(address.toSQLWildcardString());
  System.out.println();
}

Output:

a:b:c::/64
a:b:c:0:*:*:*:*
000a:000b:000c:0000:0000:0000:0000:0000/64
a:b:c:0:0:0:0:0/64
a:b:c:0:%:%:%:%
a:b:c:*::/64
a:b:c:*:*:*:*:*
000a:000b:000c:0000-ffff:0000:0000:0000:0000/64
a:b:c:*:0:0:0:0/64
a:b:c:%:%:%:%:%

IP Version-dependent Strings

Some strings are version-dependent:

public static void main(String[] args) {
  // toSubnetString() prefers '*' for IPv4 and prefix for IPv6
  System.out.println(new IPAddressString("1.2.0.0/16").getAddress().toSubnetString());
  System.out.println(new IPAddressString("a:b::/64").getAddress().toSubnetString());

  //converts IPv4-mapped to a:b:c:d:e:f:1.2.3.4 notation
  System.out.println(new IPAddressString("::ffff:a:b").getAddress().toConvertedString());
}

Output:

1.2.*.*
a:b::/64
::ffff:0.10.0.11

Collections of IP Address Strings

You can produce collections of strings:

public static void main(String[] args) {  
  IPAddress address = new IPAddressString("a:b:c::e:f").getAddress();  
  print(address);  
}

private static void print(IPAddress address) {
  //print(address.toAllStrings()); produces many strings
  print(address.toStandardStrings());
}

public static void print(String strings[]) {
  for(String str : strings) {
    System.out.println(str);
  }
}

Output:

a:b:c:0:0:0:0.14.0.15
a:b:c:0:0:0:000.014.000.015
000a:000b:000c:0000:0000:0000:0.14.0.15
000a:000b:000c:0000:0000:0000:000.014.000.015
A:B:C:0:0:0:0.14.0.15
A:B:C:0:0:0:000.014.000.015
000A:000B:000C:0000:0000:0000:0.14.0.15
000A:000B:000C:0000:0000:0000:000.014.000.015  
a:b:c::0.14.0.15
a:b:c::000.014.000.015
000a:000b:000c::0.14.0.15
000a:000b:000c::000.014.000.015
A:B:C::0.14.0.15
A:B:C::000.014.000.015
000A:000B:000C::0.14.0.15
000A:000B:000C::000.014.000.015
a:b:c:0:0:0:e:f
000a:000b:000c:0000:0000:0000:000e:000f
A:B:C:0:0:0:E:F
000A:000B:000C:0000:0000:0000:000E:000F
a:b:c::e:f
000a:000b:000c::000e:000f
A:B:C::E:F
000A:000B:000C::000E:000F

The String collections can be customized with toNormalizedString(StringOptions params)

Note that string collections never have duplicate strings. The String collections can be customized with toStrings(IPStringBuilderOptions options).

Containment and Subnet Membership

To check whether an IP address is contained by a subnet:

IPAddress address = new IPAddressString("1.2.0.0/16").getAddress();  
System.out.println(address.contains(new IPAddressString("1.2.3.4").getAddress()));  
System.out.println(address.contains(new IPAddressString("1.2.3.0/24").getAddress()));  
System.out.println(address.contains(new IPAddressString("1.2.3.0/25").getAddress()));  
System.out.println(address.contains(new IPAddressString("1.1.0.0").getAddress()));

Output:

true  
true  
true  
false

The contains method is not restricted to IP addresses or IP address prefixed addresses. There is a contains method for every Address or AddressSection.

IPAddressString has a contains methods as well, and also some additional containment methods, prefixContains and prefixEquals. These IPAddressString containment methods can have superior performance checking containment when starting from strings, because in many cases containment can be determined by looking at the strings alone, avoiding address object creation and numeric comparisons.

For checking containment of an address or subnet in a large number of subnets, or to check containment of a large number of addresses or subnets in a subnet, use the address trie data structure, using one of the subclasses of AddressTrie.

There is also an assortment of iterators for addresses, sections, and segments which represent multiple values. There is an iterator(), getLower() method and getUpper() method for every address component.

DNS Resolution and URLs

If you have a string that can be a host or an address and you wish to resolve to an address, create a HostName and use HostName.toResolvedAddress(). If you wish to obtain a string representation to be part of a URL, use HostName.toNormalizedString().

Sorting and Comparisons

Comparing and sorting can be useful for storing addresses in certain types of data structures. With the Java library, all of the core classes implement java.lang.Comparable. In fact, any AddressItem is comparable to any other, which covers almost every type in the address framework. Different representations of the same address or subnet are considered equal. Different representations of the same set of addresses are considered equal. However, HostName instances are not equal to IPAddressString instances nor IPAddress instances, and instances of IPAddressString are not equal to instances of IPAddress, not even when representing the same address or subnet.

The library provides the abstract type AddressComparator and some implementations for comparison purposes. Address classes use the subclass CountComparator for their natural ordering. When comparing subnets, you can either emphasize the count of addresses, or you can emphasize the values of the lower or upper address represented by the subnet, and comparators are provided for those variations.

The address tries provide their own sorting and comparison, which matches the comparators above with respect to individual addresses, but when comparing a prefix block to a second address or block with larger prefix, and the first prefix matches the same bits in the second, the trie comparator orders by the bit that follows the first prefix in the second.

Make your IPv4 App work with IPv6

If you want to make your app work with IPv6, that is a wise decision. IPv6 penetration is getting close to 50% in some countries.

But you have no IPv6 testing and a lot of code.

Start by replacing your IPv4 code with the types and operations in this library. But don’t use the IPv4-specific types like IPv4Address everywhere, use IPAddressString, IPAddress, and the other polymorphic types, along with their polymorphic operations such as toString, contains, and iterator that avoid exposing the specifics of IPv4. If your app uses network or host masks, start working with CIDR prefix lengths instead. Use methods like getBlockMaskPrefixLength to store prefix lengths, even if the app continues to use masking.

When you’re ready, use your existing regression tests, ensuring the app still runs as always on IPv4. Once your existing IPv4 tests all pass, you are already most of the way to supporting IPv6, without having written a single line of IPv6 code. With some apps, you may even be ready to try it out with IPv6.