netaddr 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of netaddr might be problematic. Click here for more details.
- data/changelog +27 -0
- data/lib/cidr.rb +595 -795
- data/lib/cidr_shortcuts.rb +334 -0
- data/lib/eui.rb +16 -16
- data/lib/ip_math.rb +241 -0
- data/lib/methods.rb +230 -643
- data/lib/net_addr.rb +3 -1
- data/lib/tree.rb +202 -196
- data/lib/validation_shortcuts.rb +219 -0
- data/tests/cidr_test.rb +66 -62
- data/tests/methods_test.rb +31 -50
- data/tests/tree_test.rb +1 -1
- metadata +7 -2
data/lib/methods.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
=begin rdoc
|
2
2
|
Copyleft (c) 2006 Dustin Spinhirne (www.spinhirne.com)
|
3
|
-
|
3
|
+
|
4
4
|
Licensed under the same terms as Ruby, No Warranty is provided.
|
5
5
|
=end
|
6
6
|
|
@@ -11,7 +11,7 @@ module NetAddr
|
|
11
11
|
#==============================================================================#
|
12
12
|
|
13
13
|
#===Synopsis
|
14
|
-
#Given a list of CIDR addresses or NetAddr::CIDR objects
|
14
|
+
#Given a list of CIDR addresses or NetAddr::CIDR objects,
|
15
15
|
#merge (summarize) them in the most efficient way possible. Summarization
|
16
16
|
#will only occur when the newly created supernets will not result in the
|
17
17
|
#'creation' of new IP space. For example the following blocks
|
@@ -23,155 +23,98 @@ module NetAddr
|
|
23
23
|
#10.1.0.0/26, 10.1.0.64/26) and they will be merged properly (ie 192.168.1.0/25,
|
24
24
|
#and 10.1.0.0/25 would be returned).
|
25
25
|
#
|
26
|
+
#If the :Objectify option is enabled, then any summary addresses returned will
|
27
|
+
#contain the original CIDRs used to create them within the tag value :Subnets
|
28
|
+
#(ie. cidr_x.tag[:Subnets] would be an Array of the CIDRs used to create cidr_x)
|
29
|
+
#
|
26
30
|
# Example:
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
31
|
+
# cidr1 = NetAddr::CIDR.create('192.168.1.0/27')
|
32
|
+
# cidr2 = NetAddr::CIDR.create('192.168.1.32/27')
|
33
|
+
# NetAddr.merge([cidr1,cidr2])
|
34
|
+
# ip_net_range = NetAddr.range('192.168.35.0','192.168.39.255',:Inclusive => true, :Objectify => true)
|
35
|
+
# NetAddr.merge(ip_net_range, :Objectify => true)
|
30
36
|
#
|
31
37
|
#===Arguments:
|
32
|
-
#* Array of CIDR addresses as Strings, or an Array of NetAddr::CIDR objects
|
33
|
-
#*
|
34
|
-
# :Objectify -- if true, return NetAddr::CIDR objects
|
35
|
-
# :Short -- if true, return IPv6 addresses in short-hand notation
|
38
|
+
#* list = Array of CIDR addresses as Strings, or an Array of NetAddr::CIDR objects
|
39
|
+
#* options = Hash with the following keys:
|
40
|
+
# :Objectify -- if true, return NetAddr::CIDR objects
|
41
|
+
# :Short -- if true, return IPv6 addresses in short-hand notation
|
36
42
|
#
|
37
43
|
#===Returns:
|
38
|
-
#* Array of CIDR addresses
|
44
|
+
#* Array of CIDR addresses or NetAddr::CIDR objects
|
39
45
|
#
|
40
46
|
def merge(list,options=nil)
|
41
47
|
known_args = [:Objectify, :Short]
|
42
|
-
version = nil
|
43
|
-
all_f = nil
|
44
48
|
short = false
|
45
49
|
objectify = false
|
50
|
+
verbose = false
|
46
51
|
|
47
52
|
# validate list
|
48
53
|
raise ArgumentError, "Array expected for argument 'list' but #{list.class} provided." if (!list.kind_of?(Array) )
|
49
|
-
|
54
|
+
|
50
55
|
# validate options
|
51
|
-
if (options)
|
56
|
+
if (options)
|
52
57
|
raise ArgumentError, "Hash expected for argument 'options' but #{options.class} provided." if (!options.kind_of?(Hash) )
|
53
58
|
NetAddr.validate_args(options.keys,known_args)
|
54
|
-
|
59
|
+
|
55
60
|
if (options.has_key?(:Objectify) && options[:Objectify] == true)
|
56
61
|
objectify = true
|
57
62
|
end
|
58
|
-
|
63
|
+
|
59
64
|
if (options.has_key?(:Short) && options[:Short] == true)
|
60
65
|
short = true
|
61
66
|
end
|
62
67
|
end
|
63
|
-
|
68
|
+
|
64
69
|
# make sure all are valid types of the same IP version
|
65
|
-
|
70
|
+
v4_list = []
|
71
|
+
v6_list = []
|
66
72
|
list.each do |obj|
|
67
73
|
if (!obj.kind_of?(NetAddr::CIDR))
|
68
74
|
begin
|
69
75
|
obj = NetAddr::CIDR.create(obj)
|
70
76
|
rescue Exception => error
|
71
|
-
raise ArgumentError, "
|
77
|
+
raise ArgumentError, "One of the provided CIDR addresses raised the following " +
|
72
78
|
"errors: #{error}"
|
73
79
|
end
|
74
80
|
end
|
75
81
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
end
|
81
|
-
supernet_list.push(obj)
|
82
|
+
if (obj.version == 4)
|
83
|
+
v4_list.push(obj)
|
84
|
+
else
|
85
|
+
v6_list.push(obj)
|
86
|
+
end
|
82
87
|
end
|
83
88
|
|
84
|
-
#
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
supernet_list_length = 0
|
91
|
-
until (supernet_list_length == supernet_list.length)
|
92
|
-
supernet_list_length = supernet_list.length
|
93
|
-
|
94
|
-
# categorize
|
95
|
-
supernet_list.each do |cidr|
|
96
|
-
netmask = cidr.packed_netmask
|
97
|
-
network = cidr.packed_network
|
98
|
-
if (categories.has_key?(netmask) )
|
99
|
-
categories[netmask].push(network)
|
100
|
-
else
|
101
|
-
categories[netmask] = [network]
|
102
|
-
end
|
103
|
-
end
|
104
|
-
supernet_list.clear
|
105
|
-
|
106
|
-
ordered_cats = categories.keys.sort
|
107
|
-
ordered_cats.each do |netmask|
|
108
|
-
nets = categories[netmask].sort
|
109
|
-
bitstep = (all_f + 1) - netmask
|
110
|
-
|
111
|
-
until (nets.length == 0)
|
112
|
-
# take the first network & create its supernet. this
|
113
|
-
# supernet will have x number of subnets, so we'll look
|
114
|
-
# & see if we have those subnets. if so, keep supernet & delete subnets.
|
115
|
-
to_merge = []
|
116
|
-
multiplier = 1
|
117
|
-
network1 = nets[0]
|
118
|
-
num_required = 2**multiplier
|
119
|
-
supermask = (netmask << multiplier) & all_f
|
120
|
-
supernet = supermask & network1
|
121
|
-
if (network1 == supernet)
|
122
|
-
# we have the first half of the new supernet
|
123
|
-
expected = network1
|
124
|
-
nets.each do |network|
|
125
|
-
if (network == expected)
|
126
|
-
to_merge.push(network)
|
127
|
-
expected = expected + bitstep
|
128
|
-
if ( (to_merge.length == num_required) && (nets.length > num_required) )
|
129
|
-
# we have all of our subnets for this round, but still have
|
130
|
-
# more to look at
|
131
|
-
multiplier += 1
|
132
|
-
num_required = 2**multiplier
|
133
|
-
supermask = (netmask << multiplier) & all_f
|
134
|
-
supernet = supermask & network1
|
135
|
-
end
|
136
|
-
else
|
137
|
-
break
|
138
|
-
end
|
139
|
-
end
|
140
|
-
else
|
141
|
-
# we have the second half of the new supernet only
|
142
|
-
to_merge.push(network1)
|
143
|
-
end
|
89
|
+
# summarize
|
90
|
+
v4_summary = []
|
91
|
+
v6_summary = []
|
92
|
+
if (v4_list.length != 0)
|
93
|
+
v4_summary = NetAddr.cidr_summarize(v4_list)
|
94
|
+
end
|
144
95
|
|
145
|
-
|
146
|
-
|
147
|
-
# we dont have all of our subnets, so backstep 1 bit
|
148
|
-
multiplier -= 1
|
149
|
-
supermask = (netmask << multiplier) & all_f
|
150
|
-
supernet = supermask & network1
|
151
|
-
end
|
152
|
-
|
153
|
-
# save new supernet
|
154
|
-
supernet_list.push(NetAddr::CIDR.create(supernet,
|
155
|
-
:PackedNetmask => supermask,
|
156
|
-
:Version => version))
|
157
|
-
|
158
|
-
# delete the subnets of the new supernet
|
159
|
-
(2**multiplier).times {nets.delete(to_merge.shift)}
|
160
|
-
end
|
161
|
-
end
|
162
|
-
categories.clear
|
163
|
-
supernet_list = NetAddr.sort(supernet_list)
|
96
|
+
if (v6_list.length != 0)
|
97
|
+
v6_summary = NetAddr.cidr_summarize(v6_list)
|
164
98
|
end
|
165
99
|
|
166
100
|
# decide what to return
|
101
|
+
summarized_list = []
|
167
102
|
if (!objectify)
|
168
|
-
|
169
|
-
|
170
|
-
|
103
|
+
summarized_list = []
|
104
|
+
if (v4_summary.length != 0)
|
105
|
+
v4_summary.each {|x| summarized_list.push(x.desc())}
|
106
|
+
end
|
107
|
+
|
108
|
+
if (v6_summary.length != 0)
|
109
|
+
v6_summary.each {|x| summarized_list.push(x.desc(:Short => short))}
|
110
|
+
end
|
111
|
+
|
171
112
|
else
|
172
|
-
|
113
|
+
summarized_list.concat(v4_summary) if (v4_summary.length != 0)
|
114
|
+
summarized_list.concat(v6_summary) if (v6_summary.length != 0)
|
173
115
|
end
|
174
116
|
|
117
|
+
return(summarized_list)
|
175
118
|
end
|
176
119
|
module_function :merge
|
177
120
|
|
@@ -184,14 +127,14 @@ module_function :merge
|
|
184
127
|
#netmask (bits by default) required for that subnet. IP version is assumed to be 4 unless specified otherwise.
|
185
128
|
#
|
186
129
|
# Example:
|
187
|
-
#
|
188
|
-
#
|
130
|
+
# NetAddr.minumum_size(14)
|
131
|
+
# NetAddr.minumum_size(65536, :Version => 6)
|
189
132
|
#
|
190
133
|
#===Arguments:
|
191
|
-
#* IP count as an Integer
|
192
|
-
#*
|
193
|
-
# :Extended -- If true, then return the netmask in extended format (y.y.y.y)
|
194
|
-
# :Version -- IP version - Integer
|
134
|
+
#* ipcount = IP count as an Integer
|
135
|
+
#* options = Hash with the following keys:
|
136
|
+
# :Extended -- If true, then return the netmask, as a String, in extended format (IPv4 only y.y.y.y)
|
137
|
+
# :Version -- IP version - Integer
|
195
138
|
#
|
196
139
|
#===Returns:
|
197
140
|
#* Integer or String
|
@@ -203,43 +146,23 @@ def minimum_size(ipcount, options=nil)
|
|
203
146
|
|
204
147
|
# validate ipcount
|
205
148
|
raise ArgumentError, "Integer expected for argument 'ipcount' but #{ipcount.class} provided." if (!ipcount.kind_of?(Integer))
|
206
|
-
|
149
|
+
|
207
150
|
# validate options
|
208
151
|
if (options)
|
209
152
|
raise ArgumentError, "Hash expected for argument 'options' but #{options.class} provided." if (!options.kind_of?(Hash))
|
210
|
-
|
153
|
+
|
211
154
|
NetAddr.validate_args(options.keys,known_args)
|
212
|
-
|
155
|
+
|
213
156
|
if (options.has_key?(:Version))
|
214
157
|
version = options[:Version]
|
215
158
|
end
|
216
|
-
|
159
|
+
|
217
160
|
if (options.has_key?(:Extended) && options[:Extended] == true)
|
218
161
|
extended = true
|
219
162
|
end
|
220
163
|
end
|
221
|
-
|
222
|
-
if (version == 4)
|
223
|
-
max_bits = 32
|
224
|
-
else
|
225
|
-
max_bits = 128
|
226
|
-
end
|
227
|
-
|
228
|
-
|
229
|
-
if (ipcount > 2**max_bits)
|
230
|
-
raise BoundaryError, "Required IP count exceeds number of IP addresses available " +
|
231
|
-
"for IPv#{version}."
|
232
|
-
end
|
233
164
|
|
234
|
-
|
235
|
-
bits_needed = 0
|
236
|
-
until (2**bits_needed >= ipcount)
|
237
|
-
bits_needed += 1
|
238
|
-
end
|
239
|
-
subnet_bits = max_bits - bits_needed
|
240
|
-
|
241
|
-
return(NetAddr.unpack_ip_addr(NetAddr.pack_ip_netmask(subnet_bits))) if (extended && version == 4)
|
242
|
-
return(subnet_bits)
|
165
|
+
return( ip_count_to_size(ipcount,version,extended) )
|
243
166
|
end
|
244
167
|
module_function :minimum_size
|
245
168
|
|
@@ -248,7 +171,8 @@ module_function :minimum_size
|
|
248
171
|
#==============================================================================#
|
249
172
|
|
250
173
|
#===Synopsis
|
251
|
-
#Convert IP addresses into an Integer.
|
174
|
+
#Convert IP addresses into an Integer. This method will attempt to auto-detect the IP version
|
175
|
+
#if not provided, however a slight speed increase is realized if version is provided.
|
252
176
|
#
|
253
177
|
# Example:
|
254
178
|
# pack_ip_addr('192.168.1.1')
|
@@ -256,8 +180,8 @@ module_function :minimum_size
|
|
256
180
|
# pack_ip_addr(::192.168.1.1')
|
257
181
|
#
|
258
182
|
#===Arguments:
|
259
|
-
#* IP address as a String
|
260
|
-
#*
|
183
|
+
#* ip = IP address as a String
|
184
|
+
#* options = Hash with the following keys:
|
261
185
|
# :Version -- IP version - Integer
|
262
186
|
#
|
263
187
|
#===Returns:
|
@@ -266,11 +190,12 @@ module_function :minimum_size
|
|
266
190
|
def pack_ip_addr(ip, options=nil)
|
267
191
|
known_args = [:Version]
|
268
192
|
to_validate = {}
|
269
|
-
|
193
|
+
version = nil
|
194
|
+
|
270
195
|
# validate options
|
271
196
|
if (options)
|
272
197
|
raise ArgumentError, "Hash expected for argument 'options' but #{options.class} provided." if (!options.kind_of?(Hash))
|
273
|
-
|
198
|
+
validate_args(options.keys,known_args)
|
274
199
|
|
275
200
|
if (options.has_key?(:Version))
|
276
201
|
version = options[:Version]
|
@@ -279,82 +204,15 @@ def pack_ip_addr(ip, options=nil)
|
|
279
204
|
raise VersionError, ":Version should be 4 or 6, but was '#{version}'."
|
280
205
|
end
|
281
206
|
end
|
282
|
-
end
|
283
|
-
|
284
|
-
if ( ip.kind_of?(String) )
|
285
|
-
|
286
|
-
# validate
|
287
|
-
NetAddr.validate_ip_addr(ip, to_validate)
|
288
|
-
|
289
|
-
# determine version if not provided
|
290
|
-
if (!version)
|
291
|
-
if ( ip =~ /\./ && ip !~ /:/ )
|
292
|
-
version = 4
|
293
|
-
else
|
294
|
-
version = 6
|
295
|
-
end
|
296
|
-
end
|
297
|
-
|
298
|
-
packed_ip = 0
|
299
|
-
if ( version == 4)
|
300
|
-
octets = ip.split('.')
|
301
|
-
(0..3).each do |x|
|
302
|
-
octet = octets.pop.to_i
|
303
|
-
octet = octet << 8*x
|
304
|
-
packed_ip = packed_ip | octet
|
305
|
-
end
|
306
|
-
|
307
|
-
else
|
308
|
-
# if ipv4-mapped ipv6 addr
|
309
|
-
if (ip =~ /\./)
|
310
|
-
dotted_dec = true
|
311
|
-
end
|
312
|
-
|
313
|
-
# split up by ':'
|
314
|
-
fields = []
|
315
|
-
if (ip =~ /::/)
|
316
|
-
shrthnd = ip.split( /::/ )
|
317
|
-
if (shrthnd.length == 0)
|
318
|
-
return(0)
|
319
|
-
else
|
320
|
-
first_half = shrthnd[0].split( /:/ ) if (shrthnd[0])
|
321
|
-
sec_half = shrthnd[1].split( /:/ ) if (shrthnd[1])
|
322
|
-
first_half = [] if (!first_half)
|
323
|
-
sec_half = [] if (!sec_half)
|
324
|
-
end
|
325
|
-
missing_fields = 8 - first_half.length - sec_half.length
|
326
|
-
missing_fields -= 1 if dotted_dec
|
327
|
-
fields = fields.concat(first_half)
|
328
|
-
missing_fields.times {fields.push('0')}
|
329
|
-
fields = fields.concat(sec_half)
|
330
|
-
|
331
|
-
else
|
332
|
-
fields = ip.split(':')
|
333
|
-
end
|
334
|
-
|
335
|
-
if (dotted_dec)
|
336
|
-
ipv4_addr = fields.pop
|
337
|
-
packed_v4 = NetAddr.pack_ip_addr(ipv4_addr, :Version => 4)
|
338
|
-
octets = []
|
339
|
-
2.times do
|
340
|
-
octet = packed_v4 & 0xFFFF
|
341
|
-
octets.unshift(octet.to_s(16))
|
342
|
-
packed_v4 = packed_v4 >> 16
|
343
|
-
end
|
344
|
-
fields.concat(octets)
|
345
|
-
end
|
207
|
+
end
|
346
208
|
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
packed_ip = packed_ip | field
|
352
|
-
end
|
353
|
-
|
354
|
-
end
|
209
|
+
if ( ip.kind_of?(String) )
|
210
|
+
version = detect_ip_version(ip) if (!version)
|
211
|
+
validate_ip_str(ip,version)
|
212
|
+
packed_ip = ip_str_to_int(ip,version)
|
355
213
|
|
356
214
|
else
|
357
|
-
raise ArgumentError, "String expected for argument 'ip' but #{ip.class} provided."
|
215
|
+
raise ArgumentError, "String expected for argument 'ip' but #{ip.class} provided."
|
358
216
|
end
|
359
217
|
|
360
218
|
return(packed_ip)
|
@@ -372,69 +230,51 @@ module_function :pack_ip_addr
|
|
372
230
|
#to specify the version if an IPv6 netmask of /32 or smaller is provided.
|
373
231
|
#
|
374
232
|
# Example:
|
375
|
-
#
|
376
|
-
#
|
377
|
-
#
|
378
|
-
#
|
379
|
-
#
|
233
|
+
# NetAddr.pack_ip_netmask('255.255.255.0')
|
234
|
+
# NetAddr.pack_ip_netmask('24')
|
235
|
+
# NetAddr.pack_ip_netmask(24)
|
236
|
+
# NetAddr.pack_ip_netmask('/24')
|
237
|
+
# NetAddr.pack_ip_netmask('32', :Version => 6)
|
380
238
|
#
|
381
239
|
#===Arguments
|
382
|
-
#* Netmask as a String or Integer
|
383
|
-
#*
|
384
|
-
# :Version -- IP version - Integer
|
240
|
+
#* netmask = Netmask as a String or Integer
|
241
|
+
#* options = Hash with the following keys:
|
242
|
+
# :Version -- IP version - Integer
|
385
243
|
#
|
386
244
|
#===Returns:
|
387
245
|
#* Integer
|
388
246
|
#
|
389
247
|
def pack_ip_netmask(netmask, options=nil)
|
390
248
|
known_args = [:Version]
|
391
|
-
|
392
|
-
|
393
|
-
|
249
|
+
version = 4
|
250
|
+
packed_netmask = nil
|
251
|
+
|
394
252
|
# validate options
|
395
253
|
if (options)
|
396
254
|
raise ArgumentError, "Hash expected for argument 'options' but #{options.class} provided." if (!options.kind_of?(Hash))
|
397
255
|
NetAddr.validate_args(options.keys,known_args)
|
398
|
-
|
256
|
+
|
399
257
|
if (options.has_key?(:Version))
|
400
258
|
version = options[:Version]
|
401
259
|
if (version != 4 && version != 6)
|
402
260
|
raise VersionError, ":Version should be 4 or 6, but was '#{version}'."
|
403
|
-
elsif (version == 6)
|
404
|
-
all_f = 2**128-1
|
405
|
-
else
|
406
|
-
all_f = 2**32-1
|
407
261
|
end
|
408
|
-
to_validate[:Version] = version
|
409
262
|
end
|
410
263
|
end
|
411
|
-
|
264
|
+
|
412
265
|
if (netmask.kind_of?(String))
|
413
|
-
|
414
|
-
|
415
|
-
if(netmask =~ /\./)
|
416
|
-
packed_netmask = NetAddr.pack_ip_addr(netmask)
|
266
|
+
validate_netmask_str(netmask, version)
|
267
|
+
packed_netmask = netmask_str_to_int(netmask,version)
|
417
268
|
|
418
|
-
else
|
419
|
-
# remove '/' if present
|
420
|
-
if (netmask =~ /^\// )
|
421
|
-
netmask[0] = " "
|
422
|
-
netmask.lstrip!
|
423
|
-
end
|
424
|
-
netmask = netmask.to_i
|
425
|
-
packed_netmask = all_f ^ (all_f >> netmask)
|
426
|
-
end
|
427
|
-
|
428
269
|
elsif (netmask.kind_of?(Integer))
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
270
|
+
validate_netmask_int(netmask, version, true)
|
271
|
+
packed_netmask = bits_to_mask(netmask,version)
|
272
|
+
|
433
273
|
else
|
434
274
|
raise ArgumentError, "String or Integer expected for argument 'netmask', " +
|
435
275
|
"but #{netmask.class} provided." if (!netmask.kind_of?(Integer) && !netmask.kind_of?(String))
|
436
276
|
end
|
437
|
-
|
277
|
+
|
438
278
|
return(packed_netmask)
|
439
279
|
end
|
440
280
|
module_function :pack_ip_netmask
|
@@ -447,55 +287,60 @@ module_function :pack_ip_netmask
|
|
447
287
|
#Given two CIDR addresses or NetAddr::CIDR objects of the same version,
|
448
288
|
#return all IP addresses between them. NetAddr.range will use the original IP
|
449
289
|
#address passed during the initialization of the NetAddr::CIDR objects, or the
|
450
|
-
#
|
451
|
-
#non-inclusive (don't include boundaries as part of returned data)
|
290
|
+
#IP address portion of any CIDR addresses passed. The default behavior is to be
|
291
|
+
#non-inclusive (don't include boundaries as part of returned data).
|
452
292
|
#
|
453
293
|
# Example:
|
454
|
-
#
|
455
|
-
#
|
294
|
+
# lower = NetAddr::CIDR.create('192.168.35.0')
|
295
|
+
# upper = NetAddr::CIDR.create('192.168.39.255')
|
296
|
+
# NetAddr.range(lower,upper, :Limit => 10, :Bitstep => 32)
|
297
|
+
# NetAddr.range('192.168.35.0','192.168.39.255', :Inclusive => true)
|
298
|
+
# NetAddr.range('192.168.35.0','192.168.39.255', :Inclusive => true, :Size => true)
|
456
299
|
#
|
457
300
|
#===Arguments:
|
458
|
-
#* Lower boundary CIDR as a String or NetAddr::CIDR object
|
459
|
-
#* Upper boundary CIDR as a String or NetAddr::CIDR object
|
460
|
-
#*
|
461
|
-
# :Bitstep -- enumerate in X sized steps - Integer
|
301
|
+
#* lower = Lower boundary CIDR as a String or NetAddr::CIDR object
|
302
|
+
#* upper = Upper boundary CIDR as a String or NetAddr::CIDR object
|
303
|
+
#* options = Hash with the following keys:
|
304
|
+
# :Bitstep -- enumerate in X sized steps - Integer
|
462
305
|
# :Inclusive -- if true, include boundaries in returned data
|
463
|
-
# :Limit -- limit returned list to X number of items - Integer
|
464
|
-
# :Objectify -- if true, return CIDR objects
|
465
|
-
# :Short -- if true, return IPv6 addresses in short-hand notation
|
306
|
+
# :Limit -- limit returned list to X number of items - Integer
|
307
|
+
# :Objectify -- if true, return CIDR objects
|
308
|
+
# :Short -- if true, return IPv6 addresses in short-hand notation
|
309
|
+
# :Size -- if true, return the number of addresses in this range, but not the addresses themselves
|
466
310
|
#
|
467
311
|
#===Returns:
|
468
|
-
#* Array of Strings
|
312
|
+
#* Array of Strings or NetAddr::CIDR objects, or an Integer
|
469
313
|
#
|
470
|
-
def range(
|
471
|
-
known_args = [:Bitstep, :Inclusive, :Limit, :Objectify, :Short]
|
314
|
+
def range(lower, upper, options=nil)
|
315
|
+
known_args = [:Bitstep, :Inclusive, :Limit, :Objectify, :Short, :Size]
|
472
316
|
list = []
|
473
317
|
bitstep = 1
|
474
318
|
objectify = false
|
475
319
|
short = false
|
320
|
+
size_only = false
|
476
321
|
inclusive = false
|
477
322
|
limit = nil
|
478
323
|
|
479
|
-
# if
|
324
|
+
# if lower/upper are not CIDR objects, then attempt to create
|
480
325
|
# cidr objects from them
|
481
|
-
if ( !
|
326
|
+
if ( !lower.kind_of?(NetAddr::CIDR) )
|
482
327
|
begin
|
483
|
-
|
328
|
+
lower = NetAddr::CIDR.create(lower)
|
484
329
|
rescue Exception => error
|
485
|
-
raise ArgumentError, "Argument '
|
330
|
+
raise ArgumentError, "Argument 'lower' raised the following " +
|
486
331
|
"errors: #{error}"
|
487
332
|
end
|
488
333
|
end
|
489
|
-
|
490
|
-
if ( !
|
334
|
+
|
335
|
+
if ( !upper.kind_of?(NetAddr::CIDR))
|
491
336
|
begin
|
492
|
-
|
337
|
+
upper = NetAddr::CIDR.create(upper)
|
493
338
|
rescue Exception => error
|
494
|
-
raise ArgumentError, "Argument '
|
339
|
+
raise ArgumentError, "Argument 'upper' raised the following " +
|
495
340
|
"errors: #{error}"
|
496
341
|
end
|
497
342
|
end
|
498
|
-
|
343
|
+
|
499
344
|
# validate options
|
500
345
|
if (options)
|
501
346
|
raise ArgumentError, "Hash expected for argument 'options' but #{options.class} provided." if (!options.kind_of?(Hash))
|
@@ -508,11 +353,15 @@ def range(cidr1, cidr2, options=nil)
|
|
508
353
|
if( options.has_key?(:Objectify) && options[:Objectify] == true )
|
509
354
|
objectify = true
|
510
355
|
end
|
511
|
-
|
356
|
+
|
512
357
|
if( options.has_key?(:Short) && options[:Short] == true )
|
513
358
|
short = true
|
514
359
|
end
|
515
|
-
|
360
|
+
|
361
|
+
if( options.has_key?(:Size) && options[:Size] == true )
|
362
|
+
size_only = true
|
363
|
+
end
|
364
|
+
|
516
365
|
if( options.has_key?(:Inclusive) && options[:Inclusive] == true )
|
517
366
|
inclusive = true
|
518
367
|
end
|
@@ -523,9 +372,9 @@ def range(cidr1, cidr2, options=nil)
|
|
523
372
|
end
|
524
373
|
|
525
374
|
# check version, store & sort
|
526
|
-
if (
|
527
|
-
version =
|
528
|
-
boundaries = [
|
375
|
+
if (lower.version == upper.version)
|
376
|
+
version = lower.version
|
377
|
+
boundaries = [lower.packed_ip, upper.packed_ip]
|
529
378
|
boundaries.sort
|
530
379
|
else
|
531
380
|
raise VersionError, "Provided NetAddr::CIDR objects are of different IP versions."
|
@@ -539,21 +388,25 @@ def range(cidr1, cidr2, options=nil)
|
|
539
388
|
my_ip = boundaries[0]
|
540
389
|
end_ip = boundaries[1] + 1
|
541
390
|
end
|
542
|
-
|
543
|
-
until (my_ip >= end_ip)
|
544
|
-
if (!objectify)
|
545
|
-
my_ip_s = NetAddr.unpack_ip_addr(my_ip, :Version => version)
|
546
|
-
my_ips = NetAddr.shorten(my_ips) if (short && version == 6)
|
547
|
-
list.push(my_ip_s)
|
548
|
-
else
|
549
|
-
list.push( NetAddr::CIDR.create(my_ip, :Version => version) )
|
550
|
-
end
|
551
391
|
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
392
|
+
if (!size_only)
|
393
|
+
until (my_ip >= end_ip)
|
394
|
+
if (!objectify)
|
395
|
+
my_ip_s = ip_int_to_str(my_ip, version)
|
396
|
+
my_ips = shorten(my_ips) if (short && version == 6)
|
397
|
+
list.push(my_ip_s)
|
398
|
+
else
|
399
|
+
list.push( cidr_build(version,my_ip) )
|
400
|
+
end
|
401
|
+
|
402
|
+
my_ip = my_ip + bitstep
|
403
|
+
if (limit)
|
404
|
+
limit = limit -1
|
405
|
+
break if (limit == 0)
|
406
|
+
end
|
556
407
|
end
|
408
|
+
else
|
409
|
+
list = end_ip - my_ip
|
557
410
|
end
|
558
411
|
|
559
412
|
return(list)
|
@@ -565,14 +418,14 @@ module_function :range
|
|
565
418
|
#==============================================================================#
|
566
419
|
|
567
420
|
#===Synopsis
|
568
|
-
#Take a standard IPv6 address
|
421
|
+
#Take a standard IPv6 address and format it in short-hand notation.
|
569
422
|
#The address should not contain a netmask.
|
570
423
|
#
|
571
424
|
# Example:
|
572
|
-
#
|
425
|
+
# NetAddr.shorten('fec0:0000:0000:0000:0000:0000:0000:0001')
|
573
426
|
#
|
574
427
|
#===Arguments:
|
575
|
-
#* String
|
428
|
+
#* addr = String
|
576
429
|
#
|
577
430
|
#===Returns:
|
578
431
|
#* String
|
@@ -584,7 +437,7 @@ def shorten(addr)
|
|
584
437
|
raise ArgumentError, "Expected String, but #{addr.class} provided."
|
585
438
|
end
|
586
439
|
|
587
|
-
|
440
|
+
validate_ip_str(addr, 6)
|
588
441
|
|
589
442
|
# make sure this isnt already shorthand
|
590
443
|
if (addr =~ /::/)
|
@@ -593,17 +446,17 @@ def shorten(addr)
|
|
593
446
|
|
594
447
|
# split into fields
|
595
448
|
fields = addr.split(":")
|
596
|
-
|
449
|
+
|
597
450
|
# check last field for ipv4-mapped addr
|
598
451
|
if (fields.last() =~ /\./ )
|
599
452
|
ipv4_mapped = fields.pop()
|
600
453
|
end
|
601
|
-
|
454
|
+
|
602
455
|
# look for most consecutive '0' fields
|
603
456
|
start_field,end_field = nil,nil
|
604
457
|
start_end = []
|
605
458
|
consecutive,longest = 0,0
|
606
|
-
|
459
|
+
|
607
460
|
(0..(fields.length-1)).each do |x|
|
608
461
|
fields[x] = fields[x].to_i(16)
|
609
462
|
|
@@ -620,7 +473,7 @@ def shorten(addr)
|
|
620
473
|
if (consecutive > longest)
|
621
474
|
longest = consecutive
|
622
475
|
start_end = [start_field,end_field]
|
623
|
-
start_field,end_field = nil,nil
|
476
|
+
start_field,end_field = nil,nil
|
624
477
|
end
|
625
478
|
consecutive = 0
|
626
479
|
end
|
@@ -628,7 +481,7 @@ def shorten(addr)
|
|
628
481
|
|
629
482
|
fields[x] = fields[x].to_s(16)
|
630
483
|
end
|
631
|
-
|
484
|
+
|
632
485
|
# if our longest set of 0's is at the end, then start & end fields
|
633
486
|
# are already set. if not, then make start & end fields the ones we've
|
634
487
|
# stored away in start_end
|
@@ -639,15 +492,15 @@ def shorten(addr)
|
|
639
492
|
end_field = start_end[1]
|
640
493
|
end
|
641
494
|
|
642
|
-
if (longest > 1)
|
495
|
+
if (longest > 1)
|
643
496
|
fields[start_field] = ''
|
644
497
|
start_field += 1
|
645
498
|
fields.slice!(start_field..end_field)
|
646
499
|
end
|
647
|
-
fields.push(ipv4_mapped) if (ipv4_mapped)
|
648
|
-
short = fields.join(':')
|
500
|
+
fields.push(ipv4_mapped) if (ipv4_mapped)
|
501
|
+
short = fields.join(':')
|
649
502
|
short << ':' if (short =~ /:$/)
|
650
|
-
|
503
|
+
|
651
504
|
return(short)
|
652
505
|
end
|
653
506
|
module_function :shorten
|
@@ -658,16 +511,18 @@ module_function :shorten
|
|
658
511
|
|
659
512
|
#===Synopsis
|
660
513
|
#Given a list of CIDR addresses or NetAddr::CIDR objects,
|
661
|
-
#sort them from lowest to highest by Network/Netmask.
|
514
|
+
#sort them from lowest to highest by Network/Netmask. This method will use the
|
662
515
|
#base address passed during the initialization of any NetAddr::CIDR
|
663
516
|
#objects, or the base address of any CIDR addresses passed
|
664
517
|
#
|
665
518
|
# Example:
|
666
|
-
#
|
667
|
-
#
|
519
|
+
# cidr1 = NetAddr::CIDR.create('192.168.1.32/27')
|
520
|
+
# cidr2 = NetAddr::CIDR.create('192.168.1.0/27')
|
521
|
+
# NetAddr.sort([cidr1,cidr2])
|
522
|
+
# NetAddr.sort(['192.168.1.32/27','192.168.1.0/27','192.168.2.0/24'])
|
668
523
|
#
|
669
524
|
#===Arguments:
|
670
|
-
#* Array of CIDR addresses as Strings, or Array of NetAddr::CIDR objects
|
525
|
+
#* list = Array of CIDR addresses as Strings, or Array of NetAddr::CIDR objects
|
671
526
|
#
|
672
527
|
#===Returns:
|
673
528
|
#* Array of Strings, or Array of NetAddr::CIDR objects
|
@@ -695,7 +550,7 @@ def sort(list)
|
|
695
550
|
new_cidr = cidr
|
696
551
|
end
|
697
552
|
cidr_hash[new_cidr] = cidr
|
698
|
-
|
553
|
+
|
699
554
|
version = new_cidr.version if (!version)
|
700
555
|
unless (new_cidr.version == version)
|
701
556
|
raise VersionError, "Provided CIDR addresses must all be of the same IP version."
|
@@ -703,22 +558,8 @@ def sort(list)
|
|
703
558
|
end
|
704
559
|
|
705
560
|
# sort by network. if networks are equal, sort by netmask.
|
706
|
-
sorted_list =
|
707
|
-
|
708
|
-
index = 0
|
709
|
-
sorted_list.each do
|
710
|
-
if(entry.packed_network < (sorted_list[index]).packed_network)
|
711
|
-
break
|
712
|
-
elsif (entry.packed_network == (sorted_list[index]).packed_network)
|
713
|
-
if (entry.packed_netmask < (sorted_list[index]).packed_netmask)
|
714
|
-
break
|
715
|
-
end
|
716
|
-
end
|
717
|
-
index += 1
|
718
|
-
end
|
719
|
-
sorted_list.insert(index, entry)
|
720
|
-
end
|
721
|
-
|
561
|
+
sorted_list = cidr_sort(cidr_hash.keys)
|
562
|
+
|
722
563
|
# return original values passed
|
723
564
|
ret_list = []
|
724
565
|
sorted_list.each {|x| ret_list.push(cidr_hash[x])}
|
@@ -732,16 +573,16 @@ module_function :sort
|
|
732
573
|
#==============================================================================#
|
733
574
|
|
734
575
|
#===Synopsis
|
735
|
-
#Unack a packed IP address back into a printable string.
|
736
|
-
#
|
576
|
+
#Unack a packed IP address back into a printable string. This method will attempt to auto-detect the IP version
|
577
|
+
#if not provided, however a slight speed increase is realized if version is provided.
|
737
578
|
#
|
738
579
|
# Example:
|
739
|
-
#
|
740
|
-
#
|
580
|
+
# NetAddr.unpack_ip_addr(3232235906)
|
581
|
+
# NetAddr.unpack_ip_addr(packed, :Version => 6)
|
741
582
|
#
|
742
583
|
#===Arguments:
|
743
|
-
#* Packed IP address as an Integer
|
744
|
-
#*
|
584
|
+
#* packed_ip = Packed IP address as an Integer
|
585
|
+
#* options = Hash with the following keys:
|
745
586
|
# :Version -- IP version - Integer (optional)
|
746
587
|
# :IPv4Mapped -- if true, unpack IPv6 as an IPv4 mapped address (optional)
|
747
588
|
#
|
@@ -751,74 +592,30 @@ module_function :sort
|
|
751
592
|
def unpack_ip_addr(packed_ip, options=nil)
|
752
593
|
known_args = [:Version, :IPv4Mapped]
|
753
594
|
ipv4_mapped = false
|
754
|
-
|
755
|
-
|
595
|
+
version = nil
|
596
|
+
|
756
597
|
# validate options
|
757
598
|
if (options)
|
758
599
|
raise ArgumentError, "Hash expected for argument 'options' but #{options.class} provided." if (!options.kind_of?(Hash))
|
759
600
|
NetAddr.validate_args(options.keys,known_args)
|
760
|
-
|
601
|
+
|
761
602
|
if (options.has_key?(:Version))
|
762
603
|
version = options[:Version]
|
763
|
-
to_validate[:Version] = version
|
764
604
|
if (version != 4 && version != 6)
|
765
605
|
raise VersionError, ":Version should be 4 or 6, but was '#{version}'."
|
766
606
|
end
|
767
607
|
end
|
768
|
-
|
608
|
+
|
769
609
|
if (options.has_key?(:IPv4Mapped) && options[:IPv4Mapped] == true)
|
770
610
|
ipv4_mapped = true
|
771
611
|
end
|
772
612
|
end
|
773
|
-
|
774
|
-
# validate
|
613
|
+
|
614
|
+
# validate & unpack
|
775
615
|
raise ArgumentError, "Integer expected for argument 'packed_ip', " +
|
776
616
|
"but #{packed_ip.class} provided." if (!packed_ip.kind_of?(Integer))
|
777
|
-
|
778
|
-
|
779
|
-
# set version if not set
|
780
|
-
if (!version)
|
781
|
-
if (packed_ip < 2**32)
|
782
|
-
version = 4
|
783
|
-
else
|
784
|
-
version = 6
|
785
|
-
end
|
786
|
-
end
|
787
|
-
|
788
|
-
if (version == 4)
|
789
|
-
octets = []
|
790
|
-
4.times do
|
791
|
-
octet = packed_ip & 0xFF
|
792
|
-
octets.unshift(octet.to_s)
|
793
|
-
packed_ip = packed_ip >> 8
|
794
|
-
end
|
795
|
-
ip = octets.join('.')
|
796
|
-
else
|
797
|
-
fields = []
|
798
|
-
if (!ipv4_mapped)
|
799
|
-
loop_count = 8
|
800
|
-
else
|
801
|
-
loop_count = 6
|
802
|
-
packed_v4 = packed_ip & 0xffffffff
|
803
|
-
ipv4_addr = NetAddr.unpack_ip_addr(packed_v4, :Version => 4)
|
804
|
-
fields.unshift(ipv4_addr)
|
805
|
-
packed_ip = packed_ip >> 32
|
806
|
-
end
|
807
|
-
|
808
|
-
loop_count.times do
|
809
|
-
octet = packed_ip & 0xFFFF
|
810
|
-
octet = octet.to_s(16)
|
811
|
-
packed_ip = packed_ip >> 16
|
812
|
-
|
813
|
-
# if octet < 4 characters, then pad with 0's
|
814
|
-
(4 - octet.length).times do
|
815
|
-
octet = '0' << octet
|
816
|
-
end
|
817
|
-
fields.unshift(octet)
|
818
|
-
end
|
819
|
-
ip = fields.join(':')
|
820
|
-
end
|
821
|
-
|
617
|
+
version = validate_ip_int(packed_ip, version)
|
618
|
+
ip = ip_int_to_str(packed_ip, version, ipv4_mapped)
|
822
619
|
|
823
620
|
return(ip)
|
824
621
|
end
|
@@ -833,40 +630,24 @@ module_function :unpack_ip_addr
|
|
833
630
|
#bits in the CIDR mask.
|
834
631
|
#
|
835
632
|
# Example:
|
836
|
-
#
|
633
|
+
# NetAddr.unpack_ip_netmask(0xfffffffe)
|
837
634
|
#
|
838
635
|
#===Arguments:
|
839
|
-
#* Packed netmask as an Integer
|
636
|
+
#* packed_netmask = Packed netmask as an Integer
|
840
637
|
#
|
841
638
|
#===Returns:
|
842
639
|
#* Integer
|
843
640
|
#
|
844
641
|
def unpack_ip_netmask(packed_netmask)
|
845
|
-
|
642
|
+
|
846
643
|
# validate packed_netmask
|
847
644
|
raise ArgumentError, "Integer expected for argument 'packed_netmask', " +
|
848
645
|
"but #{packed_netmask.class} provided." if (!packed_netmask.kind_of?(Integer))
|
849
|
-
|
850
|
-
if (packed_netmask < 2**32)
|
851
|
-
mask = 32
|
852
|
-
NetAddr.validate_ip_netmask(packed_netmask, :Packed => true, :Version => 4)
|
853
|
-
else
|
854
|
-
NetAddr.validate_ip_netmask(packed_netmask, :Packed => true, :Version => 6)
|
855
|
-
mask = 128
|
856
|
-
end
|
857
|
-
|
858
|
-
|
859
|
-
mask.times do
|
860
|
-
if ( (packed_netmask & 1) == 1)
|
861
|
-
break
|
862
|
-
end
|
863
|
-
packed_netmask = packed_netmask >> 1
|
864
|
-
mask = mask - 1
|
865
|
-
end
|
866
646
|
|
867
|
-
|
647
|
+
|
648
|
+
return( mask_to_bits(packed_netmask) )
|
868
649
|
end
|
869
|
-
module_function :unpack_ip_netmask
|
650
|
+
module_function :unpack_ip_netmask
|
870
651
|
|
871
652
|
#==============================================================================#
|
872
653
|
# unshorten()
|
@@ -877,10 +658,10 @@ module_function :unpack_ip_netmask
|
|
877
658
|
#notation. The address should not contain a netmask.
|
878
659
|
#
|
879
660
|
# Example:
|
880
|
-
#
|
661
|
+
# NetAddr.unshorten('fec0::1')
|
881
662
|
#
|
882
663
|
#===Arguments:
|
883
|
-
#* CIDR address as a String
|
664
|
+
#* ip = CIDR address as a String
|
884
665
|
#
|
885
666
|
#===Returns:
|
886
667
|
#* String
|
@@ -892,16 +673,16 @@ def unshorten(ip)
|
|
892
673
|
raise ArgumentError, "Expected String, but #{ip.class} provided."
|
893
674
|
end
|
894
675
|
|
895
|
-
|
676
|
+
validate_ip_str(ip, 6)
|
896
677
|
ipv4_mapped = true if (ip =~ /\./)
|
897
|
-
|
898
|
-
packed = pack_ip_addr(ip, :Version => 6)
|
678
|
+
|
679
|
+
packed = pack_ip_addr(ip, :Version => 6)
|
899
680
|
if (!ipv4_mapped)
|
900
|
-
long =
|
681
|
+
long = ip_int_to_str(packed, 6)
|
901
682
|
else
|
902
|
-
long =
|
683
|
+
long = ip_int_to_str(packed, 6, true)
|
903
684
|
end
|
904
|
-
|
685
|
+
|
905
686
|
return(long)
|
906
687
|
end
|
907
688
|
module_function :unshorten
|
@@ -917,7 +698,7 @@ module_function :unshorten
|
|
917
698
|
# NetAddr.validate_eui('01-00-5e-12-34-56')
|
918
699
|
#
|
919
700
|
# - Arguments
|
920
|
-
#* EUI address as a String
|
701
|
+
#* eui = EUI address as a String
|
921
702
|
#
|
922
703
|
#===Returns:
|
923
704
|
#* True
|
@@ -925,10 +706,10 @@ module_function :unshorten
|
|
925
706
|
def validate_eui(eui)
|
926
707
|
if (eui.kind_of?(String))
|
927
708
|
# check for invalid characters
|
928
|
-
if (eui =~ /[^0-9a-fA-
|
709
|
+
if (eui =~ /[^0-9a-fA-F\.\-\:]/)
|
929
710
|
raise ValidationError, "#{eui} is invalid (contains invalid characters)."
|
930
711
|
end
|
931
|
-
|
712
|
+
|
932
713
|
# split on formatting characters & check lengths
|
933
714
|
if (eui =~ /\-/)
|
934
715
|
fields = eui.split('-')
|
@@ -965,7 +746,8 @@ module_function :validate_eui
|
|
965
746
|
|
966
747
|
#===Synopsis
|
967
748
|
#Validate an IP address. The address should not contain a netmask.
|
968
|
-
#
|
749
|
+
#This method will attempt to auto-detect the IP version
|
750
|
+
#if not provided, however a slight speed increase is realized if version is provided.
|
969
751
|
#Raises NetAddr::ValidationError on validation failure.
|
970
752
|
#
|
971
753
|
# Example:
|
@@ -977,8 +759,8 @@ module_function :validate_eui
|
|
977
759
|
# NetAddr.validate_ip_addr(2**32-1, :Version => 4)
|
978
760
|
#
|
979
761
|
#===Arguments
|
980
|
-
#* IP address as a String or Integer
|
981
|
-
#*
|
762
|
+
#* ip = IP address as a String or Integer
|
763
|
+
#* options = Hash with the following keys:
|
982
764
|
# :Version -- IP version - Integer (optional)
|
983
765
|
#
|
984
766
|
#===Returns:
|
@@ -986,12 +768,13 @@ module_function :validate_eui
|
|
986
768
|
#
|
987
769
|
def validate_ip_addr(ip, options=nil)
|
988
770
|
known_args = [:Version]
|
989
|
-
|
771
|
+
version = nil
|
772
|
+
|
990
773
|
# validate options
|
991
774
|
if (options)
|
992
775
|
raise ArgumentError, "Hash expected for argument 'options' but #{options.class} provided." if (!options.kind_of?(Hash))
|
993
776
|
NetAddr.validate_args(options.keys,known_args)
|
994
|
-
|
777
|
+
|
995
778
|
if (options.has_key?(:Version))
|
996
779
|
version = options[:Version]
|
997
780
|
if (version != 4 && version != 6)
|
@@ -999,121 +782,19 @@ def validate_ip_addr(ip, options=nil)
|
|
999
782
|
end
|
1000
783
|
end
|
1001
784
|
end
|
1002
|
-
|
785
|
+
|
1003
786
|
if ( ip.kind_of?(String) )
|
1004
|
-
|
1005
|
-
|
1006
|
-
if (ip =~ /[^0-9a-fA-F\.:]/)
|
1007
|
-
raise ValidationError, "#{ip} is invalid (contains invalid characters)."
|
1008
|
-
end
|
1009
|
-
|
1010
|
-
# determine version if not specified
|
1011
|
-
if (!version && (ip =~ /\./ && ip !~ /:/ ) )
|
1012
|
-
version = 4
|
1013
|
-
elsif (!version && ip =~ /:/)
|
1014
|
-
version = 6
|
1015
|
-
end
|
1016
|
-
|
1017
|
-
if (version == 4)
|
1018
|
-
octets = ip.split('.')
|
1019
|
-
raise ValidationError, "#{ip} is invalid (IPv4 requires (4) octets)." if (octets.length != 4)
|
1020
|
-
|
1021
|
-
# are octets in range 0..255?
|
1022
|
-
octets.each do |octet|
|
1023
|
-
raise ValidationError, "#{ip} is invalid (IPv4 dotted-decimal format " +
|
1024
|
-
"should not contain non-numeric characters)." if (octet =~ /[^0-9]/ )
|
1025
|
-
octet = octet.to_i()
|
1026
|
-
if ( (octet < 0) || (octet >= 256) )
|
1027
|
-
raise ValidationError, "#{ip} is invalid (IPv4 octets should be between 0 and 255)."
|
1028
|
-
end
|
1029
|
-
end
|
1030
|
-
|
1031
|
-
elsif (version == 6)
|
1032
|
-
# make sure we only have at most (2) colons in a row, and then only
|
1033
|
-
# (1) instance of that
|
1034
|
-
if ( (ip =~ /:{3,}/) || (ip.split("::").length > 2) )
|
1035
|
-
raise ValidationError, "#{ip} is invalid (IPv6 field separators (:) are bad)."
|
1036
|
-
end
|
1037
|
-
|
1038
|
-
# set flags
|
1039
|
-
shorthand = false
|
1040
|
-
if (ip =~ /\./)
|
1041
|
-
dotted_dec = true
|
1042
|
-
else
|
1043
|
-
dotted_dec = false
|
1044
|
-
end
|
1045
|
-
|
1046
|
-
# split up by ':'
|
1047
|
-
fields = []
|
1048
|
-
if (ip =~ /::/)
|
1049
|
-
shorthand = true
|
1050
|
-
ip.split('::').each do |x|
|
1051
|
-
fields.concat( x.split(':') )
|
1052
|
-
end
|
1053
|
-
else
|
1054
|
-
fields.concat( ip.split(':') )
|
1055
|
-
end
|
1056
|
-
|
1057
|
-
# make sure we have the correct number of fields
|
1058
|
-
if (shorthand)
|
1059
|
-
if ( (dotted_dec && fields.length > 6) || (!dotted_dec && fields.length > 7) )
|
1060
|
-
raise ValidationError, "#{ip} is invalid (IPv6 shorthand notation has " +
|
1061
|
-
"incorrect number of fields)."
|
1062
|
-
end
|
1063
|
-
else
|
1064
|
-
if ( (dotted_dec && fields.length != 7 ) || (!dotted_dec && fields.length != 8) )
|
1065
|
-
raise ValidationError, "#{ip} is invalid (IPv6 address has " +
|
1066
|
-
"incorrect number of fields)."
|
1067
|
-
end
|
1068
|
-
end
|
1069
|
-
|
1070
|
-
# if dotted_dec then validate the last field
|
1071
|
-
if (dotted_dec)
|
1072
|
-
dotted = fields.pop()
|
1073
|
-
octets = dotted.split('.')
|
1074
|
-
raise ValidationError, "#{ip} is invalid (Legacy IPv4 portion of IPv6 " +
|
1075
|
-
"address should contain (4) octets)." if (octets.length != 4)
|
1076
|
-
octets.each do |x|
|
1077
|
-
raise ValidationError, "#{ip} is invalid (egacy IPv4 portion of IPv6 " +
|
1078
|
-
"address should not contain non-numeric characters)." if (x =~ /[^0-9]/ )
|
1079
|
-
x = x.to_i
|
1080
|
-
if ( (x < 0) || (x >= 256) )
|
1081
|
-
raise ValidationError, "#{ip} is invalid (Octets of a legacy IPv4 portion of IPv6 " +
|
1082
|
-
"address should be between 0 and 255)."
|
1083
|
-
end
|
1084
|
-
end
|
1085
|
-
end
|
1086
|
-
|
1087
|
-
# validate hex fields
|
1088
|
-
fields.each do |x|
|
1089
|
-
if (x =~ /[^0-9a-fA-F]/)
|
1090
|
-
raise ValidationError, "#{ip} is invalid (IPv6 address contains invalid hex characters)."
|
1091
|
-
else
|
1092
|
-
x = x.to_i(16)
|
1093
|
-
if ( (x < 0) || (x >= 2**16) )
|
1094
|
-
raise ValidationError, "#{ip} is invalid (Fields of an IPv6 address " +
|
1095
|
-
"should be between 0x0 and 0xFFFF)."
|
1096
|
-
end
|
1097
|
-
end
|
1098
|
-
end
|
1099
|
-
|
1100
|
-
else
|
1101
|
-
raise ValidationError, "#{ip} is invalid (Did you mean to pass an Integer instead of a String?)."
|
1102
|
-
end
|
787
|
+
version = NetAddr.detect_ip_version(ip) if (!version)
|
788
|
+
NetAddr.validate_ip_str(ip,version)
|
1103
789
|
|
1104
790
|
elsif ( ip.kind_of?(Integer) )
|
1105
|
-
|
1106
|
-
|
1107
|
-
else
|
1108
|
-
raise ValidationError, "#{ip} is invalid for IPv6 (Integer is out of bounds)." if ( (ip < 0) || (ip > 2**128-1) )
|
1109
|
-
end
|
1110
|
-
|
791
|
+
NetAddr.validate_ip_int(ip,version)
|
792
|
+
|
1111
793
|
else
|
1112
794
|
raise ArgumentError, "Integer or String expected for argument 'ip' but " +
|
1113
795
|
"#{ip.class} provided." if (!ip.kind_of?(String) && !ip.kind_of?(Integer))
|
1114
796
|
end
|
1115
797
|
|
1116
|
-
|
1117
798
|
return(true)
|
1118
799
|
end
|
1119
800
|
module_function :validate_ip_addr
|
@@ -1132,8 +813,8 @@ module_function :validate_ip_addr
|
|
1132
813
|
# NetAddr.validate_ip_netmask(0xffffffff, :Packed => true)
|
1133
814
|
#
|
1134
815
|
#===Arguments:
|
1135
|
-
#* Netmask as a String or Integer
|
1136
|
-
#*
|
816
|
+
#* netmask = Netmask as a String or Integer
|
817
|
+
#* options = Hash with the following keys:
|
1137
818
|
# :Packed -- if true, the provided Netmask is a packed Integer
|
1138
819
|
# :Version -- IP version - Integer (optional)
|
1139
820
|
#
|
@@ -1144,93 +825,29 @@ def validate_ip_netmask(netmask, options=nil)
|
|
1144
825
|
known_args = [:Packed, :Version]
|
1145
826
|
packed = false
|
1146
827
|
version = 4
|
1147
|
-
|
1148
|
-
|
828
|
+
|
1149
829
|
# validate options
|
1150
830
|
if (options)
|
1151
831
|
raise ArgumentError, "Hash expected for argument 'options' but #{options.class} provided." if (!options.kind_of?(Hash))
|
1152
832
|
NetAddr.validate_args(options.keys,known_args)
|
1153
|
-
|
833
|
+
|
1154
834
|
if (options.has_key?(:Packed) && options[:Packed] == true)
|
1155
835
|
packed = true
|
1156
836
|
end
|
1157
|
-
|
837
|
+
|
1158
838
|
if (options.has_key?(:Version))
|
1159
839
|
version = options[:Version]
|
1160
840
|
if (version != 4 && version != 6)
|
1161
841
|
raise ArgumentError, ":Version should be 4 or 6, but was '#{version}'."
|
1162
|
-
elsif (version == 6)
|
1163
|
-
max_bits = 128
|
1164
|
-
else
|
1165
|
-
max_bits = 32
|
1166
842
|
end
|
1167
|
-
end
|
843
|
+
end
|
1168
844
|
end
|
1169
|
-
|
1170
|
-
if (netmask.kind_of?(String))
|
1171
|
-
if(netmask =~ /\./)
|
1172
|
-
all_f = 2**32-1
|
1173
|
-
packed_netmask = 0
|
1174
|
-
|
1175
|
-
# validate & pack extended mask
|
1176
|
-
begin
|
1177
|
-
validate_ip_addr(netmask)
|
1178
|
-
packed_netmask = pack_ip_addr(netmask)
|
1179
|
-
rescue Exception
|
1180
|
-
raise ValidationError, "#{netmask} is an improperly formed IPv4 address."
|
1181
|
-
end
|
1182
|
-
|
1183
|
-
# cycle through the bits of hostmask and compare
|
1184
|
-
# with packed_mask. when we hit the firt '1' within
|
1185
|
-
# packed_mask (our netmask boundary), xor hostmask and
|
1186
|
-
# packed_mask. the result should be all 1's. this whole
|
1187
|
-
# process is in place to make sure that we dont have
|
1188
|
-
# and crazy masks such as 255.254.255.0
|
1189
|
-
hostmask = 1
|
1190
|
-
32.times do
|
1191
|
-
check = packed_netmask & hostmask
|
1192
|
-
if ( check != 0)
|
1193
|
-
hostmask = hostmask >> 1
|
1194
|
-
unless ( (packed_netmask ^ hostmask) == all_f)
|
1195
|
-
raise ValidationError, "#{netmask} contains '1' bits within the host portion of the netmask."
|
1196
|
-
end
|
1197
|
-
break
|
1198
|
-
else
|
1199
|
-
hostmask = hostmask << 1
|
1200
|
-
hostmask = hostmask | 1
|
1201
|
-
end
|
1202
|
-
end
|
1203
|
-
|
1204
|
-
else
|
1205
|
-
# remove '/' if present
|
1206
|
-
if (netmask =~ /^\// )
|
1207
|
-
netmask[0] = " "
|
1208
|
-
netmask.lstrip!
|
1209
|
-
end
|
1210
|
-
|
1211
|
-
# check if we have any non numeric characters
|
1212
|
-
if (netmask =~ /\D/)
|
1213
|
-
raise ValidationError, "#{netmask} contains invalid characters."
|
1214
|
-
end
|
1215
|
-
|
1216
|
-
netmask = netmask.to_i
|
1217
|
-
if (netmask > max_bits || netmask == 0 )
|
1218
|
-
raise ValidationError, "Netmask, #{netmask}, is out of bounds for IPv#{version}."
|
1219
|
-
end
|
1220
845
|
|
1221
|
-
|
1222
|
-
|
846
|
+
# validate netmask
|
847
|
+
if (netmask.kind_of?(String))
|
848
|
+
validate_netmask_str(netmask,version)
|
1223
849
|
elsif (netmask.kind_of?(Integer) )
|
1224
|
-
|
1225
|
-
if (netmask > max_bits || netmask == 0 )
|
1226
|
-
raise ValidationError, "Netmask, #{netmask}, is out of bounds for IPv#{version}."
|
1227
|
-
end
|
1228
|
-
else
|
1229
|
-
if (netmask >= 2**max_bits || netmask == 0 )
|
1230
|
-
raise ValidationError, "Packed netmask, #{netmask}, is out of bounds for IPv#{version}."
|
1231
|
-
end
|
1232
|
-
end
|
1233
|
-
|
850
|
+
validate_netmask_int(netmask,version,packed)
|
1234
851
|
else
|
1235
852
|
raise ArgumentError, "Integer or String expected for argument 'netmask' but " +
|
1236
853
|
"#{netmask.class} provided." if (!netmask.kind_of?(String) && !netmask.kind_of?(Integer))
|
@@ -1257,7 +874,7 @@ module_function :validate_ip_netmask
|
|
1257
874
|
# NetAddr.wildcard('fec0:1:*')
|
1258
875
|
#
|
1259
876
|
#===Arguments:
|
1260
|
-
#* Wildcard IP address as a String
|
877
|
+
#* ip = Wildcard IP address as a String
|
1261
878
|
#
|
1262
879
|
#===Returns:
|
1263
880
|
#* CIDR object
|
@@ -1276,18 +893,18 @@ def wildcard(ip)
|
|
1276
893
|
end
|
1277
894
|
octets.push(x)
|
1278
895
|
end
|
1279
|
-
|
896
|
+
|
1280
897
|
octets.length.times do
|
1281
898
|
mask = mask << 8
|
1282
899
|
mask = mask | 0xff
|
1283
900
|
end
|
1284
|
-
|
901
|
+
|
1285
902
|
until (octets.length == 4)
|
1286
903
|
octets.push('0')
|
1287
904
|
mask = mask << 8
|
1288
905
|
end
|
1289
906
|
ip = octets.join('.')
|
1290
|
-
|
907
|
+
|
1291
908
|
elsif (ip =~ /:/)
|
1292
909
|
version = 6
|
1293
910
|
fields = []
|
@@ -1314,46 +931,16 @@ def wildcard(ip)
|
|
1314
931
|
end
|
1315
932
|
ip = fields.join(':')
|
1316
933
|
end
|
1317
|
-
|
934
|
+
|
1318
935
|
# make & return cidr
|
1319
|
-
cidr =
|
936
|
+
cidr = cidr_build( version, ip_str_to_int(ip,version), mask )
|
937
|
+
|
1320
938
|
return(cidr)
|
1321
939
|
end
|
1322
940
|
module_function :wildcard
|
1323
941
|
|
1324
|
-
#==============================================================================#
|
1325
|
-
# NetStruct
|
1326
|
-
#==============================================================================#
|
1327
|
-
|
1328
|
-
#===NetStruct
|
1329
|
-
# Struct object used internally by NetAddr. It is not likely directly useful
|
1330
|
-
# to anyone.
|
1331
|
-
#
|
1332
|
-
# Description of fields:
|
1333
|
-
# * cidr - NetAddr::CIDR object
|
1334
|
-
# * parent - parent NetStruct in tree
|
1335
|
-
# * children - Array of children NetStruct objects
|
1336
|
-
#
|
1337
|
-
NetStruct = Struct.new(:cidr, :parent, :children) #:nodoc:
|
1338
|
-
|
1339
|
-
|
1340
|
-
# PRIVATE METHODS
|
1341
|
-
private
|
1342
942
|
|
1343
943
|
|
1344
|
-
#==============================================================================#
|
1345
|
-
# validate_args()
|
1346
|
-
#==============================================================================#
|
1347
|
-
|
1348
|
-
# validate options hash
|
1349
|
-
#
|
1350
|
-
def validate_args(to_validate,known_args)
|
1351
|
-
to_validate.each do |x|
|
1352
|
-
raise ArgumentError, "Unrecognized argument #{x}. Valid arguments are " +
|
1353
|
-
"#{known_args.join(',')}" if (!known_args.include?(x))
|
1354
|
-
end
|
1355
|
-
end
|
1356
|
-
module_function :validate_args
|
1357
944
|
|
1358
945
|
end # module NetAddr
|
1359
946
|
|