netaddr 1.5.1 → 2.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/LICENSE +201 -0
- data/README.md +29 -0
- 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 +120 -0
- data/lib/ipv6net.rb +267 -0
- data/lib/mask128.rb +70 -0
- data/lib/mask32.rb +105 -0
- data/lib/netaddr.rb +123 -27
- data/lib/util.rb +307 -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 +177 -0
- data/test/ipv6_test.rb +96 -0
- data/test/ipv6net_test.rb +157 -0
- data/test/mask128_test.rb +45 -0
- data/test/mask32_test.rb +56 -0
- data/test/netaddr_test.rb +148 -0
- data/test/run_all.rb +10 -0
- metadata +29 -24
- data/Errors +0 -7
- data/README +0 -17
- data/changelog +0 -58
- data/lib/cidr.rb +0 -2189
- data/lib/cidr_shortcuts.rb +0 -440
- data/lib/eui.rb +0 -463
- data/lib/ip_math.rb +0 -259
- data/lib/methods.rb +0 -1079
- data/lib/tree.rb +0 -921
- data/lib/validation_shortcuts.rb +0 -219
- data/license +0 -13
- data/test/cidr_test.rb +0 -544
- 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,30 +1,126 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
require File.join(File.dirname(__FILE__), 'ip_math.rb')
|
11
|
-
require File.join(File.dirname(__FILE__), 'cidr_shortcuts.rb')
|
12
|
-
require File.join(File.dirname(__FILE__), 'methods.rb')
|
13
|
-
require File.join(File.dirname(__FILE__), 'cidr.rb')
|
14
|
-
require File.join(File.dirname(__FILE__), 'tree.rb')
|
15
|
-
require File.join(File.dirname(__FILE__), 'eui.rb')
|
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"
|
16
10
|
|
17
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
|
+
|
18
20
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
21
|
+
# ValidationError is thrown when a method fails a validation test.
|
22
|
+
class ValidationError < StandardError
|
23
|
+
end
|
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?(".")) # ipv4
|
43
|
+
return IPv4.parse(ip)
|
44
|
+
end
|
45
|
+
return IPv6.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?(".")) # ipv4
|
52
|
+
return IPv4Net.parse(net)
|
53
|
+
end
|
54
|
+
return IPv6Net.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,307 @@
|
|
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 & git 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
|
71
|
+
sib = ipnet.next_sib()
|
72
|
+
ceil = NetAddr::F32
|
73
|
+
if (sib != nil)
|
74
|
+
ceil = sib.network.addr
|
75
|
+
end
|
76
|
+
|
77
|
+
0.upto(subs.length-1) do |i|
|
78
|
+
sub = subs[i]
|
79
|
+
filled.push(sub)
|
80
|
+
limit = ceil
|
81
|
+
if (i+1 < subs.length)
|
82
|
+
limit = subs[i+1].network.addr
|
83
|
+
end
|
84
|
+
filled.concat( fwdfill(sub,limit) )
|
85
|
+
end
|
86
|
+
end
|
87
|
+
return filled
|
88
|
+
end
|
89
|
+
|
90
|
+
# filter_IPv4 returns a copy of list with only IPv4 objects
|
91
|
+
def Util.filter_IPv4(list)
|
92
|
+
filtered = []
|
93
|
+
list.each do |ip|
|
94
|
+
if (ip.kind_of?(IPv4))
|
95
|
+
filtered.push(ip)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
return filtered
|
99
|
+
end
|
100
|
+
|
101
|
+
# filter_IPv4Net returns a copy of list with only IPv4Net objects
|
102
|
+
def Util.filter_IPv4Net(list)
|
103
|
+
filtered = []
|
104
|
+
list.each do |ip|
|
105
|
+
if (ip.kind_of?(IPv4Net))
|
106
|
+
filtered.push(ip)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
return filtered
|
110
|
+
end
|
111
|
+
|
112
|
+
# filter_IPv6 returns a copy of list with only IPv6 objects
|
113
|
+
def Util.filter_IPv6(list)
|
114
|
+
filtered = []
|
115
|
+
list.each do |ip|
|
116
|
+
if (ip.kind_of?(IPv6))
|
117
|
+
filtered.push(ip)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
return filtered
|
121
|
+
end
|
122
|
+
|
123
|
+
# filter_IPv6Net returns a copy of list with only IPv4Net objects
|
124
|
+
def Util.filter_IPv6Net(list)
|
125
|
+
filtered = []
|
126
|
+
list.each do |ip|
|
127
|
+
if (ip.kind_of?(IPv6Net))
|
128
|
+
filtered.push(ip)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
return filtered
|
132
|
+
end
|
133
|
+
|
134
|
+
# fwdfill returns subnets between given IPv4Net/IPv6Nett and the limit address.
|
135
|
+
# limit should be > ipnet. will create subnets up to limit.
|
136
|
+
def Util.fwdfill(ipnet,limit)
|
137
|
+
nets = []
|
138
|
+
cur = ipnet
|
139
|
+
while true do
|
140
|
+
net = cur.next
|
141
|
+
if (net == nil || net.network.addr >= limit)
|
142
|
+
break
|
143
|
+
end
|
144
|
+
nets.push(net)
|
145
|
+
cur = net
|
146
|
+
end
|
147
|
+
return nets
|
148
|
+
end
|
149
|
+
|
150
|
+
# int_to_IPv4 converts an Integer into an IPv4 address String
|
151
|
+
def Util.int_to_IPv4(i)
|
152
|
+
octets = []
|
153
|
+
3.downto(0) do |x|
|
154
|
+
octet = (i >> 8*x) & 0xFF
|
155
|
+
octets.push(octet.to_s)
|
156
|
+
end
|
157
|
+
return octets.join('.')
|
158
|
+
end
|
159
|
+
|
160
|
+
# parse_IPv4 parses an IPv4 address String into an Integer
|
161
|
+
def Util.parse_IPv4(ip)
|
162
|
+
# check that only valid characters are present
|
163
|
+
if (ip =~ /[^0-9\.]/)
|
164
|
+
raise ValidationError, "#{ip} contains invalid characters."
|
165
|
+
end
|
166
|
+
|
167
|
+
octets = ip.strip.split('.')
|
168
|
+
if (octets.length != 4)
|
169
|
+
raise ValidationError, "IPv4 requires (4) octets."
|
170
|
+
end
|
171
|
+
|
172
|
+
ipInt = 0
|
173
|
+
i = 4
|
174
|
+
octets.each do |octet|
|
175
|
+
octetI = octet.to_i()
|
176
|
+
if ( (octetI < 0) || (octetI >= 256) )
|
177
|
+
raise ValidationError, "#{ip} is out of bounds for IPv4."
|
178
|
+
end
|
179
|
+
i -= 1
|
180
|
+
ipInt = ipInt | (octetI << 8*i)
|
181
|
+
end
|
182
|
+
return ipInt
|
183
|
+
end
|
184
|
+
|
185
|
+
# parse_IPv6 parses an IPv6 address String into an Integer
|
186
|
+
def Util.parse_IPv6(ip)
|
187
|
+
# check that only valid characters are present
|
188
|
+
if (ip =~ /[^0-9a-fA-F\:]/)
|
189
|
+
raise ValidationError, "#{ip} contains invalid characters."
|
190
|
+
end
|
191
|
+
|
192
|
+
ip = ip.strip
|
193
|
+
if (ip == "::")
|
194
|
+
return 0 # zero address
|
195
|
+
end
|
196
|
+
words = []
|
197
|
+
if (ip.include?("::")) # short format
|
198
|
+
if (ip =~ /:{3,}/) # make sure only i dont have ":::"
|
199
|
+
raise ValidationError, "#{ip} contains invalid field separator."
|
200
|
+
end
|
201
|
+
if (ip.scan(/::/).length != 1)
|
202
|
+
raise ValidationError, "#{ip} contains multiple '::' sequences."
|
203
|
+
end
|
204
|
+
|
205
|
+
halves = ip.split("::")
|
206
|
+
if (halves[0] == nil) # cases such as ::1
|
207
|
+
halves[0] = "0"
|
208
|
+
end
|
209
|
+
if (halves[1] == nil) # cases such as 1::
|
210
|
+
halves[1] = "0"
|
211
|
+
end
|
212
|
+
upHalf = halves[0].split(":")
|
213
|
+
loHalf = halves[1].split(":")
|
214
|
+
numWords = upHalf.length + loHalf.length
|
215
|
+
if (numWords > 8)
|
216
|
+
raise ValidationError, "#{ip} is too long."
|
217
|
+
end
|
218
|
+
words = upHalf
|
219
|
+
(8-numWords).downto(1) do |i|
|
220
|
+
words.push("0")
|
221
|
+
end
|
222
|
+
words.concat(loHalf)
|
223
|
+
else
|
224
|
+
words = ip.split(":")
|
225
|
+
if (words.length > 8)
|
226
|
+
raise ValidationError, "#{ip} is too long."
|
227
|
+
elsif (words.length < 8)
|
228
|
+
raise ValidationError, "#{ip} is too short."
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
ipInt = 0
|
233
|
+
i = 8
|
234
|
+
words.each do |word|
|
235
|
+
i -= 1
|
236
|
+
word = word.to_i(16) << (16*i)
|
237
|
+
ipInt = ipInt | word
|
238
|
+
end
|
239
|
+
|
240
|
+
return ipInt
|
241
|
+
end
|
242
|
+
|
243
|
+
# quick_sort will return a sorted copy of the provided Array.
|
244
|
+
# The array must contain only objects which implement a cmp method and which are comparable to each other.
|
245
|
+
def Util.quick_sort(list)
|
246
|
+
if (list.length <= 1)
|
247
|
+
return [].concat(list)
|
248
|
+
end
|
249
|
+
|
250
|
+
final_list = []
|
251
|
+
lt_list = []
|
252
|
+
gt_list = []
|
253
|
+
eq_list = []
|
254
|
+
pivot = list[list.length-1]
|
255
|
+
list.each do |ip|
|
256
|
+
cmp = pivot.cmp(ip)
|
257
|
+
if (cmp == 1)
|
258
|
+
lt_list.push(ip)
|
259
|
+
elsif (cmp == -1)
|
260
|
+
gt_list.push(ip)
|
261
|
+
else
|
262
|
+
eq_list.push(ip)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
final_list.concat( quick_sort(lt_list) )
|
266
|
+
final_list.concat(eq_list)
|
267
|
+
final_list.concat( quick_sort(gt_list) )
|
268
|
+
return final_list
|
269
|
+
end
|
270
|
+
|
271
|
+
# summ_peers returns a copy of the list with any merge-able subnets summ'd together.
|
272
|
+
def Util.summ_peers(list)
|
273
|
+
summd = quick_sort(list)
|
274
|
+
while true do
|
275
|
+
list_len = summd.length
|
276
|
+
last = list_len - 1
|
277
|
+
tmp_list = []
|
278
|
+
i = 0
|
279
|
+
while (i < list_len) do
|
280
|
+
net = summd[i]
|
281
|
+
next_net = i+1
|
282
|
+
if (i != last)
|
283
|
+
# if this net and next_net summarize then discard them & keep summary
|
284
|
+
new_net = net.summ(summd[next_net])
|
285
|
+
if (new_net) # can summ. keep summary
|
286
|
+
tmp_list.push(new_net)
|
287
|
+
i += 1 # skip next_net
|
288
|
+
else # cant summ. keep existing
|
289
|
+
tmp_list.push(net)
|
290
|
+
end
|
291
|
+
else
|
292
|
+
tmp_list.push(net) # keep last
|
293
|
+
end
|
294
|
+
i += 1
|
295
|
+
end
|
296
|
+
|
297
|
+
# stop when list stops getting shorter
|
298
|
+
if (tmp_list.length == list_len)
|
299
|
+
break
|
300
|
+
end
|
301
|
+
summd = tmp_list
|
302
|
+
end
|
303
|
+
return summd
|
304
|
+
end
|
305
|
+
|
306
|
+
end # end class
|
307
|
+
end # end module
|