lib/ipaddress/ipv4.rb in ipaddress-0.5.0 vs lib/ipaddress/ipv4.rb in ipaddress-0.6.0
- old
+ new
@@ -1,6 +1,5 @@
-require 'ipaddress/ipbase'
require 'ipaddress/prefix'
module IPAddress;
#
# =Name
@@ -13,11 +12,11 @@
#
# =Description
#
# Class IPAddress::IPv4 is used to handle IPv4 type addresses.
#
- class IPv4 < IPBase
+ class IPv4
include IPAddress
include Enumerable
include Comparable
@@ -100,10 +99,11 @@
#
# 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
@@ -112,12 +112,14 @@
#
# 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
@@ -130,14 +132,16 @@
# 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)
@@ -145,33 +149,50 @@
#
# 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 address portion of
+ # the IPv4 object
+ #
+ # ip = IPAddress("172.16.100.4/22")
+ #
+ # ip.to_s
+ # #=> "172.16.100.4"
+ #
+ def to_s
+ @address
+ end
+
+ #
# Returns a string with the IP address in canonical
# form.
#
# ip = IPAddress("172.16.100.4/22")
- # ip.to_s
+ #
+ # ip.to_string
# #=> "172.16.100.4/22"
#
- def to_s
+ def to_string
"#@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
@@ -181,14 +202,16 @@
# 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)
@@ -201,10 +224,11 @@
# 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
#
def to_u32
data.unpack("N").first
@@ -214,10 +238,11 @@
#
# Returns the address portion of an IPv4 object
# in a network byte order format.
#
# ip = IPAddress("172.16.10.1/24")
+ #
# ip.data
# #=> "\254\020\n\001"
#
# It is usually used to include an IP address
# in a data packet to be sent over a socket
@@ -235,10 +260,11 @@
#
# Returns the octet specified by index
#
# ip = IPAddress("172.16.100.50/24")
+ #
# ip[0]
# #=> 172
# ip[1]
# #=> 16
# ip[2]
@@ -254,10 +280,11 @@
#
# 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
data.unpack("B*").first
@@ -265,25 +292,28 @@
#
# Returns the broadcast address for the given IP.
#
# ip = IPAddress("172.16.10.64/24")
+ #
# ip.broadcast.to_s
- # #=> "172.16.10.255/24"
+ # #=> "172.16.10.255"
#
def broadcast
self.class.parse_u32(broadcast_u32, @prefix)
end
#
# Checks 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
@@ -292,12 +322,13 @@
#
# 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"
+ # #=> "172.16.10.0"
#
def network
self.class.parse_u32(network_u32, @prefix)
end
@@ -307,19 +338,21 @@
#
# 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"
+ # #=> "192.168.100.1"
#
# 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"
+ # #=> "192.168.100.1"
#
def first
self.class.parse_u32(network_u32+1, @prefix)
end
@@ -327,34 +360,37 @@
# 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.
+ # host IP address is 192.168.100.254
#
# ip = IPAddress("192.168.100.0/24")
+ #
# ip.last.to_s
- # #=> "192.168.100.254/24"
+ # #=> "192.168.100.254"
#
# 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"
+ # #=> "192.168.100.254"
#
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 = IPAddress("10.0.0.1/29")
+ #
# ip.each do |i|
- # p i
+ # p i.to_s
# end
# #=> "10.0.0.1"
# #=> "10.0.0.2"
# #=> "10.0.0.3"
# #=> "10.0.0.4"
@@ -372,11 +408,12 @@
# network (or IP address).
#
# The object yielded is a new IPv4 object created
# from the iteration.
#
- # ip = IPaddress("10.0.0.1/29")
+ # ip = IPAddress("10.0.0.1/29")
+ #
# ip.each do |i|
# p i.address
# end
# #=> "10.0.0.0"
# #=> "10.0.0.1"
@@ -404,12 +441,12 @@
# "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"
+ # ip2 = IPAddress "172.16.0.1/16"
+ # ip3 = IPAddress "10.100.100.1/16"
#
# ip1 < ip2
# #=> true
# ip1 < ip3
# #=> false
@@ -432,11 +469,12 @@
#
# 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 = IPAddress("10.0.0.1/29")
+ #
# ip.size
# #=> 8
#
def size
broadcast_u32 - network_u32 + 1
@@ -444,11 +482,12 @@
#
# Returns an array with the IP addresses of
# all the hosts in the network.
#
- # ip = IPaddress("10.0.0.1/29")
+ # 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",
@@ -460,11 +499,12 @@
end
#
# Returns the network number in Unsigned 32bits format
#
- # ip = IPaddress("10.0.0.1/29")
+ # ip = IPAddress("10.0.0.1/29")
+ #
# ip.network_u32
# #=> 167772160
#
def network_u32
to_u32 & @prefix.to_u32
@@ -472,10 +512,11 @@
#
# 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
@@ -488,10 +529,11 @@
# 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
@@ -503,16 +545,18 @@
#
# 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
+ alias_method :arpa, :reverse
#
# Subnetting a network
#
# If the IP Address is a network, it can be divided into
@@ -522,33 +566,34 @@
#
# 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}
+ #
+ # network / 4 # implies map{|i| i.to_string}
# #=> ["172.16.10.0/26",
# "172.16.10.64/26",
# "172.16.10.128/26",
# "172.16.10.192/26"]
#
# 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}
+ #
+ # 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
#
def subnet(subnets=2)
unless (1..(2**(32-prefix.to_i))).include? subnets
raise ArgumentError, "Value #{subnets} out of range"
end
-
calculate_subnets(subnets)
end
alias_method :/, :subnet
#
@@ -562,17 +607,17 @@
#
# ip = IPAddress("172.16.10.0/24")
#
# you can supernet it with a new /23 prefix
#
- # ip.supernet(23).to_s
+ # ip.supernet(23).to_string
# #=> "172.16.10.0/23"
#
# However if you supernet it with a /22 prefix, the
# network address will change:
#
- # ip.supernet(22).to_s
+ # ip.supernet(22).to_string
# #=> "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
@@ -581,10 +626,18 @@
#
# Returns the difference between two IP addresses
# in unsigned int 32 bits format
#
+ # Example:
+ #
+ # ip1 = IPAddress("172.16.10.0/24")
+ # ip2 = IPAddress("172.16.11.0/24")
+ #
+ # puts ip1 - ip2
+ # #=> 256
+ #
def -(oth)
return (to_u32 - oth.to_u32).abs
end
#
@@ -594,72 +647,83 @@
#
# Example:
#
# ip1 = IPAddress("172.16.10.1/24")
# ip2 = IPAddress("172.16.11.2/24")
- # puts ip1 + ip2
- # #=>"172.16.10.0/23"
#
+ # p (ip1 + ip2).map {|i| i.to_string}
+ # #=> ["172.16.10.0/23"]
+ #
# If the networks are not contiguous, returns
# the two network numbers from the objects
#
+ # ip1 = IPAddress("10.0.0.1/24")
+ # ip2 = IPAddress("10.0.2.1/24")
+ #
+ # p (ip1 + ip2).map {|i| i.to_string}
+ # #=> ["10.0.0.0/24","10.0.2.0/24"]
+ #
def +(oth)
- self.class.summarize(self,oth)
+ aggregate(*[self,oth].sort.map{|i| i.network})
end
#
# Checks whether the ip address belongs to a
# RFC 791 CLASS A network, no matter
# what the subnet mask is.
#
# Example:
#
# ip = IPAddress("10.0.0.1/24")
+ #
# ip.a?
# #=> true
#
def a?
- CLASSFUL.index(8) === bits
+ CLASSFUL.key(8) === bits
end
#
# Checks whether the ip address belongs to a
# RFC 791 CLASS B network, no matter
# what the subnet mask is.
#
# Example:
#
# ip = IPAddress("172.16.10.1/24")
+ #
# ip.b?
# #=> true
#
def b?
- CLASSFUL.index(16) === bits
+ CLASSFUL.key(16) === bits
end
#
# Checks whether the ip address belongs to a
# RFC 791 CLASS C network, no matter
# what the subnet mask is.
#
# Example:
#
# ip = IPAddress("192.168.1.1/30")
+ #
# ip.c?
# #=> true
#
def c?
- CLASSFUL.index(24) === bits
+ CLASSFUL.key(24) === bits
end
#
# Return the ip address in a format compatible
# with the IPv6 Mapped IPv4 addresses
#
# Example:
#
# ip = IPAddress("172.16.10.1/24")
+ #
# ip.to_ipv6
# #=> "ac10:0a01"
#
def to_ipv6
"%.4x:%.4x" % [to_u32].pack("N").unpack("nn")
@@ -668,18 +732,20 @@
#
# Creates a new IPv4 object from an
# unsigned 32bits integer.
#
# ip = IPAddress::IPv4::parse_u32(167772160)
+ #
# ip.prefix = 8
- # ip.to_s
+ # ip.to_string
# #=> "10.0.0.0/8"
#
# The +prefix+ parameter is optional:
#
# ip = IPAddress::IPv4::parse_u32(167772160, 8)
- # ip.to_s
+ #
+ # ip.to_string
# #=> "10.0.0.0/8"
#
def self.parse_u32(u32, prefix=nil)
ip = [u32].pack("N").unpack("C4").join(".")
if prefix
@@ -697,11 +763,11 @@
# is represented with the binary "\254\020\n\001".
#
# ip = IPAddress::IPv4::parse_data "\254\020\n\001"
# ip.prefix = 24
#
- # ip.to_s
+ # ip.to_string
# #=> "172.16.10.1/24"
#
def self.parse_data(str)
self.new str.unpack("C4").join(".")
end
@@ -711,14 +777,14 @@
# returns a new object
#
# Example:
#
# str = "foobar172.16.10.1barbaz"
- # ip = self.extract str
+ # ip = IPAddress::IPv4::extract str
#
# ip.to_s
- # #=> "172.16.10.1/16"
+ # #=> "172.16.10.1"
#
def self.extract(str)
self.new REGEXP.match(str).to_s
end
@@ -768,40 +834,42 @@
#
# 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
+ #
+ # IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).to_string
# #=> "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}
+ #
+ # IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string}
# #=> ["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
+ return [args.first.network] if args.size == 1
- 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
+ i = 0
+ result = args.dup.sort.map{|ip| ip.network}
+ while i < result.size-1
+ sum = result[i] + result[i+1]
+ result[i..i+1] = sum.first if sum.size == 1
+ i += 1
end
- result << last unless result.any?{|i| i.include? last}
+ result.flatten!
if result.size == args.size
+ # nothing more to summarize
return result
else
+ # keep on summarizing
return self.summarize(*result)
end
end
#
@@ -813,16 +881,16 @@
ip.split(".").map{|i| i.to_i}.pack("C4").unpack("B*").first
end
def prefix_from_ip(ip)
bits = bits_from_address(ip)
- CLASSFUL.each {|reg,prefix| return prefix if bits =~ reg}
+ CLASSFUL.each {|reg,prefix| return Prefix32.new(prefix) if bits =~ reg}
end
def calculate_subnets(subnets)
po2 = subnets.closest_power_of_2
- new_prefix = @prefix.to_i + Math::log2(po2).to_i
+ 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)
end
@@ -840,9 +908,24 @@
dup[i..i+1] = a
return dup.reverse
end
end
return dup.reverse
+ end
+
+ def aggregate(ip1,ip2)
+ if ip1.include? ip2
+ return [ip1]
+ else
+ snet = ip1.supernet(ip1.prefix-1)
+ arr1 = ip1.subnet(2**(ip2.prefix-ip1.prefix)).map{|i| i.to_string}
+ arr2 = snet.subnet(2**(ip2.prefix-snet.prefix)).map{|i| i.to_string}
+ if (arr2 - [ip2.to_string] - arr1).empty?
+ return [snet]
+ else
+ return [ip1, ip2]
+ end
+ end
end
end # class IPv4
end # module IPAddress