summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarco Ceresa <ceresa@gmail.com>2010-01-12 16:01:01 +0000
committerMarco Ceresa <ceresa@gmail.com>2010-01-12 16:01:01 +0000
commita6c67904f9daf29142f3e6f989445edd1390f579 (patch)
tree546f0b5f42b6bca06476476fe209bc98bb05dd1c
parentb792124f5d80e8dfbaa6574dda1d5636820674b6 (diff)
downloadipaddress-a6c67904f9daf29142f3e6f989445edd1390f579.tar.gz
Subclassed Prefix with Prefix32; changed identation on ipv4.rb
-rw-r--r--lib/ipaddress/ipbase.rb4
-rw-r--r--lib/ipaddress/ipv4.rb1340
-rw-r--r--lib/ipaddress/prefix.rb65
-rw-r--r--test/ipaddress/ipv4_test.rb15
-rw-r--r--test/ipaddress/prefix_test.rb2
5 files changed, 728 insertions, 698 deletions
diff --git a/lib/ipaddress/ipbase.rb b/lib/ipaddress/ipbase.rb
index eb0aa93..20ece37 100644
--- a/lib/ipaddress/ipbase.rb
+++ b/lib/ipaddress/ipbase.rb
@@ -34,7 +34,9 @@ module IPAddress
#
def self.valid_ipv4_netmask?(addr)
arr = addr.split(".").map{|i| i.to_i}.pack("CCCC").unpack("B*").first.scan(/01/)
- valid_ipv4?(addr) && arr.empty?
+ arr.empty? && valid_ipv4?(addr)
+ rescue
+ return false
end
diff --git a/lib/ipaddress/ipv4.rb b/lib/ipaddress/ipv4.rb
index 558aa84..2ca0d49 100644
--- a/lib/ipaddress/ipv4.rb
+++ b/lib/ipaddress/ipv4.rb
@@ -15,718 +15,710 @@ require 'ipaddress/prefix'
# This library provides a complete
#
#
-module IPAddress; class IPv4 < IPBase
-
- include IPAddress
- include Enumerable
- include Comparable
-
- #
- # This Hash contains the prefix values for Classful networks
- #
- # Note that classes C, D and E will all have a default
- # prefix of /24 or 255.255.255.0
- #
- CLASSFUL = {
- /^0../ => 8, # Class A, from 0.0.0.0 to 127.255.255.255
- /^10./ => 16, # Class B, from 128.0.0.0 to 191.255.255.255
- /^110/ => 24 # Class C, D and E, from 192.0.0.0 to 255.255.255.254
- }
-
-
- #
- # Creates a new IPv4 address object.
- #
- # An IPv4 address can be expressed in any of the following forms:
- #
- # * "10.1.1.1/24": ip address and prefix. This is the common and
- # suggested way to create an object .
- # * "10.1.1.1/255.255.255.0": ip address and netmask. Although
- # convenient sometimes, this format is less clear than the previous
- # one.
- # * "10.1.1.1": if the address alone is specified, the prefix will be
- # assigned using the classful boundaries. In this case, the
- # prefix would be /8, a 255.0.0.0 netmask
- #
- # It is advisable to use the syntactic shortcut provided with the
- # IPAddress() method, as in all the examples below.
- #
- # Examples:
- #
- # # These two methods return the same object
- # ip = IPAddress::IPv4.new("10.0.0.1/24")
- # ip = IPAddress("10.0.0.1/24")
- #
- # # These three are the same
- # IPAddress("10.0.0.1/8")
- # IPAddress("10.0.0.1/255.0.0.0")
- # IPAddress("10.0.0.1")
- # #=> #<IPAddress::IPv4:0xb7b1a438
- # @octets=[10, 0, 0, 1], @address="10.0.0.1", @prefix=8>
- #
- def initialize(str)
- ip, netmask = str.split("/")
+module IPAddress;
+ class IPv4 < IPBase
- # Check the ip and remove white space
- if IPAddress.valid_ipv4?(ip)
- @address = ip.strip
- else
- raise ArgumentError, "Invalid IP #{ip.inspect}"
- end
+ include IPAddress
+ include Enumerable
+ include Comparable
- # Check the netmask
- if netmask # netmask is defined
- netmask.strip!
- if netmask =~ /^\d{1,2}$/ # netmask in cidr format
- @prefix = Prefix.new(netmask.to_i)
- elsif IPAddress.valid_ipv4_netmask?(netmask) # netmask in IP format
- @prefix = Prefix.parse_netmask(netmask)
- else # invalid netmask
- raise ArgumentError, "Invalid netmask #{netmask}"
- end
- else # netmask is nil, reverting to defaul classful mask
- @prefix = prefix_from_ip(@address)
- end
+ #
+ # This Hash contains the prefix values for Classful networks
+ #
+ # Note that classes C, D and E will all have a default
+ # prefix of /24 or 255.255.255.0
+ #
+ CLASSFUL = {
+ /^0../ => 8, # Class A, from 0.0.0.0 to 127.255.255.255
+ /^10./ => 16, # Class B, from 128.0.0.0 to 191.255.255.255
+ /^110/ => 24 # Class C, D and E, from 192.0.0.0 to 255.255.255.254
+ }
- # Array formed with the IP octets
- @octets = @address.split(".").map{|i| i.to_i}
-
- end # def initialize
+
+ #
+ # Creates a new IPv4 address object.
+ #
+ # An IPv4 address can be expressed in any of the following forms:
+ #
+ # * "10.1.1.1/24": ip address and prefix. This is the common and
+ # suggested way to create an object .
+ # * "10.1.1.1/255.255.255.0": ip address and netmask. Although
+ # convenient sometimes, this format is less clear than the previous
+ # one.
+ # * "10.1.1.1": if the address alone is specified, the prefix will be
+ # assigned using the classful boundaries. In this case, the
+ # prefix would be /8, a 255.0.0.0 netmask
+ #
+ # It is advisable to use the syntactic shortcut provided with the
+ # IPAddress() method, as in all the examples below.
+ #
+ # Examples:
+ #
+ # # These two methods return the same object
+ # ip = IPAddress::IPv4.new("10.0.0.1/24")
+ # ip = IPAddress("10.0.0.1/24")
+ #
+ # # These three are the same
+ # IPAddress("10.0.0.1/8")
+ # IPAddress("10.0.0.1/255.0.0.0")
+ # IPAddress("10.0.0.1")
+ # #=> #<IPAddress::IPv4:0xb7b1a438
+ # @octets=[10, 0, 0, 1], @address="10.0.0.1", @prefix=8>
+ #
+ def initialize(str)
+ ip, netmask = str.split("/")
+
+ # Check the ip and remove white space
+ if IPAddress.valid_ipv4?(ip)
+ @address = ip.strip
+ else
+ raise ArgumentError, "Invalid IP #{ip.inspect}"
+ end
+
+ # Check the netmask
+ if netmask # netmask is defined
+ netmask.strip!
+ if netmask =~ /^\d{1,2}$/ # netmask in cidr format
+ @prefix = Prefix32.new(netmask.to_i)
+ elsif IPAddress.valid_ipv4_netmask?(netmask) # netmask in IP format
+ @prefix = Prefix32.parse_netmask(netmask)
+ else # invalid netmask
+ raise ArgumentError, "Invalid netmask #{netmask}"
+ end
+ else # netmask is nil, reverting to defaul classful mask
+ @prefix = prefix_from_ip(@address)
+ end
- #
- # Returns the address portion of the IPv4 object
- # as a string.
- #
- # ip = IPAddress("172.16.100.4/22")
- # ip.address
- # #=> "172.16.100.4"
- #
- def address
- @address
- end
+ # Array formed with the IP octets
+ @octets = @address.split(".").map{|i| i.to_i}
+
+ end # def initialize
+
+ #
+ # Returns the address portion of the IPv4 object
+ # as a string.
+ #
+ # ip = IPAddress("172.16.100.4/22")
+ # ip.address
+ # #=> "172.16.100.4"
+ #
+ def address
+ @address
+ end
- #
- # Returns the prefix portion of the IPv4 object
- # as a IPAddress::Prefix object
- #
- # ip = IPAddress("172.16.100.4/22")
- # ip.prefix
- # #=> 22
- # ip.prefix.class
- # #=> IPAddress::Prefix
- #
- def prefix
- @prefix
- end
+ #
+ # Returns the prefix portion of the IPv4 object
+ # as a IPAddress::Prefix32 object
+ #
+ # ip = IPAddress("172.16.100.4/22")
+ # ip.prefix
+ # #=> 22
+ # ip.prefix.class
+ # #=> IPAddress::Prefix32
+ #
+ def prefix
+ @prefix
+ end
- #
- # Set a new prefix number for the object
- #
- # This is useful if you want to change the prefix
- # to an object created with IPv4::parse_u32 or
- # if the object was created using the classful
- # mask.
- #
- # ip = IPAddress("172.16.100.4")
- # puts ip
- # #=> 172.16.100.4/16
- #
- # ip.prefix = 22
- # puts ip
- # #=> 172.16.100.4/22
- #
- def prefix=(num)
- @prefix = Prefix.new(num)
- end
+ #
+ # Set a new prefix number for the object
+ #
+ # This is useful if you want to change the prefix
+ # to an object created with IPv4::parse_u32 or
+ # if the object was created using the classful
+ # mask.
+ #
+ # ip = IPAddress("172.16.100.4")
+ # puts ip
+ # #=> 172.16.100.4/16
+ #
+ # ip.prefix = 22
+ # puts ip
+ # #=> 172.16.100.4/22
+ #
+ def prefix=(num)
+ @prefix = Prefix32.new(num)
+ end
- #
- # Returns the address as an array of decimal values
- #
- # ip = IPAddress("172.16.100.4")
- # ip.octets
- # #=> [172, 16, 100, 4]
- #
- def octets
- @octets
- end
-
- #
- # Returns a string with the IP address in canonical
- # form.
- #
- # ip = IPAddress("172.16.100.4/22")
- # ip.to_s
- # #=> "172.16.100.4/22"
- #
- def to_s
- "#@address/#@prefix"
- end
+ #
+ # Returns the address as an array of decimal values
+ #
+ # ip = IPAddress("172.16.100.4")
+ # ip.octets
+ # #=> [172, 16, 100, 4]
+ #
+ def octets
+ @octets
+ end
+
+ #
+ # Returns a string with the IP address in canonical
+ # form.
+ #
+ # ip = IPAddress("172.16.100.4/22")
+ # ip.to_s
+ # #=> "172.16.100.4/22"
+ #
+ def to_s
+ "#@address/#@prefix"
+ end
- #
- # Returns the prefix as a string in IP format
- #
- # ip = IPAddress("172.16.100.4/22")
- # ip.netmask
- # #=> "255.255.252.0"
- #
- def netmask
- @prefix.to_ip
- end
+ #
+ # Returns the prefix as a string in IP format
+ #
+ # ip = IPAddress("172.16.100.4/22")
+ # ip.netmask
+ # #=> "255.255.252.0"
+ #
+ def netmask
+ @prefix.to_ip
+ end
- #
- # Like IPv4#prefix=, this method allow you to
- # change the prefix / netmask of an IP address
- # object.
- #
- # ip = IPAddress("172.16.100.4")
- # puts ip
- # #=> 172.16.100.4/16
- #
- # ip.netmask = "255.255.252.0"
- # puts ip
- # #=> 172.16.100.4/22
- #
- def netmask=(addr)
- @prefix = Prefix.parse_netmask(addr)
- end
+ #
+ # Like IPv4#prefix=, this method allow you to
+ # change the prefix / netmask of an IP address
+ # object.
+ #
+ # ip = IPAddress("172.16.100.4")
+ # puts ip
+ # #=> 172.16.100.4/16
+ #
+ # ip.netmask = "255.255.252.0"
+ # puts ip
+ # #=> 172.16.100.4/22
+ #
+ def netmask=(addr)
+ @prefix = Prefix32.parse_netmask(addr)
+ end
- #
- # Returns the address portion in unsigned
- # 32 bits integer format.
- #
- # This method is identical to the C function
- # inet_pton to create a 32 bits address family
- # structure.
- #
- # ip = IPAddress("10.0.0.0/8")
- # ip.to_u32
- # #=> 167772160
- #
- # It is usually used to include an IP address
- # in a data packet to be sent over a socket
- #
- # a = Socket.open(params) # socket details here
- # ip = IPAddress("10.1.1.0/24")
- # binary_data = ["Address: "].pack("a*") + ip.to_u32
- #
- # # Send binary data
- # a.puts binary_data
- #
- def to_u32
- @octets.pack("CCCC").unpack("N").first
- end
+ #
+ # Returns the address portion in unsigned
+ # 32 bits integer format.
+ #
+ # This method is identical to the C function
+ # inet_pton to create a 32 bits address family
+ # structure.
+ #
+ # ip = IPAddress("10.0.0.0/8")
+ # ip.to_u32
+ # #=> 167772160
+ #
+ # It is usually used to include an IP address
+ # in a data packet to be sent over a socket
+ #
+ # a = Socket.open(params) # socket details here
+ # ip = IPAddress("10.1.1.0/24")
+ # binary_data = ["Address: "].pack("a*") + ip.to_u32
+ #
+ # # Send binary data
+ # a.puts binary_data
+ #
+ def to_u32
+ @octets.pack("CCCC").unpack("N").first
+ end
- #
- # Returns the octet specified by index
- #
- # ip = IPAddress("172.16.100.50/24")
- # ip[0]
- # #=> 172
- # ip[1]
- # #=> 16
- # ip[2]
- # #=> 100
- # ip[3]
- # #=> 50
- #
- def [](index)
- @octets[index]
- end
- alias_method :octet, :[]
-
- #
- # Returns the address portion of an IP in binary format,
- # as a string containing a sequence of 0 and 1
- #
- # ip = IPAddress("127.0.0.1")
- # ip.bits
- # #=> "01111111000000000000000000000001"
- #
- def bits
- @octets.pack("CCCC").unpack("B*").first
- end
+ #
+ # Returns the octet specified by index
+ #
+ # ip = IPAddress("172.16.100.50/24")
+ # ip[0]
+ # #=> 172
+ # ip[1]
+ # #=> 16
+ # ip[2]
+ # #=> 100
+ # ip[3]
+ # #=> 50
+ #
+ def [](index)
+ @octets[index]
+ end
+ alias_method :octet, :[]
+
+ #
+ # Returns the address portion of an IP in binary format,
+ # as a string containing a sequence of 0 and 1
+ #
+ # ip = IPAddress("127.0.0.1")
+ # ip.bits
+ # #=> "01111111000000000000000000000001"
+ #
+ def bits
+ @octets.pack("CCCC").unpack("B*").first
+ end
- #
- # Returns the broadcast address for the given IP.
- #
- # ip = IPAddress("172.16.10.64/24")
- # ip.broadcast.to_s
- # #=> "172.16.10.255/24"
- #
- def broadcast
- self.class.parse_u32(broadcast_u32, @prefix)
- end
-
- #
- # Check if the IP address is actually a network
- #
- # ip = IPAddress("172.16.10.64/24")
- # ip.network?
- # #=> false
- #
- # ip = IPAddress("172.16.10.64/26")
- # ip.network?
- # #=> true
- #
- def network?
- to_u32 | @prefix.to_u32 == @prefix.to_u32
- end
+ #
+ # Returns the broadcast address for the given IP.
+ #
+ # ip = IPAddress("172.16.10.64/24")
+ # ip.broadcast.to_s
+ # #=> "172.16.10.255/24"
+ #
+ def broadcast
+ self.class.parse_u32(broadcast_u32, @prefix)
+ end
+
+ #
+ # Check if the IP address is actually a network
+ #
+ # ip = IPAddress("172.16.10.64/24")
+ # ip.network?
+ # #=> false
+ #
+ # ip = IPAddress("172.16.10.64/26")
+ # ip.network?
+ # #=> true
+ #
+ def network?
+ to_u32 | @prefix.to_u32 == @prefix.to_u32
+ end
- #
- # Returns a new IPv4 object with the network number
- # for the given IP.
- #
- # ip = IPAddress("172.16.10.64/24")
- # ip.network.to_s
- # #=> "172.16.10.0/24"
- #
- def network
- self.class.parse_u32(network_u32, @prefix)
- end
+ #
+ # Returns a new IPv4 object with the network number
+ # for the given IP.
+ #
+ # ip = IPAddress("172.16.10.64/24")
+ # ip.network.to_s
+ # #=> "172.16.10.0/24"
+ #
+ def network
+ self.class.parse_u32(network_u32, @prefix)
+ end
- #
- # Returns a new IPv4 object with the
- # first host IP address in the range.
- #
- # Example: given the 192.168.100.0/24 network, the first
- # host IP address is 192.168.100.1.
- #
- # ip = IPAddress("192.168.100.0/24")
- # ip.first.to_s
- # #=> "192.168.100.1/24"
- #
- # The object IP doesn't need to be a network: the method
- # automatically gets the network number from it
- #
- # ip = IPAddress("192.168.100.50/24")
- # ip.first.to_s
- # #=> "192.168.100.1/24"
- #
- def first
- self.class.parse_u32(network_u32+1, @prefix)
- end
+ #
+ # Returns a new IPv4 object with the
+ # first host IP address in the range.
+ #
+ # Example: given the 192.168.100.0/24 network, the first
+ # host IP address is 192.168.100.1.
+ #
+ # ip = IPAddress("192.168.100.0/24")
+ # ip.first.to_s
+ # #=> "192.168.100.1/24"
+ #
+ # The object IP doesn't need to be a network: the method
+ # automatically gets the network number from it
+ #
+ # ip = IPAddress("192.168.100.50/24")
+ # ip.first.to_s
+ # #=> "192.168.100.1/24"
+ #
+ def first
+ self.class.parse_u32(network_u32+1, @prefix)
+ end
- #
- # Like its sibling method IPv4#first, this method
- # returns a new IPv4 object with the
- # last host IP address in the range.
- #
- # Example: given the 192.168.100.0/24 network, the last
- # host IP address is 192.168.100.1.
- #
- # ip = IPAddress("192.168.100.0/24")
- # ip.last.to_s
- # #=> "192.168.100.254/24"
- #
- # The object IP doesn't need to be a network: the method
- # automatically gets the network number from it
- #
- # ip = IPAddress("192.168.100.50/24")
- # ip.last.to_s
- # #=> "192.168.100.254/24"
- #
- def last
- self.class.parse_u32(broadcast_u32-1, @prefix)
- end
+ #
+ # Like its sibling method IPv4#first, this method
+ # returns a new IPv4 object with the
+ # last host IP address in the range.
+ #
+ # Example: given the 192.168.100.0/24 network, the last
+ # host IP address is 192.168.100.1.
+ #
+ # ip = IPAddress("192.168.100.0/24")
+ # ip.last.to_s
+ # #=> "192.168.100.254/24"
+ #
+ # The object IP doesn't need to be a network: the method
+ # automatically gets the network number from it
+ #
+ # ip = IPAddress("192.168.100.50/24")
+ # ip.last.to_s
+ # #=> "192.168.100.254/24"
+ #
+ def last
+ self.class.parse_u32(broadcast_u32-1, @prefix)
+ end
- #
- # Iterates over all the hosts IP addresses for the given
- # network (or IP address).
- #
- # ip = IPaddress("10.0.0.1/29")
- # ip.each do |i|
- # p i
- # end
- # #=> "10.0.0.1"
- # #=> "10.0.0.2"
- # #=> "10.0.0.3"
- # #=> "10.0.0.4"
- # #=> "10.0.0.5"
- # #=> "10.0.0.6"
- #
- def each_host
- hosts.each do |i|
- yield i
- end
- end
+ #
+ # Iterates over all the hosts IP addresses for the given
+ # network (or IP address).
+ #
+ # ip = IPaddress("10.0.0.1/29")
+ # ip.each do |i|
+ # p i
+ # end
+ # #=> "10.0.0.1"
+ # #=> "10.0.0.2"
+ # #=> "10.0.0.3"
+ # #=> "10.0.0.4"
+ # #=> "10.0.0.5"
+ # #=> "10.0.0.6"
+ #
+ def each_host
+ hosts.each do |i|
+ yield i
+ end
+ end
- #
- # Iterates over all the IP addresses for the given
- # network (or IP address).
- #
- # The object yielded is a new IPv4 object created
- # from the iteration.
- #
- # ip = IPaddress("10.0.0.1/29")
- # ip.each do |i|
- # p i.address
- # end
- # #=> "10.0.0.0"
- # #=> "10.0.0.1"
- # #=> "10.0.0.2"
- # #=> "10.0.0.3"
- # #=> "10.0.0.4"
- # #=> "10.0.0.5"
- # #=> "10.0.0.6"
- # #=> "10.0.0.7"
- #
- def each
- (network_u32..broadcast_u32).each do |i|
- yield self.class.parse_u32(i, @prefix)
- end
- end
+ #
+ # Iterates over all the IP addresses for the given
+ # network (or IP address).
+ #
+ # The object yielded is a new IPv4 object created
+ # from the iteration.
+ #
+ # ip = IPaddress("10.0.0.1/29")
+ # ip.each do |i|
+ # p i.address
+ # end
+ # #=> "10.0.0.0"
+ # #=> "10.0.0.1"
+ # #=> "10.0.0.2"
+ # #=> "10.0.0.3"
+ # #=> "10.0.0.4"
+ # #=> "10.0.0.5"
+ # #=> "10.0.0.6"
+ # #=> "10.0.0.7"
+ #
+ def each
+ (network_u32..broadcast_u32).each do |i|
+ yield self.class.parse_u32(i, @prefix)
+ end
+ end
- #
- # Spaceship operator to compare IP addresses
- #
- # An IP address is considered to be minor if it
- # has a greater prefix (thus smaller hosts
- # portion) and a smaller u32 value.
- #
- # 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".
- #
- # 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
- # #=> false
- #
- def <=>(oth)
- if to_u32 > oth.to_u32
- return 1
- elsif to_u32 < oth.to_u32
- return -1
- else
- if prefix < oth.prefix
+ #
+ # Spaceship operator to compare IP addresses
+ #
+ # An IP address is considered to be minor if it
+ # has a greater prefix (thus smaller hosts
+ # portion) and a smaller u32 value.
+ #
+ # 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".
+ #
+ # 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
+ # #=> false
+ #
+ def <=>(oth)
+ if to_u32 > oth.to_u32
return 1
- elsif prefix > oth.prefix
+ 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
+ end
+
+ #
+ # Returns the number of IP addresses included
+ # in the network. It also counts the network
+ # address and the broadcast address.
+ #
+ # ip = IPaddress("10.0.0.1/29")
+ # ip.size
+ # #=> 8
+ #
+ def size
+ broadcast_u32 - network_u32 + 1
end
- return 0
- end
-
- #
- # Returns the number of IP addresses included
- # in the network. It also counts the network
- # number and the broadcast address.
- #
- # ip = IPaddress("10.0.0.1/29")
- # ip.size
- # #=> 8
- #
- def size
- to_a.size
- end
-
- #
- # Returns an array with the IP addresses of
- # all the hosts in the network.
- #
- # ip = IPaddress("10.0.0.1/29")
- # ip.hosts.map {|i| i.address}
- # #=> ["10.0.0.1",
- # #=> "10.0.0.2",
- # #=> "10.0.0.3",
- # #=> "10.0.0.4",
- # #=> "10.0.0.5",
- # #=> "10.0.0.6"]
- #
- def hosts
- to_a[1..-2]
- end
-
- #
- # Returns the network number in Unsigned 32bits format
- #
- # ip = IPaddress("10.0.0.1/29")
- # ip.network_u32
- # #=> 167772160
- #
- def network_u32
- to_u32 & @prefix.to_u32
- end
-
- #
- # Returns the broadcast address in Unsigned 32bits format
- #
- # ip = IPaddress("10.0.0.1/29")
- # ip.broadcast_u32
- # #=> 167772167
- #
- def broadcast_u32
- [to_u32 | ~@prefix.to_u32].pack("N").unpack("N").first
- end
-
- #
- # Checks whether a subnet includes the given IP address.
- #
- # Accepts either string with the IP or and IPAddress::IPv4
- # object.
- #
- # ip = IPAddress("192.168.10.100/24")
- #
- # addr = IPAddress("192.168.10.102/24")
- # ip.include? addr
- # #=> true
- #
- # ip.include? IPAddress("172.16.0.48/16")
- # #=> false
- #
- def include?(oth)
- @prefix <= oth.prefix and network_u32 == self.class.new(oth.address+"/#@prefix").network_u32
- # to_a.map{|i| i.address}.include?(oth.address) and @prefix <= oth.prefix
- end
- #
- # Returns the IP address in in-addr.arpa format
- # for DNS lookups
- #
- # ip = IPAddress("172.16.100.50/24")
- # ip.reverse
- # #=> "50.100.16.172.in-addr.arpa"
- #
- def reverse
- @octets.reverse.join(".") + ".in-addr.arpa"
- end
-
- #
- # 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 +num+ is an even number, the resulting networks will be
- # divided evenly from the supernet.
- #
- # network = IPAddress("172.16.10.0/24")
- # network / 4
- # #=> ["172.16.10.0/26",
- # "172.16.10.64/26",
- # "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.
- #
- # network = IPAddress("172.16.10.0/24")
- # network / 3
- # #=> ["172.16.10.0/26",
- # "172.16.10.64/26",
- # "172.16.10.128/25"]
- #
- # 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"
+ #
+ # Returns an array with the IP addresses of
+ # all the hosts in the network.
+ #
+ # ip = IPaddress("10.0.0.1/29")
+ # ip.hosts.map {|i| i.address}
+ # #=> ["10.0.0.1",
+ # #=> "10.0.0.2",
+ # #=> "10.0.0.3",
+ # #=> "10.0.0.4",
+ # #=> "10.0.0.5",
+ # #=> "10.0.0.6"]
+ #
+ def hosts
+ to_a[1..-2]
end
- if subnets.even?
- return subnet_even(subnets)
- else
- return subnet_odd(subnets)
+ #
+ # Returns the network number in Unsigned 32bits format
+ #
+ # ip = IPaddress("10.0.0.1/29")
+ # ip.network_u32
+ # #=> 167772160
+ #
+ def network_u32
+ to_u32 & @prefix.to_u32
end
- end
- alias_method :/, :subnet
-
- #
- # Returns a new IPv4 object from the supernetting
- # of the instance network.
- #
- # Supernetting is similar to subnetting, except
- # that you getting as a result a network with a
- # smaller prefix (bigger host space). For example,
- # given the network
- #
- # ip = IPAddress("172.16.10.0/24")
- #
- # you can supernet it with a new /23 prefix
- #
- # ip.supernet(23).to_s
- # #=> "172.16.10.0/23"
- #
- # However if you supernet it with a /22 prefix, the
- # network address will change:
- #
- # ip.supernet(22).to_s
- # #=> "172.16.8.0/22"
- #
- 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
- end
- #
- # Returns the difference between two IP addresses
- # in unsigned int 32 bits format
- #
- def -(oth)
- return (to_u32 - oth.to_u32).abs
- end
+ #
+ # Returns the broadcast address in Unsigned 32bits format
+ #
+ # ip = IPaddress("10.0.0.1/29")
+ # ip.broadcast_u32
+ # #=> 167772167
+ #
+ def broadcast_u32
+ [to_u32 | ~@prefix.to_u32].pack("N").unpack("N").first
+ end
- #
- # Creates a new IPv4 object from an
- # unsigned 32bits integer.
- #
- # ip = IPAddress::IPv4::parse_u32(167772160)
- # ip.prefix = 8
- # ip.to_s
- # #=> "10.0.0.0/8"
- #
- # The +prefix+ parameter is optional:
- #
- # ip = IPAddress::IPv4::parse_u32(167772160, 8)
- # ip.to_s
- # #=> "10.0.0.0/8"
- #
- def self.parse_u32(u32, prefix=nil)
- ip = [u32].pack("N").unpack("CCCC").join(".")
- if prefix
- IPAddress::IPv4.new(ip+"/#{prefix}")
- else
- IPAddress::IPv4.new(ip)
- end
- end
+ #
+ # Checks whether a subnet includes the given IP address.
+ #
+ # Accepts either string with the IP or and IPAddress::IPv4
+ # object.
+ #
+ # ip = IPAddress("192.168.10.100/24")
+ #
+ # addr = IPAddress("192.168.10.102/24")
+ # ip.include? addr
+ # #=> true
+ #
+ # ip.include? IPAddress("172.16.0.48/16")
+ # #=> false
+ #
+ def include?(oth)
+ @prefix <= oth.prefix and network_u32 == self.class.new(oth.address+"/#@prefix").network_u32
+ # to_a.map{|i| i.address}.include?(oth.address) and @prefix <= oth.prefix
+ end
- #
- # Summarization (or aggregation) is the process when two or more
- # networks are taken together to check if a supernet, including all
- # and only these networks, exists. If it exists then this supernet
- # is called the summarized (or aggregated) network.
- #
- # It is very important to understand that summarization can only
- # occur if there are no holes in the aggregated network, or, in other
- # words, if the given networks fill completely the address space
- # of the supernet. So the two rules are:
- #
- # 1) The aggregate network must contain +all+ the IP addresses of the
- # original networks;
- # 2) The aggregate network must contain +only+ the IP addresses of the
- # original networks;
- #
- # A few examples will help clarify the above. Let's consider for
- # instance the following two networks:
- #
- # ip1 = IPAddress("172.16.10.0/24")
- # ip2 = IPAddress("172.16.11.0/24")
- #
- # These two networks can be expressed using only one IP address
- # network if we change the prefix. Let Ruby do the work:
- #
- # IPAddress::IPv4::summarize(ip1,ip2).to_s
- # #=> "172.16.10.0/23"
- #
- # We note how the network "172.16.10.0/23" includes all the addresses
- # specified in the above networks, and (more important) includes
- # ONLY those addresses.
- #
- # If we summarized +ip1+ and +ip2+ with the following network:
- #
- # "172.16.0.0/16"
- #
- # we would have satisfied rule #1 above, but not rule #2. So "172.16.0.0/16"
- # is not an aggregate network for +ip1+ and +ip2+.
- #
- # If it's not possible to compute a single aggregated network for all the
- # original networks, the method returns an array with all the aggregate
- # networks found. For example, the following four networks can be
- # aggregated in a single /22:
- #
- # ip1 = IPAddress("10.0.0.1/24")
- # ip2 = IPAddress("10.0.1.1/24")
- # ip3 = IPAddress("10.0.2.1/24")
- # ip4 = IPAddress("10.0.3.1/24")
- # IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).to_s
- # #=> "10.0.0.0/22",
- #
- # But the following networks can't be summarized in a single network:
- #
- # ip1 = IPAddress("10.0.1.1/24")
- # ip2 = IPAddress("10.0.2.1/24")
- # ip3 = IPAddress("10.0.3.1/24")
- # ip4 = IPAddress("10.0.4.1/24")
- # IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).map{|i| i.to_s}
- # #=> ["10.0.1.0/24","10.0.2.0/23","10.0.4.0/24"]
- #
- def self.summarize(*args)
- # one network? no need to summarize
- return args.flatten.first if args.size == 1
-
- enum=args.sort.each_cons(2)
+ #
+ # Returns the IP address in in-addr.arpa format
+ # for DNS lookups
+ #
+ # ip = IPAddress("172.16.100.50/24")
+ # ip.reverse
+ # #=> "50.100.16.172.in-addr.arpa"
+ #
+ def reverse
+ @octets.reverse.join(".") + ".in-addr.arpa"
+ end
- result, arr, last = [], args.sort, args.sort.last.network
- arr.each_cons(2) do |x,y|
- snet = x.supernet(x.prefix.to_i-1)
- if snet.include? y
- result << snet
+ #
+ # 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 +num+ is an even number, the resulting networks will be
+ # divided evenly from the supernet.
+ #
+ # network = IPAddress("172.16.10.0/24")
+ # network / 4
+ # #=> ["172.16.10.0/26",
+ # "172.16.10.64/26",
+ # "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.
+ #
+ # network = IPAddress("172.16.10.0/24")
+ # network / 3
+ # #=> ["172.16.10.0/26",
+ # "172.16.10.64/26",
+ # "172.16.10.128/25"]
+ #
+ # 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
- result << x.network unless result.any?{|i| i.include? x}
+ return subnet_odd(subnets)
end
end
- result << last unless result.any?{|i| i.include? last}
-
- if result.size == args.size
- return result
- else
- return self.summarize(*result)
+ alias_method :/, :subnet
+
+ #
+ # Returns a new IPv4 object from the supernetting
+ # of the instance network.
+ #
+ # Supernetting is similar to subnetting, except
+ # that you getting as a result a network with a
+ # smaller prefix (bigger host space). For example,
+ # given the network
+ #
+ # ip = IPAddress("172.16.10.0/24")
+ #
+ # you can supernet it with a new /23 prefix
+ #
+ # ip.supernet(23).to_s
+ # #=> "172.16.10.0/23"
+ #
+ # However if you supernet it with a /22 prefix, the
+ # network address will change:
+ #
+ # ip.supernet(22).to_s
+ # #=> "172.16.8.0/22"
+ #
+ 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
end
- end
-
-
- #
- # private methods
- #
+ #
+ # Returns the difference between two IP addresses
+ # in unsigned int 32 bits format
+ #
+ def -(oth)
+ return (to_u32 - oth.to_u32).abs
+ end
- private
-
- def prefix_from_netmask(netmask)
- octets = netmask.split(".").map{|i| i.to_i}
- octets.pack("C"*octets.size).unpack("B*").first.count "1"
- end
+ #
+ # Creates a new IPv4 object from an
+ # unsigned 32bits integer.
+ #
+ # ip = IPAddress::IPv4::parse_u32(167772160)
+ # ip.prefix = 8
+ # ip.to_s
+ # #=> "10.0.0.0/8"
+ #
+ # The +prefix+ parameter is optional:
+ #
+ # ip = IPAddress::IPv4::parse_u32(167772160, 8)
+ # ip.to_s
+ # #=> "10.0.0.0/8"
+ #
+ def self.parse_u32(u32, prefix=nil)
+ ip = [u32].pack("N").unpack("CCCC").join(".")
+ if prefix
+ IPAddress::IPv4.new(ip+"/#{prefix}")
+ else
+ IPAddress::IPv4.new(ip)
+ end
+ end
- def netmask_from_prefix(prefix)
- bits = "1" * prefix + "0" * (32 - prefix)
- bits.pack("B*").unpack("CCCC").join(".")
- end
+ #
+ # Summarization (or aggregation) is the process when two or more
+ # networks are taken together to check if a supernet, including all
+ # and only these networks, exists. If it exists then this supernet
+ # is called the summarized (or aggregated) network.
+ #
+ # It is very important to understand that summarization can only
+ # occur if there are no holes in the aggregated network, or, in other
+ # words, if the given networks fill completely the address space
+ # of the supernet. So the two rules are:
+ #
+ # 1) The aggregate network must contain +all+ the IP addresses of the
+ # original networks;
+ # 2) The aggregate network must contain +only+ the IP addresses of the
+ # original networks;
+ #
+ # A few examples will help clarify the above. Let's consider for
+ # instance the following two networks:
+ #
+ # ip1 = IPAddress("172.16.10.0/24")
+ # ip2 = IPAddress("172.16.11.0/24")
+ #
+ # These two networks can be expressed using only one IP address
+ # network if we change the prefix. Let Ruby do the work:
+ #
+ # IPAddress::IPv4::summarize(ip1,ip2).to_s
+ # #=> "172.16.10.0/23"
+ #
+ # We note how the network "172.16.10.0/23" includes all the addresses
+ # specified in the above networks, and (more important) includes
+ # ONLY those addresses.
+ #
+ # If we summarized +ip1+ and +ip2+ with the following network:
+ #
+ # "172.16.0.0/16"
+ #
+ # we would have satisfied rule #1 above, but not rule #2. So "172.16.0.0/16"
+ # is not an aggregate network for +ip1+ and +ip2+.
+ #
+ # If it's not possible to compute a single aggregated network for all the
+ # original networks, the method returns an array with all the aggregate
+ # networks found. For example, the following four networks can be
+ # aggregated in a single /22:
+ #
+ # ip1 = IPAddress("10.0.0.1/24")
+ # ip2 = IPAddress("10.0.1.1/24")
+ # ip3 = IPAddress("10.0.2.1/24")
+ # ip4 = IPAddress("10.0.3.1/24")
+ # IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).to_s
+ # #=> "10.0.0.0/22",
+ #
+ # But the following networks can't be summarized in a single network:
+ #
+ # ip1 = IPAddress("10.0.1.1/24")
+ # ip2 = IPAddress("10.0.2.1/24")
+ # ip3 = IPAddress("10.0.3.1/24")
+ # ip4 = IPAddress("10.0.4.1/24")
+ # IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).map{|i| i.to_s}
+ # #=> ["10.0.1.0/24","10.0.2.0/23","10.0.4.0/24"]
+ #
+ def self.summarize(*args)
+ # one network? no need to summarize
+ return args.flatten.first if args.size == 1
+
+ enum=args.sort.each_cons(2)
+
+ result, arr, last = [], args.sort, args.sort.last.network
+ arr.each_cons(2) do |x,y|
+ snet = x.supernet(x.prefix.to_i-1)
+ if snet.include? y
+ result << snet
+ else
+ result << x.network unless result.any?{|i| i.include? x}
+ end
+ end
+ result << last unless result.any?{|i| i.include? last}
+
+ if result.size == args.size
+ return result
+ else
+ return self.summarize(*result)
+ end
+ end
+
+
- def bits_from_address(ip)
- ip.split(".").map{|i| i.to_i}.pack("CCCC").unpack("B*").first
- end
-
- def prefix_from_ip(ip)
- bits = bits_from_address(ip)
- CLASSFUL.each {|reg,prefix| return prefix if bits =~ reg}
- end
+ #
+ # private methods
+ #
+ private
+
+ def bits_from_address(ip)
+ ip.split(".").map{|i| i.to_i}.pack("CCCC").unpack("B*").first
+ end
+
+ def prefix_from_ip(ip)
+ 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
+ networks = Array.new
+ (0..subnets-1).each do |i|
+ mul = i * (2**(32-new_prefix))
+ networks << IPAddress::IPv4.parse_u32(network_u32+mul, new_prefix)
+ 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
+ end
+
- def subnet_even(subnets)
- new_prefix = @prefix.to_i + Math::log2(subnets).ceil
- networks = Array.new
- (0..subnets-1).each do |i|
- mul = i * (2**(32-new_prefix))
- networks << IPAddress::IPv4.parse_u32(network_u32+mul, new_prefix)
- 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
- end
-
-
-end; end # module IPAddress; class IPv4
+ end # class IPv4
+end # module IPAddress
diff --git a/lib/ipaddress/prefix.rb b/lib/ipaddress/prefix.rb
index 125bba8..61109ff 100644
--- a/lib/ipaddress/prefix.rb
+++ b/lib/ipaddress/prefix.rb
@@ -5,24 +5,13 @@ module IPAddress
attr_reader :prefix
- MASK = 0xffffffff
- SIZE = 32
-
def initialize(num)
@prefix = num.to_i
end
def bits
- # "1" * @prefix + "0" * (SIZE - @prefix)
- sprintf "%0#{SIZE}b", (MASK & ~(MASK >> @prefix))
- end
-
- def to_u32
- [bits].pack("B*").unpack("N").first
- end
-
- def to_ip
- [bits].pack("B*").unpack("CCCC").join(".")
+ "1" * @prefix + "0" * (@size - @prefix)
+ #sprintf "%0#@sizeb",(@mask & ~(@mask >> @prefix))
end
def to_s
@@ -34,9 +23,32 @@ module IPAddress
@prefix
end
+ def <=>(oth)
+ @prefix <=> oth.to_i
+ end
+
+ end # class Prefix
+
+ class Prefix32 < Prefix
+
+ def initialize(num)
+ raise ArgumentError unless (1..32).include? num
+ @mask = 0xffffffff
+ @size = 32
+ super(num)
+ end
+
+ def to_ip
+ [bits].pack("B*").unpack("CCCC").join(".")
+ end
+
def octets
to_ip.split(".").map{|i| i.to_i}
end
+
+ def to_u32
+ [bits].pack("B*").unpack("N").first
+ end
def [](index)
octets[index]
@@ -45,16 +57,31 @@ module IPAddress
def hostmask
[~to_u32].pack("N").unpack("CCCC").join(".")
end
-
- def <=>(oth)
- @prefix <=> oth.to_i
- end
-
+
def self.parse_netmask(netmask)
octets = netmask.split(".").map{|i| i.to_i}
num = octets.pack("C"*octets.size).unpack("B*").first.count "1"
return IPAddress::Prefix.new(num)
end
+
+ end # class Prefix32 < Prefix
+
+ class Prefix128 < Prefix
+
+ def initialize(num)
+ raise ArgumentError unless (1..128).include? num
+ @mask = 0xffffffffffffffffffffffffffffffff
+ @size = 128
+ super(num)
+ end
+
+ def to_u128
+ [bits].pack("B*").unpack("N4").first
+ end
+
+
+ end
+
+
- end # class Prefix
end # module IPAddress
diff --git a/test/ipaddress/ipv4_test.rb b/test/ipaddress/ipv4_test.rb
index d28082b..a43bf16 100644
--- a/test/ipaddress/ipv4_test.rb
+++ b/test/ipaddress/ipv4_test.rb
@@ -59,6 +59,7 @@ class IPv4Test < Test::Unit::TestCase
@invalid_ipv4.each do |i|
assert_raise(ArgumentError) {@klass.new(i)}
end
+ assert_raise (ArgumentError) {@klass.new("10.0.0.0/asd")}
end
def test_attributes
@@ -215,16 +216,18 @@ class IPv4Test < Test::Unit::TestCase
ip2 = @klass.new("10.1.1.1/16")
ip3 = @klass.new("172.16.1.1/14")
ip4 = @klass.new("10.1.1.1/8")
-
+
# ip1 should be major than ip2
assert_equal true, ip1 > ip2
- assert_equal false, ip1 < ip2
+ assert_equal false, ip1 < ip2
+ assert_equal false, ip2 > ip1
# ip2 should be minor than ip3
assert_equal true, ip2 < ip3
assert_equal false, ip2 > ip3
# ip1 should be minor than ip3
assert_equal true, ip1 < ip3
assert_equal false, ip1 > ip3
+ assert_equal false, ip3 < ip1
# ip1 should be equal to itself
assert_equal true, ip1 == ip1
# ip1 should be equal to ip4
@@ -234,7 +237,13 @@ class IPv4Test < Test::Unit::TestCase
assert_equal arr, [ip1,ip2,ip3].sort.map{|s| s.to_s}
end
-
+ def test_method_minus
+ ip1 = @klass.new("10.1.1.1/8")
+ ip2 = @klass.new("10.1.1.10/8")
+ assert_equal 9, ip2 - ip1
+ assert_equal 9, ip1 - ip2
+ end
+
def test_method_netmask_equal
ip = @klass.new("10.1.1.1/16")
assert_equal 16, ip.prefix.to_i
diff --git a/test/ipaddress/prefix_test.rb b/test/ipaddress/prefix_test.rb
index a2a2ffa..7e588d9 100644
--- a/test/ipaddress/prefix_test.rb
+++ b/test/ipaddress/prefix_test.rb
@@ -27,7 +27,7 @@ class PrefixTest < Test::Unit::TestCase
24 => 4294967040,
30 => 4294967292}
- @klass = IPAddress::Prefix
+ @klass = IPAddress::Prefix32
end
def test_attributes