netaddr-2.0.6/0000755000004100000410000000000014263044212013166 5ustar www-datawww-datanetaddr-2.0.6/test/0000755000004100000410000000000014263044212014145 5ustar www-datawww-datanetaddr-2.0.6/test/ipv4_test.rb0000644000004100000410000000421314263044212016413 0ustar www-datawww-data#!/usr/bin/ruby require_relative "../lib/netaddr.rb" require 'test/unit' class TestIPv4 < Test::Unit::TestCase def test_new ip = NetAddr::IPv4.new(0x80000001) assert_equal("128.0.0.1", ip.to_s) assert_raise(NetAddr::ValidationError){ NetAddr::IPv4.new(0x8000000001) } assert_raise(NetAddr::ValidationError){ NetAddr::IPv4.new(-1) } assert_raise(NetAddr::ValidationError){ NetAddr::IPv4.new("128.0.0.1") } # string end def test_parse assert_equal("128.0.0.1", NetAddr::IPv4.parse("128.0.0.1").to_s) assert_equal("0.0.0.0", NetAddr::IPv4.parse("0.0.0.0").to_s) assert_raise(NetAddr::ValidationError){ NetAddr::IPv4.parse("128.0.0.1a") } assert_raise(NetAddr::ValidationError){ NetAddr::IPv4.parse("256.0.0.1") } assert_raise(NetAddr::ValidationError){ NetAddr::IPv4.parse("128.0.0.1.1") } assert_raise(NetAddr::ValidationError){ NetAddr::IPv4.parse("128") } end def test_cmp ip = NetAddr::IPv4.parse("128.0.0.1") ip2 = NetAddr::IPv4.parse("128.0.0.0") ip3 = NetAddr::IPv4.parse("128.0.0.2") ip4 = NetAddr::IPv4.parse("128.0.0.1") assert_equal(1, ip.cmp(ip2)) assert_equal(-1, ip.cmp(ip3)) assert_equal(0, ip.cmp(ip4)) end def test_multicast_mac assert_equal(0, NetAddr::IPv4.parse("223.255.255.255").multicast_mac.addr) assert_equal("01-00-5e-00-00-00", NetAddr::IPv4.parse("224.0.0.0").multicast_mac.to_s) assert_equal("01-00-5e-02-03-05", NetAddr::IPv4.parse("230.2.3.5").multicast_mac.to_s) assert_equal("01-00-5e-13-12-17", NetAddr::IPv4.parse("235.147.18.23").multicast_mac.to_s) assert_equal("01-00-5e-7f-ff-ff", NetAddr::IPv4.parse("239.255.255.255").multicast_mac.to_s) assert_equal(0, NetAddr::IPv4.parse("240.0.0.0").multicast_mac.addr) end def test_next assert_equal("255.255.255.255", NetAddr::IPv4.parse("255.255.255.254").next().to_s) assert_nil(NetAddr::IPv4.parse("255.255.255.255").next()) end def test_prev assert_equal("0.0.0.0", NetAddr::IPv4.parse("0.0.0.1").prev().to_s) assert_nil(NetAddr::IPv4.parse("0.0.0.0").prev()) end def test_to_net ip = NetAddr::IPv4.parse("192.168.1.1") net = NetAddr::IPv4Net.parse("192.168.1.1") assert_equal(0, net.cmp(ip.to_net())) end end netaddr-2.0.6/test/netaddr_test.rb0000644000004100000410000001011014263044212017143 0ustar www-datawww-data#!/usr/bin/ruby require_relative "../lib/netaddr.rb" require 'test/unit' class TestNetAddr < Test::Unit::TestCase def test_parse_ip assert_equal("128.0.0.1", NetAddr.parse_ip("128.0.0.1").to_s) assert_equal("1::1", NetAddr.parse_ip("1::1").to_s) end def test_parse_net assert_equal("128.0.0.1/32", NetAddr.parse_net("128.0.0.1/32").to_s) assert_equal("1::/24", NetAddr.parse_net("1::1/24").to_s) assert_equal("::ffff:aabb:ccdd/128", NetAddr.parse_net("::ffff:170.187.204.221/128").to_s) end def test_ipv4_prefix_len assert_equal(32,NetAddr.ipv4_prefix_len(1)) assert_equal(27,NetAddr.ipv4_prefix_len(30)) assert_equal(24,NetAddr.ipv4_prefix_len(254)) assert_equal(16,NetAddr.ipv4_prefix_len(0xfffe)) assert_equal(8,NetAddr.ipv4_prefix_len(0xfffffe)) assert_equal(0,NetAddr.ipv4_prefix_len(0xffffffff)) end def test_sort_IPv4 ips = [] ["10.0.0.0","1.1.1.1","0.0.0.0","10.1.10.1"].each do |net| ips.push(NetAddr::IPv4.parse(net)) end sorted = NetAddr.sort_IPv4(ips) expect = [ips[2],ips[1],ips[0],ips[3]] assert_equal(expect, sorted) end def test_sort_IPv4Net nets = [] ["10.0.0.0/24", "1.0.0.0/24", "10.0.0.0/8", "192.168.1.0/26", "8.8.8.8/32"].each do |net| nets.push(NetAddr::IPv4Net.parse(net)) end sorted = NetAddr.sort_IPv4Net(nets) expect = [nets[1],nets[4],nets[0],nets[2],nets[3]] assert_equal(expect, sorted) end def test_summ_IPv4Net nets = [] ["10.0.0.0/29", "10.0.0.8/30", "10.0.0.12/30", "10.0.0.16/28", "10.0.0.32/27", "10.0.0.64/26", "10.0.0.128/25"].each do |net| nets.push(NetAddr::IPv4Net.parse(net)) end expect = ["10.0.0.0/24"] i = 0 NetAddr.summ_IPv4Net(nets).each do |net| assert_equal(expect[i],net.to_s) i += 1 end nets = [] ["10.0.0.0/24", "1.0.0.0/8", "3.4.5.6/32", "3.4.5.8/31", "0.0.0.0/0"].each do |net| nets.push(NetAddr::IPv4Net.parse(net)) end expect = ["0.0.0.0/0"] i = 0 NetAddr.summ_IPv4Net(nets).each do |net| assert_equal(expect[i],net.to_s) i += 1 end nets = [] ["10.0.1.0/25", "10.0.1.0/26", "10.0.0.16/28", "10.0.0.32/27", "10.0.0.128/26", "10.0.0.192/26", "10.0.0.32/27"].each do |net| nets.push(NetAddr::IPv4Net.parse(net)) end expect = ["10.0.0.16/28", "10.0.0.32/27", "10.0.0.128/25", "10.0.1.0/25"] i = 0 NetAddr.summ_IPv4Net(nets).each do |net| assert_equal(expect[i],net.to_s) i += 1 end nets = [] ["10.0.0.0/26","10.0.0.64/26","10.0.0.0/24","10.0.0.192/26","10.0.0.128/26"].each do |net| # test out of order nets.push(NetAddr::IPv4Net.parse(net)) end expect = ["10.0.0.0/24"] i = 0 NetAddr.summ_IPv4Net(nets).each do |net| assert_equal(expect[i],net.to_s) i += 1 end end def test_sort_IPv6 ips = [] ["1::", "3::", "2::", "::", "::1"].each do |net| ips.push(NetAddr::IPv6.parse(net)) end sorted = NetAddr.sort_IPv6(ips) expect = [ips[3],ips[4],ips[0],ips[2],ips[1]] assert_equal(expect, sorted) end def test_sort_IPv6Net nets = [] ["1::/64", "2::/64", "1::/16", "::", "::1"].each do |net| nets.push(NetAddr::IPv6Net.parse(net)) end sorted = NetAddr.sort_IPv6Net(nets) expect = [nets[3],nets[4],nets[0],nets[2],nets[1]] # ::/128 ::1/128 1::/64 1::/16 2::/64 assert_equal(expect, sorted) end def test_summ_IPv6Net nets = [] ["ff00::/13", "ff08::/14", "ff0c::/14", "ff10::/12", "ff20::/11", "ff40::/10", "ff80::/9"].each do |net| nets.push(NetAddr::IPv6Net.parse(net)) end expect = ["ff00::/8"] i = 0 NetAddr.summ_IPv6Net(nets).each do |net| assert_equal(expect[i],net.to_s) i += 1 end nets = [] ["2::/32", "::1", "fec0::/16", "1::/16", "::/0"].each do |net| nets.push(NetAddr::IPv6Net.parse(net)) end expect = ["::/0"] i = 0 NetAddr.summ_IPv6Net(nets).each do |net| assert_equal(expect[i],net.to_s) i += 1 end nets = [] ["ff80::/9", "ff10::/12", "ff80::/10", "ff20::/12", "fff0::/16", "fff1::/16", "ff80::/10"].each do |net| nets.push(NetAddr::IPv6Net.parse(net)) end expect = ["ff10::/12", "ff20::/12", "ff80::/9", "fff0::/15"] i = 0 NetAddr.summ_IPv6Net(nets).each do |net| assert_equal(expect[i],net.to_s) i += 1 end end end netaddr-2.0.6/test/mask32_test.rb0000644000004100000410000000243214263044212016632 0ustar www-datawww-data#!/usr/bin/ruby require_relative "../lib/netaddr.rb" require 'test/unit' class TestMask32 < Test::Unit::TestCase def test_new m32 = NetAddr::Mask32.new(24) assert_equal("/24", m32.to_s) assert_equal(0xffffff00, m32.mask) assert_raise(NetAddr::ValidationError){ NetAddr::Mask32.new(-1) } assert_raise(NetAddr::ValidationError){ NetAddr::Mask32.new(33) } assert_raise(NetAddr::ValidationError){ NetAddr::Mask32.new("/24") } end def test_parse m32 = NetAddr::Mask32.parse("255.255.255.0") assert_equal("/24", m32.to_s) m32 = NetAddr::Mask32.parse("/24") assert_equal("/24", m32.to_s) assert_raise(NetAddr::ValidationError){ NetAddr::Mask32.parse("255.248.255.0") } end def test_cmp m = NetAddr::Mask32.new(24) m2 = NetAddr::Mask32.new(25) m3 = NetAddr::Mask32.new(23) m4 = NetAddr::Mask32.new(24) assert_equal(1, m.cmp(m2)) assert_equal(-1, m.cmp(m3)) assert_equal(0, m.cmp(m4)) end def test_extended m32 = NetAddr::Mask32.new(24) assert_equal("255.255.255.0", m32.extended) end def test_len m = NetAddr::Mask32.new(24) assert_equal(256, m.len()) m = NetAddr::Mask32.new(26) assert_equal(64, m.len()) m = NetAddr::Mask32.new(32) assert_equal(1, m.len()) m = NetAddr::Mask32.new(0) assert_equal(0, m.len()) end end netaddr-2.0.6/test/mask128_test.rb0000644000004100000410000000213414263044212016717 0ustar www-datawww-data#!/usr/bin/ruby require_relative "../lib/netaddr.rb" require 'test/unit' class TestMask128 < Test::Unit::TestCase def test_new m128 = NetAddr::Mask128.new(24) assert_equal("/24", m128.to_s) assert_equal(0xffffff00000000000000000000000000, m128.mask) assert_raise(NetAddr::ValidationError){ NetAddr::Mask128.new(-1) } assert_raise(NetAddr::ValidationError){ NetAddr::Mask128.new(129) } assert_raise(NetAddr::ValidationError){ NetAddr::Mask128.new("/24") } end def test_parse m128 = NetAddr::Mask128.parse("/24") assert_equal("/24", m128.to_s) m128 = NetAddr::Mask128.parse("24") assert_equal("/24", m128.to_s) assert_raise(NetAddr::ValidationError){ NetAddr::Mask128.parse("/129") } end def test_cmp m = NetAddr::Mask128.new(24) m2 = NetAddr::Mask128.new(25) m3 = NetAddr::Mask128.new(23) m4 = NetAddr::Mask128.new(24) assert_equal(1, m.cmp(m2)) assert_equal(-1, m.cmp(m3)) assert_equal(0, m.cmp(m4)) end def test_len m = NetAddr::Mask128.new(65) assert_equal(2**(128-65), m.len()) m = NetAddr::Mask128.new(64) assert_equal(0, m.len()) end end netaddr-2.0.6/test/eui64_test.rb0000644000004100000410000000245314263044212016471 0ustar www-datawww-data#!/usr/bin/ruby require_relative "../lib/netaddr.rb" require 'test/unit' class TestEUI64 < Test::Unit::TestCase def test_new eui = NetAddr::EUI64.new(0) assert_equal("00-00-00-00-00-00-00-00", eui.to_s) assert_raise(NetAddr::ValidationError){ NetAddr::EUI64.new(2**64) } assert_raise(NetAddr::ValidationError){ NetAddr::EUI64.new(-1) } assert_raise(NetAddr::ValidationError){ NetAddr::EUI64.new("00-00-00-00-00-00-00-00") } # string end def test_parse assert_equal("aa-bb-cc-dd-ee-ff-00-11", NetAddr::EUI64.parse("aa-bb-cc-dd-ee-ff-00-11").to_s) assert_equal("aa-bb-cc-dd-ee-ff-00-11", NetAddr::EUI64.parse("aa:bb:cc:dd:ee:ff:00:11").to_s) assert_equal("aa-bb-cc-dd-ee-ff-00-11", NetAddr::EUI64.parse("aabb.ccdd.eeff.0011").to_s) assert_equal("aa-bb-cc-dd-ee-ff-00-11", NetAddr::EUI64.parse("aabbccddeeff0011").to_s) assert_raise(NetAddr::ValidationError){ NetAddr::EUI64.parse("aa-bb-cc-dd-ee-ff-00-11-22") } assert_raise(NetAddr::ValidationError){ NetAddr::EUI64.parse("aa-bb-cc-dd-ee-ff-gg") } assert_raise(NetAddr::ValidationError){ NetAddr::EUI64.parse("aa;bb;cc;dd;ee;ff;00;11") } end def test_to_ipv6 net = NetAddr::IPv6Net.parse("fe80::/64") eui = NetAddr::EUI64.parse("aa-bb-cc-dd-ee-ff-00-11") assert_equal("fe80::a8bb:ccdd:eeff:11", eui.to_ipv6(net).to_s) end end netaddr-2.0.6/test/run_all.rb0000644000004100000410000000051714263044212016131 0ustar www-datawww-datarequire_relative "examples.rb" require_relative "netaddr_test.rb" require_relative "eui48_test.rb" require_relative "eui64_test.rb" require_relative "ipv4_test.rb" require_relative "ipv4net_test.rb" require_relative "ipv6_test.rb" require_relative "ipv6net_test.rb" require_relative "mask32_test.rb" require_relative "mask128_test.rb" netaddr-2.0.6/test/examples.rb0000644000004100000410000001066714263044212016322 0ustar www-datawww-data#!/usr/bin/ruby require_relative "../lib/netaddr.rb" require 'test/unit' # Testable examples on how to use NetAddr class NetAddrExamples < Test::Unit::TestCase # IPv4 def test_IPv4_examples puts "\n*** Examples using IPv4 ***\n" puts "\nWhat size network do I need in order to hold 200 addresses?" puts "/" + NetAddr.ipv4_prefix_len(200).to_s assert_equal(24,NetAddr.ipv4_prefix_len(200)) puts "\nCreating IPv4Net: '10.0.0.0/24'" net = NetAddr::IPv4Net.parse("10.0.0.0/24") assert_not_nil(net) puts "\nRendering as a String: " + net.to_s assert_equal("10.0.0.0/24", net.to_s) puts "\nIterating its /26 subnets:" expect = ["10.0.0.0/26","10.0.0.64/26","10.0.0.128/26","10.0.0.192/26"] 0.upto(net.subnet_count(26) - 1) do |i| subnet = net.nth_subnet(26,i) assert_equal(expect[i], subnet.to_s) puts " " + subnet.to_s end puts "\nIts 3rd /30 subnet:" subnet30 = net.nth_subnet(30,2) assert_equal("10.0.0.8/30", subnet30.to_s) puts " " + subnet30.to_s puts "\nIterating the IPs of the /30" expect = ["10.0.0.8","10.0.0.9","10.0.0.10","10.0.0.11"] 0.upto(subnet30.len - 1) do |i| ip = subnet30.nth(i) assert_equal(expect[i], ip.to_s) puts " " + ip.to_s end puts "\nDoes 10.0.0.7 belong to the 10.0.0.8/29 subnet?" subnet29 = NetAddr::IPv4Net.parse("10.0.0.8/29") if subnet29.contains(NetAddr::IPv4.parse("10.0.0.7")) puts " yes" else puts " no" end puts "\nDoes 10.0.0.10 belong to the 10.0.0.8/29 subnet?" if subnet29.contains(NetAddr::IPv4.parse("10.0.0.10")) puts " yes" else puts " no" end puts "\nGiven the 3rd /30 of 10.0.0.0/24, fill in the holes:" expect = ["10.0.0.0/29","10.0.0.8/30","10.0.0.12/30","10.0.0.16/28","10.0.0.32/27","10.0.0.64/26","10.0.0.128/25"] i = 0 net.fill([subnet30]).each do |subnet| puts " " + subnet.to_s assert_equal(expect[i], subnet.to_s) i+=1 end list = ["10.0.1.0/24", "10.0.0.0/25", "10.0.0.128/26","10.0.2.0/24", "10.0.0.192/26",] puts "\nSummarizing this list of networks: " + list.to_s nets = [] list.each do |net| nets.push(NetAddr::IPv4Net.parse(net)) end expect = ["10.0.0.0/23", "10.0.2.0/24",] i = 0 NetAddr.summ_IPv4Net(nets).each do |net| puts " " + net.to_s assert_equal(expect[i],net.to_s) i += 1 end puts "\nThe multicast mac-address for 235.147.18.23 is:" mac = NetAddr::IPv4.parse("235.147.18.23").multicast_mac.to_s assert_equal("01-00-5e-13-12-17", mac) puts " " + mac end # IPv6 def test_IPv6_examples puts "\n\n*** Examples using IPv6 ***\n" puts "\nCreating IPv6Net: 'fec0::/62'" net = NetAddr::IPv6Net.parse("fec0::/62") assert_not_nil(net) puts "\nRendering as a String: " + net.to_s assert_equal("fec0::/62", net.to_s) puts "\nRendering as a String (long format): " + net.long assert_equal("fec0:0000:0000:0000:0000:0000:0000:0000/62", net.long) puts "\nIterating its /64 subnets:" expect = ["fec0::/64","fec0:0:0:1::/64","fec0:0:0:2::/64","fec0:0:0:3::/64"] 0.upto(net.subnet_count(64) - 1) do |i| subnet = net.nth_subnet(64,i) assert_equal(expect[i], subnet.to_s) puts " " + subnet.to_s end puts "\nIts 3rd /64 subnet:" subnet64 = net.nth_subnet(64,2) assert_equal("fec0:0:0:2::/64", subnet64.to_s) puts " " + subnet64.to_s puts "\nIterating the first 4 IPs of the /64" expect = ["fec0:0:0:2::","fec0:0:0:2::1","fec0:0:0:2::2","fec0:0:0:2::3"] 0.upto(3) do |i| ip = subnet64.nth(i) assert_equal(expect[i], ip.to_s) puts " " + ip.to_s end puts "\nGiven the 3rd /64 of fec0::/62, fill in the holes:" expect = ["fec0::/63", "fec0:0:0:2::/64","fec0:0:0:3::/64"] i = 0 net.fill([subnet64]).each do |subnet| puts " " + subnet.to_s assert_equal(expect[i], subnet.to_s) i+=1 end list = ["fec0::/63", "fec0:0:0:3::/64", "fec0:0:0:2::/64", "fe80::/17", "fe80:8000::/17"] puts "\nSummarizing this list of networks: " + list.to_s nets = [] list.each do |net| nets.push(NetAddr::IPv6Net.parse(net)) end expect = ["fe80::/16", "fec0::/62"] i = 0 NetAddr.summ_IPv6Net(nets).each do |net| puts " " + net.to_s assert_equal(expect[i],net.to_s) i += 1 end puts "\nThe IPv6 address for mac-address aa-bb-cc-dd-ee-ff within network fe80::/64 is:" net = NetAddr::IPv6Net.parse("fe80::/64") eui = NetAddr::EUI48.parse("aa-bb-cc-dd-ee-ff").to_eui64 ip = eui.to_ipv6(net) assert_equal("fe80::a8bb:ccff:fedd:eeff",ip.to_s) puts " " + ip.to_s end end netaddr-2.0.6/test/ipv4net_test.rb0000644000004100000410000001574714263044212017140 0ustar www-datawww-data#!/usr/bin/ruby require_relative "../lib/netaddr.rb" require 'test/unit' class TestIPv4Net < Test::Unit::TestCase def test_new ip = NetAddr::IPv4.parse("128.0.0.1") m32 = NetAddr::Mask32.new(24) net = NetAddr::IPv4Net.new(ip,m32) assert_equal("128.0.0.0/24", net.to_s) net = NetAddr::IPv4Net.new(ip,nil) assert_equal("128.0.0.1/32", net.to_s) end def test_parse assert_equal("128.0.0.0/24", NetAddr::IPv4Net.parse("128.0.0.1/24").to_s) assert_equal("128.0.0.0/24", NetAddr::IPv4Net.parse("128.0.0.1 255.255.255.0").to_s) assert_equal("0.0.0.0/0", NetAddr::IPv4Net.parse("0.0.0.0/0").to_s) assert_equal("128.0.0.1/32", NetAddr::IPv4Net.parse("128.0.0.1").to_s) # default /32 end def test_cmp net1 = NetAddr::IPv4Net.parse("1.1.1.0/24") net2 = NetAddr::IPv4Net.parse("1.1.0.0/24") net3 =NetAddr::IPv4Net.parse("1.1.2.0/24") net4 = NetAddr::IPv4Net.parse("1.1.1.0/25") net5 = NetAddr::IPv4Net.parse("1.1.1.0/24") assert_equal(1, net1.cmp(net2)) # ip less assert_equal(-1, net1.cmp(net3))# ip greater assert_equal(-1, net4.cmp(net1)) # ip eq, mask less assert_equal(1, net1.cmp(net4)) # ip eq, mask greater assert_equal(0, net1.cmp(net5)) # eq end def test_contains net = NetAddr::IPv4Net.parse("1.0.0.8/29") ip1 = NetAddr::IPv4.parse("1.0.0.15") ip2 = NetAddr::IPv4.parse("1.0.0.16") ip3 = NetAddr::IPv4.parse("1.0.0.7") assert_equal(true, net.contains(ip1)) assert_equal(false, net.contains(ip2)) assert_equal(false, net.contains(ip3)) end def test_extended net = NetAddr::IPv4Net.parse("128.0.0.1/24") assert_equal("128.0.0.0 255.255.255.0", net.extended) end def test_fill # filter supernet. remove subnets of subnets. basic fwd fill. parent = NetAddr::IPv4Net.parse("10.0.0.0/24") nets = [] ["10.0.0.0/24", "10.0.0.0/8", "10.0.0.8/30", "10.0.0.16/30", "10.0.0.16/28"].each do |net| nets.push(NetAddr::IPv4Net.parse(net)) end expect = ["10.0.0.0/29", "10.0.0.8/30", "10.0.0.12/30", "10.0.0.16/28", "10.0.0.32/27", "10.0.0.64/26", "10.0.0.128/25"] i = 0 parent.fill(nets).each do |net| assert_equal(expect[i],net.to_s) i += 1 end # basic backfill parent = NetAddr::IPv4Net.parse("128.0.0.0/1") nets = [] ["192.0.0.0/2"].each do |net| nets.push(NetAddr::IPv4Net.parse(net)) end expect = ["128.0.0.0/2", "192.0.0.0/2"] i = 0 parent.fill(nets).each do |net| assert_equal(expect[i],net.to_s) i += 1 end # basic fwd fill with non-contiguous subnets parent = NetAddr::IPv4Net.parse("1.0.0.0/25") nets = [] ["1.0.0.0/30", "1.0.0.64/26"].each do |net| nets.push(NetAddr::IPv4Net.parse(net)) end expect = ["1.0.0.0/30", "1.0.0.4/30", "1.0.0.8/29", "1.0.0.16/28", "1.0.0.32/27", "1.0.0.64/26"] i = 0 parent.fill(nets).each do |net| assert_equal(expect[i],net.to_s) i += 1 end # basic backfill. complex fwd fill that uses 'shrink' of the proposed 1.0.16.0/21 subnet parent = NetAddr::IPv4Net.parse("1.0.0.0/19") nets = [] ["1.0.8.0/21", "1.0.20.0/24"].each do |net| nets.push(NetAddr::IPv4Net.parse(net)) end expect = ["1.0.0.0/21","1.0.8.0/21","1.0.16.0/22","1.0.20.0/24","1.0.21.0/24","1.0.22.0/23","1.0.24.0/21"] i = 0 parent.fill(nets).each do |net| assert_equal(expect[i],net.to_s) i += 1 end # list contains the supernet parent = NetAddr::IPv4Net.parse("1.0.0.0/19") nets = [] ["1.0.0.0/19"].each do |net| nets.push(NetAddr::IPv4Net.parse(net)) end expect = [] i = 0 parent.fill(nets).each do |net| assert_equal(expect[i],net.to_s) i += 1 end end def test_len net1 = NetAddr::IPv4Net.parse("1.1.1.0/24") assert_equal(256, net1.len()) end def test_next assert_equal("1.0.0.2/31", NetAddr::IPv4Net.parse("1.0.0.0/31").next.to_s) assert_equal("1.0.0.8/29", NetAddr::IPv4Net.parse("1.0.0.4/30").next.to_s) assert_equal("1.0.0.16/28", NetAddr::IPv4Net.parse("1.0.0.8/29").next.to_s) end def test_next_sib assert_equal("255.255.255.64/26", NetAddr::IPv4Net.parse("255.255.255.0/26").next_sib.to_s) assert_equal("255.255.255.128/26", NetAddr::IPv4Net.parse("255.255.255.64/26").next_sib.to_s) assert_equal("255.255.255.192/26", NetAddr::IPv4Net.parse("255.255.255.128/26").next_sib.to_s) assert_nil(NetAddr::IPv4Net.parse("255.255.255.192/26").next_sib) end def test_nth assert_equal("1.1.1.1", NetAddr::IPv4Net.parse("1.1.1.0/26").nth(1).to_s) assert_nil(NetAddr::IPv4Net.parse("1.1.1.0/26").nth(64)) end def test_nth_subnet assert_equal("1.1.1.0/26", NetAddr::IPv4Net.parse("1.1.1.0/24").nth_subnet(26,0).to_s) assert_equal("1.1.1.64/26", NetAddr::IPv4Net.parse("1.1.1.0/24").nth_subnet(26,1).to_s) assert_nil(NetAddr::IPv4Net.parse("1.1.1.0/24").nth_subnet(26,4)) assert_nil(NetAddr::IPv4Net.parse("1.1.1.0/24").nth_subnet(26,-1)) assert_nil(NetAddr::IPv4Net.parse("1.1.1.0/24").nth_subnet(24,0)) end def test_prev assert_equal("1.0.0.0/29", NetAddr::IPv4Net.parse("1.0.0.8/30").prev.to_s) assert_equal("1.0.0.128/26", NetAddr::IPv4Net.parse("1.0.0.192/26").prev.to_s) assert_equal("1.0.0.0/25", NetAddr::IPv4Net.parse("1.0.0.128/26").prev.to_s) end def test_prev_sib assert_equal("0.0.0.64/26", NetAddr::IPv4Net.parse("0.0.0.128/26").prev_sib.to_s) assert_equal("0.0.0.0/26", NetAddr::IPv4Net.parse("0.0.0.64/26").prev_sib.to_s) assert_nil(NetAddr::IPv4Net.parse("0.0.0.0/26").prev_sib) end def test_rel net1 = NetAddr::IPv4Net.parse("1.1.1.0/24") net2 = NetAddr::IPv4Net.parse("1.1.1.0/25") net3 = NetAddr::IPv4Net.parse("1.1.1.128/25") net4 = NetAddr::IPv4Net.parse("1.1.1.0/25") assert_equal(1, net1.rel(net2)) # net eq, supernet assert_equal(-1, net2.rel(net1)) # net eq, subnet assert_equal(0, net2.rel(net2)) # eq assert_equal(1, net1.rel(net3)) # net ne, supernet assert_equal(-1, net3.rel(net1)) # net ne, subnet assert_nil(net3.rel(net4)) # unrelated end def test_resize assert_equal("1.1.1.0/24", NetAddr::IPv4Net.parse("1.1.1.0/26").resize(24).to_s) end def test_subnet_count assert_equal(2, NetAddr::IPv4Net.parse("1.1.1.0/24").subnet_count(25)) assert_equal(0, NetAddr::IPv4Net.parse("1.1.1.0/24").subnet_count(24)) assert_equal(0, NetAddr::IPv4Net.parse("1.1.1.0/24").subnet_count(33)) assert_equal(0, NetAddr::IPv4Net.parse("0.0.0.0/0").subnet_count(32)) end def test_summ net1 = NetAddr::IPv4Net.parse("1.1.1.0/30") net2 = NetAddr::IPv4Net.parse("1.1.1.4/30") net3 = NetAddr::IPv4Net.parse("1.1.1.16/28") net4 = NetAddr::IPv4Net.parse("1.1.1.0/28") net5 = NetAddr::IPv4Net.parse("1.1.2.0/30") net6 = NetAddr::IPv4Net.parse("1.1.1.4/30") net7 = NetAddr::IPv4Net.parse("1.1.1.16/28") net8 = NetAddr::IPv4Net.parse("1.1.1.32/28") net9 = NetAddr::IPv4Net.parse("1.1.1.0/29") net10 = NetAddr::IPv4Net.parse("1.1.1.8/30") assert_equal("1.1.1.0/29", net1.summ(net2).to_s) # lesser to greater assert_equal("1.1.1.0/27", net3.summ(net4).to_s) # greater to lesser assert_nil(net5.summ(net6)) # different nets assert_nil(net7.summ(net8)) # consecutive but not within bit boundary assert_nil(net9.summ(net10)) # within bit boundary, but not same size end end netaddr-2.0.6/test/eui48_test.rb0000644000004100000410000000224514263044212016472 0ustar www-datawww-data#!/usr/bin/ruby require_relative "../lib/netaddr.rb" require 'test/unit' class TestEUI48 < Test::Unit::TestCase def test_new eui = NetAddr::EUI48.new(0) assert_equal("00-00-00-00-00-00", eui.to_s) assert_raise(NetAddr::ValidationError){ NetAddr::EUI48.new(2**48) } assert_raise(NetAddr::ValidationError){ NetAddr::EUI48.new(-1) } assert_raise(NetAddr::ValidationError){ NetAddr::EUI48.new("00-00-00-00-00-00") } # string end def test_parse assert_equal("aa-bb-cc-dd-ee-ff", NetAddr::EUI48.parse("aa-bb-cc-dd-ee-ff").to_s) assert_equal("aa-bb-cc-dd-ee-ff", NetAddr::EUI48.parse("aa:bb:cc:dd:ee:ff").to_s) assert_equal("aa-bb-cc-dd-ee-ff", NetAddr::EUI48.parse("aabb.ccdd.eeff").to_s) assert_equal("aa-bb-cc-dd-ee-ff", NetAddr::EUI48.parse("aabbccddeeff").to_s) assert_raise(NetAddr::ValidationError){ NetAddr::EUI48.parse("aa-bb-cc-dd-ee-ff-00-11") } assert_raise(NetAddr::ValidationError){ NetAddr::EUI48.parse("aa-bb-cc-dd-ee-gg") } assert_raise(NetAddr::ValidationError){ NetAddr::EUI48.parse("aa;bb;cc;dd;ee;ff") } end def test_to_eui64 assert_equal("aa-bb-cc-ff-fe-dd-ee-ff", NetAddr::EUI48.parse("aa-bb-cc-dd-ee-ff").to_eui64.to_s) end end netaddr-2.0.6/test/ipv6net_test.rb0000644000004100000410000001523714263044212017134 0ustar www-datawww-data#!/usr/bin/ruby require_relative "../lib/netaddr.rb" require 'test/unit' class TestIPv6Net < Test::Unit::TestCase def test_new ip = NetAddr::IPv6.parse("1::") m128 = NetAddr::Mask128.new(24) net = NetAddr::IPv6Net.new(ip,m128) assert_equal("1::/24", net.to_s) assert_equal("1::/64", NetAddr::IPv6Net.new(ip,nil).to_s) ip = NetAddr::IPv6.parse("::") assert_equal("::/128", NetAddr::IPv6Net.new(ip,nil).to_s) end def test_parse assert_equal("1::/24", NetAddr::IPv6Net.parse("1::1/24").to_s) assert_equal("::/128", NetAddr::IPv6Net.parse("::").to_s) # default /128 assert_equal("1::/64", NetAddr::IPv6Net.parse("1::1").to_s) # default /64 end def test_cmp net1 = NetAddr::IPv6Net.parse("1:1::/32") net2 = NetAddr::IPv6Net.parse("1::/32") net3 =NetAddr::IPv6Net.parse("1:2::/32") net4 = NetAddr::IPv6Net.parse("1:1::/33") net5 = NetAddr::IPv6Net.parse("1:1::/32") assert_equal(1, net1.cmp(net2)) # ip less assert_equal(-1, net1.cmp(net3))# ip greater assert_equal(-1, net4.cmp(net1)) # ip eq, mask less assert_equal(1, net1.cmp(net4)) # ip eq, mask greater assert_equal(0, net1.cmp(net5)) # eq end def test_contains net = NetAddr::IPv6Net.parse("1:8::/29") ip1 = NetAddr::IPv6.parse("1:f::") ip2 = NetAddr::IPv6.parse("1:10::") ip3 = NetAddr::IPv6.parse("1:7::") assert_equal(true, net.contains(ip1)) assert_equal(false, net.contains(ip2)) assert_equal(false, net.contains(ip3)) end def test_fill # filter supernet. remove subnets of subnets. basic fwd fill. parent = NetAddr::IPv6Net.parse("ff00::/8") nets = [] ["ff00::/8", "ff00::/9", "ff08::/14", "fe00::/7", "ff20::/11", "ff20::/12"].each do |net| nets.push(NetAddr::IPv6Net.parse(net)) end expect = ["ff00::/9", "ff80::/9"] i = 0 parent.fill(nets).each do |net| assert_equal(expect[i],net.to_s) i += 1 end # basic backfill parent = NetAddr::IPv6Net.parse("8000::/1") nets = [] ["c000::/2"].each do |net| nets.push(NetAddr::IPv6Net.parse(net)) end expect = ["8000::/2","c000::/2"] i = 0 parent.fill(nets).each do |net| assert_equal(expect[i],net.to_s) i += 1 end # basic fwd fill with non-contiguous subnets parent = NetAddr::IPv6Net.parse("ff00::/121") nets = [] ["ff00::/126", "ff00::/120"].each do |net| nets.push(NetAddr::IPv6Net.parse(net)) end expect = ["ff00::/126", "ff00::4/126", "ff00::8/125", "ff00::10/124", "ff00::20/123", "ff00::40/122"] i = 0 parent.fill(nets).each do |net| assert_equal(expect[i],net.to_s) i += 1 end # basic backfill. complex fwd fill that uses 'shrink' of the proposed ffff:ffff:ffff:fff8::/62 subnet. designed to cross the /64 bit boundary. parent = NetAddr::IPv6Net.parse("fff:ffff:ffff:fff0::/60") nets = [] ["ffff:ffff:ffff:fff4::/62", "ffff:ffff:ffff:fffb::/65"].each do |net| nets.push(NetAddr::IPv6Net.parse(net)) end expect = ["ffff:ffff:ffff:fff0::/62", "ffff:ffff:ffff:fff4::/62", "ffff:ffff:ffff:fff8::/63", "ffff:ffff:ffff:fffa::/64", "ffff:ffff:ffff:fffb::/65", "ffff:ffff:ffff:fffb:8000::/65", "ffff:ffff:ffff:fffc::/62"] i = 0 parent.fill(nets).each do |net| assert_equal(expect[i],net.to_s) i += 1 end # list contains the supernet parent = NetAddr::IPv6Net.parse("ffff::/16") nets = [] ["ffff::/16"].each do |net| nets.push(NetAddr::IPv6Net.parse(net)) end expect = [] i = 0 parent.fill(nets).each do |net| assert_equal(expect[i],net.to_s) i += 1 end end def test_next assert_equal("::2/127", NetAddr::IPv6Net.parse("::/127").next.to_s) assert_equal("::8/125", NetAddr::IPv6Net.parse("::4/126").next.to_s) assert_equal("0:0:0:2::/63", NetAddr::IPv6Net.parse("::1:8000:0:0:0/65").next.to_s) assert_equal("0:0:0:3::/64", NetAddr::IPv6Net.parse("::2:8000:0:0:0/65").next.to_s) assert_nil(NetAddr::IPv6Net.parse("ffff::/16").next) end def test_next_sib assert_equal("0:0:0:2::/65", NetAddr::IPv6Net.parse("::1:8000:0:0:0/65",).next_sib.to_s) assert_equal("0:0:0:2::/64", NetAddr::IPv6Net.parse("0:0:0:1::/64",).next_sib.to_s) assert_equal("2::/16", NetAddr::IPv6Net.parse("1::/16").next_sib.to_s) assert_nil(NetAddr::IPv6Net.parse("ffff::/16").next_sib) end def test_nth assert_equal("1::", NetAddr::IPv6Net.parse("1::0/64").nth(0).to_s) assert_equal("::", NetAddr::IPv6Net.parse("::/127").nth(0).to_s) assert_equal("::1", NetAddr::IPv6Net.parse("::/127").nth(1).to_s) assert_nil(NetAddr::IPv6Net.parse("::/127").nth(2)) end def test_nth_subnet assert_equal("1::/30", NetAddr::IPv6Net.parse("1::/24").nth_subnet(30,0).to_s) assert_nil(NetAddr::IPv6Net.parse("1::").nth_subnet(26,4)) end def test_prev assert_equal("1::/125", NetAddr::IPv6Net.parse("1::8/126").prev.to_s) assert_equal("f::/63", NetAddr::IPv6Net.parse("f:0:0:2::/63").prev.to_s) assert_equal("e::/16", NetAddr::IPv6Net.parse("f::/63").prev.to_s) assert_nil(NetAddr::IPv6Net.parse("::").prev) end def test_prev_sib assert_equal("0:0:0:1::/64", NetAddr::IPv6Net.parse("0:0:0:2::/64").prev_sib.to_s) assert_equal("1::/16", NetAddr::IPv6Net.parse("2::/16").prev_sib.to_s) assert_nil(NetAddr::IPv6Net.parse("::/64").prev_sib) end def test_rel net1 = NetAddr::IPv6Net.parse("1::/63") net2 = NetAddr::IPv6Net.parse("1::/64") net3 = NetAddr::IPv6Net.parse("1::/60") net4 = NetAddr::IPv6Net.parse("1:0:0:1::/64") net5 = NetAddr::IPv6Net.parse("2::/64") assert_equal(1, net1.rel(net2)) # net eq, supernet assert_equal(-1, net2.rel(net1)) # net eq, subnet assert_equal(0, net2.rel(net2)) # eq assert_equal(1, net3.rel(net4)) # net ne, supernet assert_equal(-1, net4.rel(net3)) # net ne, subnet assert_nil(net2.rel(net5)) # unrelated end def test_resize assert_equal("1::/64", NetAddr::IPv6Net.parse("1::/63").resize(64).to_s) end def test_subnet_count assert_equal(2, NetAddr::IPv6Net.parse("ff::/8").subnet_count(9)) assert_equal(4, NetAddr::IPv6Net.parse("ff::/8").subnet_count(10)) assert_equal(0, NetAddr::IPv6Net.parse("ff::/8").subnet_count(8)) assert_equal(0, NetAddr::IPv6Net.parse("::/0").subnet_count(128)) end def test_summ net1 = NetAddr::IPv6Net.parse("1::/128") net2 = NetAddr::IPv6Net.parse("1::1/128") net3 = NetAddr::IPv6Net.parse("1::0/128") net4 = NetAddr::IPv6Net.parse("1::/16") net5 = NetAddr::IPv6Net.parse("2::/16") net6 = NetAddr::IPv6Net.parse("10::/12") net7 = NetAddr::IPv6Net.parse("20::/12") net8 = NetAddr::IPv6Net.parse("8::/17") assert_equal("1::/127", net1.summ(net2).to_s) # lesser to greater assert_equal("1::/127", net2.summ(net3).to_s) # greater to lesser assert_nil(net4.summ(net5)) # different nets assert_nil(net6.summ(net7)) # consecutive but not within bit boundary assert_nil(net4.summ(net8)) # within bit boundary, but not same size end end netaddr-2.0.6/test/ipv6_test.rb0000644000004100000410000001457614263044212016432 0ustar www-datawww-data#!/usr/bin/ruby require_relative "../lib/netaddr.rb" require 'test/unit' class TestIPv6 < Test::Unit::TestCase def test_new ip = NetAddr::IPv6.new(1) assert_equal(1, ip.addr) assert_raise(NetAddr::ValidationError){ NetAddr::IPv6.new(2**128) } assert_raise(NetAddr::ValidationError){ NetAddr::IPv6.new(-1) } assert_raise(NetAddr::ValidationError){ NetAddr::IPv6.new("::") } # string end def test_parse assert_equal(0, NetAddr::IPv6.parse("::").addr) assert_equal(1, NetAddr::IPv6.parse("::1").addr) assert_equal(0xfe800000000000000000000000000000, NetAddr::IPv6.parse("fe80::").addr) assert_raise(NetAddr::ValidationError){ NetAddr::IPv6.parse("fe80::1::") } assert_raise(NetAddr::ValidationError){ NetAddr::IPv6.parse("::fe80::") } assert_raise(NetAddr::ValidationError){ NetAddr::IPv6.parse("0:0:0:0:0:0:0:0:1") } assert_equal(1, NetAddr::IPv6.parse("::0:0:0:0:0:0:1").addr) assert_equal(0x00010002000300040005000600070008, NetAddr::IPv6.parse("1:2:3:4:5:6:7:8").addr) assert_equal(0x00010002000300040005000600070000, NetAddr::IPv6.parse("1:2:3:4:5:6:7::").addr) assert_equal(0x00010002000300040005000600000000, NetAddr::IPv6.parse("1:2:3:4:5:6::").addr) assert_equal(0x00010002000300040005000000000000, NetAddr::IPv6.parse("1:2:3:4:5::").addr) assert_equal(0x00010002000300040000000000000000, NetAddr::IPv6.parse("1:2:3:4::").addr) assert_equal(0x00010002000300000000000000000000, NetAddr::IPv6.parse("1:2:3::").addr) assert_equal(0x00010002000000000000000000000000, NetAddr::IPv6.parse("1:2::").addr) assert_equal(0x00010000000000000000000000000000, NetAddr::IPv6.parse("1::").addr) assert_equal(0x00000000000000000000000000000001, NetAddr::IPv6.parse("::1").addr) assert_equal(0x00000000000000000000000000010002, NetAddr::IPv6.parse("::1:2").addr) assert_equal(0x00000000000000000000000100020003, NetAddr::IPv6.parse("::1:2:3").addr) assert_equal(0x00000000000000000001000200030004, NetAddr::IPv6.parse("::1:2:3:4").addr) assert_equal(0x00000000000000010002000300040005, NetAddr::IPv6.parse("::1:2:3:4:5").addr) assert_equal(0x00000000000100020003000400050006, NetAddr::IPv6.parse("::1:2:3:4:5:6").addr) assert_equal(0x00000001000200030004000500060007, NetAddr::IPv6.parse("::1:2:3:4:5:6:7").addr) assert_raise(NetAddr::ValidationError){ NetAddr::IPv6.parse("fec0") } assert_raise(NetAddr::ValidationError){ NetAddr::IPv6.parse("fec0:::1") } assert_equal(0x0064ff9b0000000000000000c0000221, NetAddr::IPv6.parse("64:ff9b::192.0.2.33").addr) assert_equal(0x0064ff9b0000000000000000c0000221, NetAddr::IPv6.parse("64:ff9b::0:192.0.2.33").addr) assert_equal(0x0064ff9b0000000000000000c0000221, NetAddr::IPv6.parse("64:ff9b::0:0:192.0.2.33").addr) assert_equal(0x0064ff9b0000000000000000c0000221, NetAddr::IPv6.parse("64:ff9b::0:0:0:192.0.2.33").addr) assert_equal(0x0064ff9b0000000000000000c0000221, NetAddr::IPv6.parse("64:ff9b::0:0:0:0:192.0.2.33").addr) assert_equal(0x0064ff9b0000000000000000c0000221, NetAddr::IPv6.parse("64:ff9b:0:0:0:0:192.0.2.33").addr) assert_raise(NetAddr::ValidationError){ NetAddr::IPv6.parse("64:ff9b::192.0.2") } assert_raise(NetAddr::ValidationError){ NetAddr::IPv6.parse("64:ff9b::192.0.2.33.0") } assert_raise(NetAddr::ValidationError){ NetAddr::IPv6.parse("64:ff9b::192.0.256.33") } assert_raise(NetAddr::ValidationError){ NetAddr::IPv6.parse("64:ff9b:0:0:0:0:0:192.0.2.33") } assert_raise(NetAddr::ValidationError){ NetAddr::IPv6.parse("64:ff9b::0:0:0:0:0:192.0.2.33") } end def test_cmp ip = NetAddr::IPv6.parse("::1") ip2 = NetAddr::IPv6.parse("::0") ip3 =NetAddr::IPv6.parse("::2") ip4 = NetAddr::IPv6.parse("::1") assert_equal(1, ip.cmp(ip2)) assert_equal(-1, ip.cmp(ip3)) assert_equal(0, ip.cmp(ip4)) end def test_ipv4 ipv6 = NetAddr::IPv6.parse("64:ff9b::192.0.2.33") ipv4 = ipv6.ipv4() assert_equal("192.0.2.33", ipv4.to_s) ipv6 = NetAddr::IPv6.parse("2001:db8:c000:221::") ipv4 = ipv6.ipv4(32) assert_equal("192.0.2.33", ipv4.to_s) ipv6 = NetAddr::IPv6.parse("2001:db8:1c0:2:21::") ipv4 = ipv6.ipv4(40) assert_equal("192.0.2.33", ipv4.to_s) ipv6 = NetAddr::IPv6.parse("2001:db8:122:c000:2:2100::") ipv4 = ipv6.ipv4(48) assert_equal("192.0.2.33", ipv4.to_s) ipv6 = NetAddr::IPv6.parse("2001:db8:122:3c0:0:221::") ipv4 = ipv6.ipv4(56) assert_equal("192.0.2.33", ipv4.to_s) ipv6 = NetAddr::IPv6.parse("2001:db8:122:344:c0:2:2100::") ipv4 = ipv6.ipv4(64) assert_equal("192.0.2.33", ipv4.to_s) end def test_long assert_equal("0000:0000:0000:0000:0000:0000:0000:0000", NetAddr::IPv6.parse("::").long) assert_equal("fe80:0000:0000:0000:0000:0000:0000:0001", NetAddr::IPv6.parse("fe80::1").long) assert_equal("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", NetAddr::IPv6.new(NetAddr::F128).long) end def test_next assert_equal(1, NetAddr::IPv6.parse("::").next().addr) assert_nil(NetAddr::IPv6.parse("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").next()) end def test_prev assert_equal("::", NetAddr::IPv6.parse("::1").prev().to_s) assert_nil(NetAddr::IPv6.parse("::").prev()) end def test_to_net ip = NetAddr::IPv6.parse("1::") net = NetAddr::IPv6Net.parse("1::") assert_equal(0, net.cmp(ip.to_net())) end def test_to_s assert_equal("::", NetAddr::IPv6.parse("0:0:0:0:0:0:0:0").to_s) assert_equal("1::", NetAddr::IPv6.parse("1:0:0:0:0:0:0:0").to_s) assert_equal("0:1::", NetAddr::IPv6.parse("0:1:0:0:0:0:0:0").to_s) assert_equal("0:0:1::", NetAddr::IPv6.parse("0:0:1:0:0:0:0:0").to_s) assert_equal("0:0:0:1::", NetAddr::IPv6.parse("0:0:0:1:0:0:0:0").to_s) assert_equal("::1:0:0:0", NetAddr::IPv6.parse("0:0:0:0:1:0:0:0").to_s) assert_equal("::1:0:0", NetAddr::IPv6.parse("0:0:0:0:0:1:0:0").to_s) assert_equal("::1:0", NetAddr::IPv6.parse(":0:0:0:0:0:1:0").to_s) assert_equal("::1", NetAddr::IPv6.parse("0:0:0:0:0:0:0:1").to_s) assert_equal("1:0:1:1:1:1:1:1", NetAddr::IPv6.parse("1:0:1:1:1:1:1:1").to_s) # see RFC 5952 section 4.2.2 assert_equal("1::1", NetAddr::IPv6.parse("1:0:0:0:0:0:0:1").to_s) assert_equal("1:1::1", NetAddr::IPv6.parse("1:1:0:0:0:0:0:1").to_s) assert_equal("1:0:1::1", NetAddr::IPv6.parse("1:0:1:0:0:0:0:1").to_s) assert_equal("1:0:0:1::1", NetAddr::IPv6.parse("1:0:0:1:0:0:0:1").to_s) assert_equal("1::1:0:0:1", NetAddr::IPv6.parse("1:0:0:0:1:0:0:1").to_s) assert_equal("1::1:0:1", NetAddr::IPv6.parse("1:0:0:0:0:1:0:1").to_s) assert_equal("1::1:1", NetAddr::IPv6.parse("1:0:0:0:0:0:1:1").to_s) end end netaddr-2.0.6/README.md0000644000004100000410000000206014263044212014443 0ustar www-datawww-data# netaddr A Ruby library for performing calculations on IPv4 and IPv6 subnets. There is also limited support for EUI addresses. ### Version 1.x The original netaddr which I created in 2007. My use case then was creating an internal IPAM system for Rackspace. ### Version 2.x A complete rewrite and totally incompatible with 1.x. My main motivation now is to reduce bug reports resulting from the poor code quality of 1.x. # Building To run unit tests, execute the following from the top level directory ruby test/run_all.rb To build the gem, execute the following from the top level directory gem build netaddr.gemspec # Examples Example code may be found in test/example.rb. This example code runs as part of the unit tests. # Coding Style I use the following conventions: * I use tabs for indention since tabs make it really easy to adjust indention widths on the fly. * I do not follow rigid limits on line lengths. My editor auto-wraps so I add a line break where it feels appropriate. * I'm not a fan of obfuscation. I prefer clear code over fancy code. netaddr-2.0.6/netaddr.gemspec0000644000004100000410000000263114263044212016156 0ustar www-datawww-data######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: netaddr 2.0.6 ruby lib Gem::Specification.new do |s| s.name = "netaddr".freeze s.version = "2.0.6" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.require_paths = ["lib".freeze] s.authors = ["Dustin Spinhirne".freeze] s.date = "2022-07-08" s.extra_rdoc_files = ["LICENSE".freeze, "README.md".freeze] s.files = ["LICENSE".freeze, "README.md".freeze, "lib/eui48.rb".freeze, "lib/eui64.rb".freeze, "lib/ipv4.rb".freeze, "lib/ipv4net.rb".freeze, "lib/ipv6.rb".freeze, "lib/ipv6net.rb".freeze, "lib/mask128.rb".freeze, "lib/mask32.rb".freeze, "lib/netaddr.rb".freeze, "lib/util.rb".freeze, "test/eui48_test.rb".freeze, "test/eui64_test.rb".freeze, "test/examples.rb".freeze, "test/ipv4_test.rb".freeze, "test/ipv4net_test.rb".freeze, "test/ipv6_test.rb".freeze, "test/ipv6net_test.rb".freeze, "test/mask128_test.rb".freeze, "test/mask32_test.rb".freeze, "test/netaddr_test.rb".freeze, "test/run_all.rb".freeze] s.homepage = "https://github.com/dspinhirne/netaddr-rb".freeze s.licenses = ["Apache-2.0".freeze] s.rubygems_version = "3.2.5".freeze s.summary = "A Ruby library for performing calculations on IPv4 and IPv6 subnets.".freeze end netaddr-2.0.6/LICENSE0000644000004100000410000002613514263044212014202 0ustar www-datawww-data Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. netaddr-2.0.6/lib/0000755000004100000410000000000014263044212013734 5ustar www-datawww-datanetaddr-2.0.6/lib/eui64.rb0000644000004100000410000000452314263044212015221 0ustar www-datawww-datamodule NetAddr # EUI64 (Extended Unique Identifier 64-bit, or EUI-64) represents a 64-bit hardware address. class EUI64 # addr is the Integer representation of this EUI attr_reader :addr #Create an EUI64 from an Integer. Must be between 0 and 2**64-1. #Throws ValidationError on error. def initialize(i) if (!i.kind_of?(Integer)) raise ValidationError, "Expected an Integer for 'i' but got a #{i.class}." elsif ( (i < 0) || (i > 2**64-1) ) raise ValidationError, "#{i} is out of bounds for EUI64." end @addr = i end # Parse an EUI-64 string into an EUI64 type. # This will successfully parse most of the typically used formats such as: # * aa-bb-cc-dd-ee-ff-00-11 # * aa:bb:cc:dd:ee:ff:00:11 # * aabb.ccdd.eeff.0011 # * aabbccddeeff0011 # # Although, in truth, its not picky about the exact format as long as # it contains exactly 16 hex characters with the optional delimiting characters # '-', ':', or '.'. def EUI64.parse(addr) addr = addr.strip.gsub(/[\-\:\.]/,"") if (addr.length != 16) raise ValidationError, "Must contain exactly 16 hex characters with optional delimiters." elsif (addr =~ /[^0-9a-fA-F\:]/) raise ValidationError, "#{addr} contains invalid characters." end return EUI64.new(addr.to_i(16)) end # bytes returns a list containing each byte of the EUI64 as a String. def bytes() return [ (@addr >> 56 & 0xff).to_s(16).rjust(2, "0"), (@addr >> 48 & 0xff).to_s(16).rjust(2, "0"), (@addr >> 40 & 0xff).to_s(16).rjust(2, "0"), (@addr >> 32 & 0xff).to_s(16).rjust(2, "0"), (@addr >> 24 & 0xff).to_s(16).rjust(2, "0"), (@addr >> 16 & 0xff).to_s(16).rjust(2, "0"), (@addr >> 8 & 0xff).to_s(16).rjust(2, "0"), (@addr & 0xff).to_s(16).rjust(2, "0"), ] end # to_ipv6 generates an IPv6 address from this EUI64 address and the provided IPv6Net. # Nil will be returned if net is not a /64. def to_ipv6(net) if (!net.kind_of?(IPv6Net)) raise ArgumentError, "Expected an IPv6Net object for 'net' but got a #{net.class}." end if (net.netmask.prefix_len != 64) return nil end # set u/l bit to 0 hostId = @addr ^ 0x0200000000000000 ipAddr = net.network.addr | hostId return IPv6.new(ipAddr) end def to_s() return self.bytes.join("-") end end # end class EUI64 end # end module netaddr-2.0.6/lib/ipv4net.rb0000644000004100000410000001770014263044212015657 0ustar www-datawww-datamodule NetAddr #IPv4Net represents an IPv4 network. class IPv4Net #arguments: #* ip - an IPv4 object #* m32 - a Mask32 object. will default to a /32 if nil def initialize(ip,m32) if (!ip.kind_of?(IPv4)) raise ArgumentError, "Expected an IPv4 object for 'ip' but got a #{ip.class}." elsif (m32 != nil && !m32.kind_of?(Mask32)) raise ArgumentError, "Expected a Mask32 object for 'm32' but got a #{m32.class}." end if (m32 == nil) m32 = Mask32.new(32) end @m32 = m32 @base = IPv4.new(ip.addr & m32.mask) end # parse will create an IPv4Net from its string representation. Will default to a /32 netmask if not specified. # Throws ValidationError on error. def IPv4Net.parse(net) net = net.strip m32 = nil if (net.include?("/")) # cidr format addr,mask = net.split("/") m32 = Mask32.parse(mask) elsif (net.include?(" ") ) # extended format addr,mask = net.split(' ') m32 = Mask32.parse(mask) else addr = net end ip = IPv4.parse(addr) return IPv4Net.new(ip,m32) end # extended returns the IPv4Net in extended format (eg. x.x.x.x y.y.y.y) def extended() return @base.to_s + " " + Util.int_to_IPv4(@m32.mask) end # cmp compares equality with another IPv4Net. Return: # * 1 if this IPv4Net is numerically greater # * 0 if the two are equal # * -1 if this IPv4Net is numerically less # # The comparison is initially performed on using the cmp() method of the network address, however, in cases where the network # addresses are identical then the netmasks will be compared with the cmp() method of the netmask. def cmp(other) if (!other.kind_of?(IPv4Net)) raise ArgumentError, "Expected an IPv4Net object for 'other' but got a #{other.class}." end cmp = self.network.cmp(other.network) if (cmp != 0) return cmp end return self.netmask.cmp(other.netmask) end #contains returns true if the IPv4Net contains the IPv4 def contains(ip) if (!ip.kind_of?(IPv4)) raise ArgumentError, "Expected an IPv4 object for 'ip' but got a #{ip.class}." end if (@base.addr == ip.addr & @m32.mask) return true end return false end # fill returns a copy of the given Array, stripped of any networks which are not subnets of this IPv4Net # and with any missing gaps filled in. def fill(list) list = Util.filter_IPv4Net(list) return Util.fill(self,list) end # netmask returns the Mask32 object representing the netmask for this network def netmask() @m32 end # network returns the IPv4 object representing the network address def network() @base end #len returns the number of IP addresses in this network. It will return 0 for /0 networks. def len() return self.netmask.len end # next returns the next largest consecutive IP network or nil if the end of the address space is reached. def next() net = self.nth_next_sib(1) if (!net) return nil end return net.grow end # next_sib returns the network immediately following this one or nil if the end of the address space is reached. def next_sib() self.nth_next_sib(1) end # nth returns the IPv4 at the given index. # The size of the network may be determined with the len() method. # If the range is exceeded then return nil. def nth(index) if (!index.kind_of?(Integer)) raise ArgumentError, "Expected an Integer for 'index' but got a #{index.class}." elsif (index >= self.len) return nil end return IPv4.new(self.network.addr + index) end # nth_subnet returns the subnet IPv4Net at the given index. # The number of subnets may be determined with the subnet_count() method. # If the range is exceeded or an invalid prefix_len is provided then return nil. def nth_subnet(prefix_len,index) count = self.subnet_count(prefix_len) if (count == 0 || index >= count) return nil end sub0 = IPv4Net.new(self.network, Mask32.new(prefix_len)) return sub0.nth_next_sib(index) end # prev returns the previous largest consecutive IP network or nil if this is 0.0.0.0. def prev() net = self.grow return net.prev_sib end # prev_sib returns the network immediately preceding this one or nil if this network is 0.0.0.0. def prev_sib() if (self.network.addr == 0) return nil end shift = 32 - self.netmask.prefix_len addr = ((self.network.addr>>shift) - 1) << shift return IPv4Net.new(IPv4.new(addr), self.netmask) end # rel determines the relationship to another IPv4Net. Returns: # * 1 if this IPv4Net is the supernet of other # * 0 if the two are equal # * -1 if this IPv4Net is a subnet of other # * nil if the networks are unrelated def rel(other) if (!other.kind_of?(IPv4Net)) raise ArgumentError, "Expected an IPv4Net object for 'other' but got a #{other.class}." end # when networks are equal then we can look exlusively at the netmask if (self.network.addr == other.network.addr) return self.netmask.cmp(other.netmask) end # when networks are not equal we can use hostmask to test if they are # related and which is the supernet vs the subnet hostmask = self.netmask.mask ^ NetAddr::F32 otherHostmask = other.netmask.mask ^ NetAddr::F32 if (self.network.addr|hostmask == other.network.addr|hostmask) return 1 elsif (self.network.addr|otherHostmask == other.network.addr|otherHostmask) return -1 end return nil end # resize returns a copy of the network with an adjusted netmask. # Throws ValidationError on invalid prefix_len. def resize(prefix_len) m32 = Mask32.new(prefix_len) return IPv4Net.new(self.network,m32) end # subnet_count returns the number a subnets of a given prefix length that this IPv4Net contains. # It will return 0 for invalid requests (ie. bad prefix or prefix is shorter than that of this network). # It will also return 0 if the result exceeds the capacity of a 32-bit integer (ie. if you want the # of /32 a /0 will hold) def subnet_count(prefix_len) if (prefix_len <= self.netmask.prefix_len || prefix_len > 32 || prefix_len - self.netmask.prefix_len >= 32) return 0 end return 1 << (prefix_len - self.netmask.prefix_len) end # summ creates a summary address from this IPv4Net and another. # It returns nil if the two networks are incapable of being summarized. def summ(other) if (!other.kind_of?(IPv4Net)) raise ArgumentError, "Expected an IPv4Net object for 'other' but got a #{other.class}." end # netmasks must be identical if (self.netmask.prefix_len != other.netmask.prefix_len) return nil end # merge-able networks will be identical if you right shift them by the number of bits in the hostmask + 1 shift = 32 - self.netmask.prefix_len + 1 addr = self.network.addr >> shift otherAddr = other.network.addr >> shift if (addr != otherAddr) return nil end return self.resize(self.netmask.prefix_len - 1) end # to_s returns the IPv4Net as a String def to_s() return @base.to_s + @m32.to_s end # version returns "4" for IPv4 def version() return 4 end protected # grow decreases the prefix length as much as possible without crossing a bit boundary. def grow() addr = self.network.addr mask = self.netmask.mask prefix_len = self.netmask.prefix_len self.netmask.prefix_len.downto(0) do mask = (mask << 1) & NetAddr::F32 if addr|mask != mask || prefix_len == 0 # // bit boundary crossed when there are '1' bits in the host portion break end prefix_len -= 1 end return IPv4Net.new(IPv4.new(addr),Mask32.new(prefix_len)) end # nth_next_sib returns the nth next sibling network or nil if address space exceeded. def nth_next_sib(nth) if (nth < 0) return nil end shift = 32 - self.netmask.prefix_len addr = ((self.network.addr>>shift) + nth) << shift if addr > NetAddr::F32 return nil end return IPv4Net.new(IPv4.new(addr), self.netmask) end end # end class IPv4Net end # end module netaddr-2.0.6/lib/ipv6net.rb0000644000004100000410000002007714263044212015662 0ustar www-datawww-datamodule NetAddr #IPv6Net represents an IPv6 network. class IPv6Net #arguments: #* ip - an IPv6 object #* m128 - a Mask128 object. will default to a /64 if nil def initialize(ip,m128) if (!ip.kind_of?(IPv6)) raise ArgumentError, "Expected an IPv6 object for 'ip' but got a #{ip.class}." elsif (m128 != nil && !m128.kind_of?(Mask128)) raise ArgumentError, "Expected a Mask128 object for 'm128' but got a #{m128.class}." end if (m128 == nil) if (ip.addr != 0) m128 = Mask128.new(64) # use /64 mask per rfc 4291 else m128 = Mask128.new(128) # use /128 mask per rfc 4291 end end @m128 = m128 @base = IPv6.new(ip.addr & m128.mask) end # parse will create an IPv6Net from its string representation. A default netmask will be used if not specified. # Throws ValidationError on error. def IPv6Net.parse(net) m128 = nil net = net.strip if (net.include?("/")) # cidr format addr,mask = net.split("/") m128 = Mask128.parse(mask) else addr = net end ip = IPv6.parse(addr) return IPv6Net.new(ip,m128) end # cmp compares equality with another IPv6Net. Return: # * 1 if this IPv6Net is numerically greater # * 0 if the two are equal # * -1 if this IPv6Net is numerically less # # The comparison is initially performed on using the cmp() method of the network address, however, in cases where the network # addresses are identical then the netmasks will be compared with the cmp() method of the netmask. def cmp(other) if (!other.kind_of?(IPv6Net)) raise ArgumentError, "Expected an IPv6Net object for 'other' but got a #{other.class}." end cmp = self.network.cmp(other.network) if (cmp != 0) return cmp end return self.netmask.cmp(other.netmask) end #contains returns true if the IPv6Net contains the IPv6 def contains(ip) if (!ip.kind_of?(IPv6)) raise ArgumentError, "Expected an IPv6 object for 'ip' but got a #{ip.class}." end if (@base.addr == ip.addr & @m128.mask) return true end return false end # fill returns a copy of the given Array, stripped of any networks which are not subnets of this IPv6Net # and with any missing gaps filled in. def fill(list) list = Util.filter_IPv6Net(list) return Util.fill(self,list) end #len returns the number of IP addresses in this network. It will return 0 for /0 networks. def len() return self.netmask.len end # long returns the IPv6Net as a string in long (uncompressed) format def long() return @base.long() + @m128.to_s end # netmask returns the Mask128 object representing the netmask for this network def netmask() @m128 end # network returns the IPv6 object representing the network address def network() @base end # next returns the next largest consecutive IP network or nil if the end of the address space is reached. def next() net = self.nth_next_sib(1) if (!net) return nil end return net.grow end # next_sib returns the network immediately following this one or nil if the end of the address space is reached. def next_sib() self.nth_next_sib(1) end # nth returns the IPv6 at the given index. # The size of the network may be determined with the len() method. # If the range is exceeded then return nil. def nth(index) if (!index.kind_of?(Integer)) raise ArgumentError, "Expected an Integer for 'index' but got a #{index.class}." elsif (self.netmask.prefix_len < 64 || (self.netmask.prefix_len > 64 && index >= self.len)) return nil end return IPv6.new(self.network.addr + index) end # nth_subnet returns the subnet IPv6Net at the given index. # The number of subnets may be determined with the subnet_count() method. # If the range is exceeded or an invalid prefix_len is provided then return nil. def nth_subnet(prefix_len,index) count = self.subnet_count(prefix_len) if (count == 0 || index >= count) return nil end sub0 = IPv6Net.new(self.network, Mask128.new(prefix_len)) return sub0.nth_next_sib(index) end # prev returns the previous largest consecutive IP network or nil if this is ::. def prev() net = self.grow return net.prev_sib end # prev_sib returns the network immediately preceding this one or nil if this network is ::. def prev_sib() if (self.network.addr == 0) return nil end shift = 128 - self.netmask.prefix_len addr = ((self.network.addr>>shift) - 1) << shift if addr < 0 return nil end return IPv6Net.new(IPv6.new(addr), self.netmask) end # rel determines the relationship to another IPv6Net. Returns: # * 1 if this IPv6Net is the supernet of other # * 0 if the two are equal # * -1 if this IPv6Net is a subnet of other # * nil if the networks are unrelated def rel(other) if (!other.kind_of?(IPv6Net)) raise ArgumentError, "Expected an IPv6Net object for 'other' but got a #{other.class}." end # when networks are equal then we can look exlusively at the netmask if (self.network.addr == other.network.addr) return self.netmask.cmp(other.netmask) end # when networks are not equal we can use hostmask to test if they are # related and which is the supernet vs the subnet hostmask = self.netmask.mask ^ NetAddr::F128 otherHostmask = other.netmask.mask ^ NetAddr::F128 if (self.network.addr|hostmask == other.network.addr|hostmask) return 1 elsif (self.network.addr|otherHostmask == other.network.addr|otherHostmask) return -1 end return nil end # resize returns a copy of the network with an adjusted netmask. # Throws ValidationError on invalid prefix_len. def resize(prefix_len) m128 = Mask128.new(prefix_len) return IPv6Net.new(self.network,m128) end # subnet_count returns the number a subnets of a given prefix length that this IPv6Net contains. # It will return 0 for invalid requests (ie. bad prefix or prefix is shorter than that of this network). # It will also return 0 if the result exceeds the capacity of a 128-bit integer (ie. if you want the # of /128 a /0 will hold) def subnet_count(prefix_len) if (prefix_len <= self.netmask.prefix_len || prefix_len > 128 || prefix_len - self.netmask.prefix_len >= 128) return 0 end return 1 << (prefix_len - self.netmask.prefix_len) end # summ creates a summary address from this IPv6Net and another. # It returns nil if the two networks are incapable of being summarized. def summ(other) if (!other.kind_of?(IPv6Net)) raise ArgumentError, "Expected an IPv6Net object for 'other' but got a #{other.class}." end # netmasks must be identical if (self.netmask.prefix_len != other.netmask.prefix_len) return nil end # merge-able networks will be identical if you right shift them by the number of bits in the hostmask + 1 shift = 128 - self.netmask.prefix_len + 1 addr = self.network.addr >> shift otherAddr = other.network.addr >> shift if (addr != otherAddr) return nil end return self.resize(self.netmask.prefix_len - 1) end # to_s returns the IPv6Net as a String def to_s() return @base.to_s + @m128.to_s end # version returns "6" for IPv6 def version() return 6 end protected # grow decreases the prefix length as much as possible without crossing a bit boundary. def grow() addr = self.network.addr mask = self.netmask.mask prefix_len = self.netmask.prefix_len self.netmask.prefix_len.downto(0) do mask = (mask << 1) & NetAddr::F128 if addr|mask != mask || prefix_len == 0 # // bit boundary crossed when there are '1' bits in the host portion break end prefix_len -= 1 end return IPv6Net.new(IPv6.new(addr),Mask128.new(prefix_len)) end # nth_next_sib returns the nth next sibling network or nil if address space exceeded. def nth_next_sib(nth) if (nth < 0) return nil end shift = 128 - self.netmask.prefix_len addr = ((self.network.addr>>shift) + nth) << shift if addr > NetAddr::F128 return nil end return IPv6Net.new(IPv6.new(addr), self.netmask) end end # end class IPv6Net end # end module netaddr-2.0.6/lib/netaddr.rb0000644000004100000410000000662014263044212015706 0ustar www-datawww-datarequire_relative "eui48.rb" require_relative "eui64.rb" require_relative "ipv4.rb" require_relative "ipv4net.rb" require_relative "ipv6.rb" require_relative "ipv6net.rb" require_relative "mask32.rb" require_relative "mask128.rb" require_relative "util.rb" module NetAddr # Constants # 32 bits worth of '1' F32 = 2**32-1 # 128 bits worth of '1' F128 = 2**128-1 # ValidationError is thrown when a method fails a validation test. class ValidationError < StandardError end # ipv4_prefix_len returns the prefix length needed to hold the number of IP addresses specified by "size". def ipv4_prefix_len(size) prefix_len = 32 32.downto(0) do |i| hostbits = 32 - prefix_len max = 1 << hostbits if (size <= max) break end prefix_len -= 1 end return prefix_len end module_function :ipv4_prefix_len ## parse_ip parses a string into an IPv4 or IPv6 def parse_ip(ip) if (ip.include?(":")) return IPv6.parse(ip) end return IPv4.parse(ip) end module_function :parse_ip ## parse_net parses a string into an IPv4Net or IPv6Net def parse_net(net) if (net.include?(":")) return IPv6Net.parse(net) end return IPv4Net.parse(net) end module_function :parse_net # sort_IPv4 sorts a list of IPv4 objects in ascending order. # It will return a new list with any non IPv4 objects removed. def sort_IPv4(list) if ( !list.kind_of?(Array) ) raise ArgumentError, "Expected an Array for 'list' but got a #{list.class}." end filtered = Util.filter_IPv4(list) return Util.quick_sort(filtered) end module_function :sort_IPv4 # sort_IPv6 sorts a list of IPv6 objects in ascending order. # It will return a new list with any non IPv6 objects removed. def sort_IPv6(list) if ( !list.kind_of?(Array) ) raise ArgumentError, "Expected an Array for 'list' but got a #{list.class}." end filtered = Util.filter_IPv6(list) return Util.quick_sort(filtered) end module_function :sort_IPv6 # sort_IPv4Net sorts a list of IPv4Net objects in ascending order. # It will return a new list with any non IPv4Net objects removed. def sort_IPv4Net(list) if ( !list.kind_of?(Array) ) raise ArgumentError, "Expected an Array for 'list' but got a #{list.class}." end filtered = Util.filter_IPv4Net(list) return Util.quick_sort(filtered) end module_function :sort_IPv4Net # sort_IPv6Net sorts a list of IPv6Net objects in ascending order. # It will return a new list with any non IPv6Net objects removed. def sort_IPv6Net(list) if ( !list.kind_of?(Array) ) raise ArgumentError, "Expected an Array for 'list' but got a #{list.class}." end filtered = Util.filter_IPv6Net(list) return Util.quick_sort(filtered) end module_function :sort_IPv6Net # summ_IPv4Net summarizes a list of IPv4Net objects as much as possible. # It will return a new list with any non IPv4Net objects removed. def summ_IPv4Net(list) list = Util.filter_IPv4Net(list) if (list.length>1) list = Util.discard_subnets(list) return Util.summ_peers(list) end return [].concat(list) end module_function :summ_IPv4Net # summ_IPv6Net summarizes a list of IPv6Net objects as much as possible. # It will return a new list with any non IPv6Net objects removed. def summ_IPv6Net(list) list = Util.filter_IPv6Net(list) if (list.length>1) list = Util.discard_subnets(list) return Util.summ_peers(list) end return [].concat(list) end module_function :summ_IPv6Net end # end module netaddr-2.0.6/lib/ipv6.rb0000644000004100000410000001024514263044212015147 0ustar www-datawww-datamodule NetAddr #IPv6 represents a single IPv6 address. class IPv6 # addr is the Integer representation of this IP address attr_reader :addr #Create an IPv6 from an Integer. Must be between 0 and 2**128-1. #Throws ValidationError on error. def initialize(i) if (!i.kind_of?(Integer)) raise ValidationError, "Expected an Integer for 'i' but got a #{i.class}." elsif ( (i < 0) || (i > NetAddr::F128) ) raise ValidationError, "#{i} is out of bounds for IPv6." end @addr = i end # parse will create an IPv6 from its string representation (ie. "1::"). # Throws ValidationError on error. def IPv6.parse(ip) ip = ip.strip i = Util.parse_IPv6(ip) return IPv6.new(i) end # cmp compares equality with another IPv6. Return: # * 1 if this IPv6 is numerically greater # * 0 if the two are equal # * -1 if this IPv6 is numerically less def cmp(other) if (!other.kind_of?(IPv6)) raise ArgumentError, "Expected an IPv6 object for 'other' but got a #{other.class}." end if (self.addr > other.addr) return 1 elsif (self.addr < other.addr) return -1 end return 0 end # ipv4 generates an IPv4 from an IPv6 address. The IPv4 address is generated based on the mechanism described by RFC 6052. # The argument pl (prefix length) should be one of: 32, 40, 48, 56, 64, or 96. Default is 96 unless one of the supported values is provided. def ipv4(pl=96) if (pl == 32) i = (@addr >> 64) # get bits 32-63 into position return IPv4.new(i & NetAddr::F32) elsif (pl == 40) i = (@addr >> 48) & 0xff # get the last 8 bits into position i2 = (@addr & 0xffffff0000000000000000) >> 56 # get first 24 bits into position return IPv4.new(i | i2) elsif (pl == 48) i = (@addr >> 40) & 0xffff # get the last 16 bits into position i2 = (@addr & 0xffff0000000000000000) >> 48 # get first 16 bits into position return IPv4.new(i | i2) elsif (pl == 56) i = (@addr >> 32) & 0xffffff # get the last 24 bits into position i2 = (@addr & 0xff0000000000000000) >> 40 # get first 8 bits into position return IPv4.new(i | i2) elsif (pl == 64) i = (@addr >> 24) # get the 32 bits into position return IPv4.new(i & NetAddr::F32) end return IPv4.new(@addr & NetAddr::F32) end # long returns the IPv6 as a string in long (uncompressed) format def long() words = [] 7.downto(0) do |x| word = (@addr >> 16*x) & 0xffff words.push( word.to_s(16).rjust(4, "0") ) end return words.join(':') end # next returns the next consecutive IPv6 or nil if the address space is exceeded def next() if (self.addr == NetAddr::F128) return nil end return IPv6.new(self.addr + 1) end # prev returns the preceding IPv6 or nil if this is 0.0.0.0 def prev() if (self.addr == 0) return nil end return IPv6.new(self.addr - 1) end # to_net returns the IPv6 as a IPv6Net def to_net() NetAddr::IPv6Net.new(self,nil) end # to_s returns the IPv6 as a String in zero-compressed format (per rfc5952). def to_s() hexStr = ["","","","","","","",""] zeroStart, consec0, finalStart, finalLen = -1,0,-1,0 8.times do |i| # capture 2-byte word shift = 112 - 16*i wd = (self.addr >> shift) & 0xffff hexStr[i] = wd.to_s(16) # capture count of consecutive zeros if (wd == 0) if (zeroStart == -1) zeroStart = i end consec0 += 1 end # test for longest consecutive zeros when non-zero encountered or we're at the end if (wd != 0 || i == 7) if (consec0 > finalStart+finalLen-1) finalStart = zeroStart finalLen = consec0 end zeroStart = -1 consec0 = 0 end end # compress if we've found a series of zero fields in a row. # per https://tools.ietf.org/html/rfc5952#section-4.2.2 we must not compress just a single 16-bit zero field. if (finalLen > 1) head = hexStr[0,finalStart].join(":") tailStart = finalStart + finalLen tail = hexStr[tailStart..7].join(":") return head + "::" + tail end return hexStr.join(":") end # version returns "6" for IPv6 def version() return 6 end end # end class IPv6 end # end module netaddr-2.0.6/lib/ipv4.rb0000644000004100000410000000422514263044212015146 0ustar www-datawww-datamodule NetAddr #IPv4 represents a single IPv4 address. class IPv4 # addr is the Integer representation of this IP address attr_reader :addr #Create an IPv4 from an Integer. Must be between 0 and 2**32-1. #Throws ValidationError on error. def initialize(i) if (!i.kind_of?(Integer)) raise ValidationError, "Expected an Integer for 'i' but got a #{i.class}." elsif ( (i < 0) || (i > 2**32-1) ) raise ValidationError, "#{i} is out of bounds for IPv4." end @addr = i end # parse will create an IPv4 from its string representation (ie. "192.168.1.1"). # Throws ValidationError on error. def IPv4.parse(ip) ip = ip.strip i = Util.parse_IPv4(ip) return IPv4.new(i) end #cmp compares equality with another IPv4. Return: #* 1 if this IPv4 is numerically greater #* 0 if the two are equal #* -1 if this IPv4 is numerically less def cmp(other) if (!other.kind_of?(IPv4)) raise ArgumentError, "Expected an IPv4 object for 'other' but got a #{other.class}." end if (self.addr > other.addr) return 1 elsif (self.addr < other.addr) return -1 end return 0 end # multicast_mac returns the EUI48 multicast mac-address for this IP. # It will return the zero address for IPs outside of the multicast range 224.0.0.0/4. def multicast_mac mac = 0 if (@addr&0xf0000000 == 0xe0000000) # within 224.0.0.0/4 ? # map lower 23-bits of ip to 01:00:5e:00:00:00 mac = (@addr&0x007fffff) | 0x01005e000000 end return EUI48.new(mac) end # next returns the next consecutive IPv4 or nil if the address space is exceeded def next() if (self.addr == NetAddr::F32) return nil end return IPv4.new(self.addr + 1) end # prev returns the preceding IPv4 or nil if this is 0.0.0.0 def prev() if (self.addr == 0) return nil end return IPv4.new(self.addr - 1) end # to_net returns the IPv4 as a IPv4Net def to_net() NetAddr::IPv4Net.new(self,nil) end # to_s returns the IPv4 as a String def to_s() Util.int_to_IPv4(@addr) end # version returns "4" for IPv4 def version() return 4 end end # end class IPv4 end # end module netaddr-2.0.6/lib/eui48.rb0000644000004100000410000000404214263044212015217 0ustar www-datawww-datamodule NetAddr # EUI48 (Extended Unique Identifier 48-bit, or EUI-48) represents a 48-bit hardware address. # It is typically associated with mac-addresses. class EUI48 # addr is the Integer representation of this EUI attr_reader :addr #Create an EUI48 from an Integer. Must be between 0 and 2**48-1. #Throws ValidationError on error. def initialize(i) if (!i.kind_of?(Integer)) raise ValidationError, "Expected an Integer for 'i' but got a #{i.class}." elsif ( (i < 0) || (i > 2**48-1) ) raise ValidationError, "#{i} is out of bounds for EUI48." end @addr = i end # Parse an EUI-48 string into an EUI48 type. # This will successfully parse most of the typically used formats such as: # * aa-bb-cc-dd-ee-ff # * aa:bb:cc:dd:ee:ff # * aabb.ccdd.eeff # * aabbccddeeff # # Although, in truth, its not picky about the exact format as long as # it contains exactly 12 hex characters with the optional delimiting characters # '-', ':', or '.'. def EUI48.parse(addr) addr = addr.strip.gsub(/[\-\:\.]/,"") if (addr.length != 12) raise ValidationError, "Must contain exactly 12 hex characters with optional delimiters." elsif (addr =~ /[^0-9a-fA-F\:]/) raise ValidationError, "#{addr} contains invalid characters." end return EUI48.new(addr.to_i(16)) end # bytes returns a list containing each byte of the EUI48 as String. def bytes() return [ (@addr >> 40 & 0xff).to_s(16).rjust(2, "0"), (@addr >> 32 & 0xff).to_s(16).rjust(2, "0"), (@addr >> 24 & 0xff).to_s(16).rjust(2, "0"), (@addr >> 16 & 0xff).to_s(16).rjust(2, "0"), (@addr >> 8 & 0xff).to_s(16).rjust(2, "0"), (@addr & 0xff).to_s(16).rjust(2, "0"), ] end # to_eui64 converts this EUI48 into an EUI64 by inserting 0xfffe between the first and last 24-bits of the address. def to_eui64() return EUI64.new((@addr & 0xffffff000000) << 16 | (@addr & 0x000000ffffff) | 0x000000fffe000000) end def to_s() return self.bytes.join("-") end end # end class EUI48 end # end module netaddr-2.0.6/lib/util.rb0000644000004100000410000002207314263044212015242 0ustar www-datawww-datamodule NetAddr # Contains various internal util functions class Util private # backfill generates subnets between given IPv4Net/IPv6Net and the limit address. # limit should be < ipnet. will create subnets up to and including limit. def Util.backfill(ipnet,limit) nets = [] cur = ipnet while true do net = cur.prev if (net == nil || net.network.addr < limit) break end nets.unshift(net) cur = net end return nets end # discard_subnets returns a copy of the IPv4NetList with any entries which are subnets of other entries removed. def Util.discard_subnets(list) keepers = [] last = list[list.length-1] keep_last = true list.each do |net| rel = last.rel(net) if (!rel) # keep unrelated nets keepers.push(net) elsif (rel == -1) # keep supernets, but do not keep last keepers.push(net) keep_last = false end end # recursively clean up keepers if (keepers.length > 0) keepers = discard_subnets(keepers) end if keep_last keepers.unshift(last) end return keepers end # fill returns a copy of the given Array, stripped of any networks which are not subnets of ipnet # and with any missing gaps filled in. def Util.fill(ipnet,list) # sort & get rid of non subnets subs = [] discard_subnets(list).each do |sub| r = ipnet.rel(sub) if (r == 1) subs.push(sub) end end subs = quick_sort(subs) filled = [] if (subs.length > 0) # bottom fill if base missing base = ipnet.network.addr if (subs[0].network.addr != base) filled = backfill(subs[0],base) end # fill gaps between subnets 0.upto(subs.length-1) do |i| sub = subs[i] if (i+1 < subs.length) filled.concat( fwdfill(sub,ipnet,subs[i+1]) ) else filled.concat( fwdfill(sub,ipnet,nil) ) end end end return filled end # filter_IPv4 returns a copy of list with only IPv4 objects def Util.filter_IPv4(list) filtered = [] list.each do |ip| if (ip.kind_of?(IPv4)) filtered.push(ip) end end return filtered end # filter_IPv4Net returns a copy of list with only IPv4Net objects def Util.filter_IPv4Net(list) filtered = [] list.each do |ip| if (ip.kind_of?(IPv4Net)) filtered.push(ip) end end return filtered end # filter_IPv6 returns a copy of list with only IPv6 objects def Util.filter_IPv6(list) filtered = [] list.each do |ip| if (ip.kind_of?(IPv6)) filtered.push(ip) end end return filtered end # filter_IPv6Net returns a copy of list with only IPv4Net objects def Util.filter_IPv6Net(list) filtered = [] list.each do |ip| if (ip.kind_of?(IPv6Net)) filtered.push(ip) end end return filtered end # fwdfill returns subnets between given IPv4Net/IPv6Nett and the limit address. limit should be > ipnet. def Util.fwdfill(ipnet,supernet,limit) nets = [ipnet] cur = ipnet if (limit != nil) # if limit, then fill gaps between net and limit while true do nextSub = cur.next() # ensure we've not exceed the total address space if (nextSub == nil) break end # ensure we've not exceeded the address space of supernet if (supernet.rel(nextSub) == nil) break end # ensure we've not hit limit if (nextSub.network.addr == limit.network.addr) break end # check relationship to limit if (nextSub.rel(limit) != nil) # if related, then nextSub must be a supernet of limit. we need to shrink it. prefixLen = nextSub.netmask.prefix_len while true do prefixLen += 1 if (nextSub.kind_of?(IPv4Net)) nextSub = IPv4Net.new(nextSub.network, Mask32.new(prefixLen)) else nextSub = IPv6Net.new(nextSub.network, Mask128.new(prefixLen)) end if (nextSub.rel(limit) == nil) # stop when we no longer overlap with limit break end end else # otherwise, if unrelated then grow until we hit the limit prefixLen = nextSub.netmask.prefix_len mask = nextSub.netmask.mask while true do prefixLen -= 1 if (prefixLen == supernet.netmask.prefix_len) # break if we've hit the supernet boundary break end mask = mask << 1 if (nextSub.network.addr|mask != mask) # break when bit boundary crossed (there are '1' bits in the host portion) break end if (nextSub.kind_of?(IPv4Net)) grown = IPv4Net.new(nextSub.network, Mask32.new(prefixLen)) else grown = IPv6Net.new(nextSub.network, Mask128.new(prefixLen)) end if (grown.rel(limit) != nil) # if we've overlapped with limit in any way, then break break end nextSub = grown end end nets.push(nextSub) cur = nextSub end else # if no limit, then get next largest sibs until we've exceeded supernet while true do nextSub = cur.next() # ensure we've not exceed the total address space if (nextSub == nil) break end # ensure we've not exceeded the address space of supernet if (supernet.rel(nextSub) == nil) break end nets.push(nextSub) cur = nextSub end end return nets end # int_to_IPv4 converts an Integer into an IPv4 address String def Util.int_to_IPv4(i) octets = [] 3.downto(0) do |x| octet = (i >> 8*x) & 0xFF octets.push(octet.to_s) end return octets.join('.') end # parse_IPv4 parses an IPv4 address String into an Integer def Util.parse_IPv4(ip) # check that only valid characters are present if (ip =~ /[^0-9\.]/) raise ValidationError, "#{ip} contains invalid characters." end octets = ip.strip.split('.') if (octets.length != 4) raise ValidationError, "IPv4 requires (4) octets." end ipInt = 0 i = 4 octets.each do |octet| octetI = octet.to_i() if ( (octetI < 0) || (octetI >= 256) ) raise ValidationError, "#{ip} is out of bounds for IPv4." end i -= 1 ipInt = ipInt | (octetI << 8*i) end return ipInt end # parse_IPv6 parses an IPv6 address String into an Integer def Util.parse_IPv6(ip) # check that only valid characters are present if (ip =~ /[^0-9a-fA-F\:.]/) raise ValidationError, "#{ip} contains invalid characters." end ip = ip.strip if (ip == "::") return 0 # zero address end ipv4Int = nil if (ip.include?(".")) # check for ipv4 embedded addresses words = ip.split(":") begin ipv4Int = Util.parse_IPv4(words.last) rescue raise ValidationError, "IPv4-embedded IPv6 address is invalid." end ip = ip.sub(words.last,"0:0") # temporarily remove the ipv4 portion end words = [] if (ip.include?("::")) # short format if (ip =~ /:{3,}/) # make sure only i dont have ":::" raise ValidationError, "#{ip} contains invalid field separator." end if (ip.scan(/::/).length != 1) raise ValidationError, "#{ip} contains multiple '::' sequences." end halves = ip.split("::") if (halves[0] == nil) # cases such as ::1 halves[0] = "0" end if (halves[1] == nil) # cases such as 1:: halves[1] = "0" end upHalf = halves[0].split(":") loHalf = halves[1].split(":") numWords = upHalf.length + loHalf.length if (numWords > 8) raise ValidationError, "#{ip} is too long." end words = upHalf (8-numWords).downto(1) do |i| words.push("0") end words.concat(loHalf) else words = ip.split(":") if (words.length > 8) raise ValidationError, "#{ip} is too long." elsif (words.length < 8) raise ValidationError, "#{ip} is too short." end end ipInt = 0 i = 8 words.each do |word| i -= 1 word = word.to_i(16) << (16*i) ipInt = ipInt | word end if ipv4Int # re-add ipv4 portion if present ipInt = ipInt | ipv4Int end return ipInt end # quick_sort will return a sorted copy of the provided Array. # The array must contain only objects which implement a cmp method and which are comparable to each other. def Util.quick_sort(list) if (list.length <= 1) return [].concat(list) end final_list = [] lt_list = [] gt_list = [] eq_list = [] pivot = list[list.length-1] list.each do |ip| cmp = pivot.cmp(ip) if (cmp == 1) lt_list.push(ip) elsif (cmp == -1) gt_list.push(ip) else eq_list.push(ip) end end final_list.concat( quick_sort(lt_list) ) final_list.concat(eq_list) final_list.concat( quick_sort(gt_list) ) return final_list end # summ_peers returns a copy of the list with any merge-able subnets summ'd together. def Util.summ_peers(list) summd = quick_sort(list) while true do list_len = summd.length last = list_len - 1 tmp_list = [] i = 0 while (i < list_len) do net = summd[i] next_net = i+1 if (i != last) # if this net and next_net summarize then discard them & keep summary new_net = net.summ(summd[next_net]) if (new_net) # can summ. keep summary tmp_list.push(new_net) i += 1 # skip next_net else # cant summ. keep existing tmp_list.push(net) end else tmp_list.push(net) # keep last end i += 1 end # stop when list stops getting shorter if (tmp_list.length == list_len) break end summd = tmp_list end return summd end end # end class end # end module netaddr-2.0.6/lib/mask128.rb0000644000004100000410000000406514263044212015454 0ustar www-datawww-datamodule NetAddr #Mask128 represents a 128-bit netmask. class Mask128 # mask is the Integer representation of this netmask attr_reader :mask # prefix_len is the Integer prefix length of this netmask attr_reader :prefix_len # Create a Mask128 from an Integer prefix length. Valid values are 0-128. # Throws ValidationError on error. def initialize(prefix_len) if (!prefix_len.kind_of?(Integer)) raise ValidationError, "Expected an Integer for 'prefix_len' but got a #{prefix_len.class}." elsif ( (prefix_len < 0) || (prefix_len > 128) ) raise ValidationError, "#{prefix_len} must be in the range of 0-128." end @prefix_len = prefix_len @mask = NetAddr::F128 ^ (NetAddr::F128 >> @prefix_len) end # parse will create an Mask128 from its string representation. # arguments: # * mask - String representing an netmask (ie. "/64"). # # Throws ValidationError on error. def Mask128.parse(mask) mask = mask.strip if (mask.start_with?("/")) # cidr format mask = mask[1..-1] # remove "/" end return Mask128.new(Integer(mask)) rescue ArgumentError raise ValidationError, "#{mask} is not valid integer." end #cmp compares equality with another Mask128. Return: #* 1 if this Mask128 is larger in capacity #* 0 if the two are equal #* -1 if this Mask128 is smaller in capacity def cmp(other) if (!other.kind_of?(Mask128)) raise ArgumentError, "Expected an Mask128 object for 'other' but got a #{other.class}." end if (self.prefix_len < other.prefix_len) return 1 elsif (self.prefix_len > other.prefix_len) return -1 end return 0 end #len returns the number of IP addresses in this network. This is only useful if you have a subnet # smaller than a /64 as it will always return 0 for prefixes <= 64. def len() if (self.prefix_len <= 64) return 0 end return (self.mask ^ NetAddr::F128) + 1 # bit flip the netmask and add 1 end # to_s returns the Mask128 as a String def to_s() return "/#{@prefix_len}" end end # end class Mask128 end # end module netaddr-2.0.6/lib/mask32.rb0000644000004100000410000000606614263044212015371 0ustar www-datawww-datamodule NetAddr #Mask32 represents a 32-bit netmask. class Mask32 # mask is the Integer representation of this netmask attr_reader :mask # prefix_len is the Integer prefix length of this netmask attr_reader :prefix_len # Create a Mask32 from an Integer prefix length. Valid values are 0-32. # Throws ValidationError on error. def initialize(prefix_len) if (!prefix_len.kind_of?(Integer)) raise ValidationError, "Expected an Integer for 'prefix_len' but got a #{prefix_len.class}." elsif ( (prefix_len < 0) || (prefix_len > 32) ) raise ValidationError, "#{prefix_len} must be in the range of 0-32." end @prefix_len = prefix_len @mask = NetAddr::F32 ^ (NetAddr::F32 >> @prefix_len) end # parse will create an Mask32 from its string representation. # arguments: # * mask - String representing a netmask (ie. "/24" or "255.255.255.0"). # # Throws ValidationError on error. def Mask32.parse(mask) mask = mask.strip if (mask.start_with?("/")) # cidr format mask = mask[1..-1] # remove "/" end if (!mask.include?(".")) begin return Mask32.new(Integer(mask)) rescue ArgumentError raise ValidationError, "#{mask} is not valid integer." end end # for extended netmask # determine length of netmask by cycling through bit by bit and looking # for the first '1' bit, tracking the length as we go. we also want to verify # that the mask is valid (ie. not something like 255.254.255.0). we do this # by creating a hostmask which covers the '0' bits of the mask. once we have # separated the net vs host mask we xor them together. the result should be that # all bits are now '1'. if not then we know we have an invalid netmask. maskI = Util.parse_IPv4(mask) prefix = 32 hostmask = 1 i = maskI 32.downto(1) do if (i&1 == 1) hostmask = hostmask >> 1 if (maskI ^hostmask != NetAddr::F32) raise ValidationError, "#{mask} is invalid. It contains '1' bits in its host portion." end break end hostmask = (hostmask << 1) | 1 i = i>>1 prefix -= 1 end return Mask32.new(prefix) end # extended returns the Mask32 in extended format (eg. x.x.x.x) def extended() Util.int_to_IPv4(@mask) end #cmp compares equality with another Mask32. Return: #* 1 if this Mask128 is larger in capacity #* 0 if the two are equal #* -1 if this Mask128 is smaller in capacity def cmp(other) if (!other.kind_of?(Mask32)) raise ArgumentError, "Expected an Mask32 object for 'other' but got a #{other.class}." end if (self.prefix_len < other.prefix_len) return 1 elsif (self.prefix_len > other.prefix_len) return -1 end return 0 end #len returns the number of IP addresses in this network. It will always return 0 for /0 networks. def len() if (self.prefix_len == 0) return 0 end return (self.mask ^ NetAddr::F32) + 1 # bit flip the netmask and add 1 end # to_s returns the Mask32 as a String def to_s() return "/#{@prefix_len}" end end # end class Mask32 end # end module