From 293ad5ac6ed704f91d418ecaf24ae58156a587e7 Mon Sep 17 00:00:00 2001 From: Akinori MUSHA Date: Sun, 3 Sep 2017 17:27:13 +0900 Subject: [PATCH] Add IPAddr#prefix This (kind of) addresses [Feature #11210](https://bugs.ruby-lang.org/issues/11210). --- lib/ipaddr.rb | 32 +++++++++++++++++++++++++++++++- test/test_ipaddr.rb | 40 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/lib/ipaddr.rb b/lib/ipaddr.rb index 6f70ebf..ad99782 100644 --- a/lib/ipaddr.rb +++ b/lib/ipaddr.rb @@ -371,6 +371,35 @@ def to_range return clone.set(begin_addr, @family)..clone.set(end_addr, @family) end + # Returns the prefix length in bits for the ipaddr. + def prefix + case @family + when Socket::AF_INET + n = IN4MASK ^ @mask_addr + i = 32 + when Socket::AF_INET6 + n = IN6MASK ^ @mask_addr + i = 128 + else + raise AddressFamilyError, "unsupported address family" + end + while n.positive? + n >>= 1 + i -= 1 + end + i + end + + # Sets the prefix length in bits + def prefix=(prefix) + case prefix + when Integer + mask!(prefix) + else + raise InvalidPrefixError, "prefix must be an integer" + end + end + # Returns a string containing a human-readable representation of the # ipaddr. ("#") def inspect @@ -413,7 +442,8 @@ def set(addr, *family) # Set current netmask to given mask. def mask!(mask) - if mask.kind_of?(String) + case mask + when String if mask =~ /\A\d+\z/ prefixlen = mask.to_i else diff --git a/test/test_ipaddr.rb b/test/test_ipaddr.rb index 86482a0..1ad4534 100644 --- a/test/test_ipaddr.rb +++ b/test/test_ipaddr.rb @@ -21,11 +21,13 @@ def test_s_new assert_equal("::", a.to_s) assert_equal("0000:0000:0000:0000:0000:0000:0000:0000", a.to_string) assert_equal(Socket::AF_INET6, a.family) + assert_equal(128, a.prefix) a = IPAddr.new("0123:4567:89ab:cdef:0ABC:DEF0:1234:5678") assert_equal("123:4567:89ab:cdef:abc:def0:1234:5678", a.to_s) assert_equal("0123:4567:89ab:cdef:0abc:def0:1234:5678", a.to_string) assert_equal(Socket::AF_INET6, a.family) + assert_equal(128, a.prefix) a = IPAddr.new("3ffe:505:2::/48") assert_equal("3ffe:505:2::", a.to_s) @@ -34,16 +36,19 @@ def test_s_new assert_equal(false, a.ipv4?) assert_equal(true, a.ipv6?) assert_equal("#", a.inspect) + assert_equal(48, a.prefix) a = IPAddr.new("3ffe:505:2::/ffff:ffff:ffff::") assert_equal("3ffe:505:2::", a.to_s) assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string) assert_equal(Socket::AF_INET6, a.family) + assert_equal(48, a.prefix) a = IPAddr.new("0.0.0.0") assert_equal("0.0.0.0", a.to_s) assert_equal("0.0.0.0", a.to_string) assert_equal(Socket::AF_INET, a.family) + assert_equal(32, a.prefix) a = IPAddr.new("192.168.1.2") assert_equal("192.168.1.2", a.to_s) @@ -51,17 +56,27 @@ def test_s_new assert_equal(Socket::AF_INET, a.family) assert_equal(true, a.ipv4?) assert_equal(false, a.ipv6?) + assert_equal(32, a.prefix) - a = IPAddr.new("192.168.1.2/24") + a = IPAddr.new("192.168.1.2/26") assert_equal("192.168.1.0", a.to_s) assert_equal("192.168.1.0", a.to_string) assert_equal(Socket::AF_INET, a.family) - assert_equal("#", a.inspect) + assert_equal("#", a.inspect) + assert_equal(26, a.prefix) a = IPAddr.new("192.168.1.2/255.255.255.0") assert_equal("192.168.1.0", a.to_s) assert_equal("192.168.1.0", a.to_string) assert_equal(Socket::AF_INET, a.family) + assert_equal(24, a.prefix) + + (0..32).each do |prefix| + assert_equal(prefix, IPAddr.new("10.20.30.40/#{prefix}").prefix) + end + (0..128).each do |prefix| + assert_equal(prefix, IPAddr.new("1:2:3:4:5:6:7:8/#{prefix}").prefix) + end assert_equal("0:0:0:1::", IPAddr.new("0:0:0:1::").to_s) assert_equal("2001:200:300::", IPAddr.new("2001:200:300::/48").to_s) @@ -153,6 +168,27 @@ def test_ip6_int } end + def test_prefix_writer + a = IPAddr.new("192.168.1.2") + ["1", "255.255.255.0", "ffff:ffff:ffff:ffff::", nil, 1.0, -1, 33].each { |x| + assert_raise(IPAddr::InvalidPrefixError) { a.prefix = x } + } + a = IPAddr.new("1:2:3:4:5:6:7:8") + ["1", "255.255.255.0", "ffff:ffff:ffff:ffff::", nil, 1.0, -1, 129].each { |x| + assert_raise(IPAddr::InvalidPrefixError) { a.prefix = x } + } + + a = IPAddr.new("192.168.1.2") + a.prefix = 26 + assert_equal(26, a.prefix) + assert_equal("192.168.1.0", a.to_s) + + a = IPAddr.new("1:2:3:4:5:6:7:8") + a.prefix = 52 + assert_equal(52, a.prefix) + assert_equal("1:2:3::", a.to_s) + end + def test_to_s assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0001", IPAddr.new("3ffe:505:2::1").to_string) assert_equal("3ffe:505:2::1", IPAddr.new("3ffe:505:2::1").to_s)