From 9b6b1d5c8c0e55e6f9cf8ec250bae39d1e0f77d5 Mon Sep 17 00:00:00 2001 From: Victor Hahn Date: Tue, 9 Sep 2014 12:51:59 +0200 Subject: [PATCH 01/13] Provide add and subtract methods for arithmetic operations on IPv4 addresses --- lib/ipaddress/ipv4.rb | 47 +++++++++++++++++++++++++++++++++++-- test/ipaddress/ipv4_test.rb | 22 +++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/lib/ipaddress/ipv4.rb b/lib/ipaddress/ipv4.rb index a798a24..a725a9a 100644 --- a/lib/ipaddress/ipv4.rb +++ b/lib/ipaddress/ipv4.rb @@ -565,8 +565,8 @@ def include_all?(*others) # 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} + self.class.new("172.16.0.0/12"), + self.class.new("192.168.0.0/16")].any? {|i| i.include? self} end # @@ -729,6 +729,49 @@ def -(oth) def +(oth) aggregate(*[self,oth].sort.map{|i| i.network}) end + + # + # Returns a new IPv4 object which is the result + # of advancing this IP address by a given value. + # In other words, this arithmetically adds IP addresses. + # + # Will raise an error if the resulting address is in a different subnet, + # except validating is set to false. + # + # Example: + # + # ip = IPAddress::IPv4.new("172.16.10.1/24") + # ip.add(5).to_string + # #=> "172.16.10.6/24" + def add(oth, validating=true) + oth = oth.to_i if oth.kind_of? IPAddress::IPv4 # oth shall be integer + + new_obj = self.class.parse_u32(self.to_i + oth, prefix) + + if validating and self.network_u32 != new_obj.network_u32 + raise ArgumentError, "Subnet (/#{@prefix}) is not large enough." + end + + return new_obj + end + + # + # Returns a new IPv4 object which is the result + # of decreasing this IP address by a given value. + # In other words, this arithmetically subtracts IP addresses. + # + # Will raise an error if the resulting address is in a different subnet, + # except validating is set to false. + # + # Example: + # + # ip = IPAddress::IPv4.new("172.16.10.10/24") + # ip.subtract(5).to_string + # #=> "172.16.10.5/24" + def subtract(oth, validating=true) + oth = oth.to_i if oth.kind_of? IPAddress::IPv4 # oth shall be integer + return add(-oth, validating) + end # # Checks whether the ip address belongs to a diff --git a/test/ipaddress/ipv4_test.rb b/test/ipaddress/ipv4_test.rb index f144634..6ae24e2 100644 --- a/test/ipaddress/ipv4_test.rb +++ b/test/ipaddress/ipv4_test.rb @@ -433,6 +433,28 @@ def test_method_supernet assert_equal "172.16.8.0/22", @ip.supernet(22).to_string end + def test_method_add + ip = IPAddress::IPv4.new("172.16.10.1/24") + assert_equal ip.add(5), IPAddress::IPv4.new("172.16.10.6/24") + assert_equal ip.add(IPAddress::IPv4.new("0.0.0.5/6")), IPAddress::IPv4.new("172.16.10.6/24") + assert_equal ip.add(50), IPAddress::IPv4.new("172.16.10.51/24") + assert_equal ip.add(254), IPAddress::IPv4.new("172.16.10.255/24") + assert_raise(ArgumentError) {ip.add(255)} + assert_raise(ArgumentError) {ip.add(1000)} + ip = IPAddress::IPv4.new("172.16.10.1/30") + assert_equal ip.add(2), IPAddress::IPv4.new("172.16.10.3/30") + assert_raise(ArgumentError) {ip.add(3)} + end + + def test_method_subtract + ip = IPAddress::IPv4.new("172.16.10.10/24") + assert_equal ip.subtract(5), IPAddress::IPv4.new("172.16.10.5/24") + assert_equal ip.subtract(IPAddress::IPv4.new("0.0.0.5/32")), IPAddress::IPv4.new("172.16.10.5/24") + assert_equal ip.subtract(10), IPAddress::IPv4.new("172.16.10.0/24") + assert_raise(ArgumentError) {ip.subtract(11)} + assert_raise(ArgumentError) {ip.subtract(IPAddress::IPv4.new("0.0.0.11/16"))} + end + def test_classmethod_parse_u32 @decimal_values.each do |addr,int| ip = @klass.parse_u32(int) From 197e974afe872af963fd3ec366d75648b795a62d Mon Sep 17 00:00:00 2001 From: Victor Hahn Date: Tue, 9 Sep 2014 13:44:37 +0200 Subject: [PATCH 02/13] Provide add and subtract methods for arithmetic operations on IPv6 addresses --- lib/ipaddress/ipv6.rb | 43 +++++++++++++++++++++++++++++++++++++ test/ipaddress/ipv6_test.rb | 18 ++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/lib/ipaddress/ipv6.rb b/lib/ipaddress/ipv6.rb index 33d4d19..e37c514 100644 --- a/lib/ipaddress/ipv6.rb +++ b/lib/ipaddress/ipv6.rb @@ -315,6 +315,49 @@ def reverse to_hex.reverse.gsub(/./){|c| c+"."} + "ip6.arpa" end alias_method :arpa, :reverse + + # + # Returns a new IPv6 object which is the result + # of advancing this IP address by a given value. + # In other words, this arithmetically adds IP addresses. + # + # Will raise an error if the resulting address is in a different subnet, + # except validating is set to false. + # + # Example: + # + # ip = IPAddress::IPv6.new("fc42:1337::/64") + # ip.add(5).to_string + # #=> "fc42:1337::5/64" + def add(oth, validating=true) + oth = oth.to_i if oth.kind_of? IPAddress::IPv6 # oth shall be integer + + new_obj = self.class.parse_u128(self.to_i + oth, prefix) + + if validating and self.network_u128 != new_obj.network_u128 + raise ArgumentError, "Subnet (/#{@prefix}) is not large enough." + end + + return new_obj + end + + # + # Returns a new IPv6 object which is the result + # of decreasing this IP address by a given value. + # In other words, this arithmetically subtracts IP addresses. + # + # Will raise an error if the resulting address is in a different subnet, + # except validating is set to false. + # + # Example: + # + # ip = IPAddress::IPv6.new("fc42::1337::a/64") + # ip.subtract(5).to_string + # #=> "fc42:1337::5/64" + def subtract(oth, validating=true) + oth = oth.to_i if oth.kind_of? IPAddress::IPv6 # oth shall be integer + return add(-oth, validating) + end # # Returns the network number in Unsigned 128bits format diff --git a/test/ipaddress/ipv6_test.rb b/test/ipaddress/ipv6_test.rb index 8e6213c..f4b8bee 100644 --- a/test/ipaddress/ipv6_test.rb +++ b/test/ipaddress/ipv6_test.rb @@ -258,6 +258,24 @@ def test_method_compare "2001:db8:1::2/64","2001:db8:2::1/64"] assert_equal arr, [ip1,ip2,ip3,ip4].sort.map{|s| s.to_string} end + + def test_method_add + ip = IPAddress::IPv6.new("fc42:1337::/64") + assert_equal ip.add(5), IPAddress::IPv6.new("fc42:1337::5/64") + assert_equal ip.add(IPAddress::IPv6.new("::5/42")), IPAddress::IPv6.new("fc42:1337::5/64") + assert_equal ip.add(50), IPAddress::IPv6.new("fc42:1337::32/64") + ip = IPAddress::IPv6.new("fc42:1337::/120") + assert_equal ip.add(2), IPAddress::IPv6.new("fc42:1337::2/120") + assert_raise(ArgumentError) {ip.add(256)} + end + + def test_method_subtract + ip = IPAddress::IPv6.new("fc42:1337::5/64") + assert_equal ip.subtract(5), IPAddress::IPv6.new("fc42:1337::/64") + assert_equal ip.subtract(IPAddress::IPv6.new("::5/12")), IPAddress::IPv6.new("fc42:1337::0/64") + assert_raise(ArgumentError) {ip.subtract(11)} + assert_raise(ArgumentError) {ip.subtract(IPAddress::IPv6.new("::11/66"))} + end def test_classmethod_expand compressed = "2001:db8:0:cd30::" From 541dafd66f32e3fc3e3d35ce60d8187af7b141c9 Mon Sep 17 00:00:00 2001 From: Victor Hahn Date: Tue, 9 Sep 2014 13:47:13 +0200 Subject: [PATCH 03/13] New versions no longer bundle test/unit, explicitly specify test/unit instead of minitest for Ruby > 1.9.0 --- test/test_helper.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_helper.rb b/test/test_helper.rb index 61e381e..ebf13bd 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,4 +1,5 @@ require 'rubygems' +gem 'test-unit' if RUBY_VERSION >= "1.9.0" require 'test/unit' $LOAD_PATH.unshift(File.dirname(__FILE__)) From 9ed2519e8d2277df73345b9d1645e62b7523a956 Mon Sep 17 00:00:00 2001 From: Victor Hahn Date: Tue, 9 Sep 2014 13:56:11 +0200 Subject: [PATCH 04/13] Provide unit tests for non-validating arithmetic operations --- test/ipaddress/ipv4_test.rb | 2 ++ test/ipaddress/ipv6_test.rb | 1 + 2 files changed, 3 insertions(+) diff --git a/test/ipaddress/ipv4_test.rb b/test/ipaddress/ipv4_test.rb index 6ae24e2..0cdb009 100644 --- a/test/ipaddress/ipv4_test.rb +++ b/test/ipaddress/ipv4_test.rb @@ -440,6 +440,7 @@ def test_method_add assert_equal ip.add(50), IPAddress::IPv4.new("172.16.10.51/24") assert_equal ip.add(254), IPAddress::IPv4.new("172.16.10.255/24") assert_raise(ArgumentError) {ip.add(255)} + assert_equal ip.add(255, false), IPAddress::IPv4.new("172.16.11.0/24") assert_raise(ArgumentError) {ip.add(1000)} ip = IPAddress::IPv4.new("172.16.10.1/30") assert_equal ip.add(2), IPAddress::IPv4.new("172.16.10.3/30") @@ -452,6 +453,7 @@ def test_method_subtract assert_equal ip.subtract(IPAddress::IPv4.new("0.0.0.5/32")), IPAddress::IPv4.new("172.16.10.5/24") assert_equal ip.subtract(10), IPAddress::IPv4.new("172.16.10.0/24") assert_raise(ArgumentError) {ip.subtract(11)} + assert_equal ip.subtract(11, false), IPAddress::IPv4.new("172.16.9.255/24") assert_raise(ArgumentError) {ip.subtract(IPAddress::IPv4.new("0.0.0.11/16"))} end diff --git a/test/ipaddress/ipv6_test.rb b/test/ipaddress/ipv6_test.rb index f4b8bee..574a4c4 100644 --- a/test/ipaddress/ipv6_test.rb +++ b/test/ipaddress/ipv6_test.rb @@ -267,6 +267,7 @@ def test_method_add ip = IPAddress::IPv6.new("fc42:1337::/120") assert_equal ip.add(2), IPAddress::IPv6.new("fc42:1337::2/120") assert_raise(ArgumentError) {ip.add(256)} + assert_equal ip.add(256, false), IPAddress::IPv6.new("fc42:1337::100/120") end def test_method_subtract From c7644e1889b95db2f9b8d849f38a56d4990c08f6 Mon Sep 17 00:00:00 2001 From: Victor Hahn Date: Mon, 15 Sep 2014 13:40:44 +0200 Subject: [PATCH 05/13] Bump version --- ipaddress.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipaddress.gemspec b/ipaddress.gemspec index 41af7bf..b9a451f 100644 --- a/ipaddress.gemspec +++ b/ipaddress.gemspec @@ -5,7 +5,7 @@ Gem::Specification.new do |s| s.name = %q{ipaddress} - s.version = "0.8.0" + s.version = "0.8.1" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Marco Ceresa"] From 9b0aae59e72b5b016180d940d94347eaf2e2fd63 Mon Sep 17 00:00:00 2001 From: Victor Hahn Date: Mon, 15 Sep 2014 14:18:45 +0200 Subject: [PATCH 06/13] Provide IPv4::hostpart and hostpart_u32 --- lib/ipaddress/ipv4.rb | 24 ++++++++++++++++++++++++ test/ipaddress/ipv4_test.rb | 7 +++++++ 2 files changed, 31 insertions(+) diff --git a/lib/ipaddress/ipv4.rb b/lib/ipaddress/ipv4.rb index a725a9a..e8ac940 100644 --- a/lib/ipaddress/ipv4.rb +++ b/lib/ipaddress/ipv4.rb @@ -326,6 +326,18 @@ def network? def network self.class.parse_u32(network_u32, @prefix) end + + # + # Returns a new IPv4 object containing only the host part of this IP. + # + # ip = IPAddress("172.16.10.64/24") + # + # ip.hostpart.to_s + # #=> "0.0.0.64" + # + def hostpart + self.class.parse_u32(hostpart_u32, 32) + end # # Returns a new IPv4 object with the @@ -505,6 +517,18 @@ def hosts def network_u32 @u32 & @prefix.to_u32 end + + # + # Returns this address' host part in unsigned 32bits format + # + # ip = IPAddress("10.0.0.42/24") + # + # ip.host_u32 + # #=> 42 + # + def hostpart_u32 + @u32 & ~@prefix.to_u32 + end # # Returns the broadcast address in Unsigned 32bits format diff --git a/test/ipaddress/ipv4_test.rb b/test/ipaddress/ipv4_test.rb index 0cdb009..ac5cbe0 100644 --- a/test/ipaddress/ipv4_test.rb +++ b/test/ipaddress/ipv4_test.rb @@ -457,6 +457,13 @@ def test_method_subtract assert_raise(ArgumentError) {ip.subtract(IPAddress::IPv4.new("0.0.0.11/16"))} end + def test_method_hostpart + ip = IPAddress::IPv4.new("172.16.10.64/24") + assert_equal ip.hostpart.to_s, "0.0.0.64" + ip = IPAddress::IPv4.new("172.16.10.130/25") + assert_equal ip.hostpart.to_s, "0.0.0.2" + end + def test_classmethod_parse_u32 @decimal_values.each do |addr,int| ip = @klass.parse_u32(int) From 1ede7fa804ee311da67cddd537b9fcee013966dc Mon Sep 17 00:00:00 2001 From: Victor Hahn Date: Mon, 15 Sep 2014 15:08:43 +0200 Subject: [PATCH 07/13] Network calculations for IPv4 --- lib/ipaddress/ipv4.rb | 48 ++++++++++++++++++++++++++++ test/ipaddress/ipv4_test.rb | 64 ++++++++++++++++++++++++------------- 2 files changed, 90 insertions(+), 22 deletions(-) diff --git a/lib/ipaddress/ipv4.rb b/lib/ipaddress/ipv4.rb index e8ac940..adfc100 100644 --- a/lib/ipaddress/ipv4.rb +++ b/lib/ipaddress/ipv4.rb @@ -796,6 +796,54 @@ def subtract(oth, validating=true) oth = oth.to_i if oth.kind_of? IPAddress::IPv4 # oth shall be integer return add(-oth, validating) end + + # + # Returns the network address of the n-th network succeeding this one. + # + # Example: + # + # ip = IPAddress::IPv4.new("172.16.10.0/24") + # ip.advance_network(24).to_string + # #=> "172.16.52.0/24" + def advance_network(amount) + IPAddress::IPv4.parse_u32(self.network.u32 + amount*self.size, @prefix) + end + + # + # Returns the network address of the network succeeding this one. + # + # Example: + # + # ip = IPAddress::IPv4.new("172.16.10.0/24") + # ip.next_network.to_string + # #=> "172.16.11.0/24" + def next_network + advance_network 1 + end + + # + # Returns the network address of the n-th network preceeding this one. + # + # Example: + # + # ip = IPAddress::IPv4.new("172.16.10.0/24") + # ip.regress_network(5).to_string + # #=> "172.16.5.0/24" + def regress_network(amount) + advance_network -amount + end + + # + # Returns the network address of the network preceeding this one. + # + # Example: + # + # ip = IPAddress::IPv4.new("172.16.10.0/24") + # ip.previous_network.to_string + # #=> "172.16.9.0/24" + def previous_network + regress_network 1 + end # # Checks whether the ip address belongs to a diff --git a/test/ipaddress/ipv4_test.rb b/test/ipaddress/ipv4_test.rb index ac5cbe0..5f7ba9f 100644 --- a/test/ipaddress/ipv4_test.rb +++ b/test/ipaddress/ipv4_test.rb @@ -13,13 +13,13 @@ def setup "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"] + "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"] + "10.0.1-254.0", + "10.1-254.0.0"] @netmask_values = { "0.0.0.0/0" => "0.0.0.0", @@ -193,7 +193,7 @@ def test_method_each_host 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"] + "10.0.0.4","10.0.0.5","10.0.0.6"] assert_equal expected, arr end @@ -202,8 +202,8 @@ def test_method_each 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"] + "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 @@ -215,7 +215,7 @@ def test_method_size 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"] + "10.0.0.4","10.0.0.5","10.0.0.6"] assert_equal expected, ip.hosts.map {|i| i.to_s} end @@ -354,7 +354,7 @@ def test_method_plus 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 + ip2).map{|i| i.to_string} ip1 = @klass.new("10.0.0.0/23") ip2 = @klass.new("10.0.2.0/24") @@ -388,21 +388,21 @@ def test_method_split assert_equal @ip.network, @ip.split(1).first arr = ["172.16.10.0/27", "172.16.10.32/27", "172.16.10.64/27", - "172.16.10.96/27", "172.16.10.128/27", "172.16.10.160/27", - "172.16.10.192/27", "172.16.10.224/27"] + "172.16.10.96/27", "172.16.10.128/27", "172.16.10.160/27", + "172.16.10.192/27", "172.16.10.224/27"] assert_equal arr, @network.split(8).map {|s| s.to_string} arr = ["172.16.10.0/27", "172.16.10.32/27", "172.16.10.64/27", - "172.16.10.96/27", "172.16.10.128/27", "172.16.10.160/27", - "172.16.10.192/26"] + "172.16.10.96/27", "172.16.10.128/27", "172.16.10.160/27", + "172.16.10.192/26"] assert_equal arr, @network.split(7).map {|s| s.to_string} arr = ["172.16.10.0/27", "172.16.10.32/27", "172.16.10.64/27", - "172.16.10.96/27", "172.16.10.128/26", "172.16.10.192/26"] + "172.16.10.96/27", "172.16.10.128/26", "172.16.10.192/26"] assert_equal arr, @network.split(6).map {|s| s.to_string} arr = ["172.16.10.0/27", "172.16.10.32/27", "172.16.10.64/27", - "172.16.10.96/27", "172.16.10.128/25"] + "172.16.10.96/27", "172.16.10.128/25"] assert_equal arr, @network.split(5).map {|s| s.to_string} arr = ["172.16.10.0/26", "172.16.10.64/26", "172.16.10.128/26", - "172.16.10.192/26"] + "172.16.10.192/26"] assert_equal arr, @network.split(4).map {|s| s.to_string} arr = ["172.16.10.0/26", "172.16.10.64/26", "172.16.10.128/25"] assert_equal arr, @network.split(3).map {|s| s.to_string} @@ -417,7 +417,7 @@ def test_method_subnet assert_raise(ArgumentError) {@network.subnet(33)} assert_nothing_raised {@ip.subnet(30)} arr = ["172.16.10.0/26", "172.16.10.64/26", "172.16.10.128/26", - "172.16.10.192/26"] + "172.16.10.192/26"] assert_equal arr, @network.subnet(26).map {|s| s.to_string} arr = ["172.16.10.0/25", "172.16.10.128/25"] assert_equal arr, @network.subnet(25).map {|s| s.to_string} @@ -464,6 +464,26 @@ def test_method_hostpart assert_equal ip.hostpart.to_s, "0.0.0.2" end + def test_method_advance_network + ip = IPAddress::IPv4.new("172.16.10.64/24") + assert_equal ip.advance_network(42), IPAddress::IPv4.new("172.16.52.0/24") + end + + def test_method_next_network + ip = IPAddress::IPv4.new("172.16.10.64/24") + assert_equal ip.next_network, IPAddress::IPv4.new("172.16.11.0/24") + end + + def test_method_regress_network + ip = IPAddress::IPv4.new("172.16.10.64/24") + assert_equal ip.regress_network(5), IPAddress::IPv4.new("172.16.5.0/24") + end + + def test_method_previous_network + ip = IPAddress::IPv4.new("172.16.10.64/24") + assert_equal ip.previous_network, IPAddress::IPv4.new("172.16.9.0/24") + end + def test_classmethod_parse_u32 @decimal_values.each do |addr,int| ip = @klass.parse_u32(int) @@ -512,7 +532,7 @@ def test_classmethod_summarize 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} + @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") @@ -530,17 +550,17 @@ def test_classmethod_summarize 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")] + @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")] + @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")] + @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} From 316d43bca4a61b296df2d097bea7bbeaf6663593 Mon Sep 17 00:00:00 2001 From: Victor Hahn Date: Mon, 15 Sep 2014 15:23:25 +0200 Subject: [PATCH 08/13] Network calculations for IPv6 --- lib/ipaddress/ipv6.rb | 50 ++++++++++++++++++++++++++++++++++++- test/ipaddress/ipv6_test.rb | 20 +++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/lib/ipaddress/ipv6.rb b/lib/ipaddress/ipv6.rb index e37c514..d625a21 100644 --- a/lib/ipaddress/ipv6.rb +++ b/lib/ipaddress/ipv6.rb @@ -351,7 +351,7 @@ def add(oth, validating=true) # # Example: # - # ip = IPAddress::IPv6.new("fc42::1337::a/64") + # ip = IPAddress::IPv6.new("fc42:1337::a/64") # ip.subtract(5).to_string # #=> "fc42:1337::5/64" def subtract(oth, validating=true) @@ -359,6 +359,54 @@ def subtract(oth, validating=true) return add(-oth, validating) end + # + # Returns the network address of the n-th network succeeding this one. + # + # Example: + # + # ip = IPAddress::IPv6.new("fc42:1337:0:0::/64") + # ip.advance_network(5).to_string + # #=> "fc42:1337:0:5::/64" + def advance_network(amount) + IPAddress::IPv6.parse_u128(self.network.to_i + amount*self.size, @prefix) + end + + # + # Returns the network address of the network succeeding this one. + # + # Example: + # + # ip = IPAddress::IPv6.new("fc42:1337:0:0::/64") + # ip.next_network.to_string + # #=> "fc42:1337:0:1::/64" + def next_network + advance_network 1 + end + + # + # Returns the network address of the n-th network preceeding this one. + # + # Example: + # + # ip = IPAddress::IPv6.new("fc42:1337:0:5::/64") + # ip.regress_network(4).to_string + # #=> "fc42:1337:0:1::/64" + def regress_network(amount) + advance_network -amount + end + + # + # Returns the network address of the network preceeding this one. + # + # Example: + # + # ip = IPAddress::IPv6.new("fc42:1337:0:5::/64") + # ip.previous_network.to_string + # #=> "fc42:1337:0:4::/64" + def previous_network + regress_network 1 + end + # # Returns the network number in Unsigned 128bits format # diff --git a/test/ipaddress/ipv6_test.rb b/test/ipaddress/ipv6_test.rb index 574a4c4..f52c4af 100644 --- a/test/ipaddress/ipv6_test.rb +++ b/test/ipaddress/ipv6_test.rb @@ -278,6 +278,26 @@ def test_method_subtract assert_raise(ArgumentError) {ip.subtract(IPAddress::IPv6.new("::11/66"))} end + def test_method_advance_network + ip = IPAddress::IPv6.new("fc42:1337:0:0::/64") + assert_equal ip.advance_network(5), IPAddress::IPv6.new("fc42:1337:0:5::/64") + end + + def test_method_next_network + ip = IPAddress::IPv6.new("fc42:1337:0:0::/64") + assert_equal ip.next_network, IPAddress::IPv6.new("fc42:1337:0:1::/64") + end + + def test_method_regress_network + ip = IPAddress::IPv6.new("fc42:1337:0:5::/64") + assert_equal ip.regress_network(4), IPAddress::IPv6.new("fc42:1337:0:1::/64") + end + + def test_method_previous_network + ip = IPAddress::IPv6.new("fc42:1337:0:5::/64") + assert_equal ip.previous_network, IPAddress::IPv6.new("fc42:1337:0:4::/64") + end + def test_classmethod_expand compressed = "2001:db8:0:cd30::" expanded = "2001:0db8:0000:cd30:0000:0000:0000:0000" From 0931a35119da63b466d511af0c8d07a04e5ad7a3 Mon Sep 17 00:00:00 2001 From: Victor Hahn Date: Mon, 15 Sep 2014 15:41:07 +0200 Subject: [PATCH 09/13] Provide IPv6::hostpart and hostpart_u128 --- lib/ipaddress/ipv6.rb | 24 ++++++++++++++++++++++++ test/ipaddress/ipv6_test.rb | 5 +++++ 2 files changed, 29 insertions(+) diff --git a/lib/ipaddress/ipv6.rb b/lib/ipaddress/ipv6.rb index d625a21..8e49b76 100644 --- a/lib/ipaddress/ipv6.rb +++ b/lib/ipaddress/ipv6.rb @@ -407,6 +407,18 @@ def previous_network regress_network 1 end + # + # Returns a new IPv6 object containing only the host part of this IP. + # + # ip = IPAddress::IPv6.new("fc42:1337:0:5::7/64") + # + # ip.hostpart.to_s + # #=> "::7" + # + def hostpart + self.class.parse_u128(hostpart_u128, 128) + end + # # Returns the network number in Unsigned 128bits format # @@ -418,6 +430,18 @@ def previous_network def network_u128 to_u128 & @prefix.to_u128 end + + # + # Returns this address' host part in unsigned 128bits format + # + # ip = IPAddress::IPv6.new("fc42:1337:0:5::7/64") + # + # ip.host_u128 + # #=> 7 + # + def hostpart_u128 + to_u128 & ~@prefix.to_u128 + end # # Returns the broadcast address in Unsigned 128bits format diff --git a/test/ipaddress/ipv6_test.rb b/test/ipaddress/ipv6_test.rb index f52c4af..3ae51b3 100644 --- a/test/ipaddress/ipv6_test.rb +++ b/test/ipaddress/ipv6_test.rb @@ -277,6 +277,11 @@ def test_method_subtract assert_raise(ArgumentError) {ip.subtract(11)} assert_raise(ArgumentError) {ip.subtract(IPAddress::IPv6.new("::11/66"))} end + + def test_method_hostpart + ip = IPAddress::IPv6.new("fc42:1337:0:5::7/64") + assert_equal ip.hostpart.to_s, "::7" + end def test_method_advance_network ip = IPAddress::IPv6.new("fc42:1337:0:0::/64") From 54a8373895a16a099cd451feda82f580af86b9cb Mon Sep 17 00:00:00 2001 From: Manuel Widmer Date: Mon, 18 Apr 2016 13:21:26 +0200 Subject: [PATCH 10/13] integrated feedback from @francisluong removed return statements reverted unnecessary indentation changes changed ArgumentError to RuntimeError for add method --- lib/ipaddress/ipv4.rb | 10 +++++----- lib/ipaddress/ipv6.rb | 6 +++--- test/ipaddress/ipv4_test.rb | 34 +++++++++++++++++----------------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/lib/ipaddress/ipv4.rb b/lib/ipaddress/ipv4.rb index 806e61f..6823e93 100644 --- a/lib/ipaddress/ipv4.rb +++ b/lib/ipaddress/ipv4.rb @@ -640,8 +640,8 @@ def include_all?(*others) # 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} + self.class.new("172.16.0.0/12"), + self.class.new("192.168.0.0/16")].any? {|i| i.include? self} end # @@ -872,10 +872,10 @@ def add(oth, validating=true) new_obj = self.class.parse_u32(self.to_i + oth, prefix) if validating and self.network_u32 != new_obj.network_u32 - raise ArgumentError, "Subnet (/#{@prefix}) is not large enough." + raise RuntimeError, "Subnet (/#{@prefix}) is not large enough." end - return new_obj + new_obj end # @@ -893,7 +893,7 @@ def add(oth, validating=true) # #=> "172.16.10.5/24" def subtract(oth, validating=true) oth = oth.to_i if oth.kind_of? IPAddress::IPv4 # oth shall be integer - return add(-oth, validating) + add(-oth, validating) end # diff --git a/lib/ipaddress/ipv6.rb b/lib/ipaddress/ipv6.rb index 959eba8..47eff3d 100644 --- a/lib/ipaddress/ipv6.rb +++ b/lib/ipaddress/ipv6.rb @@ -344,10 +344,10 @@ def add(oth, validating=true) new_obj = self.class.parse_u128(self.to_i + oth, prefix) if validating and self.network_u128 != new_obj.network_u128 - raise ArgumentError, "Subnet (/#{@prefix}) is not large enough." + raise RuntimeError, "Subnet (/#{@prefix}) is not large enough." end - return new_obj + new_obj end # @@ -365,7 +365,7 @@ def add(oth, validating=true) # #=> "fc42:1337::5/64" def subtract(oth, validating=true) oth = oth.to_i if oth.kind_of? IPAddress::IPv6 # oth shall be integer - return add(-oth, validating) + add(-oth, validating) end # diff --git a/test/ipaddress/ipv4_test.rb b/test/ipaddress/ipv4_test.rb index e0e34b1..b8152f0 100644 --- a/test/ipaddress/ipv4_test.rb +++ b/test/ipaddress/ipv4_test.rb @@ -13,13 +13,13 @@ def setup "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"] + "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"] + "10.0.1-254.0", + "10.1-254.0.0"] @netmask_values = { "0.0.0.0/0" => "0.0.0.0", @@ -227,7 +227,7 @@ def test_method_each_host 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"] + "10.0.0.4","10.0.0.5","10.0.0.6"] assert_equal expected, arr end @@ -236,8 +236,8 @@ def test_method_each 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"] + "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 @@ -249,7 +249,7 @@ def test_method_size 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"] + "10.0.0.4","10.0.0.5","10.0.0.6"] assert_equal expected, ip.hosts.map {|i| i.to_s} end @@ -422,21 +422,21 @@ def test_method_split assert_equal @ip.network, @ip.split(1).first arr = ["172.16.10.0/27", "172.16.10.32/27", "172.16.10.64/27", - "172.16.10.96/27", "172.16.10.128/27", "172.16.10.160/27", - "172.16.10.192/27", "172.16.10.224/27"] + "172.16.10.96/27", "172.16.10.128/27", "172.16.10.160/27", + "172.16.10.192/27", "172.16.10.224/27"] assert_equal arr, @network.split(8).map {|s| s.to_string} arr = ["172.16.10.0/27", "172.16.10.32/27", "172.16.10.64/27", - "172.16.10.96/27", "172.16.10.128/27", "172.16.10.160/27", - "172.16.10.192/26"] + "172.16.10.96/27", "172.16.10.128/27", "172.16.10.160/27", + "172.16.10.192/26"] assert_equal arr, @network.split(7).map {|s| s.to_string} arr = ["172.16.10.0/27", "172.16.10.32/27", "172.16.10.64/27", - "172.16.10.96/27", "172.16.10.128/26", "172.16.10.192/26"] + "172.16.10.96/27", "172.16.10.128/26", "172.16.10.192/26"] assert_equal arr, @network.split(6).map {|s| s.to_string} arr = ["172.16.10.0/27", "172.16.10.32/27", "172.16.10.64/27", - "172.16.10.96/27", "172.16.10.128/25"] + "172.16.10.96/27", "172.16.10.128/25"] assert_equal arr, @network.split(5).map {|s| s.to_string} arr = ["172.16.10.0/26", "172.16.10.64/26", "172.16.10.128/26", - "172.16.10.192/26"] + "172.16.10.192/26"] assert_equal arr, @network.split(4).map {|s| s.to_string} arr = ["172.16.10.0/26", "172.16.10.64/26", "172.16.10.128/25"] assert_equal arr, @network.split(3).map {|s| s.to_string} @@ -450,7 +450,7 @@ def test_method_subnet assert_raises(ArgumentError) {@network.subnet(23)} assert_raises(ArgumentError) {@network.subnet(33)} arr = ["172.16.10.0/26", "172.16.10.64/26", "172.16.10.128/26", - "172.16.10.192/26"] + "172.16.10.192/26"] assert_equal arr, @network.subnet(26).map {|s| s.to_string} arr = ["172.16.10.0/25", "172.16.10.128/25"] assert_equal arr, @network.subnet(25).map {|s| s.to_string} From b0840bbafddb47b11647932c3db3578301adf8f6 Mon Sep 17 00:00:00 2001 From: Manuel Widmer Date: Mon, 18 Apr 2016 13:31:45 +0200 Subject: [PATCH 11/13] update unit-tests changed ArgumentError to RuntimeError --- test/ipaddress/ipv4_test.rb | 10 +++++----- test/ipaddress/ipv6_test.rb | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/ipaddress/ipv4_test.rb b/test/ipaddress/ipv4_test.rb index b8152f0..8743121 100644 --- a/test/ipaddress/ipv4_test.rb +++ b/test/ipaddress/ipv4_test.rb @@ -472,12 +472,12 @@ def test_method_add assert_equal ip.add(IPAddress::IPv4.new("0.0.0.5/6")), IPAddress::IPv4.new("172.16.10.6/24") assert_equal ip.add(50), IPAddress::IPv4.new("172.16.10.51/24") assert_equal ip.add(254), IPAddress::IPv4.new("172.16.10.255/24") - assert_raise(ArgumentError) {ip.add(255)} + assert_raise(RuntimeError) {ip.add(255)} assert_equal ip.add(255, false), IPAddress::IPv4.new("172.16.11.0/24") - assert_raise(ArgumentError) {ip.add(1000)} + assert_raise(RuntimeError) {ip.add(1000)} ip = IPAddress::IPv4.new("172.16.10.1/30") assert_equal ip.add(2), IPAddress::IPv4.new("172.16.10.3/30") - assert_raise(ArgumentError) {ip.add(3)} + assert_raise(RuntimeError) {ip.add(3)} end def test_method_subtract @@ -485,9 +485,9 @@ def test_method_subtract assert_equal ip.subtract(5), IPAddress::IPv4.new("172.16.10.5/24") assert_equal ip.subtract(IPAddress::IPv4.new("0.0.0.5/32")), IPAddress::IPv4.new("172.16.10.5/24") assert_equal ip.subtract(10), IPAddress::IPv4.new("172.16.10.0/24") - assert_raise(ArgumentError) {ip.subtract(11)} + assert_raise(RuntimeError) {ip.subtract(11)} assert_equal ip.subtract(11, false), IPAddress::IPv4.new("172.16.9.255/24") - assert_raise(ArgumentError) {ip.subtract(IPAddress::IPv4.new("0.0.0.11/16"))} + assert_raise(RuntimeError) {ip.subtract(IPAddress::IPv4.new("0.0.0.11/16"))} end def test_method_hostpart diff --git a/test/ipaddress/ipv6_test.rb b/test/ipaddress/ipv6_test.rb index fdfcbea..c4adb96 100644 --- a/test/ipaddress/ipv6_test.rb +++ b/test/ipaddress/ipv6_test.rb @@ -263,7 +263,7 @@ def test_method_add assert_equal ip.add(50), IPAddress::IPv6.new("fc42:1337::32/64") ip = IPAddress::IPv6.new("fc42:1337::/120") assert_equal ip.add(2), IPAddress::IPv6.new("fc42:1337::2/120") - assert_raise(ArgumentError) {ip.add(256)} + assert_raise(RuntimeError) {ip.add(256)} assert_equal ip.add(256, false), IPAddress::IPv6.new("fc42:1337::100/120") end @@ -271,8 +271,8 @@ def test_method_subtract ip = IPAddress::IPv6.new("fc42:1337::5/64") assert_equal ip.subtract(5), IPAddress::IPv6.new("fc42:1337::/64") assert_equal ip.subtract(IPAddress::IPv6.new("::5/12")), IPAddress::IPv6.new("fc42:1337::0/64") - assert_raise(ArgumentError) {ip.subtract(11)} - assert_raise(ArgumentError) {ip.subtract(IPAddress::IPv6.new("::11/66"))} + assert_raise(RuntimeError) {ip.subtract(11)} + assert_raise(RuntimeError) {ip.subtract(IPAddress::IPv6.new("::11/66"))} end def test_method_hostpart From 3094e2f4b691d3aa6c6287dc4e3780d7f394d655 Mon Sep 17 00:00:00 2001 From: Manuel Widmer Date: Mon, 18 Apr 2016 13:36:56 +0200 Subject: [PATCH 12/13] fixing unit-tests minitest syntax assert_raises --- test/ipaddress/ipv4_test.rb | 10 +++++----- test/ipaddress/ipv6_test.rb | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/ipaddress/ipv4_test.rb b/test/ipaddress/ipv4_test.rb index 8743121..3253770 100644 --- a/test/ipaddress/ipv4_test.rb +++ b/test/ipaddress/ipv4_test.rb @@ -472,12 +472,12 @@ def test_method_add assert_equal ip.add(IPAddress::IPv4.new("0.0.0.5/6")), IPAddress::IPv4.new("172.16.10.6/24") assert_equal ip.add(50), IPAddress::IPv4.new("172.16.10.51/24") assert_equal ip.add(254), IPAddress::IPv4.new("172.16.10.255/24") - assert_raise(RuntimeError) {ip.add(255)} + assert_raises(RuntimeError) {ip.add(255)} assert_equal ip.add(255, false), IPAddress::IPv4.new("172.16.11.0/24") - assert_raise(RuntimeError) {ip.add(1000)} + assert_raises(RuntimeError) {ip.add(1000)} ip = IPAddress::IPv4.new("172.16.10.1/30") assert_equal ip.add(2), IPAddress::IPv4.new("172.16.10.3/30") - assert_raise(RuntimeError) {ip.add(3)} + assert_raises(RuntimeError) {ip.add(3)} end def test_method_subtract @@ -485,9 +485,9 @@ def test_method_subtract assert_equal ip.subtract(5), IPAddress::IPv4.new("172.16.10.5/24") assert_equal ip.subtract(IPAddress::IPv4.new("0.0.0.5/32")), IPAddress::IPv4.new("172.16.10.5/24") assert_equal ip.subtract(10), IPAddress::IPv4.new("172.16.10.0/24") - assert_raise(RuntimeError) {ip.subtract(11)} + assert_raises(RuntimeError) {ip.subtract(11)} assert_equal ip.subtract(11, false), IPAddress::IPv4.new("172.16.9.255/24") - assert_raise(RuntimeError) {ip.subtract(IPAddress::IPv4.new("0.0.0.11/16"))} + assert_raises(RuntimeError) {ip.subtract(IPAddress::IPv4.new("0.0.0.11/16"))} end def test_method_hostpart diff --git a/test/ipaddress/ipv6_test.rb b/test/ipaddress/ipv6_test.rb index c4adb96..a137978 100644 --- a/test/ipaddress/ipv6_test.rb +++ b/test/ipaddress/ipv6_test.rb @@ -263,7 +263,7 @@ def test_method_add assert_equal ip.add(50), IPAddress::IPv6.new("fc42:1337::32/64") ip = IPAddress::IPv6.new("fc42:1337::/120") assert_equal ip.add(2), IPAddress::IPv6.new("fc42:1337::2/120") - assert_raise(RuntimeError) {ip.add(256)} + assert_raises(RuntimeError) {ip.add(256)} assert_equal ip.add(256, false), IPAddress::IPv6.new("fc42:1337::100/120") end @@ -271,8 +271,8 @@ def test_method_subtract ip = IPAddress::IPv6.new("fc42:1337::5/64") assert_equal ip.subtract(5), IPAddress::IPv6.new("fc42:1337::/64") assert_equal ip.subtract(IPAddress::IPv6.new("::5/12")), IPAddress::IPv6.new("fc42:1337::0/64") - assert_raise(RuntimeError) {ip.subtract(11)} - assert_raise(RuntimeError) {ip.subtract(IPAddress::IPv6.new("::11/66"))} + assert_raises(RuntimeError) {ip.subtract(11)} + assert_raises(RuntimeError) {ip.subtract(IPAddress::IPv6.new("::11/66"))} end def test_method_hostpart From 82d0d705f2acccfb3ce0bdf62ebfb9a2e710ca1d Mon Sep 17 00:00:00 2001 From: Manuel Widmer Date: Mon, 18 Apr 2016 13:44:14 +0200 Subject: [PATCH 13/13] indentation fixes --- test/ipaddress/ipv4_test.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/ipaddress/ipv4_test.rb b/test/ipaddress/ipv4_test.rb index 3253770..b528384 100644 --- a/test/ipaddress/ipv4_test.rb +++ b/test/ipaddress/ipv4_test.rb @@ -388,7 +388,7 @@ def test_method_plus 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 + ip2).map{|i| i.to_string} ip1 = @klass.new("10.0.0.0/23") ip2 = @klass.new("10.0.2.0/24") @@ -565,7 +565,7 @@ def test_classmethod_summarize 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} + @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") @@ -583,17 +583,17 @@ def test_classmethod_summarize 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")] + @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")] + @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")] + @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}