summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbluemonk <ceresa@gmail.com>2011-04-21 11:19:24 +0200
committerbluemonk <ceresa@gmail.com>2011-04-21 11:19:24 +0200
commit9caa3c8505e0a3563f6e54077d9674628e04864c (patch)
tree6a9a5e3aba5ca511ac0d6e6905a6cfa2b0be5e96
parentc95e3e6a5dde0aadc7d433efe372c282fbb3913b (diff)
downloadipaddress-ruby-stdlib.tar.gz
All the library in one file to facilitate ruby-stdlib integrationruby-stdlib
-rw-r--r--CHANGELOG.rdoc87
-rw-r--r--LICENSE20
-rw-r--r--README.rdoc935
-rw-r--r--VERSION1
-rw-r--r--ipaddress.gemspec65
-rw-r--r--lib/ipaddress.rb2039
-rw-r--r--lib/ipaddress/extensions/extensions.rb22
-rw-r--r--lib/ipaddress/ipv4.rb996
-rw-r--r--lib/ipaddress/ipv6.rb776
-rw-r--r--lib/ipaddress/prefix.rb252
-rw-r--r--test/ipaddress/extensions/extensions_test.rb18
-rw-r--r--test/ipaddress/ipv4_test.rb510
-rw-r--r--test/ipaddress/ipv6_test.rb356
-rw-r--r--test/ipaddress/prefix_test.rb159
-rw-r--r--test/ipaddress_test.rb1024
15 files changed, 3058 insertions, 4202 deletions
diff --git a/CHANGELOG.rdoc b/CHANGELOG.rdoc
deleted file mode 100644
index 19a86c4..0000000
--- a/CHANGELOG.rdoc
+++ /dev/null
@@ -1,87 +0,0 @@
-== ipaddress 0.7.5
-
-CHANGED:: IPAddress::IPv4#each_host to improve speed
-FIXED:: IPAddress::IPv4::summarize bug (summarization should now work properly)
-NEW:: IPAddress::IPv4#include_all?
-NEW:: #ipv4? and #ipv6?
-
-== ipaddress 0.7.0
-
-NEW:: IPAddress::IPv6#include?
-NEW:: IPAddress::IPv6#network_u128
-NEW:: Modified IPAddress::IPv6::Mapped to accept IPv4 mapped addresses in IPv6 format
-NEW:: IPAddress::IPv4#private?
-NEW:: IPAddress::IPv4::parse_classful
-
-== ipaddress 0.6.0
-
-=== API changes
-* IPv4#to_s now returns the address portion only,
- to retain compatibility with IPAddr. Example:
-
- IPAddress("172.16.10.1/24").to_s
- #=> "172.16.10.1" # ipaddress 0.6.0
-
- IPAddress("172.16.10.1/24").to_s
- #=> "172.16.10.1/24" # ipaddress 0.5.0
-
-* IPv6#to_s now returns the address portion only,
- to retain compatibility with IPAddr. Example:
-
- IPAddress "2001:db8::8:800:200c:417a/64".to_s
- #=> "2001:db8::8:800:200c:417a" # ipaddress 0.6.0
-
- IPAddress "2001:db8::8:800:200c:417a/64".to_s
- #=> "2001:db8::8:800:200c:417a/64" # ipaddress 0.6.0
-
-* IPv6::Unspecified#to_s, IPv6::Loopback and
- IPv6::Mapped#to_s now return the address portion only,
- to retain compatibility with IPAddr.
-* IPv4::summarize now returns an array even if the
- result is a single subnet, to keep consistency
- and avoid confusion
-
-=== New methods
-* IPv4#to_string and IPv6#to_string: print the address
- with the prefix portion, like the #to_s method in
- ipaddress 0.5.0
-* IPAddress::parse, for those who don't like the wrapper
- method IPAddress()
-* IPv6#to_string_uncompressed, returns a string with the
- uncompressed IPv6 and the prefix
-* IPv6::Mapped#to_string, returns the IPv6 Mapped address
- with IPv4 notation and the prefix
-* IPv6#reverse, returns the ip6.arpa DNS reverse lookup
- string
-* IPv4#arpa and IPv6#arpa, alias of the respective #reverse
- methods
-* Prefix#+, Prefix#-
-
-=== Library structure
-* Moved all the IPAddress module methods from
- lib/ipaddress/ipbase.rb to lib/ipaddress.rb
-* Removed IPBase superclass
-* IPv4 and IPv6 classes no longer inherit from IPBase
-* Removed lib/ipaddress/ipbase.rb
-* Removed test/ipaddress/ipbase_test.rb
-
-=== Minor fixes
-* Replaced Ruby 1.9 deprecated Hash#index with Hash#key
-* Removed require ruby-prof from tests which was causing
- users to install ruby-prof or manually remove the line
-* Removed "must" method from tests, replaced by normal
- Test::Unit methods
-* Removed duplicate Jeweler entry in Rakefile
-* Made Integer#closest_power_of_2 more general by adding
- an optional limit parameter
-* Fixed summarization algorithm (thanks to nicolas fevrier)
-* Fixed bug in prefix_from_ip (thanks to jdpace)
-
-=== Documentation
-* Normalized README rdoc headers
-* Added documentation for IPAddress::Prefix
-* Added documentation for IPAddress::IPv4 and
- IPAddress::IPv6
-* Fixed formatting
-* Fixed lots of typos
-
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 25def2b..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright (c) 2009-2011 Marco Ceresa
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.rdoc b/README.rdoc
deleted file mode 100644
index 3026119..0000000
--- a/README.rdoc
+++ /dev/null
@@ -1,935 +0,0 @@
-= IPAddress
-
-IPAddress is a Ruby library designed to make the use of IPv4 and IPv6
-addresses simple, powerful and enjoyable. It provides a complete set of
-methods to handle IP addresses for any need, from simple scripting to
-full network design.
-
-IPAddress is written with a full OO interface, and its code is easy to
-read, maintain and extend. The documentation is full of examples, to
-let you start being productive immediately.
-
-This document provides a brief introduction to the library and
-examples of typical usage.
-
-== Requirements
-
-* Ruby >= 1.8.6 (not tested with previous versions)
-
-IPAddress works perfectly on:
-
-* Ruby 1.8.6 (2007-03-13 patchlevel 0)
-* Ruby 1.8.7
-* Ruby 1.9.1
-* Ruby 1.9.2dev (2010-06-08 revision 28230)
-* Ruby 1.9.2dev (2010-07-15 revision 28653)
-* Rubinius 1.0.1 (1.8.7 release 2010-06-03 JI)
-* Ironruby >= 1.0
-
-It hasn't yet been tested on any other platform, so if you want to collaborate feel
-free to send a small report to my email address, or
-{join the discussion}[http://groups.google.com/group/ruby-ipaddress].
-
-== Why not using IPAddr?
-
-IPAddr is the IP addresses library that comes with Ruby standard
-lib. We found this library, although very well written, not very
-suitable for all our needs, and not very flexible.
-
-Some quick examples of things you can't do with IPAddr:
-
-* store both the address and the prefix information
-* quickly find the broadcast address of a network
-* iterate over hosts
-* perform subnetting or network aggregation
-
-Many methods and procedures are so old that they have been
-declared deprecated by the IETF, and some others have bugs in their
-implementation.
-
-Moreover, IPAddress is more robust and is already around 50% faster than IPAddr,
-in addition to provide an organic API with logical separation and OO structure.
-
-We hope that IPAddress will address all these issues and meet all your
-needs in network programming.
-
-== Installation
-
-Install the library using rubygems
-
- $ gem install ipaddress
-
-You can then use it in your programs:
-
- require 'rubygems' # optional
- require 'ipaddress'
-
-Another way would be to clone the git repository
-
- $ git clone git://github.com/bluemonk/ipaddress.git
-
-And then install the library
-
- $ cd ipaddress
- ipaddress$ rake install
-
-== Documentation
-
-The code is fully documented with RDoc. You can generate the
-documentation with Rake:
-
- ipaddress$ rake rdoc
-
-The latest documentation can be found online at
-{this address}[http://rubydoc.info/gems/ipaddress/0.7.0/frames]
-
-== IPv4
-
-Class IPAddress::IPv4 is used to handle IPv4 type addresses. IPAddress
-is similar to other IP Addresses libraries, like Ruby's own
-IPAddr. However it works slightly different, as we will see.
-
-=== Create a new IPv4 address
-
-The usual way to express an IP Address is using its dotted decimal
-form, such as 172.16.10.1, and a prefix, such as 24, separated by a
-slash.
-
- 172.16.10.1/24
-
-To create a new IPv4 object, you can use IPv4 own class
-
- ip = IPAddress::IPv4.new "172.16.10.1/24"
-
-or, in a easier way, using the IPAddress parse method
-
- ip = IPAddress.parse "172.16.10.1/24"
-
-which accepts and parses any kind of IP (IPv4, IPV6 and
-IPv4 IPv6 Mapped addresses).
-
-If you like syntactic sugar, you can use the wrapper method
-IPAddress(), which is built around IPAddress::parse:
-
- ip = IPAddress "172.16.10.1/24"
-
-You can specify an IPv4 address in any of two ways:
-
- IPAddress "172.16.10.1/24"
- IPAddress "172.16.10.1/255.255.255.0"
-
-In this example, prefix /24 and netmask 255.255.255.0 are the same and
-you have the flexibility to use either one of them.
-
-If you don't explicitly specify the prefix (or the subnet mask),
-IPAddress thinks you're dealing with host addresses and not with
-networks. Therefore, the default prefix will be /32, or
-255.255.255.255. For example:
-
- # let's declare an host address
- host = IPAddress::IPv4.new "10.1.1.1."
-
-The new created object will have prefix /32, which is the same
-as we created the following:
-
- host = IPAddress::IPv4.new "10.1.1.1/32"
-
-=== Handling the IPv4 address
-
-Once created, you can obtain the attributes for an IPv4 object:
-
- ip = IPAddress("172.16.10.1/24")
-
- ip.address
- #=> "172.16.10.1"
- ip.prefix
- #=> 24
-
-In case you need to retrieve the netmask in IPv4 format, you can use
-the IPv4#netmask method:
-
- ip.netmask
- #=> "255.255.255.0"
-
-A special attribute, IPv4#octets, is available to get the four
-decimal octets from the IP address:
-
- ip.octets
- #=> [172,16,10,1]
-
-Shortcut method IPv4#[], provides access to a given octet whithin the
-range:
-
- ip[1]
- #=> 16
-
-If you need to print out the IPv4 address in a canonical form, you can
-use IPv4#to_string
-
- ip.to_string
- #=> "172.16.10.l/24"
-
-=== Changing netmask
-
-You can set a new prefix (netmask) after creating an IPv4
-object. For example:
-
- ip.prefix = 25
-
- ip.to_string
- #=> "172.16.10.l/25"
-
-If you need to use a netmask in IPv4 format, you can achive so by
-using the IPv4#netmask= method
-
- ip.netmask = "255.255.255.252"
-
- ip.to_string
- #=> "172.16.10.1/30"
-
-=== Working with networks, broadcasts and addresses
-
-Some very important topics in dealing with IP addresses are the
-concepts of +network+ and +broadcast+, as well as the addresses
-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 network number is the IP which has all zeroes in the host
-portion. In our example, because the prefix is 24, we identify our
-network number to have the last 8 (32-24) bits all zeroes. Thus, IP
-address "172.16.10.1/24" belongs to network "172.16.10.0/24".
-
-This is very important because, for instance, IP "172.16.10.1/16" is
-very different to the previous one, belonging to the very different
-network "172.16.0.0/16".
-
-==== Networks
-
-With IPAddress it's very easy to calculate the network for an IP
-address:
-
- ip = IPAddress "172.16.10.1/24"
-
- net = ip.network
- #=> #<IPAddress::IPv4:0xb7a5ab24 @octets=[172, 16, 10, 0],
- @prefix=24,
- @address="172.16.10.0">
- net.to_string
- #=> "172.16.10.0/24"
-
-The IPv4#network method creates a new IPv4 object from the network
-number, calculated after the original object. We want to outline here
-that the network address is a perfect legitimate IPv4 address, which
-just happen to have all zeroes in the host portion.
-
-You can use method IPv4#network? to check whether an IP address is a
-network or not:
-
- ip1 = IPAddress "172.16.10.1/24"
- ip2 = IPAddress "172.16.10.4/30"
-
- ip1.network?
- #=> false
- ip2.network?
- #=> true
-
-==== Broadcast
-
-The broadcast address is the contrary than the network number: where
-the network number has all zeroes in the host portion, the broadcast
-address has all one's. For example, ip "172.16.10.1/24" has broadcast
-"172.16.10.255/24", where ip "172.16.10.1/16" has broadcast
-"172.16.255.255/16".
-
-Method IPv4#broadcast has the same behavior as is #network
-counterpart: it creates a new IPv4 object to handle the broadcast
-address:
-
- ip = IPAddress "172.16.10.1/24"
-
- bcast = ip.broadcast
- #=> #<IPAddress::IPv4:0xb7a406fc @octets=[172, 16, 10, 255],
- @prefix=24,
- @address="172.16.10.255">
- bcast.to_string
- #=> "172.16.10.255/24"
-
-==== Addresses, ranges and iterators
-
-So we see that the netmask essentially specifies a range for IP
-addresses that are included in a network: all the addresses between
-the network number and the broadcast. IPAddress has many methods to
-iterate between those addresses. Let's start with IPv4#each, which
-iterates over all addresses in a range
-
- ip = IPAddress "172.16.10.1/24"
-
- ip.each do |addr|
- puts addr
- end
-
-It is important to note that it doesn't matter if the original IP is a
-host IP or a network number (or a broadcast address): the #each method
-only considers the range that the original IP specifies.
-
-If you only want to iterate over hosts IP, use the IPv4#each_host
-method:
-
- ip = IPAddress "172.16.10.1/24"
-
- ip.each_host do |host|
- puts host
- end
-
-Methods IPv4#first and IPv4#last return a new object containing
-respectively the first and the last host address in the range
-
- ip = IPAddress "172.16.10.100/24"
-
- ip.first.to_string
- #=> "172.16.10.1/24"
-
- ip.last.to_string
- #=> "172.16.10.254/24"
-
-=== IP special formats
-
-The IPAddress library provides a complete set of methods to access an
-IPv4 address in special formats, such as binary, 32 bits unsigned int,
-data and hexadecimal.
-
-Let's take the following IPv4 as an example:
-
- ip = IPAddress "172.16.10.1/24"
-
- ip.address
- #=> "172.16.10.1"
-
-The first thing to highlight here is that all these conversion methods
-only take into consideration the address portion of an IPv4 object and
-not the prefix (netmask).
-
-So, to express the address in binary format, use the IPv4#bits method:
-
- ip.bits
- #=> "10101100000100000000101000000001"
-
-To calculate the 32 bits unsigned int format of the ip address, use
-the IPv4#to_u32 method
-
- ip.to_u32
- #=> 2886732289
-
-This method is the equivalent of the Unix call pton(), expressing an
-IP address in the so called +network byte order+ notation. However, if
-you want to transmit your IP over a network socket, you might need to
-transform it in data format using the IPv4#data method:
-
- ip.data
- #=> "\254\020\n\001"
-
-Finally, you can transform an IPv4 address into a format which is
-suitable to use in IPv4-IPv6 mapped addresses:
-
- ip.to_ipv6
- #=> "ac10:0a01"
-
-=== Classful networks
-
-IPAddress allows you to create and manipulate objects using the old
-and deprecated (but apparently still popular) classful networks concept.
-
-Classful networks and addresses don't have a prefix: their subnet mask
-is univocally identified by their address, and therefore diveded in classes.
-As per RFC 791, these classes are:
-
-* 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.
-
-To create a classful IP and prefix from an IP address, use the
-IPv4::parse_classful method:
-
- # classful ip
- ip = IPAddress::IPv4::parse_classful "10.1.1.1"
-
- ip.prefix
- #=> 8
-
-The method automatically created a new IPv4 object and assigned it
-the correct prefix.
-
-You can easily check which CLASSFUL network an IPv4 object belongs:
-
- ip = IPAddress("10.0.0.1/24")
- ip.a?
- #=> true
-
- ip = IPAddress("172.16.10.1/24")
- ip.b?
- #=> true
-
- ip = IPAddress("192.168.1.1/30")
- ip.c?
- #=> true
-
-Remember that these methods are only checking the address portion of an IP, and are
-independent from its prefix, as classful networks have no concept of prefix.
-
-For more information on CLASSFUL networks visit the
-{Wikipedia page}[http://en.wikipedia.org/wiki/Classful_network]
-
-=== Network design with IPAddress
-
-IPAddress includes a lot of useful methods to manipulate IPv4 and IPv6
-networks and do some basic network design.
-
-==== Subnetting
-
-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 you 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_string}
- #=> ["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_string}
- #=> ["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 divided evenly, and all the subnets will have the same number
-of hosts.
-
-==== Uneven subnetting
-
-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.
-
-As an example, let's divide network 172.16.10.0/24 into 3 different subnets:
-
- network = IPAddress("172.16.10.0/24")
-
- network.subnet(3).map{|i| i.to_string}
- #=> ["172.16.10.0/26",
- "172.16.10.64/26",
- "172.16.10.128/25"]
-
-We can go even further and divide into 11 subnets:
-
- network = IPAddress("172.16.10.0/24")
-
- network.subnet(11).map{|i| i.to_string}
- #=> ["172.16.10.0/28", "172.16.10.16/28", "172.16.10.32/28",
- "172.16.10.48/28", "172.16.10.64/28", "172.16.10.80/28",
- "172.16.10.96/28", "172.16.10.112/28", "172.16.10.128/27",
- "172.16.10.160/27", "172.16.10.192/26"]
-
-As you can see, most of the networks are /28, with a few /27 and one
-/26 to fill up the remaining space.
-
-==== Summarization
-
-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_string
- #=> "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).map{|i| i.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_string}
- #=> ["10.0.1.0/24","10.0.2.0/23","10.0.4.0/24"]
-
-In this case, the two summarizables networks have been aggregated into
-a single /23, while the other two networks have been left untouched.
-
-==== Supernetting
-
-Supernetting is a different operation than aggregation, as it only
-works on a single network and returns a new single IPv4 object,
-representing the supernet.
-
-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_string
- #=> "172.16.10.0/23"
-
-However if you supernet it with a /22 prefix, the network address will
-change:
-
- ip.supernet(22).to_string
- #=> "172.16.8.0/22"
-
-This is because "172.16.10.0/22" is not a network anymore, but an host
-address.
-
-== IPv6
-
-IPAddress is not only fantastic for IPv4 addresses, it's also great to
-handle IPv6 addresses family! Let's discover together how to use it in
-our projects.
-
-=== IPv6 addresses
-
-IPv6 addresses are 128 bits long, in contrast with IPv4 addresses
-which are only 32 bits long. An IPv6 address is generally written as
-eight groups of four hexadecimal digits, each group representing 16
-bits or two octet. For example, the following is a valid IPv6
-address:
-
- 1080:0000:0000:0000:0008:0800:200c:417a
-
-Letters in an IPv6 address are usually written downcase, as per
-RFC. You can create a new IPv6 object using uppercase letters, but
-they will be converted.
-
-==== Compression
-
-Since IPv6 addresses are very long to write, there are some
-simplifications and compressions that you can use to shorten them.
-
-* Leading zeroes: all the leading zeroes within a group can be
- omitted: "0008" would become "8"
-
-* A string of consecutive zeroes can be replaced by the string
- "::". This can be only applied once.
-
-Using compression, the IPv6 address written above can be shorten into
-the following, equivalent, address
-
- 1080::8:800:200c:417a
-
-This short version is often used in human representation.
-
-==== Network Mask
-
-As we used to do with IPv4 addresses, an IPv6 address can be written
-using the prefix notation to specify the subnet mask:
-
- 1080::8:800:200c:417a/64
-
-The /64 part means that the first 64 bits of the address are
-representing the network portion, and the last 64 bits are the host
-portion.
-
-=== Using IPAddress with IPv6 addresses
-
-All the IPv6 representations we've just seen are perfectly fine when
-you want to create a new IPv6 address:
-
- ip6 = IPAddress "1080:0000:0000:0000:0008:0800:200C:417A"
-
- ip6 = IPAddress "1080:0:0:0:8:800:200C:417A"
-
- ip6 = IPAddress "1080::8:800:200C:417A"
-
-All three are giving out the same IPv6 object. The default subnet mask
-for an IPv6 is 128, as IPv6 addresses don't have classes like IPv4
-addresses. If you want a different mask, you can go ahead and explicit
-it:
-
- ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
-
-Access the address portion and the prefix by using the respective
-methods:
-
- ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
-
- ip6.address
- #=> "2001:0db8:0000:0000:0008:0800:200c:417a"
-
- ip6.prefix
- #=> 64
-
-A compressed version of the IPv6 address can be obtained with the
-IPv6#compressed method:
-
- ip6 = IPAddress "2001:0db8:0000:0000:0008:200c:417a:00ab/64"
-
- ip6.compressed
- #=> "2001:db8::8:800:200c:417a"
-
-=== Handling the IPv6 address
-
-Accessing the groups that form an IPv6 address is very easy with the
-IPv6#groups method:
-
- ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
-
- ip6.groups
- #=> [8193, 3512, 0, 0, 8, 2048, 8204, 16762]
-
-As with IPv4 addresses, each individual group can be accessed using
-the IPv6#[] shortcut method:
-
- ip6[0]
- #=> 8193
- ip6[1]
- #=> 3512
- ip6[2]
- #=> 0
- ip6[3]
- #=> 0
-
-Note that each 16 bits group is expressed in its decimal form. You can
-also obtain the groups into hexadecimal format using the IPv6#hexs
-method:
-
- ip6.hexs
- #=> => ["2001", "0db8", "0000", "0000", "0008", "0800", "200c", "417a"]
-
-A few other methods are available to transform an IPv6 address into
-decimal representation, with IPv6.to_i
-
- ip6.to_i
- #=> 42540766411282592856906245548098208122
-
-or to hexadecimal representation
-
- ip6.to_hex
- #=> "20010db80000000000080800200c417a"
-
-To print out an IPv6 address in human readable form, use the IPv6#to_s, IPv6#to_string
-and IPv6#to_string_uncompressed methods
-
- ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
-
- ip6.to_string
- #=> "2001:db8::8:800:200c:417a/96"
-
- ip6.to_string_uncompressed
- #=> "2001:0db8:0000:0000:0008:0800:200c:417a/96"
-
-As you can see, IPv6.to_string prints out the compressed form, while
-IPv6.to_string_uncompressed uses the expanded version.
-
-==== Compressing and uncompressing
-
-If you have a string representing an IPv6 address, you can easily
-compress it and uncompress it using the two class methods IPv6::expand
-and IPv6::compress.
-
-For example, let's say you have the following uncompressed IPv6
-address:
-
- ip6str = "2001:0DB8:0000:CD30:0000:0000:0000:0000"
-
-Here is the compressed version:
-
- IPAddress::IPv6.compress ip6str
- #=> "2001:db8:0:cd30::"
-
-The other way works as well:
-
- ip6str = "2001:db8:0:cd30::"
-
- IPAddress::IPv6.expand ip6str
- #=> "2001:0DB8:0000:CD30:0000:0000:0000:0000"
-
-These methods can be used when you don't want to create a new object
-just for expanding or compressing an address (although a new object is
-actually created internally).
-
-=== New IPv6 address from other formats
-
-You can create a new IPv6 address from different formats than just a
-string representing the colon-hex groups.
-
-For instance, if you have a data stream, you can use IPv6::parse_data,
-like in the following example:
-
- data = " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
-
- ip6 = IPAddress::IPv6::parse_data data
- ip6.prefix = 64
-
- ip6.to_string
- #=> "2001:db8::8:800:200c:417a/64"
-
-A new IPv6 address can also be created from an unsigned 128 bits
-integer:
-
- u128 = 21932261930451111902915077091070067066
-
- ip6 = IPAddress::IPv6::parse_u128 u128
- ip6.prefix = 64
-
- ip6.to_string
- #=> "1080::8:800:200c:417a/64"
-
-Finally, a new IPv6 address can be created from an hex string:
-
- hex = "20010db80000000000080800200c417a"
-
- ip6 = IPAddress::IPv6::parse_hex hex
- ip6.prefix = 64
-
- ip6.to_string
- #=> "2001:db8::8:800:200c:417a/64"
-
-=== Special IPv6 addresses
-
-Some IPv6 have a special meaning and are expressed in a special form,
-quite different than an usual IPv6 address. IPAddress has built-in
-support for unspecified, loopback and mapped IPv6 addresses.
-
-==== Unspecified address
-
-The address with all zero bits is called the +unspecified+ address
-(corresponding to 0.0.0.0 in IPv4). It should be something like this:
-
- 0000:0000:0000:0000:0000:0000:0000:0000
-
-but, with the use of compression, it is usually written as just two
-colons:
-
- ::
-
-or, specifying the netmask:
-
- ::/128
-
-With IPAddress, create a new unspecified IPv6 address using its own
-subclass:
-
- ip = IPAddress::IPv6::Unspecified.new
-
- ip.to_string
- #=> "::/128"
-
-You can easily check if an IPv6 object is an unspecified address by
-using the IPv6#unspecified? method
-
- ip.unspecified?
- #=> true
-
-An unspecified IPv6 address can also be created with the wrapper
-method, like we've seen before
-
- ip = IPAddress "::"
-
- ip.unspecified?
- #=> true
-
-This address must never be assigned to an interface and is to be used
-only in software before the application has learned its host's source
-address appropriate for a pending connection. Routers must not forward
-packets with the unspecified address.
-
-==== Loopback address
-
-The loopback address is a unicast localhost address. If an
-application in a host sends packets to this address, the IPv6 stack
-will loop these packets back on the same virtual interface.
-
-Loopback addresses are expressed in the following form:
-
- ::1
-
-or, with their appropriate prefix,
-
- ::1/128
-
-As for the unspecified addresses, IPv6 loopbacks can be created with
-IPAddress calling their own class:
-
- ip = IPAddress::IPv6::Loopback.new
-
- ip.to_string
- #=> "::1/128"
-
-or by using the wrapper:
-
- ip = IPAddress "::1"
-
- ip.to_string
- #=> "::1/128"
-
-Checking if an address is loopback is easy with the IPv6#loopback?
-method:
-
- ip.loopback?
- #=> true
-
-The IPv6 loopback address corresponds to 127.0.0.1 in IPv4.
-
-==== Mapped address
-
-It is usually identified as a IPv4 mapped IPv6 address, a particular
-IPv6 address which aids the transition from IPv4 to IPv6. The
-structure of the address is
-
- ::ffff:w.y.x.z
-
-where w.x.y.z is a normal IPv4 address. For example, the following is
-a mapped IPv6 address:
-
- ::ffff:192.168.100.1
-
-IPAddress is very powerful in handling mapped IPv6 addresses, as the
-IPv4 portion is stored internally as a normal IPv4 object. Let's have
-a look at some examples. To create a new mapped address, just use the
-class builder itself
-
- ip6 = IPAddress::IPv6::Mapped.new "::ffff:172.16.10.1/128"
-
-or just use the wrapper method
-
- ip6 = IPAddress "::ffff:172.16.10.1/128"
-
-Let's check it's really a mapped address:
-
- ip6.mapped?
- #=> true
-
- ip6.to_string
- #=> "::ffff:172.16.10.1/128"
-
-Now with the +ipv4+ attribute, we can easily access the IPv4 portion
-of the mapped IPv6 address:
-
- ip6.ipv4.address
- #=> "172.16.10.1"
-
-Internally, the IPv4 address is stored as two 16 bits
-groups. Therefore all the usual methods for an IPv6 address are
-working perfectly fine:
-
- ip6.to_hex
- #=> "00000000000000000000ffffac100a01"
-
- ip6.address
- #=> "0000:0000:0000:0000:0000:ffff:ac10:0a01"
-
-A mapped IPv6 can also be created just by specify the address in the
-following format:
-
- ip6 = IPAddress "::172.16.10.1"
-
-That is, two colons and the IPv4 address. However, as by RFC, the ffff
-group will be automatically added at the beginning
-
- ip6.to_string
- => "::ffff:172.16.10.1/128"
-
-making it a mapped IPv6 compatible address.
-
-== Community
-
-Want to join the community?
-
-* {IPAddress google group}[http://groups.google.com/group/ruby-ipaddress]
-
-We've created a group to discuss about
-IPAddress future development, features and provide some kind of support.
-Feel free to join us and tell us what you think!
-
-== Thanks to
-
-Thanks to Luca Russo (vargolo) and Simone Carletti
-(weppos) for all the support and technical review. Thanks to Marco Beri,
-Bryan T. Richardson, Nicolas Fevrier, jdpace, Daniele Alessandri, jrdioko,
-Ghislain Charrier, Pawel Krzesniak, Mark Sullivan, Erik Ahlström and
-Steve Rawlinson for their support, feedback and bug reports.
-
-== Copyright
-
-Copyright (c) 2009-2011 Marco Ceresa. See LICENSE for details.
-
-
- \ No newline at end of file
diff --git a/VERSION b/VERSION
deleted file mode 100644
index da2ac9c..0000000
--- a/VERSION
+++ /dev/null
@@ -1 +0,0 @@
-0.7.5 \ No newline at end of file
diff --git a/ipaddress.gemspec b/ipaddress.gemspec
deleted file mode 100644
index 00c8b16..0000000
--- a/ipaddress.gemspec
+++ /dev/null
@@ -1,65 +0,0 @@
-# Generated by jeweler
-# DO NOT EDIT THIS FILE DIRECTLY
-# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
-# -*- encoding: utf-8 -*-
-
-Gem::Specification.new do |s|
- s.name = %q{ipaddress}
- s.version = "0.7.5"
-
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
- s.authors = ["Marco Ceresa"]
- s.date = %q{2011-04-08}
- s.description = %q{ IPAddress is a Ruby library designed to make manipulation
- of IPv4 and IPv6 addresses both powerful and simple. It mantains
- a layer of compatibility with Ruby's own IPAddr, while
- addressing many of its issues.
-}
- s.email = %q{ceresa@gmail.com}
- s.extra_rdoc_files = [
- "LICENSE",
- "README.rdoc"
- ]
- s.files = [
- ".document",
- "CHANGELOG.rdoc",
- "LICENSE",
- "README.rdoc",
- "Rakefile",
- "VERSION",
- "lib/ipaddress.rb",
- "lib/ipaddress/extensions/extensions.rb",
- "lib/ipaddress/ipv4.rb",
- "lib/ipaddress/ipv6.rb",
- "lib/ipaddress/prefix.rb",
- "test/ipaddress/extensions/extensions_test.rb",
- "test/ipaddress/ipv4_test.rb",
- "test/ipaddress/ipv6_test.rb",
- "test/ipaddress/prefix_test.rb",
- "test/ipaddress_test.rb",
- "test/test_helper.rb"
- ]
- s.homepage = %q{http://github.com/bluemonk/ipaddress}
- s.require_paths = ["lib"]
- s.rubygems_version = %q{1.3.7}
- s.summary = %q{IPv4/IPv6 addresses manipulation library}
- s.test_files = [
- "test/ipaddress/extensions/extensions_test.rb",
- "test/ipaddress/ipv4_test.rb",
- "test/ipaddress/ipv6_test.rb",
- "test/ipaddress/prefix_test.rb",
- "test/ipaddress_test.rb",
- "test/test_helper.rb"
- ]
-
- if s.respond_to? :specification_version then
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
- s.specification_version = 3
-
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
- else
- end
- else
- end
-end
-
diff --git a/lib/ipaddress.rb b/lib/ipaddress.rb
index 3627782..cfe6f14 100644
--- a/lib/ipaddress.rb
+++ b/lib/ipaddress.rb
@@ -12,11 +12,6 @@
#
#++
-require 'ipaddress/ipv4'
-require 'ipaddress/ipv6'
-require 'ipaddress/extensions/extensions'
-
-
module IPAddress
NAME = "IPAddress"
@@ -185,4 +180,2038 @@ def IPAddress(str)
IPAddress::parse str
end
+module IPAddress
+
+ #
+ # =NAME
+ #
+ # IPAddress::Prefix
+ #
+ # =SYNOPSIS
+ #
+ # Parent class for Prefix32 and Prefix128
+ #
+ # =DESCRIPTION
+ #
+ # IPAddress::Prefix is the parent class for IPAddress::Prefix32
+ # and IPAddress::Prefix128, defining some modules in common for
+ # both the subclasses.
+ #
+ # IPAddress::Prefix shouldn't be accesses directly, unless
+ # for particular needs.
+ #
+ class Prefix
+
+ include Comparable
+
+ attr_reader :prefix
+
+ #
+ # Creates a new general prefix
+ #
+ def initialize(num)
+ @prefix = num.to_i
+ end
+
+ #
+ # Returns a string with the prefix
+ #
+ def to_s
+ "#@prefix"
+ end
+ alias_method :inspect, :to_s
+
+ #
+ # Returns the prefix
+ #
+ def to_i
+ @prefix
+ end
+
+ #
+ # Compare the prefix
+ #
+ def <=>(oth)
+ @prefix <=> oth.to_i
+ end
+
+ #
+ # Sums two prefixes or a prefix to a
+ # number, returns a Fixnum
+ #
+ def +(oth)
+ if oth.is_a? Fixnum
+ self.prefix + oth
+ else
+ self.prefix + oth.prefix
+ end
+ end
+
+ #
+ # Returns the difference between two
+ # prefixes, or a prefix and a number,
+ # as a Fixnum
+ #
+ def -(oth)
+ if oth.is_a? Fixnum
+ self.prefix - oth
+ else
+ (self.prefix - oth.prefix).abs
+ end
+ end
+
+ end # class Prefix
+
+
+ class Prefix32 < Prefix
+
+ IN4MASK = 0xffffffff
+
+ #
+ # Creates a new prefix object for 32 bits IPv4 addresses
+ #
+ # prefix = IPAddress::Prefix32.new 24
+ # #=> 24
+ #
+ def initialize(num)
+ unless (0..32).include? num
+ raise ArgumentError, "Prefix must be in range 0..32, got: #{num}"
+ end
+ super(num)
+ end
+
+ #
+ # Returns the length of the host portion
+ # of a netmask.
+ #
+ # prefix = Prefix32.new 24
+ #
+ # prefix.host_prefix
+ # #=> 8
+ #
+ def host_prefix
+ 32 - @prefix
+ end
+
+ #
+ # Transforms the prefix into a string of bits
+ # representing the netmask
+ #
+ # prefix = IPAddress::Prefix32.new 24
+ #
+ # prefix.bits
+ # #=> "11111111111111111111111100000000"
+ #
+ def bits
+ "%.32b" % to_u32
+ end
+
+ #
+ # Gives the prefix in IPv4 dotted decimal format,
+ # i.e. the canonical netmask we're all used to
+ #
+ # prefix = IPAddress::Prefix32.new 24
+ #
+ # prefix.to_ip
+ # #=> "255.255.255.0"
+ #
+ def to_ip
+ [bits].pack("B*").unpack("CCCC").join(".")
+ end
+
+ #
+ # An array of octets of the IPv4 dotted decimal
+ # format
+ #
+ # prefix = IPAddress::Prefix32.new 24
+ #
+ # prefix.octets
+ # #=> [255, 255, 255, 0]
+ #
+ def octets
+ to_ip.split(".").map{|i| i.to_i}
+ end
+
+ #
+ # Unsigned 32 bits decimal number representing
+ # the prefix
+ #
+ # prefix = IPAddress::Prefix32.new 24
+ #
+ # prefix.to_u32
+ # #=> 4294967040
+ #
+ def to_u32
+ (IN4MASK >> host_prefix) << host_prefix
+ end
+
+ #
+ # Shortcut for the octecs in the dotted decimal
+ # representation
+ #
+ # prefix = IPAddress::Prefix32.new 24
+ #
+ # prefix[2]
+ # #=> 255
+ #
+ def [](index)
+ octets[index]
+ end
+
+ #
+ # The hostmask is the contrary of the subnet mask,
+ # as it shows the bits that can change within the
+ # hosts
+ #
+ # prefix = IPAddress::Prefix32.new 24
+ #
+ # prefix.hostmask
+ # #=> "0.0.0.255"
+ #
+ def hostmask
+ [~to_u32].pack("N").unpack("CCCC").join(".")
+ end
+
+ #
+ # Creates a new prefix by parsing a netmask in
+ # dotted decimal form
+ #
+ # prefix = IPAddress::Prefix32::parse_netmask "255.255.255.0"
+ # #=> 24
+ #
+ 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 self.new(num)
+ end
+
+ end # class Prefix32 < Prefix
+
+ class Prefix128 < Prefix
+
+ #
+ # Creates a new prefix object for 128 bits IPv6 addresses
+ #
+ # prefix = IPAddress::Prefix128.new 64
+ # #=> 64
+ #
+ def initialize(num=128)
+ unless (1..128).include? num.to_i
+ raise ArgumentError, "Prefix must be in range 1..128, got: #{num}"
+ end
+ super(num.to_i)
+ end
+
+ #
+ # Transforms the prefix into a string of bits
+ # representing the netmask
+ #
+ # prefix = IPAddress::Prefix128.new 64
+ #
+ # prefix.bits
+ # #=> "1111111111111111111111111111111111111111111111111111111111111111"
+ # "0000000000000000000000000000000000000000000000000000000000000000"
+ #
+ def bits
+ "1" * @prefix + "0" * (128 - @prefix)
+ end
+
+ #
+ # Unsigned 128 bits decimal number representing
+ # the prefix
+ #
+ # prefix = IPAddress::Prefix128.new 64
+ #
+ # prefix.to_u128
+ # #=> 340282366920938463444927863358058659840
+ #
+ def to_u128
+ bits.to_i(2)
+ end
+
+ end # class Prefix123 < Prefix
+
+end # module IPAddress
+
+module IPAddress;
+ #
+ # =Name
+ #
+ # IPAddress::IPv4 - IP version 4 address manipulation library
+ #
+ # =Synopsis
+ #
+ # require 'ipaddress'
+ #
+ # =Description
+ #
+ # Class IPAddress::IPv4 is used to handle IPv4 type addresses.
+ #
+ class IPv4
+
+ 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
+ }
+
+ #
+ # Regular expression to match an IPv4 address
+ #
+ REGEXP = Regexp.new(/((25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)/)
+
+ #
+ # 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
+ # set as default 32, also known as the host prefix
+ #
+ # Examples:
+ #
+ # # These two are the same
+ # ip = IPAddress::IPv4.new("10.0.0.1/24")
+ # ip = IPAddress("10.0.0.1/24")
+ #
+ # # These two are the same
+ # IPAddress::IPv4.new "10.0.0.1/8"
+ # IPAddress::IPv4.new "10.0.0.1/255.0.0.0"
+ #
+ 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 = Prefix32.new(32)
+ end
+
+ # Array formed with the IP octets
+ @octets = @address.split(".").map{|i| i.to_i}
+ # 32 bits interger containing the address
+ @u32 = (@octets[0]<< 24) + (@octets[1]<< 16) + (@octets[2]<< 8) + (@octets[3])
+
+ 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::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 = 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 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_string
+ # #=> "172.16.100.4/22"
+ #
+ 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
+ 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_i
+ # #=> 167772160
+ #
+ def u32
+ @u32
+ end
+ alias_method :to_i, :u32
+ alias_method :to_u32, :u32
+
+ #
+ # 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
+ #
+ # a = Socket.open(params) # socket details here
+ # ip = IPAddress("10.1.1.0/24")
+ # binary_data = ["Address: "].pack("a*") + ip.data
+ #
+ # # Send binary data
+ # a.puts binary_data
+ #
+ def data
+ [@u32].pack("N")
+ 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
+ data.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"
+ #
+ 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?
+ @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"
+ #
+ 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"
+ #
+ # 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"
+ #
+ 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.254
+ #
+ # ip = IPAddress("192.168.100.0/24")
+ #
+ # ip.last.to_s
+ # #=> "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"
+ #
+ 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_host do |i|
+ # p i.to_s
+ # 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
+ (network_u32+1..broadcast_u32-1).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
+ 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
+ 2 ** @prefix.host_prefix
+ 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
+ @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
+ network_u32 + size - 1
+ end
+
+ #
+ # Checks whether a subnet includes the given IP address.
+ #
+ # Accepts an 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 == (oth.to_u32 & @prefix.to_u32)
+ end
+
+ #
+ # Checks whether a subnet includes all the
+ # given IPv4 objects.
+ #
+ # ip = IPAddress("192.168.10.100/24")
+ #
+ # addr1 = IPAddress("192.168.10.102/24")
+ # addr2 = IPAddress("192.168.10.103/24")
+ #
+ # ip.include_all?(addr1,addr2)
+ # #=> true
+ #
+ def include_all?(*others)
+ others.all? {|oth| include?(oth)}
+ end
+
+ #
+ # True if the object is an IPv4 address
+ #
+ # ip = IPAddress("192.168.10.100/24")
+ #
+ # ip.ipv4?
+ # #-> true
+ #
+# def ipv4?
+# true
+# end
+
+ #
+ # True if the object is an IPv6 address
+ #
+ # ip = IPAddress("192.168.10.100/24")
+ #
+ # ip.ipv6?
+ # #-> false
+ #
+# def ipv6?
+# false
+# end
+
+ #
+ # Checks if an IPv4 address objects belongs
+ # to a private network RFC1918
+ #
+ # Example:
+ #
+ # ip = IPAddress "10.1.1.1/24"
+ # ip.private?
+ # #=> true
+ #
+ def private?
+ [self.class.new("10.0.0.0/8"),
+ self.class.new("172.16.0.0/12"),
+ self.class.new("192.168.0.0/16")].any? {|i| i.include? self}
+ 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
+ alias_method :arpa, :reverse
+
+ #
+ # 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 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_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_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**@prefix.host_prefix)).include? subnets
+ raise ArgumentError, "Value #{subnets} out of range"
+ end
+ calculate_subnets(subnets)
+ 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_string
+ # #=> "172.16.10.0/23"
+ #
+ # However if you supernet it with a /22 prefix, the
+ # network address will change:
+ #
+ # 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
+ self.class.new(@address+"/#{new_prefix}").network
+ end
+
+ #
+ # 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
+
+ #
+ # Returns a new IPv4 object which is the result
+ # of the summarization, if possible, of the two
+ # objects
+ #
+ # Example:
+ #
+ # ip1 = IPAddress("172.16.10.1/24")
+ # ip2 = IPAddress("172.16.11.2/24")
+ #
+ # 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)
+ 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.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.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.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")
+ end
+
+ #
+ # Creates a new IPv4 object from an
+ # unsigned 32bits integer.
+ #
+ # ip = IPAddress::IPv4::parse_u32(167772160)
+ #
+ # ip.prefix = 8
+ # ip.to_string
+ # #=> "10.0.0.0/8"
+ #
+ # The +prefix+ parameter is optional:
+ #
+ # ip = IPAddress::IPv4::parse_u32(167772160, 8)
+ #
+ # ip.to_string
+ # #=> "10.0.0.0/8"
+ #
+ def self.parse_u32(u32, prefix=32)
+ self.new([u32].pack("N").unpack("C4").join(".")+"/#{prefix}")
+ end
+
+ #
+ # Creates a new IPv4 object from binary data,
+ # like the one you get from a network stream.
+ #
+ # For example, on a network stream the IP 172.16.0.1
+ # is represented with the binary "\254\020\n\001".
+ #
+ # ip = IPAddress::IPv4::parse_data "\254\020\n\001"
+ # ip.prefix = 24
+ #
+ # ip.to_string
+ # #=> "172.16.10.1/24"
+ #
+ def self.parse_data(str, prefix=32)
+ self.new(str.unpack("C4").join(".")+"/#{prefix}")
+ end
+
+ #
+ # Extract an IPv4 address from a string and
+ # returns a new object
+ #
+ # Example:
+ #
+ # str = "foobar172.16.10.1barbaz"
+ # ip = IPAddress::IPv4::extract str
+ #
+ # ip.to_s
+ # #=> "172.16.10.1"
+ #
+ def self.extract(str)
+ self.new REGEXP.match(str).to_s
+ 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_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_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.first.network] if args.size == 1
+
+ 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.flatten!
+ if result.size == args.size
+ # nothing more to summarize
+ return result
+ else
+ # keep on summarizing
+ return self.summarize(*result)
+ end
+ end
+
+ #
+ # Creates a new IPv4 address object by parsing the
+ # address in a classful way.
+ #
+ # Classful addresses have a fixed netmask based on the
+ # class they belong to:
+ #
+ # * 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, D and E, from 192.0.0.0 to 255.255.255.254
+ #
+ # Note that classes C, D and E will all have a default
+ # prefix of /24 or 255.255.255.0
+ #
+ # Example:
+ #
+ # ip = IPAddress::IPv4.parse_classful "10.0.0.1"
+ #
+ # ip.netmask
+ # #=> "255.0.0.0"
+ # ip.a?
+ # #=> true
+ #
+ def self.parse_classful(ip)
+ if IPAddress.valid_ipv4?(ip)
+ address = ip.strip
+ else
+ raise ArgumentError, "Invalid IP #{ip.inspect}"
+ end
+ prefix = CLASSFUL.find{|h,k| h === ("%.8b" % address.to_i)}.last
+ self.new "#{address}/#{prefix}"
+ end
+
+ #
+ # private methods
+ #
+ private
+
+ def calculate_subnets(subnets)
+ po2 = closest_power_of_2(subnets)
+ new_prefix = @prefix + 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
+ until networks.size == subnets
+ networks = sum_first_found(networks)
+ end
+ return networks
+ end
+
+ 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
+
+ def aggregate(ip1,ip2)
+ return [ip1] if ip1.include? ip2
+
+ snet = ip1.supernet(ip1.prefix-1)
+ if snet.include_all?(ip1, ip2) && ((ip1.size + ip2.size) == snet.size)
+ return [snet]
+ else
+ return [ip1, ip2]
+ end
+ end
+
+ def log2(n); Math::log(n) / Math::log(2); end
+
+ def power_of_2?(int)
+ log2(int).to_i == log2(int)
+ end
+
+ def closest_power_of_2(int, limit=32)
+ int.upto(limit) do |i|
+ return i if power_of_2?(i)
+ end
+ end
+
+ end # class IPv4
+end # module IPAddress
+
+module IPAddress;
+ #
+ # =Name
+ #
+ # IPAddress::IPv6 - IP version 6 address manipulation library
+ #
+ # =Synopsis
+ #
+ # require 'ipaddress'
+ #
+ # =Description
+ #
+ # Class IPAddress::IPv6 is used to handle IPv6 type addresses.
+ #
+ # == IPv6 addresses
+ #
+ # IPv6 addresses are 128 bits long, in contrast with IPv4 addresses
+ # which are only 32 bits long. An IPv6 address is generally written as
+ # eight groups of four hexadecimal digits, each group representing 16
+ # bits or two octect. For example, the following is a valid IPv6
+ # address:
+ #
+ # 1080:0000:0000:0000:0008:0800:200c:417a
+ #
+ # Letters in an IPv6 address are usually written downcase, as per
+ # RFC. You can create a new IPv6 object using uppercase letters, but
+ # they will be converted.
+ #
+ # === Compression
+ #
+ # Since IPv6 addresses are very long to write, there are some
+ # semplifications and compressions that you can use to shorten them.
+ #
+ # * Leading zeroes: all the leading zeroes within a group can be
+ # omitted: "0008" would become "8"
+ #
+ # * A string of consecutive zeroes can be replaced by the string
+ # "::". This can be only applied once.
+ #
+ # Using compression, the IPv6 address written above can be shorten into
+ # the following, equivalent, address
+ #
+ # 1080::8:800:200c:417a
+ #
+ # This short version is often used in human representation.
+ #
+ # === Network Mask
+ #
+ # As we used to do with IPv4 addresses, an IPv6 address can be written
+ # using the prefix notation to specify the subnet mask:
+ #
+ # 1080::8:800:200c:417a/64
+ #
+ # The /64 part means that the first 64 bits of the address are
+ # representing the network portion, and the last 64 bits are the host
+ # portion.
+ #
+ #
+ class IPv6
+
+ include IPAddress
+ include Enumerable
+ include Comparable
+
+
+ #
+ # Format string to pretty print IPv6 addresses
+ #
+ IN6FORMAT = ("%.4x:"*8).chop
+
+ #
+ # Creates a new IPv6 address object.
+ #
+ # An IPv6 address can be expressed in any of the following forms:
+ #
+ # * "1080:0000:0000:0000:0008:0800:200C:417A": IPv6 address with no compression
+ # * "1080:0:0:0:8:800:200C:417A": IPv6 address with leading zeros compression
+ # * "1080::8:800:200C:417A": IPv6 address with full compression
+ #
+ # In all these 3 cases, a new IPv6 address object will be created, using the default
+ # subnet mask /128
+ #
+ # You can also specify the subnet mask as with IPv4 addresses:
+ #
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
+ #
+ def initialize(str)
+ ip, netmask = str.split("/")
+
+ if str =~ /:.+\./
+ raise ArgumentError, "Please use #{self.class}::Mapped for IPv4 mapped addresses"
+ end
+
+ if IPAddress.valid_ipv6?(ip)
+ @groups = self.class.groups(ip)
+ @address = IN6FORMAT % @groups
+ @compressed = compress_address
+ else
+ raise ArgumentError, "Invalid IP #{ip.inspect}"
+ end
+
+ @prefix = Prefix128.new(netmask ? netmask : 128)
+
+ end # def initialize
+
+ #
+ # Returns the IPv6 address in uncompressed form:
+ #
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
+ #
+ # ip6.address
+ # #=> "2001:0db8:0000:0000:0008:0800:200c:417a"
+ #
+ def address
+ @address
+ end
+
+ #
+ # Returns an array with the 16 bits groups in decimal
+ # format:
+ #
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
+ #
+ # ip6.groups
+ # #=> [8193, 3512, 0, 0, 8, 2048, 8204, 16762]
+ #
+ def groups
+ @groups
+ end
+
+ #
+ # Returns an instance of the prefix object
+ #
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
+ #
+ # ip6.prefix
+ # #=> 64
+ #
+ 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 IPv6::parse_u128 or
+ # if the object was created using the default prefix
+ # of 128 bits.
+ #
+ # ip6 = IPAddress("2001:db8::8:800:200c:417a")
+ #
+ # puts ip6.to_string
+ # #=> "2001:db8::8:800:200c:417a/128"
+ #
+ # ip6.prefix = 64
+ # puts ip6.to_string
+ # #=> "2001:db8::8:800:200c:417a/64"
+ #
+ def prefix=(num)
+ @prefix = Prefix128.new(num)
+ end
+
+ #
+ # Unlike its counterpart IPv6#to_string method, IPv6#to_string_uncompressed
+ # returns the whole IPv6 address and prefix in an uncompressed form
+ #
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
+ #
+ # ip6.to_string_uncompressed
+ # #=> "2001:0db8:0000:0000:0008:0800:200c:417a/64"
+ #
+ def to_string_uncompressed
+ "#@address/#@prefix"
+ end
+
+ #
+ # Returns the IPv6 address in a human readable form,
+ # using the compressed address.
+ #
+ # ip6 = IPAddress "2001:0db8:0000:0000:0008:0800:200c:417a/64"
+ #
+ # ip6.to_string
+ # #=> "2001:db8::8:800:200c:417a/64"
+ #
+ def to_string
+ "#@compressed/#@prefix"
+ end
+
+ #
+ # Returns the IPv6 address in a human readable form,
+ # using the compressed address.
+ #
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
+ #
+ # ip6.to_s
+ # #=> "2001:db8::8:800:200c:417a"
+ #
+ def to_s
+ @compressed
+ end
+
+ #
+ # Returns a decimal format (unsigned 128 bit) of the
+ # IPv6 address
+ #
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
+ #
+ # ip6.to_i
+ # #=> 42540766411282592856906245548098208122
+ #
+ def to_i
+ to_hex.hex
+ end
+ alias_method :to_u128, :to_i
+
+ #
+ # True if the IPv6 address is a network
+ #
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
+ #
+ # ip6.network?
+ # #=> false
+ #
+ # ip6 = IPAddress "2001:db8:8:800::/64"
+ #
+ # ip6.network?
+ # #=> true
+ #
+ def network?
+ to_u128 | @prefix.to_u128 == @prefix.to_u128
+ end
+
+ #
+ # Returns the 16-bits value specified by index
+ #
+ # ip = IPAddress("2001:db8::8:800:200c:417a/64")
+ #
+ # ip[0]
+ # #=> 8193
+ # ip[1]
+ # #=> 3512
+ # ip[2]
+ # #=> 0
+ # ip[3]
+ # #=> 0
+ #
+ def [](index)
+ @groups[index]
+ end
+ alias_method :group, :[]
+
+ #
+ # Returns a Base16 number representing the IPv6
+ # address
+ #
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
+ #
+ # ip6.to_hex
+ # #=> "20010db80000000000080800200c417a"
+ #
+ def to_hex
+ hexs.join("")
+ end
+
+ # Returns the address portion of an IPv6 object
+ # in a network byte order format.
+ #
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
+ #
+ # ip6.data
+ # #=> " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
+ #
+ # 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
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
+ # binary_data = ["Address: "].pack("a*") + ip.data
+ #
+ # # Send binary data
+ # a.puts binary_data
+ #
+ def data
+ @groups.pack("n8")
+ end
+
+ #
+ # Returns an array of the 16 bits groups in hexdecimal
+ # format:
+ #
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
+ #
+ # ip6.hexs
+ # #=> ["2001", "0db8", "0000", "0000", "0008", "0800", "200c", "417a"]
+ #
+ # Not to be confused with the similar IPv6#to_hex method.
+ #
+ def hexs
+ @address.split(":")
+ end
+
+ #
+ # Returns the IPv6 address in a DNS reverse lookup
+ # string, as per RFC3172 and RFC2874.
+ #
+ # ip6 = IPAddress "3ffe:505:2::f"
+ #
+ # ip6.reverse
+ # #=> "f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.arpa"
+ #
+ def reverse
+ to_hex.reverse.gsub(/./){|c| c+"."} + "ip6.arpa"
+ end
+ alias_method :arpa, :reverse
+
+ #
+ # Returns the network number in Unsigned 128bits format
+ #
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
+ #
+ # ip6.network_u128
+ # #=> 42540766411282592856903984951653826560
+ #
+ def network_u128
+ to_u128 & @prefix.to_u128
+ end
+
+ #
+ # Checks whether a subnet includes the given IP address.
+ #
+ # Example:
+ #
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
+ # addr = IPAddress "2001:db8::8:800:200c:1/128"
+ #
+ # ip6.include? addr
+ # #=> true
+ #
+ # ip6.include? IPAddress("2001:db8:1::8:800:200c:417a/76")
+ # #=> false
+ #
+ def include?(oth)
+ @prefix <= oth.prefix and network_u128 == self.class.new(oth.address+"/#@prefix").network_u128
+ end
+
+ #
+ # Compressed form of the IPv6 address
+ #
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
+ #
+ # ip6.compressed
+ # #=> "2001:db8::8:800:200c:417a"
+ #
+ def compressed
+ @compressed
+ end
+
+ #
+ # Returns true if the address is an unspecified address
+ #
+ # See IPAddress::IPv6::Unspecified for more information
+ #
+ def unspecified?
+ @prefix == 128 and @compressed == "::"
+ end
+
+ #
+ # Returns true if the address is a loopback address
+ #
+ # See IPAddress::IPv6::Loopback for more information
+ #
+ def loopback?
+ @prefix == 128 and @compressed == "::1"
+ end
+
+ #
+ # Returns true if the address is a mapped address
+ #
+ # See IPAddress::IPv6::Mapped for more information
+ #
+ def mapped?
+ to_u128 >> 32 == 0xffff
+ end
+
+ #
+ # Returns the address portion of an IP in binary format,
+ # as a string containing a sequence of 0 and 1
+ #
+ # ip6 = IPAddress("2001:db8::8:800:200c:417a")
+ #
+ # ip6.bits
+ # #=> "0010000000000001000011011011100000 [...] "
+ #
+ def bits
+ data.unpack("B*").first
+ end
+
+ #
+ # Expands an IPv6 address in the canocical form
+ #
+ # IPAddress::IPv6.expand "2001:0DB8:0:CD30::"
+ # #=> "2001:0DB8:0000:CD30:0000:0000:0000:0000"
+ #
+ def self.expand(str)
+ self.new(str).address
+ end
+
+ #
+ # Compress an IPv6 address in its compressed form
+ #
+ # IPAddress::IPv6.compress "2001:0DB8:0000:CD30:0000:0000:0000:0000"
+ # #=> "2001:db8:0:cd30::"
+ #
+ def self.compress(str)
+ self.new(str).compressed
+ end
+
+ #
+ # Literal version of the IPv6 address
+ #
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
+ #
+ # ip6.literal
+ # #=> "2001-0db8-0000-0000-0008-0800-200c-417a.ipv6-literal.net"
+ #
+ def literal
+ @address.gsub(":","-") + ".ipv6-literal.net"
+ end
+
+ #
+ # Extract 16 bits groups from a string
+ #
+ def self.groups(str)
+ l, r = if str =~ /^(.*)::(.*)$/
+ [$1,$2].map {|i| i.split ":"}
+ else
+ [str.split(":"),[]]
+ end
+ (l + Array.new(8-l.size-r.size, '0') + r).map {|i| i.hex}
+ end
+
+ #
+ # Creates a new IPv6 object from binary data,
+ # like the one you get from a network stream.
+ #
+ # For example, on a network stream the IP
+ #
+ # "2001:db8::8:800:200c:417a"
+ #
+ # is represented with the binary data
+ #
+ # " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
+ #
+ # With that data you can create a new IPv6 object:
+ #
+ # ip6 = IPAddress::IPv6::parse_data " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
+ # ip6.prefix = 64
+ #
+ # ip6.to_s
+ # #=> "2001:db8::8:800:200c:417a/64"
+ #
+ def self.parse_data(str)
+ self.new(IN6FORMAT % str.unpack("n8"))
+ end
+
+ #
+ # Creates a new IPv6 object from an
+ # unsigned 128 bits integer.
+ #
+ # ip6 = IPAddress::IPv6::parse_u128(21932261930451111902915077091070067066)
+ # ip6.prefix = 64
+ #
+ # ip6.to_s
+ # #=> "1080::8:800:200c:417a/64"
+ #
+ # The +prefix+ parameter is optional:
+ #
+ # ip6 = IPAddress::IPv6::parse_u128(21932261930451111902915077091070067066, 64)
+ #
+ # ip6.to_s
+ # #=> "1080::8:800:200c:417a/64"
+ #
+ def self.parse_u128(u128, prefix=128)
+ str = IN6FORMAT % (0..7).map{|i| (u128>>(112-16*i))&0xffff}
+ self.new(str + "/#{prefix}")
+ end
+
+ #
+ # Creates a new IPv6 object from a number expressed in
+ # hexdecimal format:
+ #
+ # ip6 = IPAddress::IPv6::parse_hex("20010db80000000000080800200c417a")
+ # ip6.prefix = 64
+ #
+ # ip6.to_s
+ # #=> "2001:db8::8:800:200c:417a/64"
+ #
+ # The +prefix+ parameter is optional:
+ #
+ # ip6 = IPAddress::IPv6::parse_hex("20010db80000000000080800200c417a", 64)
+ #
+ # ip6.to_s
+ # #=> "1080::8:800:200c:417a/64"
+ #
+ def self.parse_hex(hex, prefix=128)
+ self.parse_u128(hex.hex, prefix)
+ end
+
+ private
+
+ def compress_address
+ str = @groups.map{|i| i.to_s 16}.join ":"
+ loop do
+ break if str.sub!(/\A0:0:0:0:0:0:0:0\Z/, '::')
+ break if str.sub!(/\b0:0:0:0:0:0:0\b/, ':')
+ break if str.sub!(/\b0:0:0:0:0:0\b/, ':')
+ break if str.sub!(/\b0:0:0:0:0\b/, ':')
+ break if str.sub!(/\b0:0:0:0\b/, ':')
+ break if str.sub!(/\b0:0:0\b/, ':')
+ break if str.sub!(/\b0:0\b/, ':')
+ break
+ end
+ str.sub(/:{3,}/, '::')
+ end
+
+ end # class IPv6
+
+ #
+ # The address with all zero bits is called the +unspecified+ address
+ # (corresponding to 0.0.0.0 in IPv4). It should be something like this:
+ #
+ # 0000:0000:0000:0000:0000:0000:0000:0000
+ #
+ # but, with the use of compression, it is usually written as just two
+ # colons:
+ #
+ # ::
+ #
+ # or, specifying the netmask:
+ #
+ # ::/128
+ #
+ # With IPAddress, create a new unspecified IPv6 address using its own
+ # subclass:
+ #
+ # ip = IPAddress::IPv6::Unspecified.new
+ #
+ # ip.to_s
+ # #=> => "::/128"
+ #
+ # You can easily check if an IPv6 object is an unspecified address by
+ # using the IPv6#unspecified? method
+ #
+ # ip.unspecified?
+ # #=> true
+ #
+ # An unspecified IPv6 address can also be created with the wrapper
+ # method, like we've seen before
+ #
+ # ip = IPAddress "::"
+ #
+ # ip.unspecified?
+ # #=> true
+ #
+ # This address must never be assigned to an interface and is to be used
+ # only in software before the application has learned its host's source
+ # address appropriate for a pending connection. Routers must not forward
+ # packets with the unspecified address.
+ #
+ class IPAddress::IPv6::Unspecified < IPAddress::IPv6
+ #
+ # Creates a new IPv6 unspecified address
+ #
+ # ip = IPAddress::IPv6::Unspecified.new
+ #
+ # ip.to_s
+ # #=> => "::/128"
+ #
+ def initialize
+ @address = ("0000:"*8).chop
+ @groups = Array.new(8,0)
+ @prefix = Prefix128.new(128)
+ @compressed = compress_address
+ end
+ end # class IPv6::Unspecified
+
+ #
+ # The loopback address is a unicast localhost address. If an
+ # application in a host sends packets to this address, the IPv6 stack
+ # will loop these packets back on the same virtual interface.
+ #
+ # Loopback addresses are expressed in the following form:
+ #
+ # ::1
+ #
+ # or, with their appropriate prefix,
+ #
+ # ::1/128
+ #
+ # As for the unspecified addresses, IPv6 loopbacks can be created with
+ # IPAddress calling their own class:
+ #
+ # ip = IPAddress::IPv6::Loopback.new
+ #
+ # ip.to_s
+ # #=> "::1/128"
+ #
+ # or by using the wrapper:
+ #
+ # ip = IPAddress "::1"
+ #
+ # ip.to_s
+ # #=> "::1/128"
+ #
+ # Checking if an address is loopback is easy with the IPv6#loopback?
+ # method:
+ #
+ # ip.loopback?
+ # #=> true
+ #
+ # The IPv6 loopback address corresponds to 127.0.0.1 in IPv4.
+ #
+ class IPAddress::IPv6::Loopback < IPAddress::IPv6
+ #
+ # Creates a new IPv6 unspecified address
+ #
+ # ip = IPAddress::IPv6::Loopback.new
+ #
+ # ip.to_s
+ # #=> "::1/128"
+ #
+ def initialize
+ @address = ("0000:"*7)+"0001"
+ @groups = Array.new(7,0).push(1)
+ @prefix = Prefix128.new(128)
+ @compressed = compress_address
+ end
+ end # class IPv6::Loopback
+
+ #
+ # It is usually identified as a IPv4 mapped IPv6 address, a particular
+ # IPv6 address which aids the transition from IPv4 to IPv6. The
+ # structure of the address is
+ #
+ # ::ffff:w.y.x.z
+ #
+ # where w.x.y.z is a normal IPv4 address. For example, the following is
+ # a mapped IPv6 address:
+ #
+ # ::ffff:192.168.100.1
+ #
+ # IPAddress is very powerful in handling mapped IPv6 addresses, as the
+ # IPv4 portion is stored internally as a normal IPv4 object. Let's have
+ # a look at some examples. To create a new mapped address, just use the
+ # class builder itself
+ #
+ # ip6 = IPAddress::IPv6::Mapped.new "::ffff:172.16.10.1/128"
+ #
+ # or just use the wrapper method
+ #
+ # ip6 = IPAddress "::ffff:172.16.10.1/128"
+ #
+ # Let's check it's really a mapped address:
+ #
+ # ip6.mapped?
+ # #=> true
+ #
+ # ip6.to_s
+ # #=> "::FFFF:172.16.10.1/128"
+ #
+ # Now with the +ipv4+ attribute, we can easily access the IPv4 portion
+ # of the mapped IPv6 address:
+ #
+ # ip6.ipv4.address
+ # #=> "172.16.10.1"
+ #
+ # Internally, the IPv4 address is stored as two 16 bits
+ # groups. Therefore all the usual methods for an IPv6 address are
+ # working perfectly fine:
+ #
+ # ip6.to_hex
+ # #=> "00000000000000000000ffffac100a01"
+ #
+ # ip6.address
+ # #=> "0000:0000:0000:0000:0000:ffff:ac10:0a01"
+ #
+ # A mapped IPv6 can also be created just by specify the address in the
+ # following format:
+ #
+ # ip6 = IPAddress "::172.16.10.1"
+ #
+ # That is, two colons and the IPv4 address. However, as by RFC, the ffff
+ # group will be automatically added at the beginning
+ #
+ # ip6.to_s
+ # => "::ffff:172.16.10.1/128"
+ #
+ # making it a mapped IPv6 compatible address.
+ #
+ class IPAddress::IPv6::Mapped < IPAddress::IPv6
+
+ # Access the internal IPv4 address
+ attr_reader :ipv4
+
+ #
+ # Creates a new IPv6 IPv4-mapped address
+ #
+ # ip6 = IPAddress::IPv6::Mapped.new "::ffff:172.16.10.1/128"
+ #
+ # ipv6.ipv4.class
+ # #=> IPAddress::IPv4
+ #
+ # An IPv6 IPv4-mapped address can also be created using the
+ # IPv6 only format of the address:
+ #
+ # ip6 = IPAddress::IPv6::Mapped.new "::0d01:4403"
+ #
+ # ip6.to_s
+ # #=> "::ffff:13.1.68.3"
+ #
+ def initialize(str)
+ string, netmask = str.split("/")
+ if string =~ /\./ # IPv4 in dotted decimal form
+ @ipv4 = IPAddress::IPv4.extract(string)
+ else # IPv4 in hex form
+ groups = IPAddress::IPv6.groups(string)
+ @ipv4 = IPAddress::IPv4.parse_u32((groups[-2]<< 16)+groups[-1])
+ end
+ super("::ffff:#{@ipv4.to_ipv6}/#{netmask}")
+ end
+
+ #
+ # Similar to IPv6#to_s, but prints out the IPv4 address
+ # in dotted decimal format
+ #
+ # ip6 = IPAddress "::ffff:172.16.10.1/128"
+ #
+ # ip6.to_s
+ # #=> "::ffff:172.16.10.1"
+ #
+ def to_s
+ "::ffff:#{@ipv4.address}"
+ end
+
+ #
+ # Similar to IPv6#to_string, but prints out the IPv4 address
+ # in dotted decimal format
+ #
+ #
+ # ip6 = IPAddress "::ffff:172.16.10.1/128"
+ #
+ # ip6.to_string
+ # #=> "::ffff:172.16.10.1/128"
+ #
+ def to_string
+ "::ffff:#{@ipv4.address}/#@prefix"
+ end
+
+ #
+ # Checks if the IPv6 address is IPv4 mapped
+ #
+ # ip6 = IPAddress "::ffff:172.16.10.1/128"
+ #
+ # ip6.mapped?
+ # #=> true
+ #
+ def mapped?
+ true
+ end
+ end # class IPv6::Mapped
+
+end # module IPAddress
+
diff --git a/lib/ipaddress/extensions/extensions.rb b/lib/ipaddress/extensions/extensions.rb
deleted file mode 100644
index 4e0bc88..0000000
--- a/lib/ipaddress/extensions/extensions.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-class << Math # :nodoc:
- def log2(n); log(n) / log(2); end
-end
-
-if RUBY_VERSION =~ /1\.8/
- class Hash # :nodoc:
- alias :key :index
- end
-end
-
-class Integer # :nodoc:
- def power_of_2?
- Math::log2(self).to_i == Math::log2(self)
- end
-
- def closest_power_of_2(limit=32)
- self.upto(limit) do |i|
- return i if i.power_of_2?
- end
- end
-end
-
diff --git a/lib/ipaddress/ipv4.rb b/lib/ipaddress/ipv4.rb
deleted file mode 100644
index 2a0ca65..0000000
--- a/lib/ipaddress/ipv4.rb
+++ /dev/null
@@ -1,996 +0,0 @@
-require 'ipaddress/prefix'
-
-module IPAddress;
- #
- # =Name
- #
- # IPAddress::IPv4 - IP version 4 address manipulation library
- #
- # =Synopsis
- #
- # require 'ipaddress'
- #
- # =Description
- #
- # Class IPAddress::IPv4 is used to handle IPv4 type addresses.
- #
- class IPv4
-
- 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
- }
-
- #
- # Regular expression to match an IPv4 address
- #
- REGEXP = Regexp.new(/((25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)/)
-
- #
- # 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
- # set as default 32, also known as the host prefix
- #
- # Examples:
- #
- # # These two are the same
- # ip = IPAddress::IPv4.new("10.0.0.1/24")
- # ip = IPAddress("10.0.0.1/24")
- #
- # # These two are the same
- # IPAddress::IPv4.new "10.0.0.1/8"
- # IPAddress::IPv4.new "10.0.0.1/255.0.0.0"
- #
- 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 = Prefix32.new(32)
- end
-
- # Array formed with the IP octets
- @octets = @address.split(".").map{|i| i.to_i}
- # 32 bits interger containing the address
- @u32 = (@octets[0]<< 24) + (@octets[1]<< 16) + (@octets[2]<< 8) + (@octets[3])
-
- 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::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 = 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 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_string
- # #=> "172.16.100.4/22"
- #
- 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
- 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_i
- # #=> 167772160
- #
- def u32
- @u32
- end
- alias_method :to_i, :u32
- alias_method :to_u32, :u32
-
- #
- # 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
- #
- # a = Socket.open(params) # socket details here
- # ip = IPAddress("10.1.1.0/24")
- # binary_data = ["Address: "].pack("a*") + ip.data
- #
- # # Send binary data
- # a.puts binary_data
- #
- def data
- [@u32].pack("N")
- 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
- data.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"
- #
- 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?
- @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"
- #
- 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"
- #
- # 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"
- #
- 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.254
- #
- # ip = IPAddress("192.168.100.0/24")
- #
- # ip.last.to_s
- # #=> "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"
- #
- 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_host do |i|
- # p i.to_s
- # 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
- (network_u32+1..broadcast_u32-1).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
- 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
- 2 ** @prefix.host_prefix
- 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
- @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
- network_u32 + size - 1
- end
-
- #
- # Checks whether a subnet includes the given IP address.
- #
- # Accepts an 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 == (oth.to_u32 & @prefix.to_u32)
- end
-
- #
- # Checks whether a subnet includes all the
- # given IPv4 objects.
- #
- # ip = IPAddress("192.168.10.100/24")
- #
- # addr1 = IPAddress("192.168.10.102/24")
- # addr2 = IPAddress("192.168.10.103/24")
- #
- # ip.include_all?(addr1,addr2)
- # #=> true
- #
- def include_all?(*others)
- others.all? {|oth| include?(oth)}
- end
-
- #
- # True if the object is an IPv4 address
- #
- # ip = IPAddress("192.168.10.100/24")
- #
- # ip.ipv4?
- # #-> true
- #
-# def ipv4?
-# true
-# end
-
- #
- # True if the object is an IPv6 address
- #
- # ip = IPAddress("192.168.10.100/24")
- #
- # ip.ipv6?
- # #-> false
- #
-# def ipv6?
-# false
-# end
-
- #
- # Checks if an IPv4 address objects belongs
- # to a private network RFC1918
- #
- # Example:
- #
- # ip = IPAddress "10.1.1.1/24"
- # ip.private?
- # #=> true
- #
- def private?
- [self.class.new("10.0.0.0/8"),
- self.class.new("172.16.0.0/12"),
- self.class.new("192.168.0.0/16")].any? {|i| i.include? self}
- 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
- alias_method :arpa, :reverse
-
- #
- # 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 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_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_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**@prefix.host_prefix)).include? subnets
- raise ArgumentError, "Value #{subnets} out of range"
- end
- calculate_subnets(subnets)
- 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_string
- # #=> "172.16.10.0/23"
- #
- # However if you supernet it with a /22 prefix, the
- # network address will change:
- #
- # 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
- self.class.new(@address+"/#{new_prefix}").network
- end
-
- #
- # 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
-
- #
- # Returns a new IPv4 object which is the result
- # of the summarization, if possible, of the two
- # objects
- #
- # Example:
- #
- # ip1 = IPAddress("172.16.10.1/24")
- # ip2 = IPAddress("172.16.11.2/24")
- #
- # 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)
- 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.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.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.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")
- end
-
- #
- # Creates a new IPv4 object from an
- # unsigned 32bits integer.
- #
- # ip = IPAddress::IPv4::parse_u32(167772160)
- #
- # ip.prefix = 8
- # ip.to_string
- # #=> "10.0.0.0/8"
- #
- # The +prefix+ parameter is optional:
- #
- # ip = IPAddress::IPv4::parse_u32(167772160, 8)
- #
- # ip.to_string
- # #=> "10.0.0.0/8"
- #
- def self.parse_u32(u32, prefix=32)
- self.new([u32].pack("N").unpack("C4").join(".")+"/#{prefix}")
- end
-
- #
- # Creates a new IPv4 object from binary data,
- # like the one you get from a network stream.
- #
- # For example, on a network stream the IP 172.16.0.1
- # is represented with the binary "\254\020\n\001".
- #
- # ip = IPAddress::IPv4::parse_data "\254\020\n\001"
- # ip.prefix = 24
- #
- # ip.to_string
- # #=> "172.16.10.1/24"
- #
- def self.parse_data(str, prefix=32)
- self.new(str.unpack("C4").join(".")+"/#{prefix}")
- end
-
- #
- # Extract an IPv4 address from a string and
- # returns a new object
- #
- # Example:
- #
- # str = "foobar172.16.10.1barbaz"
- # ip = IPAddress::IPv4::extract str
- #
- # ip.to_s
- # #=> "172.16.10.1"
- #
- def self.extract(str)
- self.new REGEXP.match(str).to_s
- 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_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_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.first.network] if args.size == 1
-
- 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.flatten!
- if result.size == args.size
- # nothing more to summarize
- return result
- else
- # keep on summarizing
- return self.summarize(*result)
- end
- end
-
- #
- # Creates a new IPv4 address object by parsing the
- # address in a classful way.
- #
- # Classful addresses have a fixed netmask based on the
- # class they belong to:
- #
- # * 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, D and E, from 192.0.0.0 to 255.255.255.254
- #
- # Note that classes C, D and E will all have a default
- # prefix of /24 or 255.255.255.0
- #
- # Example:
- #
- # ip = IPAddress::IPv4.parse_classful "10.0.0.1"
- #
- # ip.netmask
- # #=> "255.0.0.0"
- # ip.a?
- # #=> true
- #
- def self.parse_classful(ip)
- if IPAddress.valid_ipv4?(ip)
- address = ip.strip
- else
- raise ArgumentError, "Invalid IP #{ip.inspect}"
- end
- prefix = CLASSFUL.find{|h,k| h === ("%.8b" % address.to_i)}.last
- self.new "#{address}/#{prefix}"
- end
-
- #
- # private methods
- #
- private
-
- def calculate_subnets(subnets)
- po2 = subnets.closest_power_of_2
- 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
- until networks.size == subnets
- networks = sum_first_found(networks)
- end
- return networks
- end
-
- 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
-
- def aggregate(ip1,ip2)
- return [ip1] if ip1.include? ip2
-
- snet = ip1.supernet(ip1.prefix-1)
- if snet.include_all?(ip1, ip2) && ((ip1.size + ip2.size) == snet.size)
- return [snet]
- else
- return [ip1, ip2]
- end
- end
- end # class IPv4
-end # module IPAddress
-
diff --git a/lib/ipaddress/ipv6.rb b/lib/ipaddress/ipv6.rb
deleted file mode 100644
index 78ebe3d..0000000
--- a/lib/ipaddress/ipv6.rb
+++ /dev/null
@@ -1,776 +0,0 @@
-require 'ipaddress/prefix'
-
-module IPAddress;
- #
- # =Name
- #
- # IPAddress::IPv6 - IP version 6 address manipulation library
- #
- # =Synopsis
- #
- # require 'ipaddress'
- #
- # =Description
- #
- # Class IPAddress::IPv6 is used to handle IPv6 type addresses.
- #
- # == IPv6 addresses
- #
- # IPv6 addresses are 128 bits long, in contrast with IPv4 addresses
- # which are only 32 bits long. An IPv6 address is generally written as
- # eight groups of four hexadecimal digits, each group representing 16
- # bits or two octect. For example, the following is a valid IPv6
- # address:
- #
- # 1080:0000:0000:0000:0008:0800:200c:417a
- #
- # Letters in an IPv6 address are usually written downcase, as per
- # RFC. You can create a new IPv6 object using uppercase letters, but
- # they will be converted.
- #
- # === Compression
- #
- # Since IPv6 addresses are very long to write, there are some
- # semplifications and compressions that you can use to shorten them.
- #
- # * Leading zeroes: all the leading zeroes within a group can be
- # omitted: "0008" would become "8"
- #
- # * A string of consecutive zeroes can be replaced by the string
- # "::". This can be only applied once.
- #
- # Using compression, the IPv6 address written above can be shorten into
- # the following, equivalent, address
- #
- # 1080::8:800:200c:417a
- #
- # This short version is often used in human representation.
- #
- # === Network Mask
- #
- # As we used to do with IPv4 addresses, an IPv6 address can be written
- # using the prefix notation to specify the subnet mask:
- #
- # 1080::8:800:200c:417a/64
- #
- # The /64 part means that the first 64 bits of the address are
- # representing the network portion, and the last 64 bits are the host
- # portion.
- #
- #
- class IPv6
-
- include IPAddress
- include Enumerable
- include Comparable
-
-
- #
- # Format string to pretty print IPv6 addresses
- #
- IN6FORMAT = ("%.4x:"*8).chop
-
- #
- # Creates a new IPv6 address object.
- #
- # An IPv6 address can be expressed in any of the following forms:
- #
- # * "1080:0000:0000:0000:0008:0800:200C:417A": IPv6 address with no compression
- # * "1080:0:0:0:8:800:200C:417A": IPv6 address with leading zeros compression
- # * "1080::8:800:200C:417A": IPv6 address with full compression
- #
- # In all these 3 cases, a new IPv6 address object will be created, using the default
- # subnet mask /128
- #
- # You can also specify the subnet mask as with IPv4 addresses:
- #
- # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
- #
- def initialize(str)
- ip, netmask = str.split("/")
-
- if str =~ /:.+\./
- raise ArgumentError, "Please use #{self.class}::Mapped for IPv4 mapped addresses"
- end
-
- if IPAddress.valid_ipv6?(ip)
- @groups = self.class.groups(ip)
- @address = IN6FORMAT % @groups
- @compressed = compress_address
- else
- raise ArgumentError, "Invalid IP #{ip.inspect}"
- end
-
- @prefix = Prefix128.new(netmask ? netmask : 128)
-
- end # def initialize
-
- #
- # Returns the IPv6 address in uncompressed form:
- #
- # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
- #
- # ip6.address
- # #=> "2001:0db8:0000:0000:0008:0800:200c:417a"
- #
- def address
- @address
- end
-
- #
- # Returns an array with the 16 bits groups in decimal
- # format:
- #
- # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
- #
- # ip6.groups
- # #=> [8193, 3512, 0, 0, 8, 2048, 8204, 16762]
- #
- def groups
- @groups
- end
-
- #
- # Returns an instance of the prefix object
- #
- # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
- #
- # ip6.prefix
- # #=> 64
- #
- 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 IPv6::parse_u128 or
- # if the object was created using the default prefix
- # of 128 bits.
- #
- # ip6 = IPAddress("2001:db8::8:800:200c:417a")
- #
- # puts ip6.to_string
- # #=> "2001:db8::8:800:200c:417a/128"
- #
- # ip6.prefix = 64
- # puts ip6.to_string
- # #=> "2001:db8::8:800:200c:417a/64"
- #
- def prefix=(num)
- @prefix = Prefix128.new(num)
- end
-
- #
- # Unlike its counterpart IPv6#to_string method, IPv6#to_string_uncompressed
- # returns the whole IPv6 address and prefix in an uncompressed form
- #
- # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
- #
- # ip6.to_string_uncompressed
- # #=> "2001:0db8:0000:0000:0008:0800:200c:417a/64"
- #
- def to_string_uncompressed
- "#@address/#@prefix"
- end
-
- #
- # Returns the IPv6 address in a human readable form,
- # using the compressed address.
- #
- # ip6 = IPAddress "2001:0db8:0000:0000:0008:0800:200c:417a/64"
- #
- # ip6.to_string
- # #=> "2001:db8::8:800:200c:417a/64"
- #
- def to_string
- "#@compressed/#@prefix"
- end
-
- #
- # Returns the IPv6 address in a human readable form,
- # using the compressed address.
- #
- # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
- #
- # ip6.to_s
- # #=> "2001:db8::8:800:200c:417a"
- #
- def to_s
- @compressed
- end
-
- #
- # Returns a decimal format (unsigned 128 bit) of the
- # IPv6 address
- #
- # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
- #
- # ip6.to_i
- # #=> 42540766411282592856906245548098208122
- #
- def to_i
- to_hex.hex
- end
- alias_method :to_u128, :to_i
-
- #
- # True if the IPv6 address is a network
- #
- # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
- #
- # ip6.network?
- # #=> false
- #
- # ip6 = IPAddress "2001:db8:8:800::/64"
- #
- # ip6.network?
- # #=> true
- #
- def network?
- to_u128 | @prefix.to_u128 == @prefix.to_u128
- end
-
- #
- # Returns the 16-bits value specified by index
- #
- # ip = IPAddress("2001:db8::8:800:200c:417a/64")
- #
- # ip[0]
- # #=> 8193
- # ip[1]
- # #=> 3512
- # ip[2]
- # #=> 0
- # ip[3]
- # #=> 0
- #
- def [](index)
- @groups[index]
- end
- alias_method :group, :[]
-
- #
- # Returns a Base16 number representing the IPv6
- # address
- #
- # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
- #
- # ip6.to_hex
- # #=> "20010db80000000000080800200c417a"
- #
- def to_hex
- hexs.join("")
- end
-
- # Returns the address portion of an IPv6 object
- # in a network byte order format.
- #
- # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
- #
- # ip6.data
- # #=> " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
- #
- # 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
- # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
- # binary_data = ["Address: "].pack("a*") + ip.data
- #
- # # Send binary data
- # a.puts binary_data
- #
- def data
- @groups.pack("n8")
- end
-
- #
- # Returns an array of the 16 bits groups in hexdecimal
- # format:
- #
- # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
- #
- # ip6.hexs
- # #=> ["2001", "0db8", "0000", "0000", "0008", "0800", "200c", "417a"]
- #
- # Not to be confused with the similar IPv6#to_hex method.
- #
- def hexs
- @address.split(":")
- end
-
- #
- # Returns the IPv6 address in a DNS reverse lookup
- # string, as per RFC3172 and RFC2874.
- #
- # ip6 = IPAddress "3ffe:505:2::f"
- #
- # ip6.reverse
- # #=> "f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.arpa"
- #
- def reverse
- to_hex.reverse.gsub(/./){|c| c+"."} + "ip6.arpa"
- end
- alias_method :arpa, :reverse
-
- #
- # Returns the network number in Unsigned 128bits format
- #
- # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
- #
- # ip6.network_u128
- # #=> 42540766411282592856903984951653826560
- #
- def network_u128
- to_u128 & @prefix.to_u128
- end
-
- #
- # Checks whether a subnet includes the given IP address.
- #
- # Example:
- #
- # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
- # addr = IPAddress "2001:db8::8:800:200c:1/128"
- #
- # ip6.include? addr
- # #=> true
- #
- # ip6.include? IPAddress("2001:db8:1::8:800:200c:417a/76")
- # #=> false
- #
- def include?(oth)
- @prefix <= oth.prefix and network_u128 == self.class.new(oth.address+"/#@prefix").network_u128
- end
-
- #
- # Compressed form of the IPv6 address
- #
- # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
- #
- # ip6.compressed
- # #=> "2001:db8::8:800:200c:417a"
- #
- def compressed
- @compressed
- end
-
- #
- # Returns true if the address is an unspecified address
- #
- # See IPAddress::IPv6::Unspecified for more information
- #
- def unspecified?
- @prefix == 128 and @compressed == "::"
- end
-
- #
- # Returns true if the address is a loopback address
- #
- # See IPAddress::IPv6::Loopback for more information
- #
- def loopback?
- @prefix == 128 and @compressed == "::1"
- end
-
- #
- # Returns true if the address is a mapped address
- #
- # See IPAddress::IPv6::Mapped for more information
- #
- def mapped?
- to_u128 >> 32 == 0xffff
- end
-
- #
- # Returns the address portion of an IP in binary format,
- # as a string containing a sequence of 0 and 1
- #
- # ip6 = IPAddress("2001:db8::8:800:200c:417a")
- #
- # ip6.bits
- # #=> "0010000000000001000011011011100000 [...] "
- #
- def bits
- data.unpack("B*").first
- end
-
- #
- # Expands an IPv6 address in the canocical form
- #
- # IPAddress::IPv6.expand "2001:0DB8:0:CD30::"
- # #=> "2001:0DB8:0000:CD30:0000:0000:0000:0000"
- #
- def self.expand(str)
- self.new(str).address
- end
-
- #
- # Compress an IPv6 address in its compressed form
- #
- # IPAddress::IPv6.compress "2001:0DB8:0000:CD30:0000:0000:0000:0000"
- # #=> "2001:db8:0:cd30::"
- #
- def self.compress(str)
- self.new(str).compressed
- end
-
- #
- # Literal version of the IPv6 address
- #
- # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
- #
- # ip6.literal
- # #=> "2001-0db8-0000-0000-0008-0800-200c-417a.ipv6-literal.net"
- #
- def literal
- @address.gsub(":","-") + ".ipv6-literal.net"
- end
-
- #
- # Extract 16 bits groups from a string
- #
- def self.groups(str)
- l, r = if str =~ /^(.*)::(.*)$/
- [$1,$2].map {|i| i.split ":"}
- else
- [str.split(":"),[]]
- end
- (l + Array.new(8-l.size-r.size, '0') + r).map {|i| i.hex}
- end
-
- #
- # Creates a new IPv6 object from binary data,
- # like the one you get from a network stream.
- #
- # For example, on a network stream the IP
- #
- # "2001:db8::8:800:200c:417a"
- #
- # is represented with the binary data
- #
- # " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
- #
- # With that data you can create a new IPv6 object:
- #
- # ip6 = IPAddress::IPv6::parse_data " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
- # ip6.prefix = 64
- #
- # ip6.to_s
- # #=> "2001:db8::8:800:200c:417a/64"
- #
- def self.parse_data(str)
- self.new(IN6FORMAT % str.unpack("n8"))
- end
-
- #
- # Creates a new IPv6 object from an
- # unsigned 128 bits integer.
- #
- # ip6 = IPAddress::IPv6::parse_u128(21932261930451111902915077091070067066)
- # ip6.prefix = 64
- #
- # ip6.to_s
- # #=> "1080::8:800:200c:417a/64"
- #
- # The +prefix+ parameter is optional:
- #
- # ip6 = IPAddress::IPv6::parse_u128(21932261930451111902915077091070067066, 64)
- #
- # ip6.to_s
- # #=> "1080::8:800:200c:417a/64"
- #
- def self.parse_u128(u128, prefix=128)
- str = IN6FORMAT % (0..7).map{|i| (u128>>(112-16*i))&0xffff}
- self.new(str + "/#{prefix}")
- end
-
- #
- # Creates a new IPv6 object from a number expressed in
- # hexdecimal format:
- #
- # ip6 = IPAddress::IPv6::parse_hex("20010db80000000000080800200c417a")
- # ip6.prefix = 64
- #
- # ip6.to_s
- # #=> "2001:db8::8:800:200c:417a/64"
- #
- # The +prefix+ parameter is optional:
- #
- # ip6 = IPAddress::IPv6::parse_hex("20010db80000000000080800200c417a", 64)
- #
- # ip6.to_s
- # #=> "1080::8:800:200c:417a/64"
- #
- def self.parse_hex(hex, prefix=128)
- self.parse_u128(hex.hex, prefix)
- end
-
- private
-
- def compress_address
- str = @groups.map{|i| i.to_s 16}.join ":"
- loop do
- break if str.sub!(/\A0:0:0:0:0:0:0:0\Z/, '::')
- break if str.sub!(/\b0:0:0:0:0:0:0\b/, ':')
- break if str.sub!(/\b0:0:0:0:0:0\b/, ':')
- break if str.sub!(/\b0:0:0:0:0\b/, ':')
- break if str.sub!(/\b0:0:0:0\b/, ':')
- break if str.sub!(/\b0:0:0\b/, ':')
- break if str.sub!(/\b0:0\b/, ':')
- break
- end
- str.sub(/:{3,}/, '::')
- end
-
- end # class IPv6
-
- #
- # The address with all zero bits is called the +unspecified+ address
- # (corresponding to 0.0.0.0 in IPv4). It should be something like this:
- #
- # 0000:0000:0000:0000:0000:0000:0000:0000
- #
- # but, with the use of compression, it is usually written as just two
- # colons:
- #
- # ::
- #
- # or, specifying the netmask:
- #
- # ::/128
- #
- # With IPAddress, create a new unspecified IPv6 address using its own
- # subclass:
- #
- # ip = IPAddress::IPv6::Unspecified.new
- #
- # ip.to_s
- # #=> => "::/128"
- #
- # You can easily check if an IPv6 object is an unspecified address by
- # using the IPv6#unspecified? method
- #
- # ip.unspecified?
- # #=> true
- #
- # An unspecified IPv6 address can also be created with the wrapper
- # method, like we've seen before
- #
- # ip = IPAddress "::"
- #
- # ip.unspecified?
- # #=> true
- #
- # This address must never be assigned to an interface and is to be used
- # only in software before the application has learned its host's source
- # address appropriate for a pending connection. Routers must not forward
- # packets with the unspecified address.
- #
- class IPAddress::IPv6::Unspecified < IPAddress::IPv6
- #
- # Creates a new IPv6 unspecified address
- #
- # ip = IPAddress::IPv6::Unspecified.new
- #
- # ip.to_s
- # #=> => "::/128"
- #
- def initialize
- @address = ("0000:"*8).chop
- @groups = Array.new(8,0)
- @prefix = Prefix128.new(128)
- @compressed = compress_address
- end
- end # class IPv6::Unspecified
-
- #
- # The loopback address is a unicast localhost address. If an
- # application in a host sends packets to this address, the IPv6 stack
- # will loop these packets back on the same virtual interface.
- #
- # Loopback addresses are expressed in the following form:
- #
- # ::1
- #
- # or, with their appropriate prefix,
- #
- # ::1/128
- #
- # As for the unspecified addresses, IPv6 loopbacks can be created with
- # IPAddress calling their own class:
- #
- # ip = IPAddress::IPv6::Loopback.new
- #
- # ip.to_s
- # #=> "::1/128"
- #
- # or by using the wrapper:
- #
- # ip = IPAddress "::1"
- #
- # ip.to_s
- # #=> "::1/128"
- #
- # Checking if an address is loopback is easy with the IPv6#loopback?
- # method:
- #
- # ip.loopback?
- # #=> true
- #
- # The IPv6 loopback address corresponds to 127.0.0.1 in IPv4.
- #
- class IPAddress::IPv6::Loopback < IPAddress::IPv6
- #
- # Creates a new IPv6 unspecified address
- #
- # ip = IPAddress::IPv6::Loopback.new
- #
- # ip.to_s
- # #=> "::1/128"
- #
- def initialize
- @address = ("0000:"*7)+"0001"
- @groups = Array.new(7,0).push(1)
- @prefix = Prefix128.new(128)
- @compressed = compress_address
- end
- end # class IPv6::Loopback
-
- #
- # It is usually identified as a IPv4 mapped IPv6 address, a particular
- # IPv6 address which aids the transition from IPv4 to IPv6. The
- # structure of the address is
- #
- # ::ffff:w.y.x.z
- #
- # where w.x.y.z is a normal IPv4 address. For example, the following is
- # a mapped IPv6 address:
- #
- # ::ffff:192.168.100.1
- #
- # IPAddress is very powerful in handling mapped IPv6 addresses, as the
- # IPv4 portion is stored internally as a normal IPv4 object. Let's have
- # a look at some examples. To create a new mapped address, just use the
- # class builder itself
- #
- # ip6 = IPAddress::IPv6::Mapped.new "::ffff:172.16.10.1/128"
- #
- # or just use the wrapper method
- #
- # ip6 = IPAddress "::ffff:172.16.10.1/128"
- #
- # Let's check it's really a mapped address:
- #
- # ip6.mapped?
- # #=> true
- #
- # ip6.to_s
- # #=> "::FFFF:172.16.10.1/128"
- #
- # Now with the +ipv4+ attribute, we can easily access the IPv4 portion
- # of the mapped IPv6 address:
- #
- # ip6.ipv4.address
- # #=> "172.16.10.1"
- #
- # Internally, the IPv4 address is stored as two 16 bits
- # groups. Therefore all the usual methods for an IPv6 address are
- # working perfectly fine:
- #
- # ip6.to_hex
- # #=> "00000000000000000000ffffac100a01"
- #
- # ip6.address
- # #=> "0000:0000:0000:0000:0000:ffff:ac10:0a01"
- #
- # A mapped IPv6 can also be created just by specify the address in the
- # following format:
- #
- # ip6 = IPAddress "::172.16.10.1"
- #
- # That is, two colons and the IPv4 address. However, as by RFC, the ffff
- # group will be automatically added at the beginning
- #
- # ip6.to_s
- # => "::ffff:172.16.10.1/128"
- #
- # making it a mapped IPv6 compatible address.
- #
- class IPAddress::IPv6::Mapped < IPAddress::IPv6
-
- # Access the internal IPv4 address
- attr_reader :ipv4
-
- #
- # Creates a new IPv6 IPv4-mapped address
- #
- # ip6 = IPAddress::IPv6::Mapped.new "::ffff:172.16.10.1/128"
- #
- # ipv6.ipv4.class
- # #=> IPAddress::IPv4
- #
- # An IPv6 IPv4-mapped address can also be created using the
- # IPv6 only format of the address:
- #
- # ip6 = IPAddress::IPv6::Mapped.new "::0d01:4403"
- #
- # ip6.to_s
- # #=> "::ffff:13.1.68.3"
- #
- def initialize(str)
- string, netmask = str.split("/")
- if string =~ /\./ # IPv4 in dotted decimal form
- @ipv4 = IPAddress::IPv4.extract(string)
- else # IPv4 in hex form
- groups = IPAddress::IPv6.groups(string)
- @ipv4 = IPAddress::IPv4.parse_u32((groups[-2]<< 16)+groups[-1])
- end
- super("::ffff:#{@ipv4.to_ipv6}/#{netmask}")
- end
-
- #
- # Similar to IPv6#to_s, but prints out the IPv4 address
- # in dotted decimal format
- #
- # ip6 = IPAddress "::ffff:172.16.10.1/128"
- #
- # ip6.to_s
- # #=> "::ffff:172.16.10.1"
- #
- def to_s
- "::ffff:#{@ipv4.address}"
- end
-
- #
- # Similar to IPv6#to_string, but prints out the IPv4 address
- # in dotted decimal format
- #
- #
- # ip6 = IPAddress "::ffff:172.16.10.1/128"
- #
- # ip6.to_string
- # #=> "::ffff:172.16.10.1/128"
- #
- def to_string
- "::ffff:#{@ipv4.address}/#@prefix"
- end
-
- #
- # Checks if the IPv6 address is IPv4 mapped
- #
- # ip6 = IPAddress "::ffff:172.16.10.1/128"
- #
- # ip6.mapped?
- # #=> true
- #
- def mapped?
- true
- end
- end # class IPv6::Mapped
-
-end # module IPAddress
-
diff --git a/lib/ipaddress/prefix.rb b/lib/ipaddress/prefix.rb
deleted file mode 100644
index aac2090..0000000
--- a/lib/ipaddress/prefix.rb
+++ /dev/null
@@ -1,252 +0,0 @@
-module IPAddress
-
- #
- # =NAME
- #
- # IPAddress::Prefix
- #
- # =SYNOPSIS
- #
- # Parent class for Prefix32 and Prefix128
- #
- # =DESCRIPTION
- #
- # IPAddress::Prefix is the parent class for IPAddress::Prefix32
- # and IPAddress::Prefix128, defining some modules in common for
- # both the subclasses.
- #
- # IPAddress::Prefix shouldn't be accesses directly, unless
- # for particular needs.
- #
- class Prefix
-
- include Comparable
-
- attr_reader :prefix
-
- #
- # Creates a new general prefix
- #
- def initialize(num)
- @prefix = num.to_i
- end
-
- #
- # Returns a string with the prefix
- #
- def to_s
- "#@prefix"
- end
- alias_method :inspect, :to_s
-
- #
- # Returns the prefix
- #
- def to_i
- @prefix
- end
-
- #
- # Compare the prefix
- #
- def <=>(oth)
- @prefix <=> oth.to_i
- end
-
- #
- # Sums two prefixes or a prefix to a
- # number, returns a Fixnum
- #
- def +(oth)
- if oth.is_a? Fixnum
- self.prefix + oth
- else
- self.prefix + oth.prefix
- end
- end
-
- #
- # Returns the difference between two
- # prefixes, or a prefix and a number,
- # as a Fixnum
- #
- def -(oth)
- if oth.is_a? Fixnum
- self.prefix - oth
- else
- (self.prefix - oth.prefix).abs
- end
- end
-
- end # class Prefix
-
-
- class Prefix32 < Prefix
-
- IN4MASK = 0xffffffff
-
- #
- # Creates a new prefix object for 32 bits IPv4 addresses
- #
- # prefix = IPAddress::Prefix32.new 24
- # #=> 24
- #
- def initialize(num)
- unless (0..32).include? num
- raise ArgumentError, "Prefix must be in range 0..32, got: #{num}"
- end
- super(num)
- end
-
- #
- # Returns the length of the host portion
- # of a netmask.
- #
- # prefix = Prefix32.new 24
- #
- # prefix.host_prefix
- # #=> 8
- #
- def host_prefix
- 32 - @prefix
- end
-
- #
- # Transforms the prefix into a string of bits
- # representing the netmask
- #
- # prefix = IPAddress::Prefix32.new 24
- #
- # prefix.bits
- # #=> "11111111111111111111111100000000"
- #
- def bits
- "%.32b" % to_u32
- end
-
- #
- # Gives the prefix in IPv4 dotted decimal format,
- # i.e. the canonical netmask we're all used to
- #
- # prefix = IPAddress::Prefix32.new 24
- #
- # prefix.to_ip
- # #=> "255.255.255.0"
- #
- def to_ip
- [bits].pack("B*").unpack("CCCC").join(".")
- end
-
- #
- # An array of octets of the IPv4 dotted decimal
- # format
- #
- # prefix = IPAddress::Prefix32.new 24
- #
- # prefix.octets
- # #=> [255, 255, 255, 0]
- #
- def octets
- to_ip.split(".").map{|i| i.to_i}
- end
-
- #
- # Unsigned 32 bits decimal number representing
- # the prefix
- #
- # prefix = IPAddress::Prefix32.new 24
- #
- # prefix.to_u32
- # #=> 4294967040
- #
- def to_u32
- (IN4MASK >> host_prefix) << host_prefix
- end
-
- #
- # Shortcut for the octecs in the dotted decimal
- # representation
- #
- # prefix = IPAddress::Prefix32.new 24
- #
- # prefix[2]
- # #=> 255
- #
- def [](index)
- octets[index]
- end
-
- #
- # The hostmask is the contrary of the subnet mask,
- # as it shows the bits that can change within the
- # hosts
- #
- # prefix = IPAddress::Prefix32.new 24
- #
- # prefix.hostmask
- # #=> "0.0.0.255"
- #
- def hostmask
- [~to_u32].pack("N").unpack("CCCC").join(".")
- end
-
- #
- # Creates a new prefix by parsing a netmask in
- # dotted decimal form
- #
- # prefix = IPAddress::Prefix32::parse_netmask "255.255.255.0"
- # #=> 24
- #
- 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 self.new(num)
- end
-
- end # class Prefix32 < Prefix
-
- class Prefix128 < Prefix
-
- #
- # Creates a new prefix object for 128 bits IPv6 addresses
- #
- # prefix = IPAddress::Prefix128.new 64
- # #=> 64
- #
- def initialize(num=128)
- unless (1..128).include? num.to_i
- raise ArgumentError, "Prefix must be in range 1..128, got: #{num}"
- end
- super(num.to_i)
- end
-
- #
- # Transforms the prefix into a string of bits
- # representing the netmask
- #
- # prefix = IPAddress::Prefix128.new 64
- #
- # prefix.bits
- # #=> "1111111111111111111111111111111111111111111111111111111111111111"
- # "0000000000000000000000000000000000000000000000000000000000000000"
- #
- def bits
- "1" * @prefix + "0" * (128 - @prefix)
- end
-
- #
- # Unsigned 128 bits decimal number representing
- # the prefix
- #
- # prefix = IPAddress::Prefix128.new 64
- #
- # prefix.to_u128
- # #=> 340282366920938463444927863358058659840
- #
- def to_u128
- bits.to_i(2)
- end
-
- end # class Prefix123 < Prefix
-
-end # module IPAddress
diff --git a/test/ipaddress/extensions/extensions_test.rb b/test/ipaddress/extensions/extensions_test.rb
deleted file mode 100644
index 06b0a2c..0000000
--- a/test/ipaddress/extensions/extensions_test.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-require 'test_helper'
-
-class ExtensionsTest < Test::Unit::TestCase
-
- def test_method_power_of_2?
- assert_equal true, 16.power_of_2?
- assert_equal false, 20.power_of_2?
- end
-
- def test_method_closest_power_of_2
- assert_equal 8, 6.closest_power_of_2
- assert_equal 16, 13.closest_power_of_2
- assert_equal 32, 24.closest_power_of_2
- end
-
-end
-
-
diff --git a/test/ipaddress/ipv4_test.rb b/test/ipaddress/ipv4_test.rb
deleted file mode 100644
index 8cb3b8f..0000000
--- a/test/ipaddress/ipv4_test.rb
+++ /dev/null
@@ -1,510 +0,0 @@
-require 'test_helper'
-
-class IPv4Test < Test::Unit::TestCase
-
- def setup
- @klass = IPAddress::IPv4
-
- @valid_ipv4 = {
- "0.0.0.0/0" => ["0.0.0.0", 0],
- "10.0.0.0" => ["10.0.0.0", 32],
- "10.0.0.1" => ["10.0.0.1", 32],
- "10.0.0.1/24" => ["10.0.0.1", 24],
- "10.0.0.1/255.255.255.0" => ["10.0.0.1", 24]}
-
- @invalid_ipv4 = ["10.0.0.256",
- "10.0.0.0.0",
- "10.0.0",
- "10.0"]
-
- @valid_ipv4_range = ["10.0.0.1-254",
- "10.0.1-254.0",
- "10.1-254.0.0"]
-
- @netmask_values = {
- "0.0.0.0/0" => "0.0.0.0",
- "10.0.0.0/8" => "255.0.0.0",
- "172.16.0.0/16" => "255.255.0.0",
- "192.168.0.0/24" => "255.255.255.0",
- "192.168.100.4/30" => "255.255.255.252"}
-
- @decimal_values ={
- "0.0.0.0/0" => 0,
- "10.0.0.0/8" => 167772160,
- "172.16.0.0/16" => 2886729728,
- "192.168.0.0/24" => 3232235520,
- "192.168.100.4/30" => 3232261124}
-
- @ip = @klass.new("172.16.10.1/24")
- @network = @klass.new("172.16.10.0/24")
-
- @broadcast = {
- "10.0.0.0/8" => "10.255.255.255/8",
- "172.16.0.0/16" => "172.16.255.255/16",
- "192.168.0.0/24" => "192.168.0.255/24",
- "192.168.100.4/30" => "192.168.100.7/30"}
-
- @networks = {
- "10.5.4.3/8" => "10.0.0.0/8",
- "172.16.5.4/16" => "172.16.0.0/16",
- "192.168.4.3/24" => "192.168.4.0/24",
- "192.168.100.5/30" => "192.168.100.4/30"}
-
- @class_a = @klass.new("10.0.0.1/8")
- @class_b = @klass.new("172.16.0.1/16")
- @class_c = @klass.new("192.168.0.1/24")
-
- @classful = {
- "10.1.1.1" => 8,
- "150.1.1.1" => 16,
- "200.1.1.1" => 24 }
-
- end
-
- def test_initialize
- @valid_ipv4.keys.each do |i|
- ip = @klass.new(i)
- assert_instance_of @klass, ip
- end
- assert_instance_of IPAddress::Prefix32, @ip.prefix
- assert_raise (ArgumentError) do
- @klass.new
- end
- assert_nothing_raised do
- @klass.new "10.0.0.0/8"
- end
- end
-
- def test_initialize_format_error
- @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_initialize_without_prefix
- assert_nothing_raised do
- @klass.new("10.10.0.0")
- end
- ip = @klass.new("10.10.0.0")
- assert_instance_of IPAddress::Prefix32, ip.prefix
- assert_equal 32, ip.prefix.to_i
- end
-
- def test_attributes
- @valid_ipv4.each do |arg,attr|
- ip = @klass.new(arg)
- assert_equal attr.first, ip.address
- assert_equal attr.last, ip.prefix.to_i
- end
- end
-
- def test_octets
- ip = @klass.new("10.1.2.3/8")
- assert_equal ip.octets, [10,1,2,3]
- end
-
- def test_initialize_should_require_ip
- assert_raise(ArgumentError) { @klass.new }
- end
-
- def test_method_data
- assert_equal "\254\020\n\001", @ip.data
- end
-
- def test_method_to_string
- @valid_ipv4.each do |arg,attr|
- ip = @klass.new(arg)
- assert_equal attr.join("/"), ip.to_string
- end
- end
-
- def test_method_to_s
- @valid_ipv4.each do |arg,attr|
- ip = @klass.new(arg)
- assert_equal attr.first, ip.to_s
- end
- end
-
- def test_netmask
- @netmask_values.each do |addr,mask|
- ip = @klass.new(addr)
- assert_equal mask, ip.netmask
- end
- end
-
- def test_method_to_u32
- @decimal_values.each do |addr,int|
- ip = @klass.new(addr)
- assert_equal int, ip.to_u32
- end
- end
-
- def test_method_network?
- assert_equal true, @network.network?
- assert_equal false, @ip.network?
- end
-
- def test_method_broadcast
- @broadcast.each do |addr,bcast|
- ip = @klass.new(addr)
- assert_instance_of @klass, ip.broadcast
- assert_equal bcast, ip.broadcast.to_string
- end
- end
-
- def test_method_network
- @networks.each do |addr,net|
- ip = @klass.new addr
- assert_instance_of @klass, ip.network
- assert_equal net, ip.network.to_string
- end
- end
-
- def test_method_bits
- ip = @klass.new("127.0.0.1")
- assert_equal "01111111000000000000000000000001", ip.bits
- end
-
- def test_method_first
- ip = @klass.new("192.168.100.0/24")
- assert_instance_of @klass, ip.first
- assert_equal "192.168.100.1", ip.first.to_s
- ip = @klass.new("192.168.100.50/24")
- assert_instance_of @klass, ip.first
- assert_equal "192.168.100.1", ip.first.to_s
- end
-
- def test_method_last
- ip = @klass.new("192.168.100.0/24")
- assert_instance_of @klass, ip.last
- assert_equal "192.168.100.254", ip.last.to_s
- ip = @klass.new("192.168.100.50/24")
- assert_instance_of @klass, ip.last
- assert_equal "192.168.100.254", ip.last.to_s
- end
-
- def test_method_each_host
- ip = @klass.new("10.0.0.1/29")
- arr = []
- ip.each_host {|i| arr << i.to_s}
- expected = ["10.0.0.1","10.0.0.2","10.0.0.3",
- "10.0.0.4","10.0.0.5","10.0.0.6"]
- assert_equal expected, arr
- end
-
- def test_method_each
- ip = @klass.new("10.0.0.1/29")
- arr = []
- ip.each {|i| arr << i.to_s}
- expected = ["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"]
- assert_equal expected, arr
- end
-
- def test_method_size
- ip = @klass.new("10.0.0.1/29")
- assert_equal 8, ip.size
- end
-
- def test_method_hosts
- ip = @klass.new("10.0.0.1/29")
- expected = ["10.0.0.1","10.0.0.2","10.0.0.3",
- "10.0.0.4","10.0.0.5","10.0.0.6"]
- assert_equal expected, ip.hosts.map {|i| i.to_s}
- end
-
- def test_method_network_u32
- assert_equal 2886732288, @ip.network_u32
- end
-
- def test_method_broadcast_u32
- assert_equal 2886732543, @ip.broadcast_u32
- end
-
- def test_method_include?
- ip = @klass.new("192.168.10.100/24")
- addr = @klass.new("192.168.10.102/24")
- assert_equal true, ip.include?(addr)
- assert_equal false, ip.include?(@klass.new("172.16.0.48"))
- ip = @klass.new("10.0.0.0/8")
- assert_equal true, ip.include?(@klass.new("10.0.0.0/9"))
- assert_equal true, ip.include?(@klass.new("10.1.1.1/32"))
- assert_equal true, ip.include?(@klass.new("10.1.1.1/9"))
- assert_equal false, ip.include?(@klass.new("172.16.0.0/16"))
- assert_equal false, ip.include?(@klass.new("10.0.0.0/7"))
- assert_equal false, ip.include?(@klass.new("5.5.5.5/32"))
- assert_equal false, ip.include?(@klass.new("11.0.0.0/8"))
- ip = @klass.new("13.13.0.0/13")
- assert_equal false, ip.include?(@klass.new("13.16.0.0/32"))
- end
-
- def test_method_include_all?
- ip = @klass.new("192.168.10.100/24")
- addr1 = @klass.new("192.168.10.102/24")
- addr2 = @klass.new("192.168.10.103/24")
- assert_equal true, ip.include_all?(addr1,addr2)
- assert_equal false, ip.include_all?(addr1, @klass.new("13.16.0.0/32"))
- end
-
- def test_method_ipv4?
- assert_equal true, @ip.ipv4?
- end
-
- def test_method_ipv6?
- assert_equal false, @ip.ipv6?
- end
-
- def test_method_private?
- assert_equal true, @klass.new("192.168.10.50/24").private?
- assert_equal true, @klass.new("192.168.10.50/16").private?
- assert_equal true, @klass.new("172.16.77.40/24").private?
- assert_equal true, @klass.new("172.16.10.50/14").private?
- assert_equal true, @klass.new("10.10.10.10/10").private?
- assert_equal true, @klass.new("10.0.0.0/8").private?
- assert_equal false, @klass.new("192.168.10.50/12").private?
- assert_equal false, @klass.new("3.3.3.3").private?
- assert_equal false, @klass.new("10.0.0.0/7").private?
- assert_equal false, @klass.new("172.32.0.0/12").private?
- assert_equal false, @klass.new("172.16.0.0/11").private?
- assert_equal false, @klass.new("192.0.0.2/24").private?
- end
-
- def test_method_octet
- assert_equal 172, @ip[0]
- assert_equal 16, @ip[1]
- assert_equal 10, @ip[2]
- assert_equal 1, @ip[3]
- end
-
- def test_method_a?
- assert_equal true, @class_a.a?
- assert_equal false, @class_b.a?
- assert_equal false, @class_c.a?
- end
-
- def test_method_b?
- assert_equal true, @class_b.b?
- assert_equal false, @class_a.b?
- assert_equal false, @class_c.b?
- end
-
- def test_method_c?
- assert_equal true, @class_c.c?
- assert_equal false, @class_a.c?
- assert_equal false, @class_b.c?
- end
-
- def test_method_to_ipv6
- assert_equal "ac10:0a01", @ip.to_ipv6
- end
-
- def test_method_reverse
- assert_equal "1.10.16.172.in-addr.arpa", @ip.reverse
- end
-
- def test_method_comparabble
- ip1 = @klass.new("10.1.1.1/8")
- 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, 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
- assert_equal true, ip1 == ip4
- # test sorting
- arr = ["10.1.1.1/16","10.1.1.1/8","172.16.1.1/14"]
- assert_equal arr, [ip1,ip2,ip3].sort.map{|s| s.to_string}
- 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_plus
- ip1 = @klass.new("172.16.10.1/24")
- ip2 = @klass.new("172.16.11.2/24")
- assert_equal ["172.16.10.0/23"], (ip1+ip2).map{|i| i.to_string}
-
- ip2 = @klass.new("172.16.12.2/24")
- assert_equal [ip1.network.to_string, ip2.network.to_string],
- (ip1 + ip2).map{|i| i.to_string}
-
- ip1 = @klass.new("10.0.0.0/23")
- ip2 = @klass.new("10.0.2.0/24")
- assert_equal ["10.0.0.0/23","10.0.2.0/24"], (ip1+ip2).map{|i| i.to_string}
-
- ip1 = @klass.new("10.0.0.0/23")
- ip2 = @klass.new("10.0.2.0/24")
- assert_equal ["10.0.0.0/23","10.0.2.0/24"], (ip2+ip1).map{|i| i.to_string}
-
- ip1 = @klass.new("10.0.0.0/16")
- ip2 = @klass.new("10.0.2.0/24")
- assert_equal ["10.0.0.0/16"], (ip1+ip2).map{|i| i.to_string}
-
- ip1 = @klass.new("10.0.0.0/23")
- ip2 = @klass.new("10.1.0.0/24")
- assert_equal ["10.0.0.0/23","10.1.0.0/24"], (ip1+ip2).map{|i| i.to_string}
-
- end
-
- def test_method_netmask_equal
- ip = @klass.new("10.1.1.1/16")
- assert_equal 16, ip.prefix.to_i
- ip.netmask = "255.255.255.0"
- 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(0)}
- assert_raise(ArgumentError) {@ip.supernet(24)}
- 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|
- ip = @klass.parse_u32(int)
- ip.prefix = addr.split("/").last.to_i
- assert_equal ip.to_string, addr
- end
- end
-
- def test_classhmethod_extract
- str = "foobar172.16.10.1barbaz"
- assert_equal "172.16.10.1", @klass.extract(str).to_s
- end
-
- def test_classmethod_summarize
-
- # Should return self if only one network given
- assert_equal [@ip.network], @klass.summarize(@ip)
-
- # Summarize homogeneous networks
- ip1 = @klass.new("172.16.10.1/24")
- ip2 = @klass.new("172.16.11.2/24")
- assert_equal ["172.16.10.0/23"], @klass.summarize(ip1,ip2).map{|i| i.to_string}
-
- ip1 = @klass.new("10.0.0.1/24")
- ip2 = @klass.new("10.0.1.1/24")
- ip3 = @klass.new("10.0.2.1/24")
- ip4 = @klass.new("10.0.3.1/24")
- assert_equal ["10.0.0.0/22"], @klass.summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string}
- assert_equal ["10.0.0.0/22"], @klass.summarize(ip4,ip3,ip2,ip1).map{|i| i.to_string}
-
- # Summarize non homogeneous networks
- ip1 = @klass.new("10.0.0.0/23")
- ip2 = @klass.new("10.0.2.0/24")
- assert_equal ["10.0.0.0/23","10.0.2.0/24"], @klass.summarize(ip1,ip2).map{|i| i.to_string}
-
- ip1 = @klass.new("10.0.0.0/16")
- ip2 = @klass.new("10.0.2.0/24")
- assert_equal ["10.0.0.0/16"], @klass.summarize(ip1,ip2).map{|i| i.to_string}
-
- ip1 = @klass.new("10.0.0.0/23")
- ip2 = @klass.new("10.1.0.0/24")
- assert_equal ["10.0.0.0/23","10.1.0.0/24"], @klass.summarize(ip1,ip2).map{|i| i.to_string}
-
- ip1 = @klass.new("10.0.0.0/23")
- ip2 = @klass.new("10.0.2.0/23")
- ip3 = @klass.new("10.0.4.0/24")
- ip4 = @klass.new("10.0.6.0/24")
- assert_equal ["10.0.0.0/22","10.0.4.0/24","10.0.6.0/24"],
- @klass.summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string}
-
- ip1 = @klass.new("10.0.1.1/24")
- ip2 = @klass.new("10.0.2.1/24")
- ip3 = @klass.new("10.0.3.1/24")
- ip4 = @klass.new("10.0.4.1/24")
- result = ["10.0.1.0/24","10.0.2.0/23","10.0.4.0/24"]
- assert_equal result, @klass.summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string}
- assert_equal result, @klass.summarize(ip4,ip3,ip2,ip1).map{|i| i.to_string}
-
- ip1 = @klass.new("10.0.1.1/24")
- ip2 = @klass.new("10.10.2.1/24")
- ip3 = @klass.new("172.16.0.1/24")
- ip4 = @klass.new("172.16.1.1/24")
- result = ["10.0.1.0/24","10.10.2.0/24","172.16.0.0/23"]
- assert_equal result, @klass.summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string}
-
- ips = [@klass.new("10.0.0.12/30"),
- @klass.new("10.0.100.0/24")]
- result = ["10.0.0.12/30", "10.0.100.0/24"]
- assert_equal result, @klass.summarize(*ips).map{|i| i.to_string}
-
- ips = [@klass.new("172.16.0.0/31"),
- @klass.new("10.10.2.1/32")]
- result = ["10.10.2.1/32", "172.16.0.0/31"]
- assert_equal result, @klass.summarize(*ips).map{|i| i.to_string}
-
- ips = [@klass.new("172.16.0.0/32"),
- @klass.new("10.10.2.1/32")]
- result = ["10.10.2.1/32", "172.16.0.0/32"]
- assert_equal result, @klass.summarize(*ips).map{|i| i.to_string}
-
- end
-
- def test_classmethod_parse_data
- ip = @klass.parse_data "\254\020\n\001"
- assert_instance_of @klass, ip
- assert_equal "172.16.10.1", ip.address
- assert_equal "172.16.10.1/32", ip.to_string
- end
-
- def test_classmethod_parse_classful
- @classful.each do |ip,prefix|
- res = @klass.parse_classful(ip)
- assert_equal prefix, res.prefix
- assert_equal "#{ip}/#{prefix}", res.to_string
- end
- assert_raise(ArgumentError){ @klass.parse_classful("192.168.256.257") }
- end
-
-end # class IPv4Test
-
-
diff --git a/test/ipaddress/ipv6_test.rb b/test/ipaddress/ipv6_test.rb
deleted file mode 100644
index 84be76c..0000000
--- a/test/ipaddress/ipv6_test.rb
+++ /dev/null
@@ -1,356 +0,0 @@
-require 'test_helper'
-
-class IPv6Test < Test::Unit::TestCase
-
- def setup
- @klass = IPAddress::IPv6
-
- @compress_addr = {
- "2001:db8:0000:0000:0008:0800:200c:417a" => "2001:db8::8:800:200c:417a",
- "2001:db8:0:0:8:800:200c:417a" => "2001:db8::8:800:200c:417a",
- "ff01:0:0:0:0:0:0:101" => "ff01::101",
- "0:0:0:0:0:0:0:1" => "::1",
- "0:0:0:0:0:0:0:0" => "::"}
-
- @valid_ipv6 = { # Kindly taken from the python IPy library
- "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210" => 338770000845734292534325025077361652240,
- "1080:0000:0000:0000:0008:0800:200C:417A" => 21932261930451111902915077091070067066,
- "1080:0:0:0:8:800:200C:417A" => 21932261930451111902915077091070067066,
- "1080:0::8:800:200C:417A" => 21932261930451111902915077091070067066,
- "1080::8:800:200C:417A" => 21932261930451111902915077091070067066,
- "FF01:0:0:0:0:0:0:43" => 338958331222012082418099330867817087043,
- "FF01:0:0::0:0:43" => 338958331222012082418099330867817087043,
- "FF01::43" => 338958331222012082418099330867817087043,
- "0:0:0:0:0:0:0:1" => 1,
- "0:0:0::0:0:1" => 1,
- "::1" => 1,
- "0:0:0:0:0:0:0:0" => 0,
- "0:0:0::0:0:0" => 0,
- "::" => 0,
- "1080:0:0:0:8:800:200C:417A" => 21932261930451111902915077091070067066,
- "1080::8:800:200C:417A" => 21932261930451111902915077091070067066}
-
- @invalid_ipv6 = [":1:2:3:4:5:6:7",
- ":1:2:3:4:5:6:7"]
-
- @ip = @klass.new "2001:db8::8:800:200c:417a/64"
- @network = @klass.new "2001:db8:8:800::/64"
- @arr = [8193,3512,0,0,8,2048,8204,16762]
- @hex = "20010db80000000000080800200c417a"
- end
-
- def test_attribute_address
- addr = "2001:0db8:0000:0000:0008:0800:200c:417a"
- assert_equal addr, @ip.address
- end
-
- def test_initialize
- assert_instance_of @klass, @ip
- @valid_ipv6.keys.each do |ip|
- assert_nothing_raised {@klass.new ip}
- end
- @invalid_ipv6.each do |ip|
- assert_raise(ArgumentError) {@klass.new ip}
- end
- assert_equal 64, @ip.prefix
-
- assert_raise(ArgumentError) {
- @klass.new "::10.1.1.1"
- }
- end
-
- def test_attribute_groups
- assert_equal @arr, @ip.groups
- end
-
- def test_method_hexs
- arr = "2001:0db8:0000:0000:0008:0800:200c:417a".split(":")
- assert_equal arr, @ip.hexs
- end
-
- def test_method_to_i
- @valid_ipv6.each do |ip,num|
- assert_equal num, @klass.new(ip).to_i
- end
- end
-
- def test_method_bits
- bits = "0010000000000001000011011011100000000000000000000" +
- "000000000000000000000000000100000001000000000000010000" +
- "0000011000100000101111010"
- assert_equal bits, @ip.bits
- end
-
- def test_method_prefix=()
- ip = @klass.new "2001:db8::8:800:200c:417a"
- assert_equal 128, ip.prefix
- ip.prefix = 64
- assert_equal 64, ip.prefix
- assert_equal "2001:db8::8:800:200c:417a/64", ip.to_string
- end
-
- def test_method_mapped?
- assert_equal false, @ip.mapped?
- ip6 = @klass.new "::ffff:1234:5678"
- assert_equal true, ip6.mapped?
- end
-
- def test_method_literal
- str = "2001-0db8-0000-0000-0008-0800-200c-417a.ipv6-literal.net"
- assert_equal str, @ip.literal
- end
-
- def test_method_group
- @arr.each_with_index do |val,index|
- assert_equal val, @ip[index]
- end
- end
-
- def test_method_ipv4?
- assert_equal false, @ip.ipv4?
- end
-
- def test_method_ipv6?
- assert_equal true, @ip.ipv6?
- end
-
- def test_method_network?
- assert_equal true, @network.network?
- assert_equal false, @ip.network?
- end
-
- def test_method_network_u128
- assert_equal 42540766411282592856903984951653826560, @ip.network_u128
- end
-
- def test_method_include?
- assert_equal true, @ip.include?(@ip)
- # test prefix on same address
- included = @klass.new "2001:db8::8:800:200c:417a/128"
- not_included = @klass.new "2001:db8::8:800:200c:417a/46"
- assert_equal true, @ip.include?(included)
- assert_equal false, @ip.include?(not_included)
- # test address on same prefix
- included = @klass.new "2001:db8::8:800:200c:0/64"
- not_included = @klass.new "2001:db8:1::8:800:200c:417a/64"
- assert_equal true, @ip.include?(included)
- assert_equal false, @ip.include?(not_included)
- # general test
- included = @klass.new "2001:db8::8:800:200c:1/128"
- not_included = @klass.new "2001:db8:1::8:800:200c:417a/76"
- assert_equal true, @ip.include?(included)
- assert_equal false, @ip.include?(not_included)
- end
-
- def test_method_to_hex
- assert_equal @hex, @ip.to_hex
- end
-
- def test_method_to_s
- assert_equal "2001:db8::8:800:200c:417a", @ip.to_s
- end
-
- def test_method_to_string
- assert_equal "2001:db8::8:800:200c:417a/64", @ip.to_string
- end
-
- def test_method_to_string_uncompressed
- str = "2001:0db8:0000:0000:0008:0800:200c:417a/64"
- assert_equal str, @ip.to_string_uncompressed
- end
-
- def test_method_data
- str = " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
- assert_equal str, @ip.data
- end
-
- def test_method_reverse
- str = "f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.arpa"
- assert_equal str, @klass.new("3ffe:505:2::f").reverse
- end
-
- def test_method_compressed
- assert_equal "1:1:1::1", @klass.new("1:1:1:0:0:0:0:1").compressed
- assert_equal "1:0:1::1", @klass.new("1:0:1:0:0:0:0:1").compressed
- assert_equal "1:0:0:1::1", @klass.new("1:0:0:1:0:0:0:1").compressed
- assert_equal "1::1:0:0:1", @klass.new("1:0:0:0:1:0:0:1").compressed
- assert_equal "1::1", @klass.new("1:0:0:0:0:0:0:1").compressed
- end
-
- def test_method_unspecified?
- assert_equal true, @klass.new("::").unspecified?
- assert_equal false, @ip.unspecified?
- end
-
- def test_method_loopback?
- assert_equal true, @klass.new("::1").loopback?
- assert_equal false, @ip.loopback?
- end
-
- def test_classmethod_expand
- compressed = "2001:db8:0:cd30::"
- expanded = "2001:0db8:0000:cd30:0000:0000:0000:0000"
- assert_equal expanded, @klass.expand(compressed)
- assert_not_equal expanded, @klass.expand("2001:0db8:0:cd3")
- assert_not_equal expanded, @klass.expand("2001:0db8::cd30")
- assert_not_equal expanded, @klass.expand("2001:0db8::cd3")
- end
-
- def test_classmethod_compress
- compressed = "2001:db8:0:cd30::"
- expanded = "2001:0db8:0000:cd30:0000:0000:0000:0000"
- assert_equal compressed, @klass.compress(expanded)
- assert_not_equal compressed, @klass.compress("2001:0db8:0:cd3")
- assert_not_equal compressed, @klass.compress("2001:0db8::cd30")
- assert_not_equal compressed, @klass.compress("2001:0db8::cd3")
- end
-
- def test_classmethod_parse_data
- str = " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
- ip = @klass.parse_data str
- assert_instance_of @klass, ip
- assert_equal "2001:0db8:0000:0000:0008:0800:200c:417a", ip.address
- assert_equal "2001:db8::8:800:200c:417a/128", ip.to_string
- end
-
- def test_classhmethod_parse_u128
- @valid_ipv6.each do |ip,num|
- assert_equal @klass.new(ip).to_s, @klass.parse_u128(num).to_s
- end
- end
-
- def test_classmethod_parse_hex
- assert_equal @ip.to_s, @klass.parse_hex(@hex,64).to_s
- end
-
-end # class IPv6Test
-
-class IPv6UnspecifiedTest < Test::Unit::TestCase
-
- def setup
- @klass = IPAddress::IPv6::Unspecified
- @ip = @klass.new
- @s = "::"
- @str = "::/128"
- @string = "0000:0000:0000:0000:0000:0000:0000:0000/128"
- @u128 = 0
- @address = "::"
- end
-
- def test_initialize
- assert_nothing_raised {@klass.new}
- assert_instance_of @klass, @ip
- end
-
- def test_attributes
- assert_equal @address, @ip.compressed
- assert_equal 128, @ip.prefix
- assert_equal true, @ip.unspecified?
- assert_equal @s, @ip.to_s
- assert_equal @str, @ip.to_string
- assert_equal @string, @ip.to_string_uncompressed
- assert_equal @u128, @ip.to_u128
- end
-
- def test_method_ipv6?
- assert_equal true, @ip.ipv6?
- end
-
-end # class IPv6UnspecifiedTest
-
-
-class IPv6LoopbackTest < Test::Unit::TestCase
-
- def setup
- @klass = IPAddress::IPv6::Loopback
- @ip = @klass.new
- @s = "::1"
- @str = "::1/128"
- @string = "0000:0000:0000:0000:0000:0000:0000:0001/128"
- @u128 = 1
- @address = "::1"
- end
-
- def test_initialize
- assert_nothing_raised {@klass.new}
- assert_instance_of @klass, @ip
- end
-
- def test_attributes
- assert_equal @address, @ip.compressed
- assert_equal 128, @ip.prefix
- assert_equal true, @ip.loopback?
- assert_equal @s, @ip.to_s
- assert_equal @str, @ip.to_string
- assert_equal @string, @ip.to_string_uncompressed
- assert_equal @u128, @ip.to_u128
- end
-
- def test_method_ipv6?
- assert_equal true, @ip.ipv6?
- end
-
-end # class IPv6LoopbackTest
-
-class IPv6MappedTest < Test::Unit::TestCase
-
- def setup
- @klass = IPAddress::IPv6::Mapped
- @ip = @klass.new("::172.16.10.1")
- @s = "::ffff:172.16.10.1"
- @str = "::ffff:172.16.10.1/128"
- @string = "0000:0000:0000:0000:0000:ffff:ac10:0a01/128"
- @u128 = 281473568475649
- @address = "::ffff:ac10:a01"
-
- @valid_mapped = {'::13.1.68.3' => 281470899930115,
- '0:0:0:0:0:ffff:129.144.52.38' => 281472855454758,
- '::ffff:129.144.52.38' => 281472855454758}
-
- @valid_mapped_ipv6 = {'::0d01:4403' => 281470899930115,
- '0:0:0:0:0:ffff:8190:3426' => 281472855454758,
- '::ffff:8190:3426' => 281472855454758}
-
- @valid_mapped_ipv6_conversion = {'::0d01:4403' => "13.1.68.3",
- '0:0:0:0:0:ffff:8190:3426' => "129.144.52.38",
- '::ffff:8190:3426' => "129.144.52.38"}
-
- end
-
- def test_initialize
- assert_nothing_raised {@klass.new("::172.16.10.1")}
- assert_instance_of @klass, @ip
- @valid_mapped.each do |ip, u128|
- assert_nothing_raised {@klass.new ip}
- assert_equal u128, @klass.new(ip).to_u128
- end
- @valid_mapped_ipv6.each do |ip, u128|
- assert_nothing_raised {@klass.new ip}
- assert_equal u128, @klass.new(ip).to_u128
- end
- end
-
- def test_mapped_from_ipv6_conversion
- @valid_mapped_ipv6_conversion.each do |ip6,ip4|
- assert_equal ip4, @klass.new(ip6).ipv4.to_s
- end
- end
-
- def test_attributes
- assert_equal @address, @ip.compressed
- assert_equal 128, @ip.prefix
- assert_equal @s, @ip.to_s
- assert_equal @str, @ip.to_string
- assert_equal @string, @ip.to_string_uncompressed
- assert_equal @u128, @ip.to_u128
- end
-
- def test_method_ipv6?
- assert_equal true, @ip.ipv6?
- end
-
- def test_mapped?
- assert_equal true, @ip.mapped?
- end
-
-end # class IPv6MappedTest
diff --git a/test/ipaddress/prefix_test.rb b/test/ipaddress/prefix_test.rb
deleted file mode 100644
index bff812e..0000000
--- a/test/ipaddress/prefix_test.rb
+++ /dev/null
@@ -1,159 +0,0 @@
-require 'test_helper'
-
-class Prefix32Test < Test::Unit::TestCase
-
- def setup
- @netmask0 = "0.0.0.0"
- @netmask8 = "255.0.0.0"
- @netmask16 = "255.255.0.0"
- @netmask24 = "255.255.255.0"
- @netmask30 = "255.255.255.252"
- @netmasks = [@netmask0,@netmask8,@netmask16,@netmask24,@netmask30]
-
- @prefix_hash = {
- "0.0.0.0" => 0,
- "255.0.0.0" => 8,
- "255.255.0.0" => 16,
- "255.255.255.0" => 24,
- "255.255.255.252" => 30}
-
- @octets_hash = {
- [0,0,0,0] => 0,
- [255,0,0,0] => 8,
- [255,255,0,0] => 16,
- [255,255,255,0] => 24,
- [255,255,255,252] => 30}
-
- @u32_hash = {
- 0 => 0,
- 8 => 4278190080,
- 16 => 4294901760,
- 24 => 4294967040,
- 30 => 4294967292}
-
- @klass = IPAddress::Prefix32
- end
-
- def test_attributes
- @prefix_hash.values.each do |num|
- prefix = @klass.new(num)
- assert_equal num, prefix.prefix
- end
- end
-
- def test_parse_netmask
- @prefix_hash.each do |netmask, num|
- prefix = @klass.parse_netmask(netmask)
- assert_equal num, prefix.prefix
- assert_instance_of @klass, prefix
- end
- end
-
- def test_method_to_ip
- @prefix_hash.each do |netmask, num|
- prefix = @klass.new(num)
- assert_equal netmask, prefix.to_ip
- end
- end
-
- def test_method_to_s
- prefix = @klass.new(8)
- assert_equal "8", prefix.to_s
- end
-
- def test_method_bits
- prefix = @klass.new(16)
- str = "1"*16 + "0"*16
- assert_equal str, prefix.bits
- end
-
- def test_method_to_u32
- @u32_hash.each do |num,u32|
- assert_equal u32, @klass.new(num).to_u32
- end
- end
-
- def test_method_plus
- p1 = @klass.new 8
- p2 = @klass.new 10
- assert_equal 18, p1+p2
- assert_equal 12, p1+4
- end
-
- def test_method_minus
- p1 = @klass.new 8
- p2 = @klass.new 24
- assert_equal 16, p1-p2
- assert_equal 16, p2-p1
- assert_equal 20, p2-4
- end
-
- def test_initialize
- assert_raise (ArgumentError) do
- @klass.new 33
- end
- assert_nothing_raised do
- @klass.new 8
- end
- assert_instance_of @klass, @klass.new(8)
- end
-
- def test_method_octets
- @octets_hash.each do |arr,pref|
- prefix = @klass.new(pref)
- assert_equal prefix.octets, arr
- end
- end
-
- def test_method_brackets
- @octets_hash.each do |arr,pref|
- prefix = @klass.new(pref)
- arr.each_with_index do |oct,index|
- assert_equal prefix[index], oct
- end
- end
- end
-
- def test_method_hostmask
- prefix = @klass.new(8)
- assert_equal "0.255.255.255", prefix.hostmask
- end
-
-end # class Prefix32Test
-
-
-class Prefix128Test < Test::Unit::TestCase
-
- def setup
- @u128_hash = {
- 32 => 340282366841710300949110269838224261120,
- 64 => 340282366920938463444927863358058659840,
- 96 => 340282366920938463463374607427473244160,
- 126 => 340282366920938463463374607431768211452}
-
- @klass = IPAddress::Prefix128
- end
-
- def test_initialize
- assert_raise (ArgumentError) do
- @klass.new 129
- end
- assert_nothing_raised do
- @klass.new 64
- end
- assert_instance_of @klass, @klass.new(64)
- end
-
- def test_method_bits
- prefix = @klass.new(64)
- str = "1"*64 + "0"*64
- assert_equal str, prefix.bits
- end
-
- def test_method_to_u32
- @u128_hash.each do |num,u128|
- assert_equal u128, @klass.new(num).to_u128
- end
- end
-
-end # class Prefix128Test
diff --git a/test/ipaddress_test.rb b/test/ipaddress_test.rb
index ed72aed..8251fc5 100644
--- a/test/ipaddress_test.rb
+++ b/test/ipaddress_test.rb
@@ -51,5 +51,1029 @@ class IPAddressTest < Test::Unit::TestCase
end
end
+
+class Prefix32Test < Test::Unit::TestCase
+
+ def setup
+ @netmask0 = "0.0.0.0"
+ @netmask8 = "255.0.0.0"
+ @netmask16 = "255.255.0.0"
+ @netmask24 = "255.255.255.0"
+ @netmask30 = "255.255.255.252"
+ @netmasks = [@netmask0,@netmask8,@netmask16,@netmask24,@netmask30]
+
+ @prefix_hash = {
+ "0.0.0.0" => 0,
+ "255.0.0.0" => 8,
+ "255.255.0.0" => 16,
+ "255.255.255.0" => 24,
+ "255.255.255.252" => 30}
+
+ @octets_hash = {
+ [0,0,0,0] => 0,
+ [255,0,0,0] => 8,
+ [255,255,0,0] => 16,
+ [255,255,255,0] => 24,
+ [255,255,255,252] => 30}
+
+ @u32_hash = {
+ 0 => 0,
+ 8 => 4278190080,
+ 16 => 4294901760,
+ 24 => 4294967040,
+ 30 => 4294967292}
+
+ @klass = IPAddress::Prefix32
+ end
+
+ def test_attributes
+ @prefix_hash.values.each do |num|
+ prefix = @klass.new(num)
+ assert_equal num, prefix.prefix
+ end
+ end
+
+ def test_parse_netmask
+ @prefix_hash.each do |netmask, num|
+ prefix = @klass.parse_netmask(netmask)
+ assert_equal num, prefix.prefix
+ assert_instance_of @klass, prefix
+ end
+ end
+
+ def test_method_to_ip
+ @prefix_hash.each do |netmask, num|
+ prefix = @klass.new(num)
+ assert_equal netmask, prefix.to_ip
+ end
+ end
+
+ def test_method_to_s
+ prefix = @klass.new(8)
+ assert_equal "8", prefix.to_s
+ end
+
+ def test_method_bits
+ prefix = @klass.new(16)
+ str = "1"*16 + "0"*16
+ assert_equal str, prefix.bits
+ end
+
+ def test_method_to_u32
+ @u32_hash.each do |num,u32|
+ assert_equal u32, @klass.new(num).to_u32
+ end
+ end
+
+ def test_method_plus
+ p1 = @klass.new 8
+ p2 = @klass.new 10
+ assert_equal 18, p1+p2
+ assert_equal 12, p1+4
+ end
+
+ def test_method_minus
+ p1 = @klass.new 8
+ p2 = @klass.new 24
+ assert_equal 16, p1-p2
+ assert_equal 16, p2-p1
+ assert_equal 20, p2-4
+ end
+
+ def test_initialize
+ assert_raise (ArgumentError) do
+ @klass.new 33
+ end
+ assert_nothing_raised do
+ @klass.new 8
+ end
+ assert_instance_of @klass, @klass.new(8)
+ end
+
+ def test_method_octets
+ @octets_hash.each do |arr,pref|
+ prefix = @klass.new(pref)
+ assert_equal prefix.octets, arr
+ end
+ end
+
+ def test_method_brackets
+ @octets_hash.each do |arr,pref|
+ prefix = @klass.new(pref)
+ arr.each_with_index do |oct,index|
+ assert_equal prefix[index], oct
+ end
+ end
+ end
+
+ def test_method_hostmask
+ prefix = @klass.new(8)
+ assert_equal "0.255.255.255", prefix.hostmask
+ end
+
+end # class Prefix32Test
+
+
+class Prefix128Test < Test::Unit::TestCase
+
+ def setup
+ @u128_hash = {
+ 32 => 340282366841710300949110269838224261120,
+ 64 => 340282366920938463444927863358058659840,
+ 96 => 340282366920938463463374607427473244160,
+ 126 => 340282366920938463463374607431768211452}
+
+ @klass = IPAddress::Prefix128
+ end
+
+ def test_initialize
+ assert_raise (ArgumentError) do
+ @klass.new 129
+ end
+ assert_nothing_raised do
+ @klass.new 64
+ end
+ assert_instance_of @klass, @klass.new(64)
+ end
+
+ def test_method_bits
+ prefix = @klass.new(64)
+ str = "1"*64 + "0"*64
+ assert_equal str, prefix.bits
+ end
+
+ def test_method_to_u32
+ @u128_hash.each do |num,u128|
+ assert_equal u128, @klass.new(num).to_u128
+ end
+ end
+
+end # class Prefix128Test
+
+
+class IPv4Test < Test::Unit::TestCase
+
+ def setup
+ @klass = IPAddress::IPv4
+
+ @valid_ipv4 = {
+ "0.0.0.0/0" => ["0.0.0.0", 0],
+ "10.0.0.0" => ["10.0.0.0", 32],
+ "10.0.0.1" => ["10.0.0.1", 32],
+ "10.0.0.1/24" => ["10.0.0.1", 24],
+ "10.0.0.1/255.255.255.0" => ["10.0.0.1", 24]}
+
+ @invalid_ipv4 = ["10.0.0.256",
+ "10.0.0.0.0",
+ "10.0.0",
+ "10.0"]
+
+ @valid_ipv4_range = ["10.0.0.1-254",
+ "10.0.1-254.0",
+ "10.1-254.0.0"]
+
+ @netmask_values = {
+ "0.0.0.0/0" => "0.0.0.0",
+ "10.0.0.0/8" => "255.0.0.0",
+ "172.16.0.0/16" => "255.255.0.0",
+ "192.168.0.0/24" => "255.255.255.0",
+ "192.168.100.4/30" => "255.255.255.252"}
+
+ @decimal_values ={
+ "0.0.0.0/0" => 0,
+ "10.0.0.0/8" => 167772160,
+ "172.16.0.0/16" => 2886729728,
+ "192.168.0.0/24" => 3232235520,
+ "192.168.100.4/30" => 3232261124}
+
+ @ip = @klass.new("172.16.10.1/24")
+ @network = @klass.new("172.16.10.0/24")
+
+ @broadcast = {
+ "10.0.0.0/8" => "10.255.255.255/8",
+ "172.16.0.0/16" => "172.16.255.255/16",
+ "192.168.0.0/24" => "192.168.0.255/24",
+ "192.168.100.4/30" => "192.168.100.7/30"}
+
+ @networks = {
+ "10.5.4.3/8" => "10.0.0.0/8",
+ "172.16.5.4/16" => "172.16.0.0/16",
+ "192.168.4.3/24" => "192.168.4.0/24",
+ "192.168.100.5/30" => "192.168.100.4/30"}
+
+ @class_a = @klass.new("10.0.0.1/8")
+ @class_b = @klass.new("172.16.0.1/16")
+ @class_c = @klass.new("192.168.0.1/24")
+
+ @classful = {
+ "10.1.1.1" => 8,
+ "150.1.1.1" => 16,
+ "200.1.1.1" => 24 }
+
+ end
+
+ def test_initialize
+ @valid_ipv4.keys.each do |i|
+ ip = @klass.new(i)
+ assert_instance_of @klass, ip
+ end
+ assert_instance_of IPAddress::Prefix32, @ip.prefix
+ assert_raise (ArgumentError) do
+ @klass.new
+ end
+ assert_nothing_raised do
+ @klass.new "10.0.0.0/8"
+ end
+ end
+
+ def test_initialize_format_error
+ @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_initialize_without_prefix
+ assert_nothing_raised do
+ @klass.new("10.10.0.0")
+ end
+ ip = @klass.new("10.10.0.0")
+ assert_instance_of IPAddress::Prefix32, ip.prefix
+ assert_equal 32, ip.prefix.to_i
+ end
+
+ def test_attributes
+ @valid_ipv4.each do |arg,attr|
+ ip = @klass.new(arg)
+ assert_equal attr.first, ip.address
+ assert_equal attr.last, ip.prefix.to_i
+ end
+ end
+
+ def test_octets
+ ip = @klass.new("10.1.2.3/8")
+ assert_equal ip.octets, [10,1,2,3]
+ end
+
+ def test_initialize_should_require_ip
+ assert_raise(ArgumentError) { @klass.new }
+ end
+
+ def test_method_data
+ assert_equal "\254\020\n\001", @ip.data
+ end
+
+ def test_method_to_string
+ @valid_ipv4.each do |arg,attr|
+ ip = @klass.new(arg)
+ assert_equal attr.join("/"), ip.to_string
+ end
+ end
+
+ def test_method_to_s
+ @valid_ipv4.each do |arg,attr|
+ ip = @klass.new(arg)
+ assert_equal attr.first, ip.to_s
+ end
+ end
+
+ def test_netmask
+ @netmask_values.each do |addr,mask|
+ ip = @klass.new(addr)
+ assert_equal mask, ip.netmask
+ end
+ end
+
+ def test_method_to_u32
+ @decimal_values.each do |addr,int|
+ ip = @klass.new(addr)
+ assert_equal int, ip.to_u32
+ end
+ end
+
+ def test_method_network?
+ assert_equal true, @network.network?
+ assert_equal false, @ip.network?
+ end
+
+ def test_method_broadcast
+ @broadcast.each do |addr,bcast|
+ ip = @klass.new(addr)
+ assert_instance_of @klass, ip.broadcast
+ assert_equal bcast, ip.broadcast.to_string
+ end
+ end
+
+ def test_method_network
+ @networks.each do |addr,net|
+ ip = @klass.new addr
+ assert_instance_of @klass, ip.network
+ assert_equal net, ip.network.to_string
+ end
+ end
+
+ def test_method_bits
+ ip = @klass.new("127.0.0.1")
+ assert_equal "01111111000000000000000000000001", ip.bits
+ end
+
+ def test_method_first
+ ip = @klass.new("192.168.100.0/24")
+ assert_instance_of @klass, ip.first
+ assert_equal "192.168.100.1", ip.first.to_s
+ ip = @klass.new("192.168.100.50/24")
+ assert_instance_of @klass, ip.first
+ assert_equal "192.168.100.1", ip.first.to_s
+ end
+
+ def test_method_last
+ ip = @klass.new("192.168.100.0/24")
+ assert_instance_of @klass, ip.last
+ assert_equal "192.168.100.254", ip.last.to_s
+ ip = @klass.new("192.168.100.50/24")
+ assert_instance_of @klass, ip.last
+ assert_equal "192.168.100.254", ip.last.to_s
+ end
+
+ def test_method_each_host
+ ip = @klass.new("10.0.0.1/29")
+ arr = []
+ ip.each_host {|i| arr << i.to_s}
+ expected = ["10.0.0.1","10.0.0.2","10.0.0.3",
+ "10.0.0.4","10.0.0.5","10.0.0.6"]
+ assert_equal expected, arr
+ end
+
+ def test_method_each
+ ip = @klass.new("10.0.0.1/29")
+ arr = []
+ ip.each {|i| arr << i.to_s}
+ expected = ["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"]
+ assert_equal expected, arr
+ end
+
+ def test_method_size
+ ip = @klass.new("10.0.0.1/29")
+ assert_equal 8, ip.size
+ end
+
+ def test_method_hosts
+ ip = @klass.new("10.0.0.1/29")
+ expected = ["10.0.0.1","10.0.0.2","10.0.0.3",
+ "10.0.0.4","10.0.0.5","10.0.0.6"]
+ assert_equal expected, ip.hosts.map {|i| i.to_s}
+ end
+
+ def test_method_network_u32
+ assert_equal 2886732288, @ip.network_u32
+ end
+
+ def test_method_broadcast_u32
+ assert_equal 2886732543, @ip.broadcast_u32
+ end
+
+ def test_method_include?
+ ip = @klass.new("192.168.10.100/24")
+ addr = @klass.new("192.168.10.102/24")
+ assert_equal true, ip.include?(addr)
+ assert_equal false, ip.include?(@klass.new("172.16.0.48"))
+ ip = @klass.new("10.0.0.0/8")
+ assert_equal true, ip.include?(@klass.new("10.0.0.0/9"))
+ assert_equal true, ip.include?(@klass.new("10.1.1.1/32"))
+ assert_equal true, ip.include?(@klass.new("10.1.1.1/9"))
+ assert_equal false, ip.include?(@klass.new("172.16.0.0/16"))
+ assert_equal false, ip.include?(@klass.new("10.0.0.0/7"))
+ assert_equal false, ip.include?(@klass.new("5.5.5.5/32"))
+ assert_equal false, ip.include?(@klass.new("11.0.0.0/8"))
+ ip = @klass.new("13.13.0.0/13")
+ assert_equal false, ip.include?(@klass.new("13.16.0.0/32"))
+ end
+
+ def test_method_include_all?
+ ip = @klass.new("192.168.10.100/24")
+ addr1 = @klass.new("192.168.10.102/24")
+ addr2 = @klass.new("192.168.10.103/24")
+ assert_equal true, ip.include_all?(addr1,addr2)
+ assert_equal false, ip.include_all?(addr1, @klass.new("13.16.0.0/32"))
+ end
+
+ def test_method_ipv4?
+ assert_equal true, @ip.ipv4?
+ end
+
+ def test_method_ipv6?
+ assert_equal false, @ip.ipv6?
+ end
+
+ def test_method_private?
+ assert_equal true, @klass.new("192.168.10.50/24").private?
+ assert_equal true, @klass.new("192.168.10.50/16").private?
+ assert_equal true, @klass.new("172.16.77.40/24").private?
+ assert_equal true, @klass.new("172.16.10.50/14").private?
+ assert_equal true, @klass.new("10.10.10.10/10").private?
+ assert_equal true, @klass.new("10.0.0.0/8").private?
+ assert_equal false, @klass.new("192.168.10.50/12").private?
+ assert_equal false, @klass.new("3.3.3.3").private?
+ assert_equal false, @klass.new("10.0.0.0/7").private?
+ assert_equal false, @klass.new("172.32.0.0/12").private?
+ assert_equal false, @klass.new("172.16.0.0/11").private?
+ assert_equal false, @klass.new("192.0.0.2/24").private?
+ end
+
+ def test_method_octet
+ assert_equal 172, @ip[0]
+ assert_equal 16, @ip[1]
+ assert_equal 10, @ip[2]
+ assert_equal 1, @ip[3]
+ end
+
+ def test_method_a?
+ assert_equal true, @class_a.a?
+ assert_equal false, @class_b.a?
+ assert_equal false, @class_c.a?
+ end
+
+ def test_method_b?
+ assert_equal true, @class_b.b?
+ assert_equal false, @class_a.b?
+ assert_equal false, @class_c.b?
+ end
+
+ def test_method_c?
+ assert_equal true, @class_c.c?
+ assert_equal false, @class_a.c?
+ assert_equal false, @class_b.c?
+ end
+
+ def test_method_to_ipv6
+ assert_equal "ac10:0a01", @ip.to_ipv6
+ end
+
+ def test_method_reverse
+ assert_equal "1.10.16.172.in-addr.arpa", @ip.reverse
+ end
+
+ def test_method_comparabble
+ ip1 = @klass.new("10.1.1.1/8")
+ 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, 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
+ assert_equal true, ip1 == ip4
+ # test sorting
+ arr = ["10.1.1.1/16","10.1.1.1/8","172.16.1.1/14"]
+ assert_equal arr, [ip1,ip2,ip3].sort.map{|s| s.to_string}
+ 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_plus
+ ip1 = @klass.new("172.16.10.1/24")
+ ip2 = @klass.new("172.16.11.2/24")
+ assert_equal ["172.16.10.0/23"], (ip1+ip2).map{|i| i.to_string}
+
+ ip2 = @klass.new("172.16.12.2/24")
+ assert_equal [ip1.network.to_string, ip2.network.to_string],
+ (ip1 + ip2).map{|i| i.to_string}
+
+ ip1 = @klass.new("10.0.0.0/23")
+ ip2 = @klass.new("10.0.2.0/24")
+ assert_equal ["10.0.0.0/23","10.0.2.0/24"], (ip1+ip2).map{|i| i.to_string}
+
+ ip1 = @klass.new("10.0.0.0/23")
+ ip2 = @klass.new("10.0.2.0/24")
+ assert_equal ["10.0.0.0/23","10.0.2.0/24"], (ip2+ip1).map{|i| i.to_string}
+
+ ip1 = @klass.new("10.0.0.0/16")
+ ip2 = @klass.new("10.0.2.0/24")
+ assert_equal ["10.0.0.0/16"], (ip1+ip2).map{|i| i.to_string}
+
+ ip1 = @klass.new("10.0.0.0/23")
+ ip2 = @klass.new("10.1.0.0/24")
+ assert_equal ["10.0.0.0/23","10.1.0.0/24"], (ip1+ip2).map{|i| i.to_string}
+
+ end
+
+ def test_method_netmask_equal
+ ip = @klass.new("10.1.1.1/16")
+ assert_equal 16, ip.prefix.to_i
+ ip.netmask = "255.255.255.0"
+ 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(0)}
+ assert_raise(ArgumentError) {@ip.supernet(24)}
+ 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|
+ ip = @klass.parse_u32(int)
+ ip.prefix = addr.split("/").last.to_i
+ assert_equal ip.to_string, addr
+ end
+ end
+
+ def test_classhmethod_extract
+ str = "foobar172.16.10.1barbaz"
+ assert_equal "172.16.10.1", @klass.extract(str).to_s
+ end
+
+ def test_classmethod_summarize
+
+ # Should return self if only one network given
+ assert_equal [@ip.network], @klass.summarize(@ip)
+
+ # Summarize homogeneous networks
+ ip1 = @klass.new("172.16.10.1/24")
+ ip2 = @klass.new("172.16.11.2/24")
+ assert_equal ["172.16.10.0/23"], @klass.summarize(ip1,ip2).map{|i| i.to_string}
+
+ ip1 = @klass.new("10.0.0.1/24")
+ ip2 = @klass.new("10.0.1.1/24")
+ ip3 = @klass.new("10.0.2.1/24")
+ ip4 = @klass.new("10.0.3.1/24")
+ assert_equal ["10.0.0.0/22"], @klass.summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string}
+ assert_equal ["10.0.0.0/22"], @klass.summarize(ip4,ip3,ip2,ip1).map{|i| i.to_string}
+
+ # Summarize non homogeneous networks
+ ip1 = @klass.new("10.0.0.0/23")
+ ip2 = @klass.new("10.0.2.0/24")
+ assert_equal ["10.0.0.0/23","10.0.2.0/24"], @klass.summarize(ip1,ip2).map{|i| i.to_string}
+
+ ip1 = @klass.new("10.0.0.0/16")
+ ip2 = @klass.new("10.0.2.0/24")
+ assert_equal ["10.0.0.0/16"], @klass.summarize(ip1,ip2).map{|i| i.to_string}
+
+ ip1 = @klass.new("10.0.0.0/23")
+ ip2 = @klass.new("10.1.0.0/24")
+ assert_equal ["10.0.0.0/23","10.1.0.0/24"], @klass.summarize(ip1,ip2).map{|i| i.to_string}
+
+ ip1 = @klass.new("10.0.0.0/23")
+ ip2 = @klass.new("10.0.2.0/23")
+ ip3 = @klass.new("10.0.4.0/24")
+ ip4 = @klass.new("10.0.6.0/24")
+ assert_equal ["10.0.0.0/22","10.0.4.0/24","10.0.6.0/24"],
+ @klass.summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string}
+
+ ip1 = @klass.new("10.0.1.1/24")
+ ip2 = @klass.new("10.0.2.1/24")
+ ip3 = @klass.new("10.0.3.1/24")
+ ip4 = @klass.new("10.0.4.1/24")
+ result = ["10.0.1.0/24","10.0.2.0/23","10.0.4.0/24"]
+ assert_equal result, @klass.summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string}
+ assert_equal result, @klass.summarize(ip4,ip3,ip2,ip1).map{|i| i.to_string}
+
+ ip1 = @klass.new("10.0.1.1/24")
+ ip2 = @klass.new("10.10.2.1/24")
+ ip3 = @klass.new("172.16.0.1/24")
+ ip4 = @klass.new("172.16.1.1/24")
+ result = ["10.0.1.0/24","10.10.2.0/24","172.16.0.0/23"]
+ assert_equal result, @klass.summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string}
+
+ ips = [@klass.new("10.0.0.12/30"),
+ @klass.new("10.0.100.0/24")]
+ result = ["10.0.0.12/30", "10.0.100.0/24"]
+ assert_equal result, @klass.summarize(*ips).map{|i| i.to_string}
+
+ ips = [@klass.new("172.16.0.0/31"),
+ @klass.new("10.10.2.1/32")]
+ result = ["10.10.2.1/32", "172.16.0.0/31"]
+ assert_equal result, @klass.summarize(*ips).map{|i| i.to_string}
+
+ ips = [@klass.new("172.16.0.0/32"),
+ @klass.new("10.10.2.1/32")]
+ result = ["10.10.2.1/32", "172.16.0.0/32"]
+ assert_equal result, @klass.summarize(*ips).map{|i| i.to_string}
+
+ end
+
+ def test_classmethod_parse_data
+ ip = @klass.parse_data "\254\020\n\001"
+ assert_instance_of @klass, ip
+ assert_equal "172.16.10.1", ip.address
+ assert_equal "172.16.10.1/32", ip.to_string
+ end
+
+ def test_classmethod_parse_classful
+ @classful.each do |ip,prefix|
+ res = @klass.parse_classful(ip)
+ assert_equal prefix, res.prefix
+ assert_equal "#{ip}/#{prefix}", res.to_string
+ end
+ assert_raise(ArgumentError){ @klass.parse_classful("192.168.256.257") }
+ end
+
+end # class IPv4Test
+
+
+
+class IPv6Test < Test::Unit::TestCase
+
+ def setup
+ @klass = IPAddress::IPv6
+
+ @compress_addr = {
+ "2001:db8:0000:0000:0008:0800:200c:417a" => "2001:db8::8:800:200c:417a",
+ "2001:db8:0:0:8:800:200c:417a" => "2001:db8::8:800:200c:417a",
+ "ff01:0:0:0:0:0:0:101" => "ff01::101",
+ "0:0:0:0:0:0:0:1" => "::1",
+ "0:0:0:0:0:0:0:0" => "::"}
+
+ @valid_ipv6 = { # Kindly taken from the python IPy library
+ "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210" => 338770000845734292534325025077361652240,
+ "1080:0000:0000:0000:0008:0800:200C:417A" => 21932261930451111902915077091070067066,
+ "1080:0:0:0:8:800:200C:417A" => 21932261930451111902915077091070067066,
+ "1080:0::8:800:200C:417A" => 21932261930451111902915077091070067066,
+ "1080::8:800:200C:417A" => 21932261930451111902915077091070067066,
+ "FF01:0:0:0:0:0:0:43" => 338958331222012082418099330867817087043,
+ "FF01:0:0::0:0:43" => 338958331222012082418099330867817087043,
+ "FF01::43" => 338958331222012082418099330867817087043,
+ "0:0:0:0:0:0:0:1" => 1,
+ "0:0:0::0:0:1" => 1,
+ "::1" => 1,
+ "0:0:0:0:0:0:0:0" => 0,
+ "0:0:0::0:0:0" => 0,
+ "::" => 0,
+ "1080:0:0:0:8:800:200C:417A" => 21932261930451111902915077091070067066,
+ "1080::8:800:200C:417A" => 21932261930451111902915077091070067066}
+
+ @invalid_ipv6 = [":1:2:3:4:5:6:7",
+ ":1:2:3:4:5:6:7"]
+
+ @ip = @klass.new "2001:db8::8:800:200c:417a/64"
+ @network = @klass.new "2001:db8:8:800::/64"
+ @arr = [8193,3512,0,0,8,2048,8204,16762]
+ @hex = "20010db80000000000080800200c417a"
+ end
+
+ def test_attribute_address
+ addr = "2001:0db8:0000:0000:0008:0800:200c:417a"
+ assert_equal addr, @ip.address
+ end
+
+ def test_initialize
+ assert_instance_of @klass, @ip
+ @valid_ipv6.keys.each do |ip|
+ assert_nothing_raised {@klass.new ip}
+ end
+ @invalid_ipv6.each do |ip|
+ assert_raise(ArgumentError) {@klass.new ip}
+ end
+ assert_equal 64, @ip.prefix
+
+ assert_raise(ArgumentError) {
+ @klass.new "::10.1.1.1"
+ }
+ end
+
+ def test_attribute_groups
+ assert_equal @arr, @ip.groups
+ end
+
+ def test_method_hexs
+ arr = "2001:0db8:0000:0000:0008:0800:200c:417a".split(":")
+ assert_equal arr, @ip.hexs
+ end
+
+ def test_method_to_i
+ @valid_ipv6.each do |ip,num|
+ assert_equal num, @klass.new(ip).to_i
+ end
+ end
+
+ def test_method_bits
+ bits = "0010000000000001000011011011100000000000000000000" +
+ "000000000000000000000000000100000001000000000000010000" +
+ "0000011000100000101111010"
+ assert_equal bits, @ip.bits
+ end
+
+ def test_method_prefix=()
+ ip = @klass.new "2001:db8::8:800:200c:417a"
+ assert_equal 128, ip.prefix
+ ip.prefix = 64
+ assert_equal 64, ip.prefix
+ assert_equal "2001:db8::8:800:200c:417a/64", ip.to_string
+ end
+
+ def test_method_mapped?
+ assert_equal false, @ip.mapped?
+ ip6 = @klass.new "::ffff:1234:5678"
+ assert_equal true, ip6.mapped?
+ end
+
+ def test_method_literal
+ str = "2001-0db8-0000-0000-0008-0800-200c-417a.ipv6-literal.net"
+ assert_equal str, @ip.literal
+ end
+
+ def test_method_group
+ @arr.each_with_index do |val,index|
+ assert_equal val, @ip[index]
+ end
+ end
+
+ def test_method_ipv4?
+ assert_equal false, @ip.ipv4?
+ end
+
+ def test_method_ipv6?
+ assert_equal true, @ip.ipv6?
+ end
+
+ def test_method_network?
+ assert_equal true, @network.network?
+ assert_equal false, @ip.network?
+ end
+
+ def test_method_network_u128
+ assert_equal 42540766411282592856903984951653826560, @ip.network_u128
+ end
+
+ def test_method_include?
+ assert_equal true, @ip.include?(@ip)
+ # test prefix on same address
+ included = @klass.new "2001:db8::8:800:200c:417a/128"
+ not_included = @klass.new "2001:db8::8:800:200c:417a/46"
+ assert_equal true, @ip.include?(included)
+ assert_equal false, @ip.include?(not_included)
+ # test address on same prefix
+ included = @klass.new "2001:db8::8:800:200c:0/64"
+ not_included = @klass.new "2001:db8:1::8:800:200c:417a/64"
+ assert_equal true, @ip.include?(included)
+ assert_equal false, @ip.include?(not_included)
+ # general test
+ included = @klass.new "2001:db8::8:800:200c:1/128"
+ not_included = @klass.new "2001:db8:1::8:800:200c:417a/76"
+ assert_equal true, @ip.include?(included)
+ assert_equal false, @ip.include?(not_included)
+ end
+
+ def test_method_to_hex
+ assert_equal @hex, @ip.to_hex
+ end
+
+ def test_method_to_s
+ assert_equal "2001:db8::8:800:200c:417a", @ip.to_s
+ end
+
+ def test_method_to_string
+ assert_equal "2001:db8::8:800:200c:417a/64", @ip.to_string
+ end
+
+ def test_method_to_string_uncompressed
+ str = "2001:0db8:0000:0000:0008:0800:200c:417a/64"
+ assert_equal str, @ip.to_string_uncompressed
+ end
+
+ def test_method_data
+ str = " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
+ assert_equal str, @ip.data
+ end
+
+ def test_method_reverse
+ str = "f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.arpa"
+ assert_equal str, @klass.new("3ffe:505:2::f").reverse
+ end
+
+ def test_method_compressed
+ assert_equal "1:1:1::1", @klass.new("1:1:1:0:0:0:0:1").compressed
+ assert_equal "1:0:1::1", @klass.new("1:0:1:0:0:0:0:1").compressed
+ assert_equal "1:0:0:1::1", @klass.new("1:0:0:1:0:0:0:1").compressed
+ assert_equal "1::1:0:0:1", @klass.new("1:0:0:0:1:0:0:1").compressed
+ assert_equal "1::1", @klass.new("1:0:0:0:0:0:0:1").compressed
+ end
+
+ def test_method_unspecified?
+ assert_equal true, @klass.new("::").unspecified?
+ assert_equal false, @ip.unspecified?
+ end
+
+ def test_method_loopback?
+ assert_equal true, @klass.new("::1").loopback?
+ assert_equal false, @ip.loopback?
+ end
+
+ def test_classmethod_expand
+ compressed = "2001:db8:0:cd30::"
+ expanded = "2001:0db8:0000:cd30:0000:0000:0000:0000"
+ assert_equal expanded, @klass.expand(compressed)
+ assert_not_equal expanded, @klass.expand("2001:0db8:0:cd3")
+ assert_not_equal expanded, @klass.expand("2001:0db8::cd30")
+ assert_not_equal expanded, @klass.expand("2001:0db8::cd3")
+ end
+
+ def test_classmethod_compress
+ compressed = "2001:db8:0:cd30::"
+ expanded = "2001:0db8:0000:cd30:0000:0000:0000:0000"
+ assert_equal compressed, @klass.compress(expanded)
+ assert_not_equal compressed, @klass.compress("2001:0db8:0:cd3")
+ assert_not_equal compressed, @klass.compress("2001:0db8::cd30")
+ assert_not_equal compressed, @klass.compress("2001:0db8::cd3")
+ end
+
+ def test_classmethod_parse_data
+ str = " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
+ ip = @klass.parse_data str
+ assert_instance_of @klass, ip
+ assert_equal "2001:0db8:0000:0000:0008:0800:200c:417a", ip.address
+ assert_equal "2001:db8::8:800:200c:417a/128", ip.to_string
+ end
+
+ def test_classhmethod_parse_u128
+ @valid_ipv6.each do |ip,num|
+ assert_equal @klass.new(ip).to_s, @klass.parse_u128(num).to_s
+ end
+ end
+
+ def test_classmethod_parse_hex
+ assert_equal @ip.to_s, @klass.parse_hex(@hex,64).to_s
+ end
+
+end # class IPv6Test
+
+class IPv6UnspecifiedTest < Test::Unit::TestCase
+
+ def setup
+ @klass = IPAddress::IPv6::Unspecified
+ @ip = @klass.new
+ @s = "::"
+ @str = "::/128"
+ @string = "0000:0000:0000:0000:0000:0000:0000:0000/128"
+ @u128 = 0
+ @address = "::"
+ end
+
+ def test_initialize
+ assert_nothing_raised {@klass.new}
+ assert_instance_of @klass, @ip
+ end
+
+ def test_attributes
+ assert_equal @address, @ip.compressed
+ assert_equal 128, @ip.prefix
+ assert_equal true, @ip.unspecified?
+ assert_equal @s, @ip.to_s
+ assert_equal @str, @ip.to_string
+ assert_equal @string, @ip.to_string_uncompressed
+ assert_equal @u128, @ip.to_u128
+ end
+
+ def test_method_ipv6?
+ assert_equal true, @ip.ipv6?
+ end
+
+end # class IPv6UnspecifiedTest
+
+
+class IPv6LoopbackTest < Test::Unit::TestCase
+
+ def setup
+ @klass = IPAddress::IPv6::Loopback
+ @ip = @klass.new
+ @s = "::1"
+ @str = "::1/128"
+ @string = "0000:0000:0000:0000:0000:0000:0000:0001/128"
+ @u128 = 1
+ @address = "::1"
+ end
+
+ def test_initialize
+ assert_nothing_raised {@klass.new}
+ assert_instance_of @klass, @ip
+ end
+
+ def test_attributes
+ assert_equal @address, @ip.compressed
+ assert_equal 128, @ip.prefix
+ assert_equal true, @ip.loopback?
+ assert_equal @s, @ip.to_s
+ assert_equal @str, @ip.to_string
+ assert_equal @string, @ip.to_string_uncompressed
+ assert_equal @u128, @ip.to_u128
+ end
+
+ def test_method_ipv6?
+ assert_equal true, @ip.ipv6?
+ end
+
+end # class IPv6LoopbackTest
+
+class IPv6MappedTest < Test::Unit::TestCase
+
+ def setup
+ @klass = IPAddress::IPv6::Mapped
+ @ip = @klass.new("::172.16.10.1")
+ @s = "::ffff:172.16.10.1"
+ @str = "::ffff:172.16.10.1/128"
+ @string = "0000:0000:0000:0000:0000:ffff:ac10:0a01/128"
+ @u128 = 281473568475649
+ @address = "::ffff:ac10:a01"
+
+ @valid_mapped = {'::13.1.68.3' => 281470899930115,
+ '0:0:0:0:0:ffff:129.144.52.38' => 281472855454758,
+ '::ffff:129.144.52.38' => 281472855454758}
+
+ @valid_mapped_ipv6 = {'::0d01:4403' => 281470899930115,
+ '0:0:0:0:0:ffff:8190:3426' => 281472855454758,
+ '::ffff:8190:3426' => 281472855454758}
+
+ @valid_mapped_ipv6_conversion = {'::0d01:4403' => "13.1.68.3",
+ '0:0:0:0:0:ffff:8190:3426' => "129.144.52.38",
+ '::ffff:8190:3426' => "129.144.52.38"}
+
+ end
+
+ def test_initialize
+ assert_nothing_raised {@klass.new("::172.16.10.1")}
+ assert_instance_of @klass, @ip
+ @valid_mapped.each do |ip, u128|
+ assert_nothing_raised {@klass.new ip}
+ assert_equal u128, @klass.new(ip).to_u128
+ end
+ @valid_mapped_ipv6.each do |ip, u128|
+ assert_nothing_raised {@klass.new ip}
+ assert_equal u128, @klass.new(ip).to_u128
+ end
+ end
+
+ def test_mapped_from_ipv6_conversion
+ @valid_mapped_ipv6_conversion.each do |ip6,ip4|
+ assert_equal ip4, @klass.new(ip6).ipv4.to_s
+ end
+ end
+
+ def test_attributes
+ assert_equal @address, @ip.compressed
+ assert_equal 128, @ip.prefix
+ assert_equal @s, @ip.to_s
+ assert_equal @str, @ip.to_string
+ assert_equal @string, @ip.to_string_uncompressed
+ assert_equal @u128, @ip.to_u128
+ end
+
+ def test_method_ipv6?
+ assert_equal true, @ip.ipv6?
+ end
+
+ def test_mapped?
+ assert_equal true, @ip.mapped?
+ end
+
+end # class IPv6MappedTest
+