class IPAddress::IPv6

Overview

Class IPAddress::IPv6 is used to handle IPv6 type addresses.

IPv6 addresses

IPv6 addresses are 128 bits long, in contrast with IPv4 addresses which are only 32 bits long. An IPv6 address is generally written as eight groups of four hexadecimal digits, each group representing 16 bits or two octets. For example, the following is a valid IPv6 address:

2001:0db8:0000:0000:0008:0800:200c:417a

Letters in an IPv6 address are usually written downcase, as per RFC. You can create a new IPv6 object using uppercase letters, but they will be converted.

Compression

Since IPv6 addresses are very long to write, there are some simplifications and compressions that you can use to shorten them.

Using compression, the IPv6 address written above can be shorten into the following, equivalent, address:

2001:db8::8:800:200c:417a

This short version is often used in human representation.

Network Mask

As we used to do with IPv4 addresses, an IPv6 address can be written using the prefix notation to specify the subnet mask:

2001:db8::8:800:200c:417a/64

The /64 part means that the first 64 bits of the address are representing the network portion, and the last 64 bits are the host portion.

Included Modules

Direct Known Subclasses

Defined in:

ipaddress/ipv6.cr

Constant Summary

IN6FORMAT = ("%04x:" * 8).rchop

Format string to pretty print IPv6 addresses.

REGEXP = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/

Inspired by https://gist.github.com/cpetschnig/294476

Constructors

Class Method Summary

Instance Method Summary

Instance methods inherited from module IPAddress

ipv4? ipv4?, ipv6? ipv6?

Constructor methods inherited from module IPAddress

new(addr : String | Int) : IPAddress new, parse(addr : String | Int) : IPAddress parse

Class methods inherited from module IPAddress

aton(addr : String) : UInt32 aton, ntoa(uint : UInt32) : String
ntoa(int : Int) : String
ntoa
, valid?(addr : String) valid?, valid_ipv4?(addr : String) valid_ipv4?, valid_ipv4_netmask?(addr : String) valid_ipv4_netmask?, valid_ipv6?(addr : String) valid_ipv6?

Constructor Detail

def self.new(addr : String, netmask = nil) #

Creates a new IPv6 address object.

An IPv6 address can be expressed in any of the following forms:

  • 2001:0db8:0000:0000:0008:0800:200C:417A: IPv6 address with no compression
  • 2001:db8:0:0:8:800:200C:417A: IPv6 address with leading zeros compression
  • 2001:db8::8:800:200C:417A: IPv6 address with full compression

In all these 3 cases, a new IPv6 address object will be created, using the default subnet mask /128

You can also specify the subnet mask as with IPv4 addresses:

# These two are the same:
ip6 = IPAddress::IPv6.new "2001:db8::8:800:200c:417a/64"
ip6 = IPAddress.new "2001:db8::8:800:200c:417a/64"

[View source]
def self.parse_data(data : Bytes, prefix = 128) : IPv6 #

Creates a new IPv6 object from binary data, like the one you get from a network stream.

For example, on a network stream the IP

"2001:db8::8:800:200c:417a"

is represented with the binary data

Bytes[32, 1, 13, 184, 0, 0, 0, 0, 0, 8, 8, 0, 32, 12, 65, 122]

With that data you can create a new IPv6 object:

ip6 = IPAddress::IPv6.parse_data Bytes[32, 1, 13, 184, 0, 0, 0, 0, 0, 8, 8, 0, 32, 12, 65, 122]
ip6.prefix = 64

ip6.to_s # => "2001:db8::8:800:200c:417a/64"

[View source]
def self.parse_hex(hex : String, prefix = 128) : IPv6 #

Creates a new IPv6 object from a number expressed in hexadecimal format.

ip6 = IPAddress::IPv6.parse_hex "20010db80000000000080800200c417a"
ip6.prefix = 64

ip6.to_string # => "2001:db8::8:800:200c:417a/64"

The prefix parameter is optional:

ip6 = IPAddress::IPv6.parse_hex "20010db80000000000080800200c417a", 64
ip6.to_string # => "2001:db8::8:800:200c:417a/64"

[View source]
def self.parse_u128(u128 : BigInt, prefix = 128) : IPv6 #

Creates a new IPv6 object from an unsigned 128 bits integer.

ip6 = IPAddress::IPv6.parse_u128 "42540766411282592856906245548098208122".to_big_i
ip6.prefix = 64

ip6.to_string # => "2001:db8::8:800:200c:417a/64"

The prefix parameter is optional:

ip6 = IPAddress::IPv6.parse_u128 "42540766411282592856906245548098208122".to_big_i, 64
ip6.to_string # => "2001:db8::8:800:200c:417a/64"

[View source]

Class Method Detail

def self.compress(addr : String) : String #

Compress an IPv6 address in its compressed form.

IPAddress::IPv6.compress "2001:0DB8:0000:CD30:0000:0000:0000:0000"
# => "2001:db8:0:cd30::"

[View source]
def self.expand(addr : String) : String #

Expands an IPv6 address in the canonical form.

IPAddress::IPv6.expand "2001:0DB8:0:CD30::"
# => "2001:0DB8:0000:CD30:0000:0000:0000:0000"

[View source]
def self.groups(addr : String) : Array(Int32) #

Extract 16 bit groups from a string.


[View source]
def self.valid?(addr : String) #

Returns true if the given string is a valid IPv6 address.

IPAddress::IPv6.valid? "2002::1"          # => true
IPAddress::IPv6.valid? "2002::DEAD::BEEF" # => false

[View source]

Instance Method Detail

def <=>(other : IPv6) #

Spaceship operator to compare IPv6 objects.

Comparing IPv6 addresses is useful to ordinate them into lists that match our intuitive perception of ordered IP addresses.

The first comparison criteria is the u128 value. For example, 2001:db8:1::1 will be considered to be less than 2001:db8:2::1, because, in a ordered list, we expect 2001:db8:1::1 to come before 2001:db8:2::1.

The second criteria, in case two IPv6 objects have identical addresses, is the prefix. An higher prefix will be considered greater than a lower prefix. This is because we expect to see 2001:db8:1::1/64 come before 2001:db8:1::1/65.

ip1 = IPAddress.new "2001:db8:1::1/64"
ip2 = IPAddress.new "2001:db8:2::1/64"
ip3 = IPAddress.new "2001:db8:1::1/65"

ip1 < ip2 # => true
ip1 < ip3 # => false

[ip1, ip2, ip3].sort.map &.to_string
# => ["2001:db8:1::1/64", "2001:db8:1::1/65", "2001:db8:2::1/64"]

[View source]
def ==(other : self) #
Description copied from class Reference

Returns true if this reference is the same as other. Invokes same?.


def [](index : Int32) : Int32 #

Returns the 16-bits value specified by index.

ip6 = IPAddress.new "2001:db8::8:800:200c:417a/64"

ip6[0] # => 8193
ip6[1] # => 3512
ip6[2] # => 0
ip6[3] # => 0

See also: #groups


[View source]
def []=(index : Int32, value : Int32) : Nil #

Updates the 16-bits value specified at index.

See also: #groups


[View source]
def address : String #

Returns the IPv6 address in uncompressed form.

ip6 = IPAddress.new "2001:db8::8:800:200c:417a/64"
ip6.address # => "2001:0db8:0000:0000:0008:0800:200c:417a"

[View source]
def bits : String #

Returns the address portion of an IP in binary format, as a string containing a sequence of 0 and 1.

ip6 = IPAddress.new "2001:db8::8:800:200c:417a"
ip6.bits # => "0010000000000001000011011011100000 [...]"

[View source]
def broadcast_u128 : BigInt #

Returns the broadcast address in unsigned 128 bits format.

ip6 = IPAddress.new "2001:db8::8:800:200c:417a/64"
ip6.broadcast_u128 # => 42540766411282592875350729025363378175

NOTE Please note that there is no broadcast concept in IPv6 addresses as in IPv4 addresses, and this method is just a helper to other functions.


[View source]
def compressed : String #

Compressed form of the IPv6 address.

ip6 = IPAddress.new "2001:db8::8:800:200c:417a/64"
ip6.compressed # => "2001:db8::8:800:200c:417a"

[View source]
def data : Bytes #

Returns the address portion of an IPv6 object in a network byte order format (IO::ByteFormat::NetworkEndian).

ip6 = IPAddress.new "2001:db8::8:800:200c:417a/64"
ip6.data # => Bytes[172, 16, 10, 1]

It is usually used to include an IP address in a data packet to be sent over a socket

ip6 = IPAddress.new "2001:db8::8:800:200c:417a/64"
socket = Socket.new(params) # socket details here

# Send binary data
socket.send "Address: "
socket.send ip6.data

[View source]
def each(&) : Nil #

Iterates over all the IP addresses for the given network (or IP address).

The object yielded is a new IPv6 object created from the iteration.

ip6 = IPAddress.new "2001:db8::4/125"

ip6.each do |i|
  p i.compressed
end
# => "2001:db8::"
# => "2001:db8::1"
# => "2001:db8::2"
# => "2001:db8::3"
# => "2001:db8::4"
# => "2001:db8::5"
# => "2001:db8::6"
# => "2001:db8::7"

NOTE If the host portion is very large, this method can be very slow and possibly hang your system!


[View source]
def groups : Array(Int32) #

Returns an array with the 16 bit groups in decimal format.

ip6 = IPAddress.new "2001:db8::8:800:200c:417a/64"
ip6.groups # => [8193, 3512, 0, 0, 8, 2048, 8204, 16762]

[View source]
def hash(hasher) #
Description copied from class Reference

See Object#hash(hasher)


def hexs : Array(String) #

Returns an array of the 16 bits groups in hexadecimal format.

ip6 = IPAddress.new "2001:db8::8:800:200c:417a/64"
ip6.hexs # => ["2001", "0db8", "0000", "0000", "0008", "0800", "200c", "417a"]

Not to be confused with the similar #to_hex method.


[View source]
def includes?(others : Array(IPv6)) #

ditto


[View source]
def includes?(other) #

Checks whether a subnet includes the given IP address.

ip6 = IPAddress.new "2001:db8::8:800:200c:417a/64"

addr1 = IPAddress.new "2001:db8::8:800:200c:1/128"
addr2 = IPAddress.new "2001:db8:1::8:800:200c:417a/76"

ip6.includes? addr1 # => true
ip6.includes? addr2 # => false

[View source]
def includes?(*others : IPv6) #

Checks whether a subnet includes all the given IPv6 objects.

ip6 = IPAddress.new "2001:db8::4/125"

addr1 = IPAddress.new "2001:db8::2/125"
addr2 = IPAddress.new "2001:db8::7/125"

ip6.includes? addr1, addr2 # => true

[View source]
def link_local? #

Checks if an IPv6 address objects belongs to a link-local network RFC4291.

ip = IPAddress.new "fe80::1"
ip.link_local? # => true

[View source]
def literal : String #

Literal version of the IPv6 address.

ip6 = IPAddress.new "2001:db8::8:800:200c:417a/64"
ip6.literal # => "2001-0db8-0000-0000-0008-0800-200c-417a.ipv6-literal.net"

[View source]
def loopback? #

Returns true if the address is a loopback address.

See IPv6::Loopback for more information.


[View source]
def mapped? #

Returns true if the address is a mapped address.

See IPv6::Mapped for more information.


[View source]
def network : IPv6 #

Returns a new IPv6 object with the network number for the given IP.

ip6 = IPAddress.new "2001:db8:1:1:1:1:1:1/32"
ip6.network.to_string # => "2001:db8::/32"

[View source]
def network? #

Returns true if the IPv6 address is a network.

ip6 = IPAddress.new "2001:db8::8:800:200c:417a/64"
ip6.network? # => false

ip6 = IPAddress.new "2001:db8:8:800::/64"
ip6.network? # => true

[View source]
def network_u128 : BigInt #

Returns the network number in unsigned 128 bits format.

ip6 = IPAddress.new "2001:db8::8:800:200c:417a/64"
ip6.network_u128 # => 42540766411282592856903984951653826560

[View source]
def pred : IPv6 #

Returns the predecessor to the IP address.

ip6 = IPAddress.new "2001:db8::8:800:200c:417a/64"
ip6.pred.to_string # => "2001:db8::8:800:200c:4179/64"

[View source]
def prefix : Prefix128 #

Returns an instance of the prefix object.

ip6 = IPAddress.new "2001:db8::8:800:200c:417a/64"
ip6.prefix # => 64

[View source]
def prefix=(prefix : Int32) : Prefix128 #

Set a new prefix number for the object.

This is useful if you want to change the prefix to an object created with IPv6.parse_u128 or if the object was created using the default prefix of 128 bits.

ip6 = IPAddress.new "2001:db8::8:800:200c:417a"
ip6.to_string # => "2001:db8::8:800:200c:417a/128"

ip6.prefix = 64
ip6.to_string # => "2001:db8::8:800:200c:417a/64"

[View source]
def reverse : String #

Returns the IPv6 address in a DNS reverse lookup string, as per RFC3172 and RFC2874.

ip6 = IPAddress.new "3ffe:505:2::f"
ip6.reverse # => "f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.arpa"

[View source]
def size : BigInt #

Returns the number of IP addresses included in the network. It also counts the network address and the broadcast address.

ip6 = IPAddress.new "2001:db8::8:800:200c:417a/64"
ip6.size # => 18446744073709551616

[View source]
def succ : IPv6 #

Returns the successor to the IP address.

ip6 = IPAddress.new "2001:db8::8:800:200c:417a/64"
ip6.succ.to_string # => "2001:db8::8:800:200c:417b/64"

[View source]
def to_big_i : BigInt #

Returns a decimal format (unsigned 128 bit) of the IPv6 address.

ip6 = IPAddress.new "2001:db8::8:800:200c:417a/64"
ip6.to_big_i # => 42540766411282592856906245548098208122

[View source]
def to_hex : String #

Returns a base16 number representing the IPv6 address.

ip6 = IPAddress.new "2001:db8::8:800:200c:417a/64"
ip6.to_hex # => "20010db80000000000080800200c417a"

[View source]
def to_s(io : IO) #

Appends to the given IO the IPv6 address in a human readable form, using the compressed address.

ip6 = IPAddress.new "2001:db8::8:800:200c:417a/64"
ip6.to_s # => "2001:db8::8:800:200c:417a"

[View source]
def to_string : String #

Returns the IPv6 address in a human readable form, using the compressed address.

ip6 = IPAddress.new "2001:0db8:0000:0000:0008:0800:200c:417a/64"
ip6.to_string # => "2001:db8::8:800:200c:417a/64"

[View source]
def to_string_uncompressed : String #

Unlike its counterpart #to_string method, #to_string_uncompressed returns the whole IPv6 address and prefix in an uncompressed form.

ip6 = IPAddress.new "2001:db8::8:800:200c:417a/64"
ip6.to_string_uncompressed # => "2001:0db8:0000:0000:0008:0800:200c:417a/64"

[View source]
def unique_local? #

Checks if an IPv6 address objects belongs to a unique-local network RFC4193.

ip = IPAddress.new "fc00::1"
ip.unique_local? # => true

[View source]
def unspecified? #

Returns true if the address is an unspecified address.

See IPv6::Unspecified for more information.


[View source]