summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.rdoc66
-rw-r--r--lib/ipaddress/extensions/extensions.rb12
-rw-r--r--lib/ipaddress/ipv4.rb48
-rw-r--r--test/ipaddress/ipv4_test.rb26
4 files changed, 110 insertions, 42 deletions
diff --git a/README.rdoc b/README.rdoc
index 875fa6d..564c7f1 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -81,9 +81,9 @@ If you don't specify a prefix (or a subnet mask), then the library
will create an object base on the CLASSFUL network from the given
IP. Remember the CLASSFUL network are the following (RFC 791)
- 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, from 192.0.0.0 to 255.255.255.255
+* 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, from 192.0.0.0 to 255.255.255.255
Since classful networks here are only considered to calculate the default
prefix number, classes D and E are not considered.
@@ -168,8 +168,8 @@ included in a range.
When you specify an IPv4 address such as "172.16.10.1/24", you are
actually handling two different information:
- * The IP address itself, "172.16.10.1"
- * The subnet mask which indicates the network
+* The IP address itself, "172.16.10.1"
+* The subnet mask which indicates the network
The network number is the IP which has all zeroes in the host
portion. In our example, because the prefix is 24, we identify our
@@ -309,17 +309,51 @@ IPAddress includes a lot of useful methods to manipulate IPv4 and IPv6
networks and do some basic network design.
=== Subnetting
- # Subnetting a network
- #
- # If the IP Address is a network, it can be divided into
- # multiple networks. If +self+ is not a network, the
- # method will calculate the network from the IP and then
- # subnet it.
- #
- # If +subnets+ is an even number, the resulting networks will be
- # divided evenly from the supernet.
- #
- # network = IPAddress("172.16.10.0/24")
+
+The process of subnetting is the division of a network into smaller
+(in terms of hosts capacity) networks, called subnets, so that they
+all share a common root, which is the starting network.
+
+For example, if the have network "172.16.10.0/24", we can subnet it
+into 4 smaller subnets. The new prefix will be /26, because 4 is 2^2
+and therefore we add 2 bits to the network prefix (24+2=26).
+
+Subnetting is easy with IPAddress. Let's work out the last example:
+
+ network = IPAddress("172.16.10.0/24")
+
+ subnets = network / 4
+ #=> [#<IPAddress::IPv4:0xb7b10e10 @octets=[172,16,10,0] [...]
+ #<IPAddress::IPv4:0xb7b0f1b4 @octets=[172,16,10,64] [...]
+ #<IPAddress::IPv4:0xb7b0e5ac @octets=[172,16,10,128] [...]
+ #<IPAddress::IPv4:0xb7b0e0c0 @octets=[172,16,10,192] [...]]
+
+ subnets.map{|i| i.to_s}
+ #=> ["172.16.10.0/26", "172.16.10.64/26", "172.16.10.128/26",
+ "172.16.10.192/26"]
+
+You can also use method IPv4#subnets, which is an alias for
+IPv4#/. Please note that you don't have to specify a network to
+calculate a subnet: if the IPv4 object is a host IPv4, the method will
+calculate the network number for that network and then subnet it. For
+example:
+
+ ip = IPAddress("172.16.10.58/24")
+
+ ip.subnet(4).map{|i| i.to_s}
+ #=> ["172.16.10.0/26", "172.16.10.64/26", "172.16.10.128/26",
+ "172.16.10.192/26"]
+
+Usually, subnetting implies dividing a network to a number of subnets
+which is a power of two: in this way, you can be sure that the network
+will be divived evenly, and all the subnets will have the same number
+of hosts.
+IPAddress also handles un-even subnetting: if you specify any number
+(up to the prefix limit), the network will be divided so that the
+first power-of-two networks will be even, and all the rest will just
+fill out the space.
+
+
# network / 4 # implies map{|i| i.to_s}
# #=> ["172.16.10.0/26",
# "172.16.10.64/26",
diff --git a/lib/ipaddress/extensions/extensions.rb b/lib/ipaddress/extensions/extensions.rb
index ca73f9a..3090244 100644
--- a/lib/ipaddress/extensions/extensions.rb
+++ b/lib/ipaddress/extensions/extensions.rb
@@ -2,3 +2,15 @@ class << Math
def log2(n); log(n) / log(2); end
end
+class Integer
+ def power_of_2?
+ Math::log2(self).to_i == Math::log2(self)
+ end
+
+ def closest_power_of_2
+ self.upto(32) do |i|
+ return i if i.power_of_2?
+ end
+ end
+end
+
diff --git a/lib/ipaddress/ipv4.rb b/lib/ipaddress/ipv4.rb
index 823d216..7c5bc0a 100644
--- a/lib/ipaddress/ipv4.rb
+++ b/lib/ipaddress/ipv4.rb
@@ -519,8 +519,8 @@ module IPAddress;
# method will calculate the network from the IP and then
# subnet it.
#
- # If +subnets+ is an even number, the resulting networks will be
- # divided evenly from the supernet.
+ # If +subnets+ is an power of two number, the resulting
+ # networks will be divided evenly from the supernet.
#
# network = IPAddress("172.16.10.0/24")
# network / 4 # implies map{|i| i.to_s}
@@ -529,9 +529,9 @@ module IPAddress;
# "172.16.10.128/26",
# "172.16.10.192/26"]
#
- # If +num+ is a odd number, the supernet will be
- # divided into num-1 networks with a even number of hosts and
- # a last network with the remaining addresses.
+ # If +num+ is any other number, the supernet will be
+ # divided into some networks with a even number of hosts and
+ # other networks with the remaining addresses.
#
# network = IPAddress("172.16.10.0/24")
# network / 3 # implies map{|i| i.to_s}
@@ -539,18 +539,14 @@ module IPAddress;
# "172.16.10.64/26",
# "172.16.10.128/25"]
#
- # Returns an array of IPAddress objects,
+ # Returns an array of IPAddress objects
#
def subnet(subnets=2)
unless (1..(2**(32-prefix.to_i))).include? subnets
raise ArgumentError, "Value #{subnets} out of range"
end
-
- if subnets.even?
- return subnet_even(subnets)
- else
- return subnet_odd(subnets)
- end
+
+ calculate_subnets(subnets)
end
alias_method :/, :subnet
@@ -820,23 +816,33 @@ module IPAddress;
bits = bits_from_address(ip)
CLASSFUL.each {|reg,prefix| return prefix if bits =~ reg}
end
-
- def subnet_even(subnets)
- new_prefix = @prefix.to_i + Math::log2(subnets).ceil
+
+ def calculate_subnets(subnets)
+ po2 = subnets.closest_power_of_2
+ new_prefix = @prefix.to_i + Math::log2(po2).to_i
networks = Array.new
- (0..subnets-1).each do |i|
+ (0..po2-1).each do |i|
mul = i * (2**(32-new_prefix))
networks << IPAddress::IPv4.parse_u32(network_u32+mul, new_prefix)
end
+ until networks.size == subnets
+ networks = sum_first_found(networks)
+ end
return networks
end
- def subnet_odd(subnets)
- networks = subnet_even(subnets+1)
- networks[-2..-1] = IPAddress::IPv4.summarize(networks[-2],networks[-1])
- return networks
+ 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
+ if a.size == 1
+ dup[i..i+1] = a
+ return dup.reverse
+ end
+ end
+ return dup.reverse
end
-
+
end # class IPv4
end # module IPAddress
diff --git a/test/ipaddress/ipv4_test.rb b/test/ipaddress/ipv4_test.rb
index d84859a..d734921 100644
--- a/test/ipaddress/ipv4_test.rb
+++ b/test/ipaddress/ipv4_test.rb
@@ -301,14 +301,30 @@ class IPv4Test < Test::Unit::TestCase
def test_method_subnet
assert_raise(ArgumentError) {@ip.subnet(0)}
assert_raise(ArgumentError) {@ip.subnet(257)}
-
- # Even subnets
- arr = ["172.16.10.0/26", "172.16.10.64/26", "172.16.10.128/26", "172.16.10.192/26"]
+
+ 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_s}
+ 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_s}
+ 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_s}
+ 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_s}
+ 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_s}
-
- # Odd subnets
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_s}
+ arr = ["172.16.10.0/25", "172.16.10.128/25"]
+ assert_equal arr, @network.subnet(2).map {|s| s.to_s}
+ arr = ["172.16.10.0/24"]
+ assert_equal arr, @network.subnet(1).map {|s| s.to_s}
end
def test_method_supernet