lib/ipaddress/ipv6.rb in ipaddress-0.7.5 vs lib/ipaddress/ipv6.rb in ipaddress-0.8.0

- old
+ new

@@ -20,11 +20,11 @@ # 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: # - # 1080:0000:0000:0000:0008:0800:200c:417a + # 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. # @@ -40,20 +40,20 @@ # "::". This can be only applied once. # # Using compression, the IPv6 address written above can be shorten into # the following, equivalent, address # - # 1080::8:800:200c:417a + # 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: # - # 1080::8:800:200c:417a/64 + # 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. # @@ -73,13 +73,13 @@ # # Creates a new IPv6 address object. # # An IPv6 address can be expressed in any of the following forms: # - # * "1080:0000:0000:0000:0008:0800:200C:417A": IPv6 address with no compression - # * "1080:0:0:0:8:800:200C:417A": IPv6 address with leading zeros compression - # * "1080::8:800:200C:417A": IPv6 address with full compression + # * "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: @@ -327,10 +327,40 @@ def network_u128 to_u128 & @prefix.to_u128 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. + # + def broadcast_u128 + network_u128 + size - 1 + 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 + # + def size + 2 ** @prefix.host_prefix + end + + # # Checks whether a subnet includes the given IP address. # # Example: # # ip6 = IPAddress "2001:db8::8:800:200c:417a/64" @@ -382,12 +412,79 @@ # See IPAddress::IPv6::Mapped for more information # def mapped? to_u128 >> 32 == 0xffff 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! + # + def each + (network_u128..broadcast_u128).each do |i| + yield self.class.parse_u128(i, @prefix) + end + end + + # + # 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"] + # + def <=>(oth) + return prefix <=> oth.prefix if to_u128 == oth.to_u128 + to_u128 <=> oth.to_u128 + 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") # @@ -429,10 +526,23 @@ def literal @address.gsub(":","-") + ".ipv6-literal.net" 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" + # + def network + self.class.parse_u128(network_u128, @prefix) + end + + # # Extract 16 bits groups from a string # def self.groups(str) l, r = if str =~ /^(.*)::(.*)$/ [$1,$2].map {|i| i.split ":"} @@ -468,22 +578,22 @@ # # Creates a new IPv6 object from an # unsigned 128 bits integer. # - # ip6 = IPAddress::IPv6::parse_u128(21932261930451111902915077091070067066) + # ip6 = IPAddress::IPv6::parse_u128(42540766411282592856906245548098208122) # ip6.prefix = 64 # - # ip6.to_s - # #=> "1080::8:800:200c:417a/64" + # ip6.to_string + # #=> "2001:db8::8:800:200c:417a/64" # # The +prefix+ parameter is optional: # - # ip6 = IPAddress::IPv6::parse_u128(21932261930451111902915077091070067066, 64) + # ip6 = IPAddress::IPv6::parse_u128(42540766411282592856906245548098208122, 64) # - # ip6.to_s - # #=> "1080::8:800:200c:417a/64" + # ip6.to_string + # #=> "2001:db8::8:800:200c:417a/64" # def self.parse_u128(u128, prefix=128) str = IN6FORMAT % (0..7).map{|i| (u128>>(112-16*i))&0xffff} self.new(str + "/#{prefix}") end @@ -493,19 +603,19 @@ # hexdecimal format: # # ip6 = IPAddress::IPv6::parse_hex("20010db80000000000080800200c417a") # ip6.prefix = 64 # - # ip6.to_s + # ip6.to_string # #=> "2001:db8::8:800:200c:417a/64" # # The +prefix+ parameter is optional: # # ip6 = IPAddress::IPv6::parse_hex("20010db80000000000080800200c417a", 64) # - # ip6.to_s - # #=> "1080::8:800:200c:417a/64" + # ip6.to_string + # #=> "2001:db8::8:800:200c:417a/64" # def self.parse_hex(hex, prefix=128) self.parse_u128(hex.hex, prefix) end @@ -603,18 +713,18 @@ # As for the unspecified addresses, IPv6 loopbacks can be created with # IPAddress calling their own class: # # ip = IPAddress::IPv6::Loopback.new # - # ip.to_s + # ip.to_string # #=> "::1/128" # # or by using the wrapper: # # ip = IPAddress "::1" # - # ip.to_s + # ip.to_string # #=> "::1/128" # # Checking if an address is loopback is easy with the IPv6#loopback? # method: # @@ -627,11 +737,11 @@ # # Creates a new IPv6 unspecified address # # ip = IPAddress::IPv6::Loopback.new # - # ip.to_s + # ip.to_string # #=> "::1/128" # def initialize @address = ("0000:"*7)+"0001" @groups = Array.new(7,0).push(1) @@ -666,11 +776,11 @@ # Let's check it's really a mapped address: # # ip6.mapped? # #=> true # - # ip6.to_s + # ip6.to_string # #=> "::FFFF:172.16.10.1/128" # # Now with the +ipv4+ attribute, we can easily access the IPv4 portion # of the mapped IPv6 address: # @@ -693,11 +803,11 @@ # ip6 = IPAddress "::172.16.10.1" # # That is, two colons and the IPv4 address. However, as by RFC, the ffff # group will be automatically added at the beginning # - # ip6.to_s + # ip6.to_string # => "::ffff:172.16.10.1/128" # # making it a mapped IPv6 compatible address. # class IPAddress::IPv6::Mapped < IPAddress::IPv6 @@ -716,10 +826,10 @@ # An IPv6 IPv4-mapped address can also be created using the # IPv6 only format of the address: # # ip6 = IPAddress::IPv6::Mapped.new "::0d01:4403" # - # ip6.to_s + # ip6.to_string # #=> "::ffff:13.1.68.3" # def initialize(str) string, netmask = str.split("/") if string =~ /\./ # IPv4 in dotted decimal form