diff options
author | bluemonk <ceresa@gmail.com> | 2011-05-15 12:04:20 +0200 |
---|---|---|
committer | bluemonk <ceresa@gmail.com> | 2011-05-15 12:04:20 +0200 |
commit | 8599c8736b9902e8e6cd7d53455d8e29815d8187 (patch) | |
tree | 16c8cab8dfb5fab8aaa82e9a963c1ddcc7d2eb8d | |
parent | bda1e2bb645cf6dcba6fc65f9c9ef12d8003505e (diff) | |
download | ipaddress-8599c8736b9902e8e6cd7d53455d8e29815d8187.tar.gz |
Added IPv4#split and reworked IPv4#subnet as per RFC3531 (closes #14)
-rw-r--r-- | CHANGELOG.rdoc | 2 | ||||
-rw-r--r-- | lib/ipaddress.rb | 3 | ||||
-rw-r--r-- | lib/ipaddress/ipv4.rb | 73 | ||||
-rw-r--r-- | lib/ipaddress/prefix.rb | 2 | ||||
-rw-r--r-- | test/ipaddress/ipv4_test.rb | 87 |
5 files changed, 103 insertions, 64 deletions
diff --git a/CHANGELOG.rdoc b/CHANGELOG.rdoc index 4b1d63d..2c695e8 100644 --- a/CHANGELOG.rdoc +++ b/CHANGELOG.rdoc @@ -3,11 +3,13 @@ CHANGED:: Removed extension methods and extension directory to facilitate integration with the stdlib CHANGED:: Reworked IPv4#<=>, now intuitively sorts objects based on the prefix CHANGED:: IPv4#supernet now returns "0.0.0.0/0" if supernetting with a prefix less than 1 +CHANGED:: IPv4#subnet now accept a new prefix instead of number of subnets (as per RFC3531) NEW:: IPv6#network NEW:: Prefix128#host_prefix NEW:: IPv6#broadcast_u128 NEW:: IPv6#each NEW:: IPv6#<=> +NEW:: IPv4#split == ipaddress 0.7.5 diff --git a/lib/ipaddress.rb b/lib/ipaddress.rb index a9894f4..4d01402 100644 --- a/lib/ipaddress.rb +++ b/lib/ipaddress.rb @@ -143,6 +143,9 @@ module IPAddress false end + # + # Deprecate method + # def self.deprecate(message = nil) # :nodoc: message ||= "You are using deprecated behavior which will be removed from the next major or minor release." warn("DEPRECATION WARNING: #{message}") diff --git a/lib/ipaddress/ipv4.rb b/lib/ipaddress/ipv4.rb index 76ece23..a798a24 100644 --- a/lib/ipaddress/ipv4.rb +++ b/lib/ipaddress/ipv4.rb @@ -584,10 +584,10 @@ module IPAddress; 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. # @@ -613,15 +613,19 @@ module IPAddress; # "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 @@ -654,6 +658,37 @@ module IPAddress; 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 # @@ -934,28 +969,12 @@ module IPAddress; # private - def power_of_2?(int) - Math::log2(int).to_i == Math::log2(int) - end - - def closest_power_of_2(num, limit=32) - num.upto(limit) do |i| - return i if power_of_2?(i) - end - end - - def calculate_subnets(subnets) - po2 = closest_power_of_2(subnets) - new_prefix = @prefix + Math::log2(po2).to_i - networks = Array.new - (0..po2-1).each do |i| - mul = i * (2**(32-new_prefix)) - networks << self.class.parse_u32(network_u32+mul, new_prefix) - end - until networks.size == subnets - networks = sum_first_found(networks) + def newprefix(num) + num.upto(32) do |i| + if (a = Math::log2(i).to_i) == Math::log2(i) + return @prefix + a + end end - return networks end def sum_first_found(arr) diff --git a/lib/ipaddress/prefix.rb b/lib/ipaddress/prefix.rb index 688b962..f3d585d 100644 --- a/lib/ipaddress/prefix.rb +++ b/lib/ipaddress/prefix.rb @@ -18,7 +18,7 @@ module IPAddress # IPAddress::Prefix shouldn't be accesses directly, unless # for particular needs. # - class Prefix + class Prefix include Comparable diff --git a/test/ipaddress/ipv4_test.rb b/test/ipaddress/ipv4_test.rb index c6d6dfb..6d6386f 100644 --- a/test/ipaddress/ipv4_test.rb +++ b/test/ipaddress/ipv4_test.rb @@ -377,42 +377,57 @@ class IPv4Test < Test::Unit::TestCase assert_equal 24, ip.prefix.to_i end - def test_method_subnet - assert_raise(ArgumentError) {@ip.subnet(0)} - assert_raise(ArgumentError) {@ip.subnet(257)} - - arr = ["172.16.10.0/27", "172.16.10.32/27", "172.16.10.64/27", - "172.16.10.96/27", "172.16.10.128/27", "172.16.10.160/27", - "172.16.10.192/27", "172.16.10.224/27"] - assert_equal arr, @network.subnet(8).map {|s| s.to_string} - arr = ["172.16.10.0/27", "172.16.10.32/27", "172.16.10.64/27", - "172.16.10.96/27", "172.16.10.128/27", "172.16.10.160/27", - "172.16.10.192/26"] - assert_equal arr, @network.subnet(7).map {|s| s.to_string} - arr = ["172.16.10.0/27", "172.16.10.32/27", "172.16.10.64/27", - "172.16.10.96/27", "172.16.10.128/26", "172.16.10.192/26"] - assert_equal arr, @network.subnet(6).map {|s| s.to_string} - arr = ["172.16.10.0/27", "172.16.10.32/27", "172.16.10.64/27", - "172.16.10.96/27", "172.16.10.128/25"] - assert_equal arr, @network.subnet(5).map {|s| s.to_string} - arr = ["172.16.10.0/26", "172.16.10.64/26", "172.16.10.128/26", - "172.16.10.192/26"] - assert_equal arr, @network.subnet(4).map {|s| s.to_string} - arr = ["172.16.10.0/26", "172.16.10.64/26", "172.16.10.128/25"] - assert_equal arr, @network.subnet(3).map {|s| s.to_string} - arr = ["172.16.10.0/25", "172.16.10.128/25"] - assert_equal arr, @network.subnet(2).map {|s| s.to_string} - arr = ["172.16.10.0/24"] - assert_equal arr, @network.subnet(1).map {|s| s.to_string} - end - - def test_method_supernet - assert_raise(ArgumentError) {@ip.supernet(24)} - assert_equal "0.0.0.0/0", @ip.supernet(0).to_string - assert_equal "0.0.0.0/0", @ip.supernet(-2).to_string - assert_equal "172.16.10.0/23", @ip.supernet(23).to_string - assert_equal "172.16.8.0/22", @ip.supernet(22).to_string - end + def test_method_split + assert_raise(ArgumentError) {@ip.split(0)} + assert_raise(ArgumentError) {@ip.split(257)} + + assert_equal @ip.network, @ip.split(1).first + + arr = ["172.16.10.0/27", "172.16.10.32/27", "172.16.10.64/27", + "172.16.10.96/27", "172.16.10.128/27", "172.16.10.160/27", + "172.16.10.192/27", "172.16.10.224/27"] + assert_equal arr, @network.split(8).map {|s| s.to_string} + arr = ["172.16.10.0/27", "172.16.10.32/27", "172.16.10.64/27", + "172.16.10.96/27", "172.16.10.128/27", "172.16.10.160/27", + "172.16.10.192/26"] + assert_equal arr, @network.split(7).map {|s| s.to_string} + arr = ["172.16.10.0/27", "172.16.10.32/27", "172.16.10.64/27", + "172.16.10.96/27", "172.16.10.128/26", "172.16.10.192/26"] + assert_equal arr, @network.split(6).map {|s| s.to_string} + arr = ["172.16.10.0/27", "172.16.10.32/27", "172.16.10.64/27", + "172.16.10.96/27", "172.16.10.128/25"] + assert_equal arr, @network.split(5).map {|s| s.to_string} + arr = ["172.16.10.0/26", "172.16.10.64/26", "172.16.10.128/26", + "172.16.10.192/26"] + assert_equal arr, @network.split(4).map {|s| s.to_string} + arr = ["172.16.10.0/26", "172.16.10.64/26", "172.16.10.128/25"] + assert_equal arr, @network.split(3).map {|s| s.to_string} + arr = ["172.16.10.0/25", "172.16.10.128/25"] + assert_equal arr, @network.split(2).map {|s| s.to_string} + arr = ["172.16.10.0/24"] + assert_equal arr, @network.split(1).map {|s| s.to_string} + end + + def test_method_subnet + assert_raise(ArgumentError) {@network.subnet(23)} + assert_raise(ArgumentError) {@network.subnet(33)} + assert_nothing_raised {@ip.subnet(30)} + arr = ["172.16.10.0/26", "172.16.10.64/26", "172.16.10.128/26", + "172.16.10.192/26"] + assert_equal arr, @network.subnet(26).map {|s| s.to_string} + arr = ["172.16.10.0/25", "172.16.10.128/25"] + assert_equal arr, @network.subnet(25).map {|s| s.to_string} + arr = ["172.16.10.0/24"] + assert_equal arr, @network.subnet(24).map {|s| s.to_string} + end + + def test_method_supernet + assert_raise(ArgumentError) {@ip.supernet(24)} + assert_equal "0.0.0.0/0", @ip.supernet(0).to_string + assert_equal "0.0.0.0/0", @ip.supernet(-2).to_string + assert_equal "172.16.10.0/23", @ip.supernet(23).to_string + assert_equal "172.16.8.0/22", @ip.supernet(22).to_string + end def test_classmethod_parse_u32 @decimal_values.each do |addr,int| |