diff --git a/lib/ipaddress/ipv4.rb b/lib/ipaddress/ipv4.rb index a123e71..6823e93 100644 --- a/lib/ipaddress/ipv4.rb +++ b/lib/ipaddress/ipv4.rb @@ -363,6 +363,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 @@ -556,6 +568,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 @@ -828,6 +852,97 @@ 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 RuntimeError, "Subnet (/#{@prefix}) is not large enough." + end + + 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 + 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/lib/ipaddress/ipv6.rb b/lib/ipaddress/ipv6.rb index 88098bc..47eff3d 100644 --- a/lib/ipaddress/ipv6.rb +++ b/lib/ipaddress/ipv6.rb @@ -324,7 +324,110 @@ 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 RuntimeError, "Subnet (/#{@prefix}) is not large enough." + end + + 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 + 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 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 # @@ -336,6 +439,18 @@ def reverse 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/ipv4_test.rb b/test/ipaddress/ipv4_test.rb index 19264e2..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") @@ -466,6 +466,57 @@ 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_raises(RuntimeError) {ip.add(255)} + assert_equal ip.add(255, false), IPAddress::IPv4.new("172.16.11.0/24") + 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_raises(RuntimeError) {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_raises(RuntimeError) {ip.subtract(11)} + assert_equal ip.subtract(11, false), IPAddress::IPv4.new("172.16.9.255/24") + assert_raises(RuntimeError) {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_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) diff --git a/test/ipaddress/ipv6_test.rb b/test/ipaddress/ipv6_test.rb index dcfb601..a137978 100644 --- a/test/ipaddress/ipv6_test.rb +++ b/test/ipaddress/ipv6_test.rb @@ -255,6 +255,50 @@ 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_raises(RuntimeError) {ip.add(256)} + assert_equal ip.add(256, false), IPAddress::IPv6.new("fc42:1337::100/120") + 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_raises(RuntimeError) {ip.subtract(11)} + assert_raises(RuntimeError) {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") + 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::"