Class: IPAddress::IPv6

Inherits:
Object
  • Object
show all
Includes:
Comparable, Enumerable, IPAddress
Defined in:
lib/ipaddress/ipv6.rb

Overview

Name

IPAddress::IPv6 - IP version 6 address manipulation library

Synopsis

require 'ipaddress'

Description

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 octect. 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 semplifications and compressions that you can use to shorten them.

  • Leading zeroes: all the leading zeroes within a group can be omitted: “0008” would become “8”

  • A string of consecutive zeroes can be replaced by the string “::”. This can be only applied once.

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.

Direct Known Subclasses

Loopback, Mapped, Unspecified

Defined Under Namespace

Classes: Loopback, Mapped, Unspecified

Constant Summary collapse

IN6FORMAT =

Format string to pretty print IPv6 addresses

("%.4x:"*8).chop

Constants included from IPAddress

AUTHORS, GEM, NAME

Class Method Summary collapse

Instance Method Summary collapse

Methods included from IPAddress

deprecate, #ipv4?, #ipv6?, ntoa, parse, valid?, valid_ip?, valid_ipv4?, valid_ipv4_netmask?, valid_ipv4_subnet?, valid_ipv6?, valid_ipv6_subnet?

Constructor Details

#initialize(str) ⇒ IPv6

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:

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

Raises:

  • (ArgumentError)


89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/ipaddress/ipv6.rb', line 89

def initialize(str)
  raise ArgumentError, "Nil IP" unless str
  ip, netmask = str.split("/")

  if str =~ /:.+\./
    raise ArgumentError, "Please use #{self.class}::Mapped for IPv4 mapped addresses"
  end
  
  if IPAddress.valid_ipv6?(ip)
    @groups = self.class.groups(ip)
    @address = IN6FORMAT % @groups
    @compressed = compress_address
  else
    raise ArgumentError, "Invalid IP #{ip.inspect}"
  end

  @prefix = Prefix128.new(netmask ? netmask : 128)
  @allocator = 0

end

Class Method Details

.compress(str) ⇒ Object

Compress an IPv6 address in its compressed form

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


562
563
564
# File 'lib/ipaddress/ipv6.rb', line 562

def self.compress(str)
  self.new(str).compressed
end

.expand(str) ⇒ Object

Expands an IPv6 address in the canocical form

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


552
553
554
# File 'lib/ipaddress/ipv6.rb', line 552

def self.expand(str)
  self.new(str).address
end

.groups(str) ⇒ Object

Extract 16 bits groups from a string



594
595
596
597
598
599
600
601
# File 'lib/ipaddress/ipv6.rb', line 594

def self.groups(str)
  l, r = if str =~ /^(.*)::(.*)$/
           [$1,$2].map {|i| i.split ":"}
         else
           [str.split(":"),[]]
         end
  (l + Array.new(8-l.size-r.size, '0') + r).map {|i| i.hex}
end

.parse_data(str) ⇒ Object

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

" \001\r\270\000\000\000\000\000\b\b\000 \fAz"

With that data you can create a new IPv6 object:

ip6 = IPAddress::IPv6::parse_data " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
ip6.prefix = 64

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


623
624
625
# File 'lib/ipaddress/ipv6.rb', line 623

def self.parse_data(str)
  self.new(IN6FORMAT % str.unpack("n8"))
end

.parse_hex(hex, prefix = 128) ⇒ Object

Creates a new IPv6 object from a number expressed in hexdecimal 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"


666
667
668
# File 'lib/ipaddress/ipv6.rb', line 666

def self.parse_hex(hex, prefix=128)
  self.parse_u128(hex.hex, prefix)
end

.parse_u128(u128, prefix = 128) ⇒ Object

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

ip6 = IPAddress::IPv6::parse_u128(42540766411282592856906245548098208122)
ip6.prefix = 64

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

The prefix parameter is optional:

ip6 = IPAddress::IPv6::parse_u128(42540766411282592856906245548098208122, 64)

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


644
645
646
647
# File 'lib/ipaddress/ipv6.rb', line 644

def self.parse_u128(u128, prefix=128)
  str = IN6FORMAT % (0..7).map{|i| (u128>>(112-16*i))&0xffff}
  self.new(str + "/#{prefix}")
end

Instance Method Details

#<=>(oth) ⇒ Object

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

Example:

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

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

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


526
527
528
529
530
# File 'lib/ipaddress/ipv6.rb', line 526

def <=>(oth)
  return nil unless oth.is_a?(self.class)
  return prefix <=> oth.prefix if to_u128 == oth.to_u128  
  to_u128 <=> oth.to_u128
end

#[](index) ⇒ Object Also known as: group

Returns the 16-bits value specified by index

ip = IPAddress("2001:db8::8:800:200c:417a/64")

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


252
253
254
# File 'lib/ipaddress/ipv6.rb', line 252

def [](index)
  @groups[index]
end

#[]=(index, value) ⇒ Object Also known as: group=

Updated the octet specified at index



260
261
262
263
# File 'lib/ipaddress/ipv6.rb', line 260

def []=(index, value)
  @groups[index] = value
  initialize("#{IN6FORMAT % @groups}/#{prefix}")
end

#addressObject

Returns the IPv6 address in uncompressed form:

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

ip6.address
  #=> "2001:0db8:0000:0000:0008:0800:200c:417a"


118
119
120
# File 'lib/ipaddress/ipv6.rb', line 118

def address
  @address
end

#allocate(skip = 0) ⇒ Object

Allocates a new ip from the current subnet. Optional skip parameter can be used to skip addresses.

Will raise StopIteration exception when all addresses have been allocated

Example:

ip = IPAddress("10.0.0.0/24")
ip.allocate
  #=> "10.0.0.1/24"
ip.allocate
  #=> "10.0.0.2/24"
ip.allocate(2)
  #=> "10.0.0.5/24"

Uses an internal @allocator which tracks the state of allocated addresses.



690
691
692
693
694
695
696
697
698
# File 'lib/ipaddress/ipv6.rb', line 690

def allocate(skip=0)
    @allocator += 1 + skip

    next_ip = network_u128+@allocator
    if next_ip > broadcast_u128
        raise StopIteration
    end
    self.class.parse_u128(next_ip, @prefix)
end

#bitsObject

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

ip6 = IPAddress("2001:db8::8:800:200c:417a")

ip6.bits 
  #=> "0010000000000001000011011011100000 [...] "


542
543
544
# File 'lib/ipaddress/ipv6.rb', line 542

def bits
  data.unpack("B*").first
end

#broadcast_u128Object

Returns the broadcast address in Unsigned 128bits format

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

ip6.broadcast_u128
  #=> 42540766411282592875350729025363378175

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



354
355
356
# File 'lib/ipaddress/ipv6.rb', line 354

def broadcast_u128
  network_u128 + size - 1
end

#compressedObject

Compressed form of the IPv6 address

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

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


398
399
400
# File 'lib/ipaddress/ipv6.rb', line 398

def compressed
  @compressed
end

#dataObject

Returns the address portion of an IPv6 object in a network byte order format.

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

ip6.data
  #=> " \001\r\270\000\000\000\000\000\b\b\000 \fAz"

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

a = Socket.open(params) # socket details here
ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
binary_data = ["Address: "].pack("a*") + ip.data

# Send binary data
a.puts binary_data


297
298
299
# File 'lib/ipaddress/ipv6.rb', line 297

def data
  @groups.pack("n8")
end

#eachObject

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("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"

WARNING: if the host portion is very large, this method can be very slow and possibly hang your system!



488
489
490
491
492
# File 'lib/ipaddress/ipv6.rb', line 488

def each
  (network_u128..broadcast_u128).each do |i|
    yield self.class.parse_u128(i, @prefix)
  end
end

#groupsObject

Returns an array with the 16 bits groups in decimal format:

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

ip6.groups
  #=> [8193, 3512, 0, 0, 8, 2048, 8204, 16762]


131
132
133
# File 'lib/ipaddress/ipv6.rb', line 131

def groups
  @groups
end

#hexsObject

Returns an array of the 16 bits groups in hexdecimal format:

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

ip6.hexs
  #=> ["2001", "0db8", "0000", "0000", "0008", "0800", "200c", "417a"]

Not to be confused with the similar IPv6#to_hex method.



312
313
314
# File 'lib/ipaddress/ipv6.rb', line 312

def hexs
  @address.split(":")
end

#include?(oth) ⇒ Boolean

Checks whether a subnet includes the given IP address.

Example:

ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
addr = IPAddress "2001:db8::8:800:200c:1/128"

ip6.include? addr
  #=> true

ip6.include? IPAddress("2001:db8:1::8:800:200c:417a/76")
  #=> false

Returns:

  • (Boolean)


386
387
388
# File 'lib/ipaddress/ipv6.rb', line 386

def include?(oth)
  @prefix <= oth.prefix and network_u128 == self.class.new(oth.address+"/#@prefix").network_u128
end

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

Example:

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

Returns:

  • (Boolean)


405
406
407
# File 'lib/ipaddress/ipv6.rb', line 405

def link_local?
  @groups[0] == 0xfe80
end

#literalObject

Literal version of the IPv6 address

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

ip6.literal
  #=> "2001-0db8-0000-0000-0008-0800-200c-417a.ipv6-literal.net"


574
575
576
# File 'lib/ipaddress/ipv6.rb', line 574

def literal
  @address.gsub(":","-") + ".ipv6-literal.net"
end

#loopback?Boolean

Returns true if the address is a loopback address

See IPAddress::IPv6::Loopback for more information

Returns:

  • (Boolean)


423
424
425
# File 'lib/ipaddress/ipv6.rb', line 423

def loopback?
  @prefix == 128 and @compressed == "::1"
end

#mapped?Boolean

Returns true if the address is a mapped address

See IPAddress::IPv6::Mapped for more information

Returns:

  • (Boolean)


460
461
462
# File 'lib/ipaddress/ipv6.rb', line 460

def mapped?
  to_u128 >> 32 == 0xffff
end

#networkObject

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

ip = IPAddress "2001:db8:1:1:1:1:1:1/32" 

ip.network.to_string
  #=> "2001:db8::/32"


587
588
589
# File 'lib/ipaddress/ipv6.rb', line 587

def network
  self.class.parse_u128(network_u128, @prefix)
end

#network?Boolean

True if the IPv6 address is a network

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

ip6.network?
  #=> false

ip6 = IPAddress "2001:db8:8:800::/64"

ip6.network?
  #=> true

Returns:

  • (Boolean)


234
235
236
# File 'lib/ipaddress/ipv6.rb', line 234

def network?
  to_u128 | @prefix.to_u128 == @prefix.to_u128
end

#network_u128Object

Returns the network number in Unsigned 128bits format

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

ip6.network_u128
  #=> 42540766411282592856903984951653826560


338
339
340
# File 'lib/ipaddress/ipv6.rb', line 338

def network_u128
  to_u128 & @prefix.to_u128
end

#prefixObject

Returns an instance of the prefix object

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

ip6.prefix
  #=> 64


143
144
145
# File 'lib/ipaddress/ipv6.rb', line 143

def prefix
  @prefix
end

#prefix=(num) ⇒ Object

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("2001:db8::8:800:200c:417a")

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

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


164
165
166
# File 'lib/ipaddress/ipv6.rb', line 164

def prefix=(num)
  @prefix = Prefix128.new(num)
end

#reverseObject Also known as: arpa

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

ip6 = IPAddress "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"


325
326
327
# File 'lib/ipaddress/ipv6.rb', line 325

def reverse
  to_hex.reverse.gsub(/./){|c| c+"."} + "ip6.arpa"
end

#sizeObject

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

ip6 = IPAddress("2001:db8::8:800:200c:417a/64")

ip6.size
  #=> 18446744073709551616


368
369
370
# File 'lib/ipaddress/ipv6.rb', line 368

def size
  2 ** @prefix.host_prefix
end

#to_hexObject

Returns a Base16 number representing the IPv6 address

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

ip6.to_hex
  #=> "20010db80000000000080800200c417a"


275
276
277
# File 'lib/ipaddress/ipv6.rb', line 275

def to_hex
  hexs.join("")
end

#to_iObject Also known as: to_u128

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

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

ip6.to_i
  #=> 42540766411282592856906245548098208122


216
217
218
# File 'lib/ipaddress/ipv6.rb', line 216

def to_i
  to_hex.hex
end

#to_sObject

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

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

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


203
204
205
# File 'lib/ipaddress/ipv6.rb', line 203

def to_s
  @compressed
end

#to_stringObject

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

ip6 = IPAddress "2001:0db8:0000:0000:0008:0800:200c:417a/64"

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


190
191
192
# File 'lib/ipaddress/ipv6.rb', line 190

def to_string
  "#@compressed/#@prefix"
end

#to_string_uncompressedObject

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

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

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


177
178
179
# File 'lib/ipaddress/ipv6.rb', line 177

def to_string_uncompressed
  "#@address/#@prefix"
end

#unique_local?Boolean

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

Example:

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

Returns:

  • (Boolean)


451
452
453
# File 'lib/ipaddress/ipv6.rb', line 451

def unique_local?
  [self.class.new("fc00::/7")].any? {|i| i.include? self}
end

#unspecified?Boolean

Returns true if the address is an unspecified address

See IPAddress::IPv6::Unspecified for more information

Returns:

  • (Boolean)


414
415
416
# File 'lib/ipaddress/ipv6.rb', line 414

def unspecified?
  @prefix == 128 and @compressed == "::"
end