summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbluemonk <ceresa@gmail.com>2011-05-15 12:04:20 +0200
committerbluemonk <ceresa@gmail.com>2011-05-15 12:04:20 +0200
commit8599c8736b9902e8e6cd7d53455d8e29815d8187 (patch)
tree16c8cab8dfb5fab8aaa82e9a963c1ddcc7d2eb8d
parentbda1e2bb645cf6dcba6fc65f9c9ef12d8003505e (diff)
downloadipaddress-8599c8736b9902e8e6cd7d53455d8e29815d8187.tar.gz
Added IPv4#split and reworked IPv4#subnet as per RFC3531 (closes #14)
-rw-r--r--CHANGELOG.rdoc2
-rw-r--r--lib/ipaddress.rb3
-rw-r--r--lib/ipaddress/ipv4.rb73
-rw-r--r--lib/ipaddress/prefix.rb2
-rw-r--r--test/ipaddress/ipv4_test.rb87
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|