Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Addition/subtraction and Next/previous-network fuctions #80

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions lib/ipaddress/ipv4.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 & [email protected]_u32
end

#
# Returns the broadcast address in Unsigned 32bits format
Expand Down Expand Up @@ -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
Expand Down
115 changes: 115 additions & 0 deletions lib/ipaddress/ipv6.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
#
Expand All @@ -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 & [email protected]_u128
end

#
# Returns the broadcast address in Unsigned 128bits format
Expand Down
53 changes: 52 additions & 1 deletion test/ipaddress/ipv4_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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)
Expand Down
44 changes: 44 additions & 0 deletions test/ipaddress/ipv6_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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::"
Expand Down