class IPAddress::IPv6
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.
Constants
- IN6FORMAT
Format string to pretty print IPv6 addresses
Public Class Methods
Compress an IPv6 address in its compressed form
IPAddress::IPv6.compress "2001:0DB8:0000:CD30:0000:0000:0000:0000" #=> "2001:db8:0:cd30::"
# File lib/ipaddress/ipv6.rb, line 514 def self.compress(str) self.new(str).compressed end
Expands an IPv6 address in the canocical form
IPAddress::IPv6.expand "2001:0DB8:0:CD30::" #=> "2001:0DB8:0000:CD30:0000:0000:0000:0000"
# File lib/ipaddress/ipv6.rb, line 504 def self.expand(str) self.new(str).address end
Extract 16 bits groups from a string
# File lib/ipaddress/ipv6.rb, line 546 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
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"
# File lib/ipaddress/ipv6.rb, line 89 def initialize(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) end
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"
# File lib/ipaddress/ipv6.rb, line 575 def self.parse_data(str) self.new(IN6FORMAT % str.unpack("n8")) end
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"
# File lib/ipaddress/ipv6.rb, line 618 def self.parse_hex(hex, prefix=128) self.parse_u128(hex.hex, prefix) end
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"
# File lib/ipaddress/ipv6.rb, line 596 def self.parse_u128(u128, prefix=128) str = IN6FORMAT % (0..7).map{|i| (u128>>(112-16*i))&0xffff} self.new(str + "/#{prefix}") end
Public Instance Methods
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"]
# File lib/ipaddress/ipv6.rb, line 480 def <=>(oth) return prefix <=> oth.prefix if to_u128 == oth.to_u128 to_u128 <=> oth.to_u128 end
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
# File lib/ipaddress/ipv6.rb, line 250 def [](index) @groups[index] end
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"
# File lib/ipaddress/ipv6.rb, line 116 def address @address end
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 [...] "
# File lib/ipaddress/ipv6.rb, line 494 def bits data.unpack("B*").first end
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.
# File lib/ipaddress/ipv6.rb, line 343 def broadcast_u128 network_u128 + size - 1 end
Compressed form of the IPv6 address
ip6 = IPAddress "2001:db8::8:800:200c:417a/64" ip6.compressed #=> "2001:db8::8:800:200c:417a"
# File lib/ipaddress/ipv6.rb, line 387 def compressed @compressed end
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
# File lib/ipaddress/ipv6.rb, line 286 def data @groups.pack("n8") end
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!
# File lib/ipaddress/ipv6.rb, line 442 def each (network_u128..broadcast_u128).each do |i| yield self.class.parse_u128(i, @prefix) end end
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]
# File lib/ipaddress/ipv6.rb, line 129 def groups @groups end
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 #to_hex method.
# File lib/ipaddress/ipv6.rb, line 301 def hexs @address.split(":") end
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
# File lib/ipaddress/ipv6.rb, line 375 def include?(oth) @prefix <= oth.prefix and network_u128 == self.class.new(oth.address+"/#@prefix").network_u128 end
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"
# File lib/ipaddress/ipv6.rb, line 526 def literal @address.gsub(":","-") + ".ipv6-literal.net" end
Returns true if the address is a loopback address
See IPAddress::IPv6::Loopback for more information
# File lib/ipaddress/ipv6.rb, line 405 def loopback? @prefix == 128 and @compressed == "::1" end
Returns true if the address is a mapped address
See IPAddress::IPv6::Mapped for more information
# File lib/ipaddress/ipv6.rb, line 414 def mapped? to_u128 >> 32 == 0xffff end
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"
# File lib/ipaddress/ipv6.rb, line 539 def network self.class.parse_u128(network_u128, @prefix) end
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
# File lib/ipaddress/ipv6.rb, line 232 def network? to_u128 | @prefix.to_u128 == @prefix.to_u128 end
Returns the network number in Unsigned 128bits format
ip6 = IPAddress "2001:db8::8:800:200c:417a/64" ip6.network_u128 #=> 42540766411282592856903984951653826560
# File lib/ipaddress/ipv6.rb, line 327 def network_u128 to_u128 & @prefix.to_u128 end
Returns an instance of the prefix object
ip6 = IPAddress "2001:db8::8:800:200c:417a/64" ip6.prefix #=> 64
# File lib/ipaddress/ipv6.rb, line 141 def prefix @prefix end
Set a new prefix number for the object
This is useful if you want to change the prefix to an object created with ::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"
# File lib/ipaddress/ipv6.rb, line 162 def prefix=(num) @prefix = Prefix128.new(num) end
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"
# File lib/ipaddress/ipv6.rb, line 314 def reverse to_hex.reverse.gsub(/./){|c| c+"."} + "ip6.arpa" end
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
# File lib/ipaddress/ipv6.rb, line 357 def size 2 ** @prefix.host_prefix end
Returns a Base16 number representing the IPv6 address
ip6 = IPAddress "2001:db8::8:800:200c:417a/64" ip6.to_hex #=> "20010db80000000000080800200c417a"
# File lib/ipaddress/ipv6.rb, line 264 def to_hex hexs.join("") end
Returns a decimal format (unsigned 128 bit) of the IPv6 address
ip6 = IPAddress "2001:db8::8:800:200c:417a/64" ip6.to_i #=> 42540766411282592856906245548098208122
# File lib/ipaddress/ipv6.rb, line 214 def to_i to_hex.hex end
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"
# File lib/ipaddress/ipv6.rb, line 201 def to_s @compressed end
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"
# File lib/ipaddress/ipv6.rb, line 188 def to_string "#@compressed/#@prefix" end
Unlike its counterpart #to_string method, #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"
# File lib/ipaddress/ipv6.rb, line 175 def to_string_uncompressed "#@address/#@prefix" end
Returns true if the address is an unspecified address
See IPAddress::IPv6::Unspecified for more information
# File lib/ipaddress/ipv6.rb, line 396 def unspecified? @prefix == 128 and @compressed == "::" end
Private Instance Methods
# File lib/ipaddress/ipv6.rb, line 624 def compress_address str = @groups.map{|i| i.to_s 16}.join ":" loop do break if str.sub!(/\A0:0:0:0:0:0:0:0\Z/, '::') break if str.sub!(/\b0:0:0:0:0:0:0\b/, ':') break if str.sub!(/\b0:0:0:0:0:0\b/, ':') break if str.sub!(/\b0:0:0:0:0\b/, ':') break if str.sub!(/\b0:0:0:0\b/, ':') break if str.sub!(/\b0:0:0\b/, ':') break if str.sub!(/\b0:0\b/, ':') break end str.sub(/:{3,}/, '::') end