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

- old
+ new

@@ -424,43 +424,44 @@ yield self.class.parse_u32(i, @prefix) end end # - # Spaceship operator to compare IP addresses + # Spaceship operator to compare IPv4 objects # - # An IP address is considered to be minor if it - # has a greater prefix (thus smaller hosts - # portion) and a smaller u32 value. + # Comparing IPv4 addresses is useful to ordinate + # them into lists that match our intuitive + # perception of ordered IP addresses. + # + # The first comparison criteria is the u32 value. + # For example, 10.100.100.1 will be considered + # to be less than 172.16.0.1, because, in a ordered list, + # we expect 10.100.100.1 to come before 172.16.0.1. # - # For example, "10.100.100.1/8" is smaller than - # "172.16.0.1/16", but it's bigger than "10.100.100.1/16". + # The second criteria, in case two IPv4 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 + # 10.100.100.0/24 come before 10.100.100.0/25. # # Example: # # ip1 = IPAddress "10.100.100.1/8" # ip2 = IPAddress "172.16.0.1/16" # ip3 = IPAddress "10.100.100.1/16" # # ip1 < ip2 # #=> true - # ip1 < ip3 + # ip1 > ip3 # #=> false # + # [ip1,ip2,ip3].sort.map{|i| i.to_string} + # #=> ["10.100.100.1/8","10.100.100.1/16","172.16.0.1/16"] + # def <=>(oth) - if to_u32 > oth.to_u32 - return 1 - elsif to_u32 < oth.to_u32 - return -1 - else - if prefix < oth.prefix - return 1 - elsif prefix > oth.prefix - return -1 - end - end - return 0 + return prefix <=> oth.prefix if to_u32 == oth.to_u32 + to_u32 <=> oth.to_u32 end # # Returns the number of IP addresses included # in the network. It also counts the network @@ -551,34 +552,10 @@ def include_all?(*others) others.all? {|oth| include?(oth)} end # - # True if the object is an IPv4 address - # - # ip = IPAddress("192.168.10.100/24") - # - # ip.ipv4? - # #-> true - # -# def ipv4? -# true -# end - - # - # True if the object is an IPv6 address - # - # ip = IPAddress("192.168.10.100/24") - # - # ip.ipv6? - # #-> false - # -# def ipv6? -# false -# end - - # # Checks if an IPv4 address objects belongs # to a private network RFC1918 # # Example: # @@ -605,14 +582,14 @@ @octets.reverse.join(".") + ".in-addr.arpa" end alias_method :arpa, :reverse # - # Subnetting a network + # Splits a network into different subnets # # If the IP Address is a network, it can be divided into - # multiple networks. If +self+ is not a network, the + # multiple networks. If +self+ is not a network, this # method will calculate the network from the IP and then # subnet it. # # If +subnets+ is an power of two number, the resulting # networks will be divided evenly from the supernet. @@ -634,19 +611,23 @@ # network / 3 # implies map{|i| i.to_string} # #=> ["172.16.10.0/26", # "172.16.10.64/26", # "172.16.10.128/25"] # - # Returns an array of IPAddress objects + # Returns an array of IPv4 objects # - def subnet(subnets=2) + def split(subnets=2) unless (1..(2**@prefix.host_prefix)).include? subnets raise ArgumentError, "Value #{subnets} out of range" end - calculate_subnets(subnets) + networks = subnet(newprefix(subnets)) + until networks.size == subnets + networks = sum_first_found(networks) + end + return networks end - alias_method :/, :subnet + alias_method :/, :split # # Returns a new IPv4 object from the supernetting # of the instance network. # @@ -665,18 +646,51 @@ # However if you supernet it with a /22 prefix, the # network address will change: # # ip.supernet(22).to_string # #=> "172.16.8.0/22" - # + # + # If +new_prefix+ is less than 1, returns 0.0.0.0/0 + # def supernet(new_prefix) - raise ArgumentError, "Can't supernet a /1 network" if new_prefix < 1 raise ArgumentError, "New prefix must be smaller than existing prefix" if new_prefix >= @prefix.to_i - self.class.new(@address+"/#{new_prefix}").network + return self.class.new("0.0.0.0/0") if new_prefix < 1 + return self.class.new(@address+"/#{new_prefix}").network end # + # This method implements the subnetting function + # similar to the one described in RFC3531. + # + # By specifying a new prefix, the method calculates + # the network number for the given IPv4 object + # and calculates the subnets associated to the new + # prefix. + # + # For example, given the following network: + # + # ip = IPAddress "172.16.10.0/24" + # + # we can calculate the subnets with a /26 prefix + # + # ip.subnets(26).map{&:to_string) + # #=> ["172.16.10.0/26", "172.16.10.64/26", + # "172.16.10.128/26", "172.16.10.192/26"] + # + # The resulting number of subnets will of course always be + # a power of two. + # + def subnet(subprefix) + unless ((@prefix.to_i)..32).include? subprefix + raise ArgumentError, "New prefix must be between #@prefix and 32" + end + Array.new(2**(subprefix-@prefix.to_i)) do |i| + self.class.parse_u32(network_u32+(i*(2**(32-subprefix))), subprefix) + end + end + + # # Returns the difference between two IP addresses # in unsigned int 32 bits format # # Example: # @@ -926,22 +940,22 @@ # # * Class A, from 0.0.0.0 to 127.255.255.255 # * Class B, from 128.0.0.0 to 191.255.255.255 # * Class C, D and E, from 192.0.0.0 to 255.255.255.254 # - # Note that classes C, D and E will all have a default - # prefix of /24 or 255.255.255.0 - # # Example: # # ip = IPAddress::IPv4.parse_classful "10.0.0.1" # # ip.netmask # #=> "255.0.0.0" # ip.a? # #=> true # + # Note that classes C, D and E will all have a default + # prefix of /24 or 255.255.255.0 + # def self.parse_classful(ip) if IPAddress.valid_ipv4?(ip) address = ip.strip else raise ArgumentError, "Invalid IP #{ip.inspect}" @@ -952,28 +966,22 @@ # # private methods # private - - def calculate_subnets(subnets) - po2 = subnets.closest_power_of_2 - new_prefix = @prefix + Math::log2(po2).to_i - networks = Array.new - (0..po2-1).each do |i| - mul = i * (2**(32-new_prefix)) - networks << IPAddress::IPv4.parse_u32(network_u32+mul, new_prefix) + + def newprefix(num) + num.upto(32) do |i| + if (a = Math::log2(i).to_i) == Math::log2(i) + return @prefix + a + end end - until networks.size == subnets - networks = sum_first_found(networks) - end - return networks end def sum_first_found(arr) dup = arr.dup.reverse dup.each_with_index do |obj,i| - a = [IPAddress::IPv4.summarize(obj,dup[i+1])].flatten + a = [self.class.summarize(obj,dup[i+1])].flatten if a.size == 1 dup[i..i+1] = a return dup.reverse end end