netaddr 1.5.3 → 2.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +201 -0
- data/README.md +28 -8
- data/lib/eui48.rb +65 -0
- data/lib/eui64.rb +77 -0
- data/lib/ipv4.rb +87 -0
- data/lib/ipv4net.rb +262 -0
- data/lib/ipv6.rb +146 -0
- data/lib/ipv6net.rb +267 -0
- data/lib/mask128.rb +70 -0
- data/lib/mask32.rb +105 -0
- data/lib/netaddr.rb +122 -20
- data/lib/util.rb +373 -0
- data/test/eui48_test.rb +30 -0
- data/test/eui64_test.rb +32 -0
- data/test/examples.rb +155 -0
- data/test/ipv4_test.rb +60 -0
- data/test/ipv4net_test.rb +206 -0
- data/test/ipv6_test.rb +135 -0
- data/test/ipv6net_test.rb +199 -0
- data/test/mask128_test.rb +45 -0
- data/test/mask32_test.rb +56 -0
- data/test/netaddr_test.rb +149 -0
- data/test/run_all.rb +10 -0
- metadata +25 -20
- data/Errors +0 -7
- data/changelog +0 -52
- data/lib/cidr.rb +0 -2014
- data/lib/cidr_shortcuts.rb +0 -401
- data/lib/eui.rb +0 -402
- data/lib/ip_math.rb +0 -227
- data/lib/methods.rb +0 -1013
- data/lib/tree.rb +0 -816
- data/lib/validation_shortcuts.rb +0 -201
- data/license +0 -13
- data/test/cidr_test.rb +0 -545
- data/test/eui_test.rb +0 -101
- data/test/methods_test.rb +0 -331
- data/test/tree_test.rb +0 -347
data/lib/mask128.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
module NetAddr
|
2
|
+
|
3
|
+
#Mask128 represents a 128-bit netmask.
|
4
|
+
class Mask128
|
5
|
+
# mask is the Integer representation of this netmask
|
6
|
+
attr_reader :mask
|
7
|
+
|
8
|
+
# prefix_len is the Integer prefix length of this netmask
|
9
|
+
attr_reader :prefix_len
|
10
|
+
|
11
|
+
# Create a Mask128 from an Integer prefix length. Valid values are 0-128.
|
12
|
+
# Throws ValidationError on error.
|
13
|
+
def initialize(prefix_len)
|
14
|
+
if (!prefix_len.kind_of?(Integer))
|
15
|
+
raise ValidationError, "Expected an Integer for 'prefix_len' but got a #{prefix_len.class}."
|
16
|
+
elsif ( (prefix_len < 0) || (prefix_len > 128) )
|
17
|
+
raise ValidationError, "#{prefix_len} must be in the range of 0-128."
|
18
|
+
end
|
19
|
+
@prefix_len = prefix_len
|
20
|
+
@mask = NetAddr::F128 ^ (NetAddr::F128 >> @prefix_len)
|
21
|
+
end
|
22
|
+
|
23
|
+
# parse will create an Mask128 from its string representation.
|
24
|
+
# arguments:
|
25
|
+
# * mask - String representing an netmask (ie. "/64").
|
26
|
+
#
|
27
|
+
# Throws ValidationError on error.
|
28
|
+
def Mask128.parse(mask)
|
29
|
+
mask = mask.strip
|
30
|
+
if (mask.start_with?("/")) # cidr format
|
31
|
+
mask = mask[1..-1] # remove "/"
|
32
|
+
end
|
33
|
+
return Mask128.new(Integer(mask))
|
34
|
+
rescue ArgumentError
|
35
|
+
raise ValidationError, "#{mask} is not valid integer."
|
36
|
+
end
|
37
|
+
|
38
|
+
#cmp compares equality with another Mask128. Return:
|
39
|
+
#* 1 if this Mask128 is larger in capacity
|
40
|
+
#* 0 if the two are equal
|
41
|
+
#* -1 if this Mask128 is smaller in capacity
|
42
|
+
def cmp(other)
|
43
|
+
if (!other.kind_of?(Mask128))
|
44
|
+
raise ArgumentError, "Expected an Mask128 object for 'other' but got a #{other.class}."
|
45
|
+
end
|
46
|
+
if (self.prefix_len < other.prefix_len)
|
47
|
+
return 1
|
48
|
+
elsif (self.prefix_len > other.prefix_len)
|
49
|
+
return -1
|
50
|
+
end
|
51
|
+
return 0
|
52
|
+
end
|
53
|
+
|
54
|
+
#len returns the number of IP addresses in this network. This is only useful if you have a subnet
|
55
|
+
# smaller than a /64 as it will always return 0 for prefixes <= 64.
|
56
|
+
def len()
|
57
|
+
if (self.prefix_len <= 64)
|
58
|
+
return 0
|
59
|
+
end
|
60
|
+
return (self.mask ^ NetAddr::F128) + 1 # bit flip the netmask and add 1
|
61
|
+
end
|
62
|
+
|
63
|
+
# to_s returns the Mask128 as a String
|
64
|
+
def to_s()
|
65
|
+
return "/#{@prefix_len}"
|
66
|
+
end
|
67
|
+
|
68
|
+
end # end class Mask128
|
69
|
+
|
70
|
+
end # end module
|
data/lib/mask32.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
module NetAddr
|
2
|
+
|
3
|
+
#Mask32 represents a 32-bit netmask.
|
4
|
+
class Mask32
|
5
|
+
# mask is the Integer representation of this netmask
|
6
|
+
attr_reader :mask
|
7
|
+
|
8
|
+
# prefix_len is the Integer prefix length of this netmask
|
9
|
+
attr_reader :prefix_len
|
10
|
+
|
11
|
+
# Create a Mask32 from an Integer prefix length. Valid values are 0-32.
|
12
|
+
# Throws ValidationError on error.
|
13
|
+
def initialize(prefix_len)
|
14
|
+
if (!prefix_len.kind_of?(Integer))
|
15
|
+
raise ValidationError, "Expected an Integer for 'prefix_len' but got a #{prefix_len.class}."
|
16
|
+
elsif ( (prefix_len < 0) || (prefix_len > 32) )
|
17
|
+
raise ValidationError, "#{prefix_len} must be in the range of 0-32."
|
18
|
+
end
|
19
|
+
@prefix_len = prefix_len
|
20
|
+
@mask = NetAddr::F32 ^ (NetAddr::F32 >> @prefix_len)
|
21
|
+
end
|
22
|
+
|
23
|
+
# parse will create an Mask32 from its string representation.
|
24
|
+
# arguments:
|
25
|
+
# * mask - String representing a netmask (ie. "/24" or "255.255.255.0").
|
26
|
+
#
|
27
|
+
# Throws ValidationError on error.
|
28
|
+
def Mask32.parse(mask)
|
29
|
+
mask = mask.strip
|
30
|
+
if (mask.start_with?("/")) # cidr format
|
31
|
+
mask = mask[1..-1] # remove "/"
|
32
|
+
end
|
33
|
+
|
34
|
+
if (!mask.include?("."))
|
35
|
+
begin
|
36
|
+
return Mask32.new(Integer(mask))
|
37
|
+
rescue ArgumentError
|
38
|
+
raise ValidationError, "#{mask} is not valid integer."
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# for extended netmask
|
43
|
+
# determine length of netmask by cycling through bit by bit and looking
|
44
|
+
# for the first '1' bit, tracking the length as we go. we also want to verify
|
45
|
+
# that the mask is valid (ie. not something like 255.254.255.0). we do this
|
46
|
+
# by creating a hostmask which covers the '0' bits of the mask. once we have
|
47
|
+
# separated the net vs host mask we xor them together. the result should be that
|
48
|
+
# all bits are now '1'. if not then we know we have an invalid netmask.
|
49
|
+
maskI = Util.parse_IPv4(mask)
|
50
|
+
prefix = 32
|
51
|
+
hostmask = 1
|
52
|
+
i = maskI
|
53
|
+
32.downto(1) do
|
54
|
+
if (i&1 == 1)
|
55
|
+
hostmask = hostmask >> 1
|
56
|
+
if (maskI ^hostmask != NetAddr::F32)
|
57
|
+
raise ValidationError, "#{mask} is invalid. It contains '1' bits in its host portion."
|
58
|
+
end
|
59
|
+
break
|
60
|
+
end
|
61
|
+
hostmask = (hostmask << 1) | 1
|
62
|
+
i = i>>1
|
63
|
+
prefix -= 1
|
64
|
+
end
|
65
|
+
return Mask32.new(prefix)
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
# extended returns the Mask32 in extended format (eg. x.x.x.x)
|
70
|
+
def extended()
|
71
|
+
Util.int_to_IPv4(@mask)
|
72
|
+
end
|
73
|
+
|
74
|
+
#cmp compares equality with another Mask32. Return:
|
75
|
+
#* 1 if this Mask128 is larger in capacity
|
76
|
+
#* 0 if the two are equal
|
77
|
+
#* -1 if this Mask128 is smaller in capacity
|
78
|
+
def cmp(other)
|
79
|
+
if (!other.kind_of?(Mask32))
|
80
|
+
raise ArgumentError, "Expected an Mask32 object for 'other' but got a #{other.class}."
|
81
|
+
end
|
82
|
+
if (self.prefix_len < other.prefix_len)
|
83
|
+
return 1
|
84
|
+
elsif (self.prefix_len > other.prefix_len)
|
85
|
+
return -1
|
86
|
+
end
|
87
|
+
return 0
|
88
|
+
end
|
89
|
+
|
90
|
+
#len returns the number of IP addresses in this network. It will always return 0 for /0 networks.
|
91
|
+
def len()
|
92
|
+
if (self.prefix_len == 0)
|
93
|
+
return 0
|
94
|
+
end
|
95
|
+
return (self.mask ^ NetAddr::F32) + 1 # bit flip the netmask and add 1
|
96
|
+
end
|
97
|
+
|
98
|
+
# to_s returns the Mask32 as a String
|
99
|
+
def to_s()
|
100
|
+
return "/#{@prefix_len}"
|
101
|
+
end
|
102
|
+
|
103
|
+
end # end class Mask32
|
104
|
+
|
105
|
+
end # end module
|
data/lib/netaddr.rb
CHANGED
@@ -1,24 +1,126 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
1
|
+
require_relative "eui48.rb"
|
2
|
+
require_relative "eui64.rb"
|
3
|
+
require_relative "ipv4.rb"
|
4
|
+
require_relative "ipv4net.rb"
|
5
|
+
require_relative "ipv6.rb"
|
6
|
+
require_relative "ipv6net.rb"
|
7
|
+
require_relative "mask32.rb"
|
8
|
+
require_relative "mask128.rb"
|
9
|
+
require_relative "util.rb"
|
10
10
|
|
11
11
|
module NetAddr
|
12
|
+
# Constants
|
13
|
+
|
14
|
+
# 32 bits worth of '1'
|
15
|
+
F32 = 2**32-1
|
16
|
+
|
17
|
+
# 128 bits worth of '1'
|
18
|
+
F128 = 2**128-1
|
19
|
+
|
12
20
|
|
13
|
-
|
21
|
+
# ValidationError is thrown when a method fails a validation test.
|
22
|
+
class ValidationError < StandardError
|
14
23
|
end
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
24
|
+
|
25
|
+
# ipv4_prefix_len returns the prefix length needed to hold the number of IP addresses specified by "size".
|
26
|
+
def ipv4_prefix_len(size)
|
27
|
+
prefix_len = 32
|
28
|
+
32.downto(0) do |i|
|
29
|
+
hostbits = 32 - prefix_len
|
30
|
+
max = 1 << hostbits
|
31
|
+
if (size <= max)
|
32
|
+
break
|
33
|
+
end
|
34
|
+
prefix_len -= 1
|
35
|
+
end
|
36
|
+
return prefix_len
|
37
|
+
end
|
38
|
+
module_function :ipv4_prefix_len
|
39
|
+
|
40
|
+
## parse_ip parses a string into an IPv4 or IPv6
|
41
|
+
def parse_ip(ip)
|
42
|
+
if (ip.include?(":"))
|
43
|
+
return IPv6.parse(ip)
|
44
|
+
end
|
45
|
+
return IPv4.parse(ip)
|
46
|
+
end
|
47
|
+
module_function :parse_ip
|
48
|
+
|
49
|
+
## parse_net parses a string into an IPv4Net or IPv6Net
|
50
|
+
def parse_net(net)
|
51
|
+
if (net.include?(":"))
|
52
|
+
return IPv6Net.parse(net)
|
53
|
+
end
|
54
|
+
return IPv4Net.parse(net)
|
55
|
+
end
|
56
|
+
module_function :parse_net
|
57
|
+
|
58
|
+
# sort_IPv4 sorts a list of IPv4 objects in ascending order.
|
59
|
+
# It will return a new list with any non IPv4 objects removed.
|
60
|
+
def sort_IPv4(list)
|
61
|
+
if ( !list.kind_of?(Array) )
|
62
|
+
raise ArgumentError, "Expected an Array for 'list' but got a #{list.class}."
|
63
|
+
end
|
64
|
+
filtered = Util.filter_IPv4(list)
|
65
|
+
return Util.quick_sort(filtered)
|
66
|
+
end
|
67
|
+
module_function :sort_IPv4
|
68
|
+
|
69
|
+
# sort_IPv6 sorts a list of IPv6 objects in ascending order.
|
70
|
+
# It will return a new list with any non IPv6 objects removed.
|
71
|
+
def sort_IPv6(list)
|
72
|
+
if ( !list.kind_of?(Array) )
|
73
|
+
raise ArgumentError, "Expected an Array for 'list' but got a #{list.class}."
|
74
|
+
end
|
75
|
+
filtered = Util.filter_IPv6(list)
|
76
|
+
return Util.quick_sort(filtered)
|
77
|
+
end
|
78
|
+
module_function :sort_IPv6
|
79
|
+
|
80
|
+
# sort_IPv4Net sorts a list of IPv4Net objects in ascending order.
|
81
|
+
# It will return a new list with any non IPv4Net objects removed.
|
82
|
+
def sort_IPv4Net(list)
|
83
|
+
if ( !list.kind_of?(Array) )
|
84
|
+
raise ArgumentError, "Expected an Array for 'list' but got a #{list.class}."
|
85
|
+
end
|
86
|
+
filtered = Util.filter_IPv4Net(list)
|
87
|
+
return Util.quick_sort(filtered)
|
88
|
+
end
|
89
|
+
module_function :sort_IPv4Net
|
90
|
+
|
91
|
+
# sort_IPv6Net sorts a list of IPv6Net objects in ascending order.
|
92
|
+
# It will return a new list with any non IPv6Net objects removed.
|
93
|
+
def sort_IPv6Net(list)
|
94
|
+
if ( !list.kind_of?(Array) )
|
95
|
+
raise ArgumentError, "Expected an Array for 'list' but got a #{list.class}."
|
96
|
+
end
|
97
|
+
filtered = Util.filter_IPv6Net(list)
|
98
|
+
return Util.quick_sort(filtered)
|
99
|
+
end
|
100
|
+
module_function :sort_IPv6Net
|
101
|
+
|
102
|
+
# summ_IPv4Net summarizes a list of IPv4Net objects as much as possible.
|
103
|
+
# It will return a new list with any non IPv4Net objects removed.
|
104
|
+
def summ_IPv4Net(list)
|
105
|
+
list = Util.filter_IPv4Net(list)
|
106
|
+
if (list.length>1)
|
107
|
+
list = Util.discard_subnets(list)
|
108
|
+
return Util.summ_peers(list)
|
109
|
+
end
|
110
|
+
return [].concat(list)
|
111
|
+
end
|
112
|
+
module_function :summ_IPv4Net
|
113
|
+
|
114
|
+
# summ_IPv6Net summarizes a list of IPv6Net objects as much as possible.
|
115
|
+
# It will return a new list with any non IPv6Net objects removed.
|
116
|
+
def summ_IPv6Net(list)
|
117
|
+
list = Util.filter_IPv6Net(list)
|
118
|
+
if (list.length>1)
|
119
|
+
list = Util.discard_subnets(list)
|
120
|
+
return Util.summ_peers(list)
|
121
|
+
end
|
122
|
+
return [].concat(list)
|
123
|
+
end
|
124
|
+
module_function :summ_IPv6Net
|
125
|
+
|
126
|
+
end # end module
|
data/lib/util.rb
ADDED
@@ -0,0 +1,373 @@
|
|
1
|
+
module NetAddr
|
2
|
+
|
3
|
+
# Contains various internal util functions
|
4
|
+
class Util
|
5
|
+
private
|
6
|
+
|
7
|
+
# backfill generates subnets between given IPv4Net/IPv6Net and the limit address.
|
8
|
+
# limit should be < ipnet. will create subnets up to and including limit.
|
9
|
+
def Util.backfill(ipnet,limit)
|
10
|
+
nets = []
|
11
|
+
cur = ipnet
|
12
|
+
while true do
|
13
|
+
net = cur.prev
|
14
|
+
if (net == nil || net.network.addr < limit)
|
15
|
+
break
|
16
|
+
end
|
17
|
+
nets.unshift(net)
|
18
|
+
cur = net
|
19
|
+
end
|
20
|
+
return nets
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
# discard_subnets returns a copy of the IPv4NetList with any entries which are subnets of other entries removed.
|
25
|
+
def Util.discard_subnets(list)
|
26
|
+
keepers = []
|
27
|
+
last = list[list.length-1]
|
28
|
+
keep_last = true
|
29
|
+
list.each do |net|
|
30
|
+
rel = last.rel(net)
|
31
|
+
if (!rel) # keep unrelated nets
|
32
|
+
keepers.push(net)
|
33
|
+
elsif (rel == -1) # keep supernets, but do not keep last
|
34
|
+
keepers.push(net)
|
35
|
+
keep_last = false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# recursively clean up keepers
|
40
|
+
if (keepers.length > 0)
|
41
|
+
keepers = discard_subnets(keepers)
|
42
|
+
end
|
43
|
+
if keep_last
|
44
|
+
keepers.unshift(last)
|
45
|
+
end
|
46
|
+
return keepers
|
47
|
+
end
|
48
|
+
|
49
|
+
# fill returns a copy of the given Array, stripped of any networks which are not subnets of ipnet
|
50
|
+
# and with any missing gaps filled in.
|
51
|
+
def Util.fill(ipnet,list)
|
52
|
+
# sort & get rid of non subnets
|
53
|
+
subs = []
|
54
|
+
discard_subnets(list).each do |sub|
|
55
|
+
r = ipnet.rel(sub)
|
56
|
+
if (r == 1)
|
57
|
+
subs.push(sub)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
subs = quick_sort(subs)
|
61
|
+
|
62
|
+
filled = []
|
63
|
+
if (subs.length > 0)
|
64
|
+
# bottom fill if base missing
|
65
|
+
base = ipnet.network.addr
|
66
|
+
if (subs[0].network.addr != base)
|
67
|
+
filled = backfill(subs[0],base)
|
68
|
+
end
|
69
|
+
|
70
|
+
# fill gaps between subnets
|
71
|
+
0.upto(subs.length-1) do |i|
|
72
|
+
sub = subs[i]
|
73
|
+
if (i+1 < subs.length)
|
74
|
+
filled.concat( fwdfill(sub,ipnet,subs[i+1]) )
|
75
|
+
else
|
76
|
+
filled.concat( fwdfill(sub,ipnet,nil) )
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
return filled
|
81
|
+
end
|
82
|
+
|
83
|
+
# filter_IPv4 returns a copy of list with only IPv4 objects
|
84
|
+
def Util.filter_IPv4(list)
|
85
|
+
filtered = []
|
86
|
+
list.each do |ip|
|
87
|
+
if (ip.kind_of?(IPv4))
|
88
|
+
filtered.push(ip)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
return filtered
|
92
|
+
end
|
93
|
+
|
94
|
+
# filter_IPv4Net returns a copy of list with only IPv4Net objects
|
95
|
+
def Util.filter_IPv4Net(list)
|
96
|
+
filtered = []
|
97
|
+
list.each do |ip|
|
98
|
+
if (ip.kind_of?(IPv4Net))
|
99
|
+
filtered.push(ip)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
return filtered
|
103
|
+
end
|
104
|
+
|
105
|
+
# filter_IPv6 returns a copy of list with only IPv6 objects
|
106
|
+
def Util.filter_IPv6(list)
|
107
|
+
filtered = []
|
108
|
+
list.each do |ip|
|
109
|
+
if (ip.kind_of?(IPv6))
|
110
|
+
filtered.push(ip)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
return filtered
|
114
|
+
end
|
115
|
+
|
116
|
+
# filter_IPv6Net returns a copy of list with only IPv4Net objects
|
117
|
+
def Util.filter_IPv6Net(list)
|
118
|
+
filtered = []
|
119
|
+
list.each do |ip|
|
120
|
+
if (ip.kind_of?(IPv6Net))
|
121
|
+
filtered.push(ip)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
return filtered
|
125
|
+
end
|
126
|
+
|
127
|
+
# fwdfill returns subnets between given IPv4Net/IPv6Nett and the limit address. limit should be > ipnet.
|
128
|
+
def Util.fwdfill(ipnet,supernet,limit)
|
129
|
+
nets = [ipnet]
|
130
|
+
cur = ipnet
|
131
|
+
if (limit != nil) # if limit, then fill gaps between net and limit
|
132
|
+
while true do
|
133
|
+
nextSub = cur.next()
|
134
|
+
# ensure we've not exceed the total address space
|
135
|
+
if (nextSub == nil)
|
136
|
+
break
|
137
|
+
end
|
138
|
+
# ensure we've not exceeded the address space of supernet
|
139
|
+
if (supernet.rel(nextSub) == nil)
|
140
|
+
break
|
141
|
+
end
|
142
|
+
# ensure we've not hit limit
|
143
|
+
if (nextSub.network.addr == limit.network.addr)
|
144
|
+
break
|
145
|
+
end
|
146
|
+
|
147
|
+
# check relationship to limit
|
148
|
+
if (nextSub.rel(limit) != nil) # if related, then nextSub must be a supernet of limit. we need to shrink it.
|
149
|
+
prefixLen = nextSub.netmask.prefix_len
|
150
|
+
while true do
|
151
|
+
prefixLen += 1
|
152
|
+
if (nextSub.kind_of?(IPv4Net))
|
153
|
+
nextSub = IPv4Net.new(nextSub.network, Mask32.new(prefixLen))
|
154
|
+
else
|
155
|
+
nextSub = IPv6Net.new(nextSub.network, Mask128.new(prefixLen))
|
156
|
+
end
|
157
|
+
if (nextSub.rel(limit) == nil) # stop when we no longer overlap with limit
|
158
|
+
break
|
159
|
+
end
|
160
|
+
end
|
161
|
+
else # otherwise, if unrelated then grow until we hit the limit
|
162
|
+
prefixLen = nextSub.netmask.prefix_len
|
163
|
+
mask = nextSub.netmask.mask
|
164
|
+
while true do
|
165
|
+
prefixLen -= 1
|
166
|
+
if (prefixLen == supernet.netmask.prefix_len) # break if we've hit the supernet boundary
|
167
|
+
break
|
168
|
+
end
|
169
|
+
mask = mask << 1
|
170
|
+
if (nextSub.network.addr|mask != mask) # break when bit boundary crossed (there are '1' bits in the host portion)
|
171
|
+
break
|
172
|
+
end
|
173
|
+
if (nextSub.kind_of?(IPv4Net))
|
174
|
+
grown = IPv4Net.new(nextSub.network, Mask32.new(prefixLen))
|
175
|
+
else
|
176
|
+
grown = IPv6Net.new(nextSub.network, Mask128.new(prefixLen))
|
177
|
+
end
|
178
|
+
if (grown.rel(limit) != nil) # if we've overlapped with limit in any way, then break
|
179
|
+
break
|
180
|
+
end
|
181
|
+
nextSub = grown
|
182
|
+
end
|
183
|
+
end
|
184
|
+
nets.push(nextSub)
|
185
|
+
cur = nextSub
|
186
|
+
end
|
187
|
+
else # if no limit, then get next largest sibs until we've exceeded supernet
|
188
|
+
while true do
|
189
|
+
nextSub = cur.next()
|
190
|
+
# ensure we've not exceed the total address space
|
191
|
+
if (nextSub == nil)
|
192
|
+
break
|
193
|
+
end
|
194
|
+
# ensure we've not exceeded the address space of supernet
|
195
|
+
if (supernet.rel(nextSub) == nil)
|
196
|
+
break
|
197
|
+
end
|
198
|
+
nets.push(nextSub)
|
199
|
+
cur = nextSub
|
200
|
+
end
|
201
|
+
end
|
202
|
+
return nets
|
203
|
+
end
|
204
|
+
|
205
|
+
# int_to_IPv4 converts an Integer into an IPv4 address String
|
206
|
+
def Util.int_to_IPv4(i)
|
207
|
+
octets = []
|
208
|
+
3.downto(0) do |x|
|
209
|
+
octet = (i >> 8*x) & 0xFF
|
210
|
+
octets.push(octet.to_s)
|
211
|
+
end
|
212
|
+
return octets.join('.')
|
213
|
+
end
|
214
|
+
|
215
|
+
# parse_IPv4 parses an IPv4 address String into an Integer
|
216
|
+
def Util.parse_IPv4(ip)
|
217
|
+
# check that only valid characters are present
|
218
|
+
if (ip =~ /[^0-9\.]/)
|
219
|
+
raise ValidationError, "#{ip} contains invalid characters."
|
220
|
+
end
|
221
|
+
|
222
|
+
octets = ip.strip.split('.')
|
223
|
+
if (octets.length != 4)
|
224
|
+
raise ValidationError, "IPv4 requires (4) octets."
|
225
|
+
end
|
226
|
+
|
227
|
+
ipInt = 0
|
228
|
+
i = 4
|
229
|
+
octets.each do |octet|
|
230
|
+
octetI = octet.to_i()
|
231
|
+
if ( (octetI < 0) || (octetI >= 256) )
|
232
|
+
raise ValidationError, "#{ip} is out of bounds for IPv4."
|
233
|
+
end
|
234
|
+
i -= 1
|
235
|
+
ipInt = ipInt | (octetI << 8*i)
|
236
|
+
end
|
237
|
+
return ipInt
|
238
|
+
end
|
239
|
+
|
240
|
+
# parse_IPv6 parses an IPv6 address String into an Integer
|
241
|
+
def Util.parse_IPv6(ip)
|
242
|
+
# check that only valid characters are present
|
243
|
+
if (ip =~ /[^0-9a-fA-F\:.]/)
|
244
|
+
raise ValidationError, "#{ip} contains invalid characters."
|
245
|
+
end
|
246
|
+
|
247
|
+
ip = ip.strip
|
248
|
+
if (ip == "::")
|
249
|
+
return 0 # zero address
|
250
|
+
end
|
251
|
+
ipv4Int = nil
|
252
|
+
if (ip.include?(".")) # check for ipv4 embedded addresses
|
253
|
+
words = ip.split(":")
|
254
|
+
begin
|
255
|
+
ipv4Int = Util.parse_IPv4(words.last)
|
256
|
+
rescue
|
257
|
+
raise ValidationError, "IPv4-embedded IPv6 address is invalid."
|
258
|
+
end
|
259
|
+
ip = ip.sub(words.last,"0:0") # temporarily remove the ipv4 portion
|
260
|
+
end
|
261
|
+
words = []
|
262
|
+
if (ip.include?("::")) # short format
|
263
|
+
if (ip =~ /:{3,}/) # make sure only i dont have ":::"
|
264
|
+
raise ValidationError, "#{ip} contains invalid field separator."
|
265
|
+
end
|
266
|
+
if (ip.scan(/::/).length != 1)
|
267
|
+
raise ValidationError, "#{ip} contains multiple '::' sequences."
|
268
|
+
end
|
269
|
+
|
270
|
+
halves = ip.split("::")
|
271
|
+
if (halves[0] == nil) # cases such as ::1
|
272
|
+
halves[0] = "0"
|
273
|
+
end
|
274
|
+
if (halves[1] == nil) # cases such as 1::
|
275
|
+
halves[1] = "0"
|
276
|
+
end
|
277
|
+
upHalf = halves[0].split(":")
|
278
|
+
loHalf = halves[1].split(":")
|
279
|
+
numWords = upHalf.length + loHalf.length
|
280
|
+
if (numWords > 8)
|
281
|
+
raise ValidationError, "#{ip} is too long."
|
282
|
+
end
|
283
|
+
words = upHalf
|
284
|
+
(8-numWords).downto(1) do |i|
|
285
|
+
words.push("0")
|
286
|
+
end
|
287
|
+
words.concat(loHalf)
|
288
|
+
else
|
289
|
+
words = ip.split(":")
|
290
|
+
if (words.length > 8)
|
291
|
+
raise ValidationError, "#{ip} is too long."
|
292
|
+
elsif (words.length < 8)
|
293
|
+
raise ValidationError, "#{ip} is too short."
|
294
|
+
end
|
295
|
+
end
|
296
|
+
ipInt = 0
|
297
|
+
i = 8
|
298
|
+
words.each do |word|
|
299
|
+
i -= 1
|
300
|
+
word = word.to_i(16) << (16*i)
|
301
|
+
ipInt = ipInt | word
|
302
|
+
end
|
303
|
+
if ipv4Int # re-add ipv4 portion if present
|
304
|
+
ipInt = ipInt | ipv4Int
|
305
|
+
end
|
306
|
+
return ipInt
|
307
|
+
end
|
308
|
+
|
309
|
+
# quick_sort will return a sorted copy of the provided Array.
|
310
|
+
# The array must contain only objects which implement a cmp method and which are comparable to each other.
|
311
|
+
def Util.quick_sort(list)
|
312
|
+
if (list.length <= 1)
|
313
|
+
return [].concat(list)
|
314
|
+
end
|
315
|
+
|
316
|
+
final_list = []
|
317
|
+
lt_list = []
|
318
|
+
gt_list = []
|
319
|
+
eq_list = []
|
320
|
+
pivot = list[list.length-1]
|
321
|
+
list.each do |ip|
|
322
|
+
cmp = pivot.cmp(ip)
|
323
|
+
if (cmp == 1)
|
324
|
+
lt_list.push(ip)
|
325
|
+
elsif (cmp == -1)
|
326
|
+
gt_list.push(ip)
|
327
|
+
else
|
328
|
+
eq_list.push(ip)
|
329
|
+
end
|
330
|
+
end
|
331
|
+
final_list.concat( quick_sort(lt_list) )
|
332
|
+
final_list.concat(eq_list)
|
333
|
+
final_list.concat( quick_sort(gt_list) )
|
334
|
+
return final_list
|
335
|
+
end
|
336
|
+
|
337
|
+
# summ_peers returns a copy of the list with any merge-able subnets summ'd together.
|
338
|
+
def Util.summ_peers(list)
|
339
|
+
summd = quick_sort(list)
|
340
|
+
while true do
|
341
|
+
list_len = summd.length
|
342
|
+
last = list_len - 1
|
343
|
+
tmp_list = []
|
344
|
+
i = 0
|
345
|
+
while (i < list_len) do
|
346
|
+
net = summd[i]
|
347
|
+
next_net = i+1
|
348
|
+
if (i != last)
|
349
|
+
# if this net and next_net summarize then discard them & keep summary
|
350
|
+
new_net = net.summ(summd[next_net])
|
351
|
+
if (new_net) # can summ. keep summary
|
352
|
+
tmp_list.push(new_net)
|
353
|
+
i += 1 # skip next_net
|
354
|
+
else # cant summ. keep existing
|
355
|
+
tmp_list.push(net)
|
356
|
+
end
|
357
|
+
else
|
358
|
+
tmp_list.push(net) # keep last
|
359
|
+
end
|
360
|
+
i += 1
|
361
|
+
end
|
362
|
+
|
363
|
+
# stop when list stops getting shorter
|
364
|
+
if (tmp_list.length == list_len)
|
365
|
+
break
|
366
|
+
end
|
367
|
+
summd = tmp_list
|
368
|
+
end
|
369
|
+
return summd
|
370
|
+
end
|
371
|
+
|
372
|
+
end # end class
|
373
|
+
end # end module
|