ipadmin 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +77 -13
- data/lib/cidr.rb +1459 -0
- data/lib/ip_admin.rb +3 -3682
- data/lib/methods.rb +2168 -0
- data/lib/tree.rb +735 -0
- data/tests/cidr_test.rb +34 -2
- data/tests/methods_test.rb +151 -102
- metadata +5 -2
data/lib/ip_admin.rb
CHANGED
@@ -4,3687 +4,8 @@
|
|
4
4
|
=end
|
5
5
|
|
6
6
|
|
7
|
-
|
7
|
+
require File.join(File.dirname(__FILE__), 'methods.rb')
|
8
|
+
require File.join(File.dirname(__FILE__), 'cidr.rb')
|
9
|
+
require File.join(File.dirname(__FILE__), 'tree.rb')
|
8
10
|
|
9
|
-
#==============================================================================#
|
10
|
-
# arpa()
|
11
|
-
#==============================================================================#
|
12
|
-
|
13
|
-
# Using the provided IPAdmin::CIDR object,
|
14
|
-
# return either an in-addr.arpa. or ip6.arpa. string. The netmask will be used
|
15
|
-
# to determine the length of the returned arpa string.
|
16
|
-
#
|
17
|
-
# - Arguments:
|
18
|
-
# * IPAdmin::CIDR object
|
19
|
-
#
|
20
|
-
# - Returns:
|
21
|
-
# * IP address in in-addr.arpa or ip6.arpa format
|
22
|
-
#
|
23
|
-
# Note:
|
24
|
-
# IPAdmin.arpa will use the original IP address passed during the initialization
|
25
|
-
# of the CIDR objects. This IP can be found using the CIDR.ip() method.
|
26
|
-
#
|
27
|
-
# Example:
|
28
|
-
# arpa = IPAdmin.arpa(cidr)
|
29
|
-
#
|
30
|
-
def arpa(object)
|
31
|
-
|
32
|
-
unless (object.kind_of? IPAdmin::CIDR)
|
33
|
-
raise ArgumentError, "Expected IPAdmin::CIDR object, " +
|
34
|
-
"but #{object.class} provided."
|
35
|
-
end
|
36
|
-
|
37
|
-
base = object.ip()
|
38
|
-
netmask = object.bits()
|
39
|
-
|
40
|
-
if (object.version == 4)
|
41
|
-
net = base.split('.')
|
42
|
-
|
43
|
-
if (netmask)
|
44
|
-
while (netmask < 32)
|
45
|
-
net.pop
|
46
|
-
netmask = netmask + 8
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
arpa = net.reverse.join('.')
|
51
|
-
arpa << ".in-addr.arpa."
|
52
|
-
|
53
|
-
elsif (object.version == 6)
|
54
|
-
fields = base.split(':')
|
55
|
-
net = []
|
56
|
-
fields.each do |field|
|
57
|
-
(field.split("")).each do |x|
|
58
|
-
net.push(x)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
if (netmask)
|
63
|
-
while (netmask < 128)
|
64
|
-
net.pop
|
65
|
-
netmask = netmask + 4
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
arpa = net.reverse.join('.')
|
70
|
-
arpa << ".ip6.arpa."
|
71
|
-
|
72
|
-
end
|
73
|
-
|
74
|
-
return(arpa)
|
75
|
-
end
|
76
|
-
module_function :arpa
|
77
|
-
|
78
|
-
#======================================#
|
79
|
-
#
|
80
|
-
#======================================#
|
81
|
-
|
82
|
-
|
83
|
-
#==============================================================================#
|
84
|
-
# compare()
|
85
|
-
#==============================================================================#
|
86
|
-
|
87
|
-
# Compare IPAdmin::CIDR or NetStruct objects, and determine if one
|
88
|
-
# is the supernet of the other.
|
89
|
-
#
|
90
|
-
# - Arguments:
|
91
|
-
# * Two IPAdmin::CIDR or NetStruct objects
|
92
|
-
#
|
93
|
-
# - Returns:
|
94
|
-
# * if one object is a subnet of another, then return an array in order of
|
95
|
-
# [supernet,subnet]
|
96
|
-
# * if both are equal, return 1
|
97
|
-
# * if neither is a supernet of the other, return nil
|
98
|
-
#
|
99
|
-
# Example:
|
100
|
-
# supernet,subnet = IPAdmin.compare(cidr1,cidr2)
|
101
|
-
#
|
102
|
-
|
103
|
-
def compare(obj1,obj2)
|
104
|
-
ret_val = nil
|
105
|
-
|
106
|
-
# validate arguments
|
107
|
-
unless ( obj1.kind_of?(IPAdmin::CIDR) ||
|
108
|
-
obj1.kind_of?(IPAdmin::NetStruct) )
|
109
|
-
raise ArgumentError, "Expected IPAdmin::CIDR or NetStruct " +
|
110
|
-
"as first argument, but #{obj1.class} provided."
|
111
|
-
end
|
112
|
-
|
113
|
-
unless ( obj2.kind_of?(IPAdmin::CIDR) ||
|
114
|
-
obj2.kind_of?(IPAdmin::NetStruct) )
|
115
|
-
raise ArgumentError, "Expected IPAdmin::CIDR or NetStruct " +
|
116
|
-
"as second argument, but #{obj2.class} provided."
|
117
|
-
end
|
118
|
-
|
119
|
-
# make sure both are same version
|
120
|
-
unless (obj1.version == obj2.version )
|
121
|
-
raise "Provided objects are of different IP versions."
|
122
|
-
end
|
123
|
-
|
124
|
-
|
125
|
-
# get network/netmask of each
|
126
|
-
objects = [obj1,obj2]
|
127
|
-
networks = []
|
128
|
-
netmasks = []
|
129
|
-
for obj in objects
|
130
|
-
if ( obj.kind_of?(IPAdmin::CIDR) )
|
131
|
-
networks.push(obj.packed_network)
|
132
|
-
netmasks.push(obj.packed_netmask)
|
133
|
-
|
134
|
-
elsif ( obj.kind_of?(IPAdmin::NetStruct) )
|
135
|
-
networks.push(obj.network)
|
136
|
-
netmasks.push(obj.netmask)
|
137
|
-
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
# return 1's if objects are equal otherwise
|
142
|
-
# whichever netmask is smaller will be the supernet.
|
143
|
-
# if we '&' both networks by the supernet, and they are
|
144
|
-
# equal, then the supernet is the parent of the other network
|
145
|
-
if ( (networks[0] == networks[1]) && (netmasks[0] == netmasks[1]) )
|
146
|
-
ret_val = 1
|
147
|
-
elsif (netmasks[0] < netmasks[1])
|
148
|
-
if ( (netmasks[0] & networks[0]) == (netmasks[0] & networks[1]) )
|
149
|
-
ret_val = [obj1,obj2]
|
150
|
-
end
|
151
|
-
elsif (netmasks[1] < netmasks[0])
|
152
|
-
if ( (netmasks[1] & networks[0]) == (netmasks[1] & networks[1]) )
|
153
|
-
ret_val = [obj2,obj1]
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
return(ret_val)
|
158
|
-
end
|
159
|
-
module_function :compare
|
160
|
-
|
161
|
-
#======================================#
|
162
|
-
#
|
163
|
-
#======================================#
|
164
|
-
|
165
|
-
|
166
|
-
#==============================================================================#
|
167
|
-
# create_net_struct()
|
168
|
-
#==============================================================================#
|
169
|
-
|
170
|
-
# Create an IPAdmin::NetStruct object from an IPAdmin::CIDR or NetStruct object.
|
171
|
-
# This type of Struct is used internally for various tasks, and is not likely
|
172
|
-
# to be useful to anyone.
|
173
|
-
#
|
174
|
-
# - Arguments:
|
175
|
-
# * IPAdmin::CIDR or NetStruct object
|
176
|
-
#
|
177
|
-
# - Returns:
|
178
|
-
# * IPAdmin::NetStruct object
|
179
|
-
#
|
180
|
-
# Example:
|
181
|
-
# net_struct = IPAdmin.create_net_struct(object)
|
182
|
-
#
|
183
|
-
def create_net_struct(object)
|
184
|
-
|
185
|
-
if ( object.kind_of?(IPAdmin::CIDR) )
|
186
|
-
network = object.packed_ip
|
187
|
-
netmask = object.packed_netmask
|
188
|
-
|
189
|
-
elsif ( object.kind_of?(IPAdmin::NetStruct) )
|
190
|
-
network = object.network
|
191
|
-
netmask = object.netmask
|
192
|
-
else
|
193
|
-
raise ArgumentError, "Expected IPAdmin::CIDR or NetStruct "+
|
194
|
-
"object, but #{object.class} provided."
|
195
|
-
end
|
196
|
-
|
197
|
-
version = object.version
|
198
|
-
net_struct = NetStruct.new(network,netmask,version,object,[])
|
199
|
-
|
200
|
-
return(net_struct)
|
201
|
-
end
|
202
|
-
module_function :create_net_struct
|
203
|
-
|
204
|
-
#======================================#
|
205
|
-
#
|
206
|
-
#======================================#
|
207
|
-
|
208
|
-
|
209
|
-
#==============================================================================#
|
210
|
-
# merge()
|
211
|
-
#==============================================================================#
|
212
|
-
|
213
|
-
# Given a list of IPAdmin::CIDR or NetStruct objects
|
214
|
-
# merge (supernet) them in the most efficient way possible. Supernetting
|
215
|
-
# will only occur when the newly created supernet will not result in the
|
216
|
-
# 'creation' of additional space. For example the following blocks
|
217
|
-
# (192.168.0.0/24, 192.168.1.0/24, and 192.168.2.0/24) would merge into
|
218
|
-
# 192.168.0.0/23 and 192.168.2.0/24 rather than into 192.168.0.0/22
|
219
|
-
#
|
220
|
-
# - Arguments:
|
221
|
-
# * Hash with the following fields:
|
222
|
-
# - :List -- Array of IPAdmin::CIDR or NetStruct objects
|
223
|
-
# - :NetStruct -- if true, return IPAdmin::NetStruct objects (optional)
|
224
|
-
# - :Objectify -- if true, return IPAdmin::CIDR objects (optional)
|
225
|
-
# - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
|
226
|
-
#
|
227
|
-
# - Returns:
|
228
|
-
# * Array of IPAdmin::CIDR or IPAdmin::NetStruct objects
|
229
|
-
#
|
230
|
-
# Example:
|
231
|
-
# supernets = IPAdmin.merge(:List => list)
|
232
|
-
#
|
233
|
-
def merge(options)
|
234
|
-
version = nil
|
235
|
-
short = false
|
236
|
-
objectify = false
|
237
|
-
|
238
|
-
# validate options
|
239
|
-
unless ( options.kind_of?(Hash) )
|
240
|
-
raise ArgumentError, "Hash expected but #{options.class} provided."
|
241
|
-
end
|
242
|
-
|
243
|
-
unless (options[:List])
|
244
|
-
raise ArgumentError, "Missing argument: List."
|
245
|
-
end
|
246
|
-
|
247
|
-
if (options[:Short])
|
248
|
-
short = true
|
249
|
-
end
|
250
|
-
|
251
|
-
if (options[:Objectify])
|
252
|
-
objectify = true
|
253
|
-
end
|
254
|
-
|
255
|
-
list = options[:List]
|
256
|
-
ret_struct = 1 if (options[:NetStruct])
|
257
|
-
|
258
|
-
# make sure list is an array
|
259
|
-
unless ( list.kind_of?(Array) )
|
260
|
-
raise ArgumentError, "Expected Array for option :List, " +
|
261
|
-
"but #{list.class} provided."
|
262
|
-
end
|
263
|
-
|
264
|
-
# make sure all are valid types of the same IP version
|
265
|
-
list.each do |obj|
|
266
|
-
unless (obj.kind_of?(IPAdmin::CIDR) || obj.kind_of?(IPAdmin::NetStruct) )
|
267
|
-
raise ArgumentError, "Expected IPAdmin::CIDR or NetStruct " +
|
268
|
-
"object but #{obj.class} provided."
|
269
|
-
end
|
270
|
-
|
271
|
-
version = obj.version if (!version)
|
272
|
-
unless (obj.version == version)
|
273
|
-
raise "Provided objects must be of the same IP version."
|
274
|
-
end
|
275
|
-
end
|
276
|
-
|
277
|
-
# make all_f
|
278
|
-
if (version == 4)
|
279
|
-
all_f = 2**32 - 1
|
280
|
-
else
|
281
|
-
all_f = 2**128 - 1
|
282
|
-
end
|
283
|
-
|
284
|
-
# make sure that our list is in order, and contains no duplicates
|
285
|
-
list = IPAdmin.sort(list)
|
286
|
-
index = 0
|
287
|
-
(list.length).times do
|
288
|
-
if ((index > 0)&&(IPAdmin.compare(list[index],list[index-1]) == 1))
|
289
|
-
list.delete_at(index)
|
290
|
-
else
|
291
|
-
index += 1
|
292
|
-
end
|
293
|
-
end
|
294
|
-
|
295
|
-
# create supernet_list from list
|
296
|
-
supernet_list = []
|
297
|
-
list.each do |obj|
|
298
|
-
if (obj.kind_of?(IPAdmin::CIDR))
|
299
|
-
supernet_list.push(IPAdmin.create_net_struct(obj))
|
300
|
-
|
301
|
-
elsif ( obj.kind_of?(IPAdmin::NetStruct) )
|
302
|
-
if (obj.subnets && obj.subnets.length > 0)
|
303
|
-
# get all child subnets of this branch entry
|
304
|
-
children = merge(:List => obj.subnets, :NetStruct => 1)
|
305
|
-
|
306
|
-
# if any child subnets are equal to the parent subnet
|
307
|
-
# then copy the grandchild subnets and delete the child
|
308
|
-
children.each do |child|
|
309
|
-
if ( (obj.network == child.network) &&
|
310
|
-
(obj.netmask == child.netmask) )
|
311
|
-
if (child.subnets && child.subnets.length > 0)
|
312
|
-
grandchildren = child.subnets
|
313
|
-
children.concat(grandchildren)
|
314
|
-
end
|
315
|
-
children.delete(child)
|
316
|
-
children = IPAdmin.sort(children)
|
317
|
-
break
|
318
|
-
end
|
319
|
-
end
|
320
|
-
|
321
|
-
obj.subnets.clear
|
322
|
-
obj.subnets.concat(children)
|
323
|
-
end
|
324
|
-
|
325
|
-
supernet_list.push(obj)
|
326
|
-
end
|
327
|
-
end
|
328
|
-
|
329
|
-
# merge subnets of this new branch by removing them from 'supernet_list',
|
330
|
-
# and categorizing them into hash of arrays (key=netmask)
|
331
|
-
# within each categorization we merge contiguous subnets
|
332
|
-
# and then remove them from that category & put them back into
|
333
|
-
# 'supernet_list'. we do this until our list stops getting any shorter
|
334
|
-
categories = {}
|
335
|
-
supernet_list_length = 0
|
336
|
-
until (supernet_list_length == supernet_list.length)
|
337
|
-
supernet_list_length = supernet_list.length
|
338
|
-
|
339
|
-
# categorize
|
340
|
-
supernet_list.each do |entry|
|
341
|
-
netmask = entry.netmask
|
342
|
-
if (categories.has_key?(netmask) )
|
343
|
-
(categories[netmask]).push(entry)
|
344
|
-
else
|
345
|
-
categories[netmask] = [entry]
|
346
|
-
end
|
347
|
-
end
|
348
|
-
supernet_list.clear
|
349
|
-
|
350
|
-
categories.each_key do |netmask|
|
351
|
-
entries = categories[netmask]
|
352
|
-
bitstep = (all_f + 1) - netmask
|
353
|
-
|
354
|
-
# this entire process depends on the list being in order
|
355
|
-
until (entries.length == 0)
|
356
|
-
to_merge = []
|
357
|
-
multiplier = 1
|
358
|
-
first = entries[0]
|
359
|
-
num_required = 2**multiplier
|
360
|
-
supermask = (netmask << multiplier) & all_f
|
361
|
-
supernet = supermask & first.network
|
362
|
-
if (first.network == supernet)
|
363
|
-
# take first entry and use it to form a base
|
364
|
-
# supernet address. this supernet should have
|
365
|
-
# x number of subnets in it, so we look for
|
366
|
-
# those subnets & if found store them
|
367
|
-
expected = first.network
|
368
|
-
entries.each do |entry|
|
369
|
-
if ( (entry.network == expected) && (first.network == supernet) )
|
370
|
-
to_merge.push(entry)
|
371
|
-
expected = expected + bitstep
|
372
|
-
if ( (to_merge.length == num_required) &&
|
373
|
-
(entries.length > num_required ) )
|
374
|
-
multiplier += 1
|
375
|
-
num_required = 2**multiplier
|
376
|
-
supermask = (netmask << multiplier) & all_f
|
377
|
-
supernet = supermask & first.network
|
378
|
-
end
|
379
|
-
else
|
380
|
-
# if entry is a duplicate, then kill it
|
381
|
-
if (IPAdmin.compare(entry,to_merge.last) == 1)
|
382
|
-
entries.delete(entry)
|
383
|
-
end
|
384
|
-
break
|
385
|
-
end
|
386
|
-
end
|
387
|
-
else
|
388
|
-
to_merge.push(first)
|
389
|
-
end
|
390
|
-
|
391
|
-
# make sure we actually have all of our subnets
|
392
|
-
# create our new supernet
|
393
|
-
unless (to_merge.length == num_required)
|
394
|
-
multiplier -= 1
|
395
|
-
supermask = (netmask << multiplier) & all_f
|
396
|
-
supernet = supermask & first.network
|
397
|
-
end
|
398
|
-
net_struct = NetStruct.new(supernet,supermask,version,nil,[])
|
399
|
-
|
400
|
-
# now that we have the child members of our new supernet
|
401
|
-
# take any grandchild subnets that they may have and
|
402
|
-
# add them to the new supernet. then kill the children
|
403
|
-
(2**multiplier).times do
|
404
|
-
to_kill = to_merge.shift
|
405
|
-
net_struct.subnets.concat(to_kill.subnets) if (to_kill.subnets)
|
406
|
-
entries.delete(to_kill)
|
407
|
-
end
|
408
|
-
supernet_list.push(net_struct)
|
409
|
-
end
|
410
|
-
end
|
411
|
-
categories.clear
|
412
|
-
supernet_list = IPAdmin.sort(supernet_list)
|
413
|
-
end
|
414
|
-
|
415
|
-
# if '!ret_struct', return CIDR's
|
416
|
-
if (!ret_struct)
|
417
|
-
supernets = []
|
418
|
-
supernet_list.each do |entry|
|
419
|
-
if (version == 4)
|
420
|
-
network = IPAdmin.unpack_ipv4_addr(entry.network)
|
421
|
-
netmask = IPAdmin.unpack_ipv4_netmask(entry.netmask)
|
422
|
-
else
|
423
|
-
network = IPAdmin.unpack_ipv6_addr(entry.network)
|
424
|
-
network = IPAdmin.shorten(network) if (short && !objectify)
|
425
|
-
netmask = IPAdmin.unpack_ipv6_netmask(entry.netmask)
|
426
|
-
end
|
427
|
-
|
428
|
-
if (!objectify)
|
429
|
-
supernets.push("#{network}/#{netmask}")
|
430
|
-
else
|
431
|
-
supernets.push(IPAdmin::CIDR.new(:CIDR => "#{network}/#{netmask}"))
|
432
|
-
end
|
433
|
-
end
|
434
|
-
else
|
435
|
-
supernets = supernet_list
|
436
|
-
end
|
437
|
-
|
438
|
-
return(supernets)
|
439
|
-
end
|
440
|
-
module_function :merge
|
441
|
-
|
442
|
-
#======================================#
|
443
|
-
#
|
444
|
-
#======================================#
|
445
|
-
|
446
|
-
|
447
|
-
#==============================================================================#
|
448
|
-
# pack_ipv4_addr()
|
449
|
-
#==============================================================================#
|
450
|
-
|
451
|
-
# Convert IPv4 addresses into an integer. No attempt at
|
452
|
-
# validation is performed.
|
453
|
-
#
|
454
|
-
# - Arguments:
|
455
|
-
# * IPv4 address
|
456
|
-
#
|
457
|
-
# - Returns:
|
458
|
-
# * packed IPv4 address or exception on error.
|
459
|
-
#
|
460
|
-
# Example:
|
461
|
-
# packed = IPAdmin.pack_ipv4_addr('192.168.1.1')
|
462
|
-
#
|
463
|
-
def pack_ipv4_addr(ip)
|
464
|
-
|
465
|
-
# is this a string?
|
466
|
-
unless (ip.kind_of? String)
|
467
|
-
raise ArgumentError, "Expected String, but #{ip.class} provided."
|
468
|
-
end
|
469
|
-
|
470
|
-
# pack our ip
|
471
|
-
octets = ip.split( /\./ ).reverse
|
472
|
-
packed_ip = 0
|
473
|
-
|
474
|
-
(0..3).each do |x|
|
475
|
-
octets[x] = (octets[x]).to_i
|
476
|
-
octets[x] = octets[x] << 8*x
|
477
|
-
packed_ip = packed_ip | octets[x]
|
478
|
-
end
|
479
|
-
|
480
|
-
return(packed_ip)
|
481
|
-
end
|
482
|
-
module_function :pack_ipv4_addr
|
483
|
-
|
484
|
-
#======================================#
|
485
|
-
#
|
486
|
-
#======================================#
|
487
|
-
|
488
|
-
|
489
|
-
#==============================================================================#
|
490
|
-
# pack_ipv4_netmask()
|
491
|
-
#==============================================================================#
|
492
|
-
|
493
|
-
# Convert IPv4 netmask into an integer. Only very basic
|
494
|
-
# validation is performed.
|
495
|
-
#
|
496
|
-
# - Arguments:
|
497
|
-
# * IPv4 netmask in cidr or extended notation
|
498
|
-
#
|
499
|
-
# - Returns:
|
500
|
-
# * packed IPv4 netmask or exception on error.
|
501
|
-
#
|
502
|
-
# Example:
|
503
|
-
# packed = IPAdmin.pack_ipv4_netmask('255.255.255.0')
|
504
|
-
# packed = IPAdmin.pack_ipv4_netmask('24')
|
505
|
-
# packed = IPAdmin.pack_ipv4_netmask('/24')
|
506
|
-
# packed = IPAdmin.pack_ipv4_netmask(24)
|
507
|
-
#
|
508
|
-
def pack_ipv4_netmask(netmask)
|
509
|
-
all_f = 2**32-1
|
510
|
-
|
511
|
-
# is this a CIDR or Extended mask?
|
512
|
-
if(netmask =~ /\./)
|
513
|
-
# pack extended mask
|
514
|
-
begin
|
515
|
-
packed_netmask = pack_ipv4_addr(netmask)
|
516
|
-
rescue Exception
|
517
|
-
raise "#{netmask} is not a valid IPv4 netmask."
|
518
|
-
end
|
519
|
-
|
520
|
-
else
|
521
|
-
# remove '/' if present
|
522
|
-
if (netmask =~ /^\// )
|
523
|
-
netmask[0] = " "
|
524
|
-
netmask.lstrip!
|
525
|
-
end
|
526
|
-
|
527
|
-
# check if we have any non numeric characters
|
528
|
-
if (netmask =~ /\D/)
|
529
|
-
raise "#{netmask} is not a valid IPv4 netmask."
|
530
|
-
end
|
531
|
-
|
532
|
-
if (netmask.kind_of? String)
|
533
|
-
netmask = netmask.to_i
|
534
|
-
end
|
535
|
-
|
536
|
-
packed_netmask = all_f ^ (all_f >> netmask)
|
537
|
-
end
|
538
|
-
|
539
|
-
return(packed_netmask)
|
540
|
-
end
|
541
|
-
module_function :pack_ipv4_netmask
|
542
|
-
|
543
|
-
#======================================#
|
544
|
-
#
|
545
|
-
#======================================#
|
546
|
-
|
547
|
-
|
548
|
-
#==============================================================================#
|
549
|
-
# pack_ipv6_addr()
|
550
|
-
#==============================================================================#
|
551
|
-
|
552
|
-
# Convert IPv6 addresses into an integer. No attempt at
|
553
|
-
# validation is performed.
|
554
|
-
#
|
555
|
-
# - Arguments:
|
556
|
-
# * IPv6 address
|
557
|
-
#
|
558
|
-
# - Returns:
|
559
|
-
# * packed IPv6 address or exception on error.
|
560
|
-
#
|
561
|
-
# Example:
|
562
|
-
# packed = IPAdmin.pack_ipv6_addr('fec0::1')
|
563
|
-
#
|
564
|
-
def pack_ipv6_addr(ip)
|
565
|
-
# is this a string?
|
566
|
-
unless (ip.kind_of? String)
|
567
|
-
raise ArgumentError, "Expected String, but #{ip.class} provided."
|
568
|
-
end
|
569
|
-
|
570
|
-
# look for a '::' to see if this address is in shorthand
|
571
|
-
# if found, split on it
|
572
|
-
hex_fields = []
|
573
|
-
if (ip =~ /::/)
|
574
|
-
shrthnd = ip.split( /::/ )
|
575
|
-
if (ip =~ /^::/)
|
576
|
-
sec_half = shrthnd[1].split( /:/ )
|
577
|
-
zero_pads = 8 - sec_half.length
|
578
|
-
(1..zero_pads).each {hex_fields.push('0')}
|
579
|
-
sec_half.each {|field| hex_fields.push(field)}
|
580
|
-
elsif (ip =~ /::$/)
|
581
|
-
hex_fields = shrthnd[0].split( /:/ )
|
582
|
-
zero_pads = 8 - hex_fields.length
|
583
|
-
(1..zero_pads).each {hex_fields.push('0')}
|
584
|
-
else
|
585
|
-
first_half = shrthnd[0].split( /:/ )
|
586
|
-
sec_half = shrthnd[1].split( /:/ )
|
587
|
-
zero_pads = 8 - (first_half.length + sec_half.length)
|
588
|
-
first_half.each {|field| hex_fields.push(field)}
|
589
|
-
(1..zero_pads).each {hex_fields.push('0')}
|
590
|
-
sec_half.each {|field| hex_fields.push(field)}
|
591
|
-
end
|
592
|
-
|
593
|
-
else
|
594
|
-
hex_fields = ip.split( /:/ )
|
595
|
-
end
|
596
|
-
|
597
|
-
# pack
|
598
|
-
hex_fields.reverse!
|
599
|
-
packed_ip = 0
|
600
|
-
(0..7).each do |x|
|
601
|
-
hex = hex_fields[x]
|
602
|
-
base16 = hex.to_i(16)
|
603
|
-
|
604
|
-
base16 = base16 << 16*x
|
605
|
-
packed_ip = packed_ip | base16
|
606
|
-
end
|
607
|
-
|
608
|
-
|
609
|
-
return(packed_ip)
|
610
|
-
end
|
611
|
-
module_function :pack_ipv6_addr
|
612
|
-
|
613
|
-
#======================================#
|
614
|
-
#
|
615
|
-
#======================================#
|
616
|
-
|
617
|
-
|
618
|
-
#==============================================================================#
|
619
|
-
# pack_ipv6_netmask()
|
620
|
-
#==============================================================================#
|
621
|
-
|
622
|
-
# Convert IPv6 netmask into an integer. Only very basic
|
623
|
-
# validation is performed.
|
624
|
-
#
|
625
|
-
# - Arguments:
|
626
|
-
# * IPv6 netmask in cidr notation
|
627
|
-
#
|
628
|
-
# - Returns:
|
629
|
-
# * packed IPv6 netmask or exception on error.
|
630
|
-
#
|
631
|
-
# Example:
|
632
|
-
# packed = IPAdmin.pack_ipv6_netmask('64')
|
633
|
-
# packed = IPAdmin.pack_ipv6_netmask('/64')
|
634
|
-
# packed = IPAdmin.pack_ipv6_netmask(64)
|
635
|
-
#
|
636
|
-
def pack_ipv6_netmask(netmask)
|
637
|
-
all_f = 2**128-1
|
638
|
-
|
639
|
-
# remove '/' if present
|
640
|
-
if (netmask =~ /^\// )
|
641
|
-
netmask[0] = " "
|
642
|
-
netmask.lstrip!
|
643
|
-
end
|
644
|
-
|
645
|
-
if (netmask !~ /\D/)
|
646
|
-
# pack
|
647
|
-
if (netmask.kind_of? String)
|
648
|
-
netmask = netmask.to_i
|
649
|
-
end
|
650
|
-
|
651
|
-
packed_netmask = all_f ^ (all_f >> netmask)
|
652
|
-
|
653
|
-
else
|
654
|
-
raise "#{netmask} is not a valid IPv6 netmask."
|
655
|
-
|
656
|
-
end
|
657
|
-
|
658
|
-
return(packed_netmask)
|
659
|
-
end
|
660
|
-
module_function :pack_ipv6_netmask
|
661
|
-
|
662
|
-
#======================================#
|
663
|
-
#
|
664
|
-
#======================================#
|
665
|
-
|
666
|
-
|
667
|
-
#==============================================================================#
|
668
|
-
# range()
|
669
|
-
#==============================================================================#
|
670
|
-
|
671
|
-
# Given two IPAdmin::CIDR objects of the same version, return all IP
|
672
|
-
# addresses between them (non-inclusive).
|
673
|
-
#
|
674
|
-
# - Arguments:
|
675
|
-
# * Hash with the following fields:
|
676
|
-
# - :Bitstep -- enumerate in X sized steps (optional)
|
677
|
-
# - :Boundaries -- array of (2) IPAdmin::CIDR objects
|
678
|
-
# - :Limit -- limit returned list to X number of items (optional)
|
679
|
-
# - :Objectify -- if true, return IPAdmin::CIDR objects (optional)
|
680
|
-
# - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
|
681
|
-
#
|
682
|
-
# - Returns:
|
683
|
-
# * Array of IP addresses or IPAdmin::CIDR objects
|
684
|
-
#
|
685
|
-
# Note:
|
686
|
-
# IPAdmin.range will use the original IP address passed during the initialization
|
687
|
-
# of the CIDR objects. This IP can be found using the CIDR.ip() method.
|
688
|
-
#
|
689
|
-
# Example:
|
690
|
-
# list = IPAdmin.range(:Boundaries => [cidr1,cidr2])
|
691
|
-
#
|
692
|
-
def range(options)
|
693
|
-
list = []
|
694
|
-
bitstep = 1
|
695
|
-
objectify = false
|
696
|
-
short = false
|
697
|
-
limit = nil
|
698
|
-
|
699
|
-
# check options
|
700
|
-
if (options)
|
701
|
-
unless ( options.has_key?(:Boundaries) )
|
702
|
-
raise ArgumentError, "Missing argument: Boundaries."
|
703
|
-
end
|
704
|
-
|
705
|
-
if (options[:Boundaries].length == 2)
|
706
|
-
(cidr1,cidr2) = options[:Boundaries]
|
707
|
-
else
|
708
|
-
raise ArgumentError, "Two IPAdmin::CIDR ojects are required. " +
|
709
|
-
"as Boundaries."
|
710
|
-
end
|
711
|
-
|
712
|
-
if( options.has_key?(:Bitstep) )
|
713
|
-
bitstep = options[:Bitstep]
|
714
|
-
end
|
715
|
-
|
716
|
-
if( options.has_key?(:Objectify) )
|
717
|
-
objectify = true
|
718
|
-
end
|
719
|
-
|
720
|
-
if( options.has_key?(:Short) )
|
721
|
-
short = true
|
722
|
-
end
|
723
|
-
|
724
|
-
if( options.has_key?(:Limit) )
|
725
|
-
limit = options[:Limit]
|
726
|
-
end
|
727
|
-
end
|
728
|
-
|
729
|
-
# check our objects
|
730
|
-
unless (cidr1.kind_of?(IPAdmin::CIDR) && cidr2.kind_of?(IPAdmin::CIDR))
|
731
|
-
raise "One or more values provided under :Boundaries "+
|
732
|
-
"is not a valid IPAdmin::CIDR object."
|
733
|
-
end
|
734
|
-
|
735
|
-
# check version, store & sort
|
736
|
-
if (cidr1.version == cidr2.version)
|
737
|
-
version = cidr1.version
|
738
|
-
boundaries = [cidr1.packed_ip, cidr2.packed_ip]
|
739
|
-
boundaries.sort
|
740
|
-
else
|
741
|
-
raise "Provided IPAdmin::CIDR objects are of different IP versions."
|
742
|
-
end
|
743
|
-
|
744
|
-
# dump our range
|
745
|
-
if (version == 4)
|
746
|
-
my_ip = boundaries[0] + 1
|
747
|
-
|
748
|
-
until (my_ip >= boundaries[1])
|
749
|
-
if (!objectify)
|
750
|
-
list.push( IPAdmin.unpack_ipv4_addr(my_ip) )
|
751
|
-
else
|
752
|
-
my_ip_s = IPAdmin.unpack_ipv4_addr(my_ip)
|
753
|
-
list.push( IPAdmin::CIDR.new(:CIDR => my_ip_s) )
|
754
|
-
end
|
755
|
-
|
756
|
-
my_ip = my_ip + bitstep
|
757
|
-
if (limit)
|
758
|
-
limit = limit -1
|
759
|
-
break if (limit == 0)
|
760
|
-
end
|
761
|
-
end
|
762
|
-
|
763
|
-
elsif (version == 6)
|
764
|
-
my_ip = boundaries[0] + 1
|
765
|
-
|
766
|
-
until (my_ip == boundaries[1])
|
767
|
-
if (!objectify)
|
768
|
-
my_ips = IPAdmin.unpack_ipv6_addr(my_ip)
|
769
|
-
my_ips = IPAdmin.shorten(my_ips) if (short)
|
770
|
-
list.push(my_ips)
|
771
|
-
|
772
|
-
else
|
773
|
-
my_ip_s = IPAdmin.unpack_ipv6_addr(my_ip)
|
774
|
-
list.push( IPAdmin::CIDR.new(:CIDR => my_ip_s) )
|
775
|
-
end
|
776
|
-
|
777
|
-
my_ip = my_ip + bitstep
|
778
|
-
if (limit)
|
779
|
-
limit = limit -1
|
780
|
-
break if (limit == 0)
|
781
|
-
end
|
782
|
-
end
|
783
|
-
end
|
784
|
-
|
785
|
-
return(list)
|
786
|
-
end
|
787
|
-
module_function :range
|
788
|
-
|
789
|
-
#======================================#
|
790
|
-
#
|
791
|
-
#======================================#
|
792
|
-
|
793
|
-
|
794
|
-
#==============================================================================#
|
795
|
-
# shorten()
|
796
|
-
#==============================================================================#
|
797
|
-
|
798
|
-
# Take a standard IPv6 address, and format it in short-hand notation.
|
799
|
-
# The address should not contain a netmask.
|
800
|
-
#
|
801
|
-
# - Arguments:
|
802
|
-
# * IPv6 address
|
803
|
-
#
|
804
|
-
# - Returns:
|
805
|
-
# * IPv6 short-hand address.
|
806
|
-
#
|
807
|
-
# Example:
|
808
|
-
# short = IPAdmin.shorten('fec0:0000:0000:0000:0000:0000:0000:0001')
|
809
|
-
#
|
810
|
-
def shorten(addr)
|
811
|
-
|
812
|
-
# is this a string?
|
813
|
-
unless (addr.kind_of? String)
|
814
|
-
raise ArgumentError, "Expected String, but #{addr.class} provided."
|
815
|
-
end
|
816
|
-
|
817
|
-
validate_ipv6_addr(addr)
|
818
|
-
|
819
|
-
# make sure this isnt already shorthand
|
820
|
-
if (addr =~ /::/)
|
821
|
-
return(addr)
|
822
|
-
end
|
823
|
-
|
824
|
-
# look for most consecutive '0' fields
|
825
|
-
start_field,end_field = nil,nil
|
826
|
-
start_end = []
|
827
|
-
consecutive,longest = 0,0
|
828
|
-
fields = addr.split(":")
|
829
|
-
|
830
|
-
(0..(fields.length-1)).each do |x|
|
831
|
-
fields[x] = fields[x].to_i(16)
|
832
|
-
|
833
|
-
if (fields[x] == 0)
|
834
|
-
if (!start_field)
|
835
|
-
start_field = x
|
836
|
-
end_field = x
|
837
|
-
else
|
838
|
-
end_field = x
|
839
|
-
end
|
840
|
-
consecutive += 1
|
841
|
-
else
|
842
|
-
if (start_field)
|
843
|
-
if (consecutive > longest)
|
844
|
-
longest = consecutive
|
845
|
-
start_end = [start_field,end_field]
|
846
|
-
start_field,end_field = nil,nil
|
847
|
-
end
|
848
|
-
consecutive = 0
|
849
|
-
end
|
850
|
-
end
|
851
|
-
|
852
|
-
fields[x] = fields[x].to_s(16)
|
853
|
-
end
|
854
|
-
|
855
|
-
# if our longest set of 0's is at the end, then start & end fields
|
856
|
-
# are already set. if not, then make start & end fields the ones we've
|
857
|
-
# stored away in start_end
|
858
|
-
if (consecutive > longest)
|
859
|
-
longest = consecutive
|
860
|
-
else
|
861
|
-
start_field = start_end[0]
|
862
|
-
end_field = start_end[1]
|
863
|
-
end
|
864
|
-
|
865
|
-
if (longest > 1)
|
866
|
-
fields[start_field] = ''
|
867
|
-
start_field += 1
|
868
|
-
fields.slice!(start_field..end_field)
|
869
|
-
end
|
870
|
-
short = fields.join(':')
|
871
|
-
short << ':' if (short =~ /:$/)
|
872
|
-
|
873
|
-
return(short)
|
874
|
-
end
|
875
|
-
module_function :shorten
|
876
|
-
|
877
|
-
#======================================#
|
878
|
-
#
|
879
|
-
#======================================#
|
880
|
-
|
881
|
-
|
882
|
-
#==============================================================================#
|
883
|
-
# sort()
|
884
|
-
#==============================================================================#
|
885
|
-
|
886
|
-
# Given a list of IPAdmin::CIDR or NetStruct objects
|
887
|
-
# sort them from lowest to highest by Network/Netmask.
|
888
|
-
#
|
889
|
-
# - Arguments:
|
890
|
-
# * Array of IPAdmin::CIDR or NetStruct objects
|
891
|
-
#
|
892
|
-
# - Returns:
|
893
|
-
# * Sorted Array
|
894
|
-
#
|
895
|
-
# Note:
|
896
|
-
# IPAdmin.sort will use the original IP address passed during the initialization
|
897
|
-
# of the CIDR objects. This IP can be found using the CIDR.ip() method.
|
898
|
-
#
|
899
|
-
# Example:
|
900
|
-
# sorted = IPAdmin.sort(list)
|
901
|
-
#
|
902
|
-
def sort(list)
|
903
|
-
|
904
|
-
# make sure list is an array
|
905
|
-
unless ( list.kind_of?(Array) )
|
906
|
-
raise ArgumentError, "Array of IPAdmin::CIDR or NetStruct " +
|
907
|
-
"objects expected, but #{list.class} provided."
|
908
|
-
end
|
909
|
-
|
910
|
-
# make sure all are valid types of the same IP version
|
911
|
-
version = nil
|
912
|
-
list.each do |obj|
|
913
|
-
unless (obj.kind_of?(IPAdmin::CIDR) || obj.kind_of?(IPAdmin::NetStruct) )
|
914
|
-
raise ArgumentError, "Expected IPAdmin::CIDR or NetStruct " +
|
915
|
-
"object but #{obj.class} provided."
|
916
|
-
end
|
917
|
-
|
918
|
-
version = obj.version if (!version)
|
919
|
-
unless (obj.version == version)
|
920
|
-
raise "Provided objects must all be of the same IP version."
|
921
|
-
end
|
922
|
-
end
|
923
|
-
|
924
|
-
# create unsorted_list from list
|
925
|
-
unsorted_list = []
|
926
|
-
list.each do |obj|
|
927
|
-
unsorted_list.push(IPAdmin.create_net_struct(obj))
|
928
|
-
end
|
929
|
-
|
930
|
-
# sort by network. if networks are equal, sort by netmask.
|
931
|
-
sorted_list = []
|
932
|
-
unsorted_list.each do |entry|
|
933
|
-
index = 0
|
934
|
-
sorted_list.each do
|
935
|
-
if(entry.network < (sorted_list[index]).network)
|
936
|
-
break
|
937
|
-
elsif (entry.network == (sorted_list[index]).network)
|
938
|
-
if (entry.netmask < (sorted_list[index]).netmask)
|
939
|
-
break
|
940
|
-
end
|
941
|
-
end
|
942
|
-
index += 1
|
943
|
-
end
|
944
|
-
sorted_list.insert(index, entry)
|
945
|
-
end
|
946
|
-
|
947
|
-
# replace sorted_list entries with their .object
|
948
|
-
index = 0
|
949
|
-
sorted_list.length.times do
|
950
|
-
sorted_list[index] = (sorted_list[index]).object
|
951
|
-
index += 1
|
952
|
-
end
|
953
|
-
|
954
|
-
return(sorted_list)
|
955
|
-
end
|
956
|
-
module_function :sort
|
957
|
-
|
958
|
-
#======================================#
|
959
|
-
#
|
960
|
-
#======================================#
|
961
|
-
|
962
|
-
|
963
|
-
#==============================================================================#
|
964
|
-
# unpack_ipv4_addr()
|
965
|
-
#==============================================================================#
|
966
|
-
|
967
|
-
# Unack a packed IPv4 address back into a printable string. No attempt at
|
968
|
-
# validation is performed.
|
969
|
-
#
|
970
|
-
# - Arguments:
|
971
|
-
# * Byte-packed IPv4 address
|
972
|
-
#
|
973
|
-
# - Returns:
|
974
|
-
# * IPv4 address.
|
975
|
-
#
|
976
|
-
# Example:
|
977
|
-
# unpacked = IPAdmin.unpack_ipv4_addr(packed)
|
978
|
-
#
|
979
|
-
def unpack_ipv4_addr(packed_ip)
|
980
|
-
octets = []
|
981
|
-
(0..3).each do |x|
|
982
|
-
octets[x] = packed_ip & 0xFF
|
983
|
-
octets[x] = (octets[x]).to_s
|
984
|
-
packed_ip = packed_ip >> 8
|
985
|
-
end
|
986
|
-
|
987
|
-
octets.reverse!
|
988
|
-
ip = octets.join('.')
|
989
|
-
|
990
|
-
return(ip)
|
991
|
-
end
|
992
|
-
module_function :unpack_ipv4_addr
|
993
|
-
|
994
|
-
#======================================#
|
995
|
-
#
|
996
|
-
#======================================#
|
997
|
-
|
998
|
-
|
999
|
-
#==============================================================================#
|
1000
|
-
# unpack_ipv4_netmask()
|
1001
|
-
#==============================================================================#
|
1002
|
-
|
1003
|
-
# Unack a packed IPv4 netmask into a integer representing the number of
|
1004
|
-
# bits in the CIDR mask. No attempt at validation is performed.
|
1005
|
-
#
|
1006
|
-
# - Arguments:
|
1007
|
-
# * Byte-packed IPv4 netmask
|
1008
|
-
#
|
1009
|
-
# - Returns:
|
1010
|
-
# * IPv4 netmask as number of bits (cidr format).
|
1011
|
-
#
|
1012
|
-
# Example:
|
1013
|
-
# unpacked = IPAdmin.unpack_ipv4_netmask(packed)
|
1014
|
-
#
|
1015
|
-
def unpack_ipv4_netmask(packed_mask)
|
1016
|
-
mask = 32
|
1017
|
-
32.times do
|
1018
|
-
if ( (packed_mask & 1) != 0)
|
1019
|
-
break
|
1020
|
-
end
|
1021
|
-
packed_mask = packed_mask >> 1
|
1022
|
-
mask = mask - 1
|
1023
|
-
end
|
1024
|
-
|
1025
|
-
return(mask)
|
1026
|
-
end
|
1027
|
-
module_function :unpack_ipv4_netmask
|
1028
|
-
|
1029
|
-
#======================================#
|
1030
|
-
#
|
1031
|
-
#======================================#
|
1032
|
-
|
1033
|
-
|
1034
|
-
#==============================================================================#
|
1035
|
-
# unpack_ipv6_addr()
|
1036
|
-
#==============================================================================#
|
1037
|
-
|
1038
|
-
# Unack a packed IPv6 address back into a printable string. No attempt at
|
1039
|
-
# validation is performed.
|
1040
|
-
#
|
1041
|
-
# - Arguments:
|
1042
|
-
# * Byte-packed IPv6 address
|
1043
|
-
#
|
1044
|
-
# - Returns:
|
1045
|
-
# * IPv6 address.
|
1046
|
-
#
|
1047
|
-
# Example:
|
1048
|
-
# unpacked = IPAdmin.unpack_ipv6_addr(packed)
|
1049
|
-
#
|
1050
|
-
def unpack_ipv6_addr(packed_ip)
|
1051
|
-
hex_fields = []
|
1052
|
-
(0..7).each do |x|
|
1053
|
-
hex_fields[x] = packed_ip & 0xFFFF
|
1054
|
-
hex_fields[x] = (hex_fields[x]).to_s(16)
|
1055
|
-
packed_ip = packed_ip >> 16
|
1056
|
-
|
1057
|
-
# if hex_fields[x] < 4 characters, then pad with 0's
|
1058
|
-
(4 - hex_fields[x].length).times do
|
1059
|
-
hex_fields[x] = '0' << hex_fields[x]
|
1060
|
-
end
|
1061
|
-
end
|
1062
|
-
|
1063
|
-
hex_fields.reverse!
|
1064
|
-
ip = hex_fields.join(':')
|
1065
|
-
|
1066
|
-
return(ip)
|
1067
|
-
end
|
1068
|
-
module_function :unpack_ipv6_addr
|
1069
|
-
|
1070
|
-
#======================================#
|
1071
|
-
#
|
1072
|
-
#======================================#
|
1073
|
-
|
1074
|
-
|
1075
|
-
#==============================================================================#
|
1076
|
-
# unpack_ipv6_netmask()
|
1077
|
-
#==============================================================================#
|
1078
|
-
|
1079
|
-
# Unack a packed IPv6 netmask into a integer representing the number of
|
1080
|
-
# bits in the CIDR mask. No attempt at validation is performed.
|
1081
|
-
#
|
1082
|
-
# - Arguments:
|
1083
|
-
# * Byte-packed IPv6 netmask
|
1084
|
-
#
|
1085
|
-
# - Returns:
|
1086
|
-
# * IPv6 netmask as number of bits (cidr format).
|
1087
|
-
#
|
1088
|
-
# Example:
|
1089
|
-
# unpacked = IPAdmin.unpack_ipv6_netmask(packed)
|
1090
|
-
#
|
1091
|
-
def unpack_ipv6_netmask(packed_mask)
|
1092
|
-
|
1093
|
-
mask = 128
|
1094
|
-
128.times do
|
1095
|
-
if ( (packed_mask & 1) == 1)
|
1096
|
-
break
|
1097
|
-
end
|
1098
|
-
mask = mask - 1
|
1099
|
-
packed_mask = packed_mask >> 1
|
1100
|
-
end
|
1101
|
-
|
1102
|
-
return(mask)
|
1103
|
-
end
|
1104
|
-
module_function :unpack_ipv6_netmask
|
1105
|
-
|
1106
|
-
#======================================#
|
1107
|
-
#
|
1108
|
-
#======================================#
|
1109
|
-
|
1110
|
-
|
1111
|
-
#==============================================================================#
|
1112
|
-
# unshorten()
|
1113
|
-
#==============================================================================#
|
1114
|
-
|
1115
|
-
# Take an IPv6 address in short-hand format, and expand it into standard
|
1116
|
-
# notation. The address should not contain a netmask.
|
1117
|
-
#
|
1118
|
-
# - Arguments:
|
1119
|
-
# * IPv6 address
|
1120
|
-
#
|
1121
|
-
# - Returns:
|
1122
|
-
# * IPv6 short-hand address.
|
1123
|
-
#
|
1124
|
-
# Example:
|
1125
|
-
# long = IPAdmin.unshorten('fec0::1')
|
1126
|
-
#
|
1127
|
-
def unshorten(addr)
|
1128
|
-
|
1129
|
-
# is this a string?
|
1130
|
-
unless (addr.kind_of? String)
|
1131
|
-
raise ArgumentError, "Expected String, but #{addr.class} provided."
|
1132
|
-
end
|
1133
|
-
|
1134
|
-
packed = validate_ipv6_addr(addr)
|
1135
|
-
long = unpack_ipv6_addr(packed)
|
1136
|
-
|
1137
|
-
return(long)
|
1138
|
-
end
|
1139
|
-
module_function :unshorten
|
1140
|
-
|
1141
|
-
#======================================#
|
1142
|
-
#
|
1143
|
-
#======================================#
|
1144
|
-
|
1145
|
-
|
1146
|
-
#==============================================================================#
|
1147
|
-
# validate_ipv4_addr()
|
1148
|
-
#==============================================================================#
|
1149
|
-
|
1150
|
-
# Validate IPv4 addresses. The address should not contain a netmask.
|
1151
|
-
#
|
1152
|
-
# - Arguments:
|
1153
|
-
# * IPv4 address
|
1154
|
-
#
|
1155
|
-
# - Returns:
|
1156
|
-
# * packed IP on valid, or exception on error.
|
1157
|
-
#
|
1158
|
-
# Example:
|
1159
|
-
# IPAdmin.validate_ipv4_addr('192.168.1.1')
|
1160
|
-
#
|
1161
|
-
def validate_ipv4_addr(ip)
|
1162
|
-
|
1163
|
-
# is this a string?
|
1164
|
-
unless (ip.kind_of? String)
|
1165
|
-
raise ArgumentError, "Expected String, but #{ip.class} provided."
|
1166
|
-
end
|
1167
|
-
|
1168
|
-
# check validity of characters in the addr
|
1169
|
-
if ( (ip =~ /\.{2,}?/ ) || (ip =~ /[^0-9\.]/) )
|
1170
|
-
raise "#{ip} is not a valid IPv4 address."
|
1171
|
-
end
|
1172
|
-
|
1173
|
-
# do we have 4 octets?
|
1174
|
-
octets = ip.split( /\./ ).reverse
|
1175
|
-
if (octets.length != 4)
|
1176
|
-
raise "#{ip} is not a valid IPv4 address."
|
1177
|
-
end
|
1178
|
-
|
1179
|
-
# are octets in range 0..255?
|
1180
|
-
packed_ip = 0
|
1181
|
-
(0..3).each do |x|
|
1182
|
-
octets[x] = octets[x].to_i
|
1183
|
-
unless ( (octets[x] >= 0) && (octets[x] < 256 ) )
|
1184
|
-
raise "#{ip} is not a valid IPv4 address."
|
1185
|
-
end
|
1186
|
-
octets[x] = octets[x] << 8*x
|
1187
|
-
packed_ip = packed_ip | octets[x]
|
1188
|
-
end
|
1189
|
-
|
1190
|
-
# dont allow first octet to be 0
|
1191
|
-
if (octets[3] == 0)
|
1192
|
-
raise "#{ip} is not a valid IPv4 address."
|
1193
|
-
end
|
1194
|
-
|
1195
|
-
return(packed_ip)
|
1196
|
-
|
1197
|
-
end
|
1198
|
-
module_function :validate_ipv4_addr
|
1199
|
-
|
1200
|
-
#======================================#
|
1201
|
-
#
|
1202
|
-
#======================================#
|
1203
|
-
|
1204
|
-
|
1205
|
-
#==============================================================================#
|
1206
|
-
# validate_ipv4_netmask()
|
1207
|
-
#==============================================================================#
|
1208
|
-
|
1209
|
-
# Validate IPv4 Netmask.
|
1210
|
-
#
|
1211
|
-
# - Arguments:
|
1212
|
-
# * IPv4 netmask in cidr or extended notation
|
1213
|
-
#
|
1214
|
-
# - Returns:
|
1215
|
-
# * packed netmask on valid, or exception on error.
|
1216
|
-
#
|
1217
|
-
# Example:
|
1218
|
-
# IPAdmin.validate_ipv4_netmask('255.255.255.0')
|
1219
|
-
# IPAdmin.validate_ipv4_netmask('24')
|
1220
|
-
# IPAdmin.validate_ipv4_netmask('/24')
|
1221
|
-
# IPAdmin.validate_ipv4_netmask(24)
|
1222
|
-
#
|
1223
|
-
def validate_ipv4_netmask(netmask)
|
1224
|
-
all_f = 2**32 - 1
|
1225
|
-
packed_mask = nil
|
1226
|
-
|
1227
|
-
# is this a CIDR or Extended mask?
|
1228
|
-
if(netmask =~ /\./)
|
1229
|
-
# validate & pack extended mask
|
1230
|
-
begin
|
1231
|
-
validate_ipv4_addr(netmask)
|
1232
|
-
packed_netmask = pack_ipv4_addr(netmask)
|
1233
|
-
|
1234
|
-
rescue Exception
|
1235
|
-
raise "#{netmask} is not a valid IPv4 netmask."
|
1236
|
-
end
|
1237
|
-
|
1238
|
-
# cycle through the bits of hostmask and compare
|
1239
|
-
# with packed_mask. when we hit the firt '1' within
|
1240
|
-
# packed_mask (our netmask boundary), xor hostmask and
|
1241
|
-
# packed_mask. the result should be all 1's. this whole
|
1242
|
-
# process is in place to make sure that we dont have
|
1243
|
-
# and crazy masks such as 255.254.255.0
|
1244
|
-
hostmask = 1
|
1245
|
-
32.times do
|
1246
|
-
check = packed_netmask & hostmask
|
1247
|
-
if ( check != 0)
|
1248
|
-
hostmask = hostmask >> 1
|
1249
|
-
unless ( (packed_netmask ^ hostmask) == all_f)
|
1250
|
-
raise "#{netmask} is not a valid IPv4 netmask."
|
1251
|
-
end
|
1252
|
-
break
|
1253
|
-
else
|
1254
|
-
hostmask = hostmask << 1
|
1255
|
-
hostmask = hostmask | 1
|
1256
|
-
end
|
1257
|
-
end
|
1258
|
-
|
1259
|
-
else
|
1260
|
-
# remove '/' if present
|
1261
|
-
if (netmask =~ /^\// )
|
1262
|
-
netmask[0] = " "
|
1263
|
-
netmask.lstrip!
|
1264
|
-
end
|
1265
|
-
|
1266
|
-
# check if we have any non numeric characters
|
1267
|
-
if (netmask =~ /\D/)
|
1268
|
-
raise "#{netmask} is not a valid IPv4 netmask."
|
1269
|
-
end
|
1270
|
-
|
1271
|
-
# are we between 1 and 32 inclusive
|
1272
|
-
if (netmask.kind_of? String)
|
1273
|
-
netmask = netmask.to_i
|
1274
|
-
end
|
1275
|
-
|
1276
|
-
if ( (netmask > 32) || (netmask == 0) )
|
1277
|
-
raise "#{netmask} is not a valid IPv4 netmask."
|
1278
|
-
end
|
1279
|
-
|
1280
|
-
packed_netmask = all_f ^ (all_f >> netmask)
|
1281
|
-
end
|
1282
|
-
|
1283
|
-
return(packed_netmask)
|
1284
|
-
end
|
1285
|
-
module_function :validate_ipv4_netmask
|
1286
|
-
|
1287
|
-
#======================================#
|
1288
|
-
#
|
1289
|
-
#======================================#
|
1290
|
-
|
1291
|
-
|
1292
|
-
#==============================================================================#
|
1293
|
-
# validate_ipv6_addr()
|
1294
|
-
#==============================================================================#
|
1295
|
-
|
1296
|
-
# Validate IPv6 addresses. The address should not contain a netmask.
|
1297
|
-
#
|
1298
|
-
# - Arguments:
|
1299
|
-
# * IPv6 address
|
1300
|
-
#
|
1301
|
-
# - Returns:
|
1302
|
-
# * packed IP on valid, or exception on error.
|
1303
|
-
#
|
1304
|
-
# Example:
|
1305
|
-
# IPAdmin.validate_ipv6_addr('fec0::')
|
1306
|
-
#
|
1307
|
-
def validate_ipv6_addr(ip)
|
1308
|
-
# is this a string?
|
1309
|
-
unless (ip.kind_of? String)
|
1310
|
-
raise ArgumentError, "Expected String, but #{ip.class} provided."
|
1311
|
-
end
|
1312
|
-
|
1313
|
-
# check validity of characters in the addr
|
1314
|
-
if ( (ip =~ /:{3,}?/ ) || (ip =~ /[^0-9a-fA-F:]/) )
|
1315
|
-
raise "#{ip} is not a valid IPv6 address."
|
1316
|
-
end
|
1317
|
-
|
1318
|
-
# look for a '::' to see if this address is in shorthand
|
1319
|
-
# if found, split on it & make sure that we have at most
|
1320
|
-
# two elements
|
1321
|
-
if (ip =~ /::/)
|
1322
|
-
shrthnd = ip.split( /::/ )
|
1323
|
-
unless ( (shrthnd.length > 0) && (shrthnd.length < 3) )
|
1324
|
-
raise "#{ip} is not a valid IPv6 address."
|
1325
|
-
end
|
1326
|
-
end
|
1327
|
-
|
1328
|
-
if (shrthnd)
|
1329
|
-
# if shorthand, we should have between 1 and 7
|
1330
|
-
# hex fields
|
1331
|
-
hex_fields = []
|
1332
|
-
shrthnd.each do |x|
|
1333
|
-
elements = x.split( /:/ )
|
1334
|
-
elements.each {|x| hex_fields.push(x)}
|
1335
|
-
end
|
1336
|
-
if ( (hex_fields.length < 1) || (hex_fields.length > 7) )
|
1337
|
-
raise "#{ip} is not a valid IPv6 address."
|
1338
|
-
end
|
1339
|
-
|
1340
|
-
else
|
1341
|
-
# since no shorthand notation was detected we should
|
1342
|
-
# have exactly 8 hex fields
|
1343
|
-
hex_fields = ip.split( /:/ )
|
1344
|
-
if (hex_fields.length != 8)
|
1345
|
-
raise "#{ip} is not a valid IPv6 address."
|
1346
|
-
end
|
1347
|
-
|
1348
|
-
end
|
1349
|
-
|
1350
|
-
# check that we have no more than 4 characters in each
|
1351
|
-
# hex field
|
1352
|
-
hex_fields.each do |x|
|
1353
|
-
if (x.length > 4)
|
1354
|
-
raise "#{ip} is not a valid IPv6 address."
|
1355
|
-
end
|
1356
|
-
end
|
1357
|
-
|
1358
|
-
packed_ip = IPAdmin.pack_ipv6_addr(ip)
|
1359
|
-
return(packed_ip)
|
1360
|
-
end
|
1361
|
-
module_function :validate_ipv6_addr
|
1362
|
-
|
1363
|
-
#======================================#
|
1364
|
-
#
|
1365
|
-
#======================================#
|
1366
|
-
|
1367
|
-
|
1368
|
-
#==============================================================================#
|
1369
|
-
# validate_ipv6_netmask()
|
1370
|
-
#==============================================================================#
|
1371
|
-
|
1372
|
-
# Validate IPv6 netmask.
|
1373
|
-
#
|
1374
|
-
# - Arguments:
|
1375
|
-
# * IPv6 netmask in cidr notation
|
1376
|
-
#
|
1377
|
-
# - Returns:
|
1378
|
-
# * packed netmask on valid, or exception on error.
|
1379
|
-
#
|
1380
|
-
# Example:
|
1381
|
-
# IPAdmin.validate_ipv6_netmask('64')
|
1382
|
-
# IPAdmin.validate_ipv6_netmask('/64')
|
1383
|
-
# IPAdmin.validate_ipv6_netmask(64)
|
1384
|
-
#
|
1385
|
-
def validate_ipv6_netmask(netmask)
|
1386
|
-
all_f = 2**128 -1
|
1387
|
-
|
1388
|
-
# remove '/' if present
|
1389
|
-
if (netmask =~ /^\// )
|
1390
|
-
netmask[0] = " "
|
1391
|
-
netmask.lstrip!
|
1392
|
-
end
|
1393
|
-
|
1394
|
-
if (netmask =~ /\D/)
|
1395
|
-
raise "#{netmask} is not a valid IPv6 netmask."
|
1396
|
-
|
1397
|
-
else
|
1398
|
-
# are we between 1 and 128 inclusive
|
1399
|
-
if (netmask.kind_of? String)
|
1400
|
-
netmask = netmask.to_i
|
1401
|
-
end
|
1402
|
-
|
1403
|
-
if ( (netmask > 128) || (netmask == 0) )
|
1404
|
-
raise "#{netmask} is not a valid IPv6 netmask."
|
1405
|
-
end
|
1406
|
-
|
1407
|
-
end
|
1408
|
-
|
1409
|
-
packed_netmask = all_f ^ (all_f >> netmask)
|
1410
|
-
return(packed_netmask)
|
1411
|
-
end
|
1412
|
-
module_function :validate_ipv6_netmask
|
1413
|
-
|
1414
|
-
#======================================#
|
1415
|
-
#
|
1416
|
-
#======================================#
|
1417
|
-
|
1418
|
-
|
1419
|
-
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
1420
|
-
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
1421
|
-
#
|
1422
|
-
# BEGIN class CIDR
|
1423
|
-
#
|
1424
|
-
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
1425
|
-
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
1426
|
-
|
1427
|
-
=begin rdoc
|
1428
|
-
A class & series of methods for creating and manipulating CIDR network
|
1429
|
-
addresses. Both IPv4 and IPv6 are supported.
|
1430
|
-
|
1431
|
-
This class accepts a CIDR address in (x.x.x.x/yy or xxxx::/yy) format for
|
1432
|
-
IPv4 and IPv6, or (x.x.x.x/y.y.y.y) for IPv4. An optional tag hash may be
|
1433
|
-
provided with each CIDR as a way of adding custom labels to the object.
|
1434
|
-
|
1435
|
-
Upon initialization, the IP version is auto-detected and assigned to the
|
1436
|
-
object. The original IP/Netmask passed within the CIDR is stored and then
|
1437
|
-
used to determine the confines of the CIDR block. Various properties of the
|
1438
|
-
CIDR block are accessible via several different methods. There are also
|
1439
|
-
methods for modifying the CIDR or creating new derivative CIDR's.
|
1440
|
-
|
1441
|
-
An example CIDR object is as follows:
|
1442
|
-
IPAdmin::CIDR.new(:CIDR => '192.168.1.20/24')
|
1443
|
-
|
1444
|
-
This would create a CIDR object (192.168.1.0/24) with the following properties:
|
1445
|
-
version = 4
|
1446
|
-
base network = 192.168.1.0
|
1447
|
-
ip address = 192.168.1.20
|
1448
|
-
netmask = /24 (255.255.255.0)
|
1449
|
-
size = 256 IP addresses
|
1450
|
-
broadcast = 192.168.1.255
|
1451
|
-
|
1452
|
-
You can see how the CIDR object is based around the entire IP space
|
1453
|
-
defined by the provided IP/Netmask pair, and not necessarily the individual
|
1454
|
-
IP address itself.
|
1455
|
-
=end
|
1456
|
-
|
1457
|
-
class CIDR
|
1458
|
-
|
1459
|
-
# instance variables
|
1460
|
-
# @all_f
|
1461
|
-
# @hostmask
|
1462
|
-
# @ip
|
1463
|
-
# @max_bits
|
1464
|
-
# @netmask
|
1465
|
-
# @network
|
1466
|
-
# @tag
|
1467
|
-
# @version
|
1468
|
-
|
1469
|
-
#==============================================================================#
|
1470
|
-
# attr_reader/attr_writer
|
1471
|
-
#==============================================================================#
|
1472
|
-
|
1473
|
-
# IP version 4 or 6.
|
1474
|
-
attr_reader :version
|
1475
|
-
|
1476
|
-
# Hash of custom tags. Should be in the format tag => value.
|
1477
|
-
attr_reader :tag
|
1478
|
-
|
1479
|
-
# Hash of custom tags. Should be in the format tag => value.
|
1480
|
-
#
|
1481
|
-
# Example:
|
1482
|
-
# cidr4.tag['test'] = 'modified cidr4 tag'
|
1483
|
-
# puts cidr4.tag['test'] --> modified cidr4 tag
|
1484
|
-
#
|
1485
|
-
def tag=(new_tag)
|
1486
|
-
unless (new_tag.kind_of? Hash)
|
1487
|
-
raise ArgumentError, "Expected Hash, but #{new_tag.class} provided."
|
1488
|
-
end
|
1489
|
-
@tag = new_tag
|
1490
|
-
end
|
1491
|
-
|
1492
|
-
#======================================#
|
1493
|
-
#
|
1494
|
-
#======================================#
|
1495
|
-
|
1496
|
-
|
1497
|
-
#==============================================================================#
|
1498
|
-
# initialize()
|
1499
|
-
#==============================================================================#
|
1500
|
-
|
1501
|
-
# - Arguments:
|
1502
|
-
# * Hash with the following fields:
|
1503
|
-
# - :CIDR -- IPv4 or IPv6 cidr block
|
1504
|
-
# - :Netmask -- IPv4 netmask in extended format (optional)
|
1505
|
-
# - :Tag -- Custom descriptor tag. Hash, tag => value. (optional)
|
1506
|
-
#
|
1507
|
-
# Note:
|
1508
|
-
# Will assume a host address (/32 or /128) if netmask not specified
|
1509
|
-
# as either part of the :CIDR or :Netmask arguments.
|
1510
|
-
#
|
1511
|
-
# Example:
|
1512
|
-
# cidr4 = IPAdmin::CIDR.new(:CIDR => '192.168.1.1/24')
|
1513
|
-
# cidr6 = IPAdmin::CIDR.new(:CIDR => 'fec0::/64',
|
1514
|
-
# :Tag => {'interface' => 'g0/1'})
|
1515
|
-
#
|
1516
|
-
#
|
1517
|
-
def initialize(options)
|
1518
|
-
unless (options.kind_of? Hash)
|
1519
|
-
raise ArgumentError, "Expected Hash, but #{options.class} provided."
|
1520
|
-
end
|
1521
|
-
|
1522
|
-
unless ( options.has_key?(:CIDR) )
|
1523
|
-
raise ArgumentError, "Missing argument: CIDR."
|
1524
|
-
end
|
1525
|
-
|
1526
|
-
cidr = options[:CIDR]
|
1527
|
-
|
1528
|
-
if (cidr =~/\./) # assume IPv4
|
1529
|
-
@version = 4
|
1530
|
-
@max_bits = 32
|
1531
|
-
@all_f = 2**@max_bits - 1
|
1532
|
-
|
1533
|
-
if (cidr =~ /\//)
|
1534
|
-
ip,netmask = cidr.split(/\//)
|
1535
|
-
|
1536
|
-
# validate
|
1537
|
-
@ip = IPAdmin.validate_ipv4_addr(ip)
|
1538
|
-
@netmask = IPAdmin.validate_ipv4_netmask(netmask)
|
1539
|
-
|
1540
|
-
elsif ( options.has_key?(:Netmask) )
|
1541
|
-
netmask = options[:Netmask]
|
1542
|
-
|
1543
|
-
# validate
|
1544
|
-
@ip = IPAdmin.validate_ipv4_addr(cidr)
|
1545
|
-
@netmask = IPAdmin.validate_ipv4_netmask(netmask)
|
1546
|
-
|
1547
|
-
else
|
1548
|
-
@ip = IPAdmin.validate_ipv4_addr(cidr)
|
1549
|
-
@netmask = IPAdmin.pack_ipv4_netmask(32)
|
1550
|
-
|
1551
|
-
end
|
1552
|
-
|
1553
|
-
@hostmask = @netmask ^ @all_f
|
1554
|
-
|
1555
|
-
elsif (cidr =~/:/) # assume IPv6
|
1556
|
-
@version = 6
|
1557
|
-
@max_bits = 128
|
1558
|
-
@all_f = 2**@max_bits - 1
|
1559
|
-
|
1560
|
-
if (cidr =~ /\//)
|
1561
|
-
ip,netmask = cidr.split(/\//)
|
1562
|
-
|
1563
|
-
# validate
|
1564
|
-
@ip = IPAdmin.validate_ipv6_addr(ip)
|
1565
|
-
@netmask = IPAdmin.validate_ipv6_netmask(netmask)
|
1566
|
-
|
1567
|
-
else
|
1568
|
-
@ip = IPAdmin.validate_ipv6_addr(cidr)
|
1569
|
-
@netmask = IPAdmin.pack_ipv6_netmask(128)
|
1570
|
-
|
1571
|
-
end
|
1572
|
-
|
1573
|
-
@hostmask = @netmask ^ @all_f
|
1574
|
-
|
1575
|
-
else
|
1576
|
-
raise "Invalid CIDR address format: #{cidr}."
|
1577
|
-
end
|
1578
|
-
|
1579
|
-
# get @network
|
1580
|
-
@network = (@ip & @netmask)
|
1581
|
-
|
1582
|
-
# set tag if present
|
1583
|
-
if ( options.has_key?(:Tag) )
|
1584
|
-
@tag = options[:Tag]
|
1585
|
-
unless (@tag.kind_of? Hash)
|
1586
|
-
raise "Expected Hash, but #{@tag.class} provided for option :Tag."
|
1587
|
-
end
|
1588
|
-
end
|
1589
|
-
|
1590
|
-
end
|
1591
|
-
|
1592
|
-
#======================================#
|
1593
|
-
#
|
1594
|
-
#======================================#
|
1595
|
-
|
1596
|
-
|
1597
|
-
#==============================================================================#
|
1598
|
-
# bits()
|
1599
|
-
#==============================================================================#
|
1600
|
-
|
1601
|
-
# Provide number of bits in Netmask.
|
1602
|
-
#
|
1603
|
-
# - Arguments:
|
1604
|
-
# * none
|
1605
|
-
#
|
1606
|
-
# - Returns:
|
1607
|
-
# * Number of bits in Netmask.
|
1608
|
-
#
|
1609
|
-
# Example:
|
1610
|
-
# puts cidr4.bits() --> 24
|
1611
|
-
#
|
1612
|
-
def bits()
|
1613
|
-
if (@version == 4)
|
1614
|
-
bits = IPAdmin.unpack_ipv4_netmask(@netmask)
|
1615
|
-
else
|
1616
|
-
bits = IPAdmin.unpack_ipv6_netmask(@netmask)
|
1617
|
-
end
|
1618
|
-
|
1619
|
-
return(bits)
|
1620
|
-
end
|
1621
|
-
|
1622
|
-
#======================================#
|
1623
|
-
#
|
1624
|
-
#======================================#
|
1625
|
-
|
1626
|
-
|
1627
|
-
#==============================================================================#
|
1628
|
-
# contains?()
|
1629
|
-
#==============================================================================#
|
1630
|
-
|
1631
|
-
# Determines if this object contains (is supernet of)
|
1632
|
-
# provided IPAdmin::CIDR object.
|
1633
|
-
#
|
1634
|
-
# - Arguments:
|
1635
|
-
# * IPAdmin:CIDR object
|
1636
|
-
#
|
1637
|
-
# - Returns:
|
1638
|
-
# * true or false
|
1639
|
-
#
|
1640
|
-
# Example:
|
1641
|
-
# cidr4_2 = IPAdmin::CIDR.new(:CIDR => '192.168.1.0/26')
|
1642
|
-
# contains? = cidr4.contains(cidr4_2) --> true
|
1643
|
-
#
|
1644
|
-
def contains?(object)
|
1645
|
-
is_contained = false
|
1646
|
-
|
1647
|
-
if (object.kind_of?(IPAdmin::CIDR))
|
1648
|
-
network = object.packed_network
|
1649
|
-
netmask = object.packed_netmask
|
1650
|
-
|
1651
|
-
else
|
1652
|
-
raise ArgumentError, "Expected IPAdmin::CIDR " +
|
1653
|
-
" object but #{object.class} provided."
|
1654
|
-
end
|
1655
|
-
|
1656
|
-
|
1657
|
-
unless (object.version == @version)
|
1658
|
-
raise "Attempted to compare a version #{object.version} CIDR " +
|
1659
|
-
"with a version #{@version} CIDR."
|
1660
|
-
end
|
1661
|
-
|
1662
|
-
# if network == @network, then we have to go by netmask length
|
1663
|
-
# else we can tell by or'ing network and @network by @hostmask
|
1664
|
-
# and comparing the results
|
1665
|
-
if (network == @network)
|
1666
|
-
is_contained = true if (netmask > @netmask)
|
1667
|
-
|
1668
|
-
else
|
1669
|
-
if ( (network | @hostmask) == (@network | @hostmask) )
|
1670
|
-
is_contained = true
|
1671
|
-
end
|
1672
|
-
end
|
1673
|
-
|
1674
|
-
return(is_contained)
|
1675
|
-
end
|
1676
|
-
|
1677
|
-
#======================================#
|
1678
|
-
#
|
1679
|
-
#======================================#
|
1680
|
-
|
1681
|
-
|
1682
|
-
#==============================================================================#
|
1683
|
-
# desc()
|
1684
|
-
#==============================================================================#
|
1685
|
-
|
1686
|
-
# Displays base network, and netmask of this object.
|
1687
|
-
#
|
1688
|
-
# - Arguments:
|
1689
|
-
# * Optional hash with the following fields:
|
1690
|
-
# - :IP -- true, return the ip/netmask passed during initialization (optional)
|
1691
|
-
# - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
|
1692
|
-
#
|
1693
|
-
# - Returns:
|
1694
|
-
# * Description in network/netmask format
|
1695
|
-
#
|
1696
|
-
# Example:
|
1697
|
-
# puts cidr4.desc() --> 192.168.1.0/24
|
1698
|
-
# puts cidr4.desc(:IP => 1) --> 192.168.1.1/24
|
1699
|
-
#
|
1700
|
-
def desc(options=nil)
|
1701
|
-
short = false
|
1702
|
-
orig_ip = false
|
1703
|
-
|
1704
|
-
if (options)
|
1705
|
-
unless (options.kind_of? Hash)
|
1706
|
-
raise ArgumentError, "Expected Hash, but #{options.class} provided."
|
1707
|
-
end
|
1708
|
-
|
1709
|
-
if (options.has_key?(:Short))
|
1710
|
-
short = true
|
1711
|
-
end
|
1712
|
-
|
1713
|
-
if (options.has_key?(:IP))
|
1714
|
-
orig_ip = true
|
1715
|
-
end
|
1716
|
-
end
|
1717
|
-
|
1718
|
-
if (@version == 4)
|
1719
|
-
if (!orig_ip)
|
1720
|
-
ip = IPAdmin.unpack_ipv4_addr(@network)
|
1721
|
-
else
|
1722
|
-
ip = IPAdmin.unpack_ipv4_addr(@ip)
|
1723
|
-
end
|
1724
|
-
mask = IPAdmin.unpack_ipv4_netmask(@netmask)
|
1725
|
-
else
|
1726
|
-
if (!orig_ip)
|
1727
|
-
ip = IPAdmin.unpack_ipv6_addr(@network)
|
1728
|
-
else
|
1729
|
-
ip = IPAdmin.unpack_ipv6_addr(@ip)
|
1730
|
-
end
|
1731
|
-
ip = IPAdmin.shorten(ip) if (short)
|
1732
|
-
mask = IPAdmin.unpack_ipv6_netmask(@netmask)
|
1733
|
-
end
|
1734
|
-
|
1735
|
-
return("#{ip}/#{mask}")
|
1736
|
-
|
1737
|
-
end
|
1738
|
-
|
1739
|
-
#======================================#
|
1740
|
-
#
|
1741
|
-
#======================================#
|
1742
|
-
|
1743
|
-
|
1744
|
-
#==============================================================================#
|
1745
|
-
# enumerate()
|
1746
|
-
#==============================================================================#
|
1747
|
-
|
1748
|
-
# Provide all IP addresses contained within the IP space of this CIDR.
|
1749
|
-
# (warning: this can be quite large for big blocks)
|
1750
|
-
#
|
1751
|
-
# - Arguments:
|
1752
|
-
# * Optional Hash with the following fields:
|
1753
|
-
# - :Bitstep -- enumerate in X sized steps (optional)
|
1754
|
-
# - :Limit -- limit returned list to X number of items (optional)
|
1755
|
-
# - :Objectify -- if true, return IPAdmin::CIDR objects (optional)
|
1756
|
-
# - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
|
1757
|
-
#
|
1758
|
-
# - Returns:
|
1759
|
-
# * Array of IP addresses or IPAdmin::CIDR objects
|
1760
|
-
#
|
1761
|
-
# Example:
|
1762
|
-
# ip_list = cidr4.enumerate(:Bitstep => 2, :Limit => 2) --> ['192.168.1.0','192.168.1.1']
|
1763
|
-
#
|
1764
|
-
def enumerate(options=nil)
|
1765
|
-
bitstep = 1
|
1766
|
-
objectify = false
|
1767
|
-
limit = nil
|
1768
|
-
short = false
|
1769
|
-
|
1770
|
-
if (options)
|
1771
|
-
if( options.has_key?(:Bitstep) )
|
1772
|
-
bitstep = options[:Bitstep]
|
1773
|
-
end
|
1774
|
-
|
1775
|
-
if( options.has_key?(:Objectify) )
|
1776
|
-
objectify = true
|
1777
|
-
end
|
1778
|
-
|
1779
|
-
if( options.has_key?(:Limit) )
|
1780
|
-
limit = options[:Limit]
|
1781
|
-
end
|
1782
|
-
|
1783
|
-
if( options.has_key?(:Short) )
|
1784
|
-
short = true
|
1785
|
-
end
|
1786
|
-
end
|
1787
|
-
|
1788
|
-
list = []
|
1789
|
-
if (@version == 4)
|
1790
|
-
my_ip = @network
|
1791
|
-
change_mask = @hostmask | my_ip
|
1792
|
-
|
1793
|
-
until ( change_mask != (@hostmask | @network) )
|
1794
|
-
if (!objectify)
|
1795
|
-
list.push( IPAdmin.unpack_ipv4_addr(my_ip) )
|
1796
|
-
else
|
1797
|
-
my_ip_s = IPAdmin.unpack_ipv4_addr(my_ip)
|
1798
|
-
list.push( IPAdmin::CIDR.new(:CIDR => my_ip_s) )
|
1799
|
-
end
|
1800
|
-
my_ip = my_ip + bitstep
|
1801
|
-
change_mask = @hostmask | my_ip
|
1802
|
-
if (limit)
|
1803
|
-
limit = limit -1
|
1804
|
-
break if (limit == 0)
|
1805
|
-
end
|
1806
|
-
end
|
1807
|
-
|
1808
|
-
elsif (@version == 6)
|
1809
|
-
my_ip = @network
|
1810
|
-
change_mask = @hostmask | my_ip
|
1811
|
-
|
1812
|
-
until ( change_mask != (@hostmask | @network) )
|
1813
|
-
if (!objectify)
|
1814
|
-
my_ip_s = IPAdmin.unpack_ipv6_addr(my_ip)
|
1815
|
-
my_ip_s = IPAdmin.shorten(my_ip_s) if (short)
|
1816
|
-
list.push(my_ip_s)
|
1817
|
-
else
|
1818
|
-
my_ip_s = IPAdmin.unpack_ipv6_addr(my_ip)
|
1819
|
-
list.push( IPAdmin::CIDR.new(:CIDR => my_ip_s) )
|
1820
|
-
end
|
1821
|
-
my_ip = my_ip + bitstep
|
1822
|
-
change_mask = @hostmask | my_ip
|
1823
|
-
if (limit)
|
1824
|
-
limit = limit -1
|
1825
|
-
break if (limit == 0)
|
1826
|
-
end
|
1827
|
-
end
|
1828
|
-
|
1829
|
-
else
|
1830
|
-
list = nil
|
1831
|
-
|
1832
|
-
end
|
1833
|
-
|
1834
|
-
return(list)
|
1835
|
-
end
|
1836
|
-
|
1837
|
-
#======================================#
|
1838
|
-
#
|
1839
|
-
#======================================#
|
1840
|
-
|
1841
|
-
|
1842
|
-
#==============================================================================#
|
1843
|
-
# hostmask_ext()
|
1844
|
-
#==============================================================================#
|
1845
|
-
|
1846
|
-
# Provide IPv4 Hostmask in extended format.
|
1847
|
-
#
|
1848
|
-
# - Arguments:
|
1849
|
-
# * none
|
1850
|
-
#
|
1851
|
-
# - Returns:
|
1852
|
-
# * Hostmask in extended (y.y.y.y) format.
|
1853
|
-
#
|
1854
|
-
# Example:
|
1855
|
-
# puts cidr4.hostmask_ext() --> 0.0.0.255
|
1856
|
-
#
|
1857
|
-
def hostmask_ext()
|
1858
|
-
if (@version == 4)
|
1859
|
-
hostmask = IPAdmin.unpack_ipv4_addr(@hostmask)
|
1860
|
-
else
|
1861
|
-
raise "IPv6 does not support extended hostmask notation."
|
1862
|
-
end
|
1863
|
-
|
1864
|
-
return(hostmask)
|
1865
|
-
end
|
1866
|
-
|
1867
|
-
#======================================#
|
1868
|
-
#
|
1869
|
-
#======================================#
|
1870
|
-
|
1871
|
-
|
1872
|
-
#==============================================================================#
|
1873
|
-
# ip()
|
1874
|
-
#==============================================================================#
|
1875
|
-
|
1876
|
-
# Provide original IP address passed during initialization.
|
1877
|
-
#
|
1878
|
-
# - Arguments:
|
1879
|
-
# * Optional Hash with the following fields:
|
1880
|
-
# - :Objectify -- if true, return IPAdmin::CIDR object (optional)
|
1881
|
-
# - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
|
1882
|
-
#
|
1883
|
-
# - Returns:
|
1884
|
-
# * IP address or IPAdmin::CIDR object.
|
1885
|
-
#
|
1886
|
-
# Example:
|
1887
|
-
# puts cidr4.ip() --> 192.168.1.1
|
1888
|
-
#
|
1889
|
-
def ip(options=nil)
|
1890
|
-
objectify = false
|
1891
|
-
short = false
|
1892
|
-
|
1893
|
-
if (options)
|
1894
|
-
unless(options.kind_of?(Hash))
|
1895
|
-
raise Argumenterror, "Expected Hash, but " +
|
1896
|
-
"#{options.class} provided."
|
1897
|
-
end
|
1898
|
-
|
1899
|
-
if( options.has_key?(:Short) )
|
1900
|
-
short = true
|
1901
|
-
end
|
1902
|
-
|
1903
|
-
if( options.has_key?(:Objectify) )
|
1904
|
-
objectify = true
|
1905
|
-
end
|
1906
|
-
end
|
1907
|
-
|
1908
|
-
if (@version == 4)
|
1909
|
-
ip = IPAdmin.unpack_ipv4_addr(@ip)
|
1910
|
-
else
|
1911
|
-
ip = IPAdmin.unpack_ipv6_addr(@ip)
|
1912
|
-
ip = IPAdmin.shorten(ip) if (short && !objectify)
|
1913
|
-
end
|
1914
|
-
|
1915
|
-
if (objectify)
|
1916
|
-
ip = IPAdmin::CIDR.new(:CIDR => ip)
|
1917
|
-
end
|
1918
|
-
|
1919
|
-
return(ip)
|
1920
|
-
end
|
1921
|
-
|
1922
|
-
#======================================#
|
1923
|
-
#
|
1924
|
-
#======================================#
|
1925
|
-
|
1926
|
-
|
1927
|
-
#==============================================================================#
|
1928
|
-
# last()
|
1929
|
-
#==============================================================================#
|
1930
|
-
|
1931
|
-
# Provide last IP address in this CIDR object. The broadcast() method is
|
1932
|
-
# aliased to this method, and thus works for IPv6 despite the fact that the
|
1933
|
-
# IPv6 protocol does not support IP broadcasting.
|
1934
|
-
#
|
1935
|
-
# - Arguments:
|
1936
|
-
# * Optional Hash with the following fields:
|
1937
|
-
# - :Objectify -- if true, return IPAdmin::CIDR object (optional)
|
1938
|
-
# - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
|
1939
|
-
#
|
1940
|
-
# - Returns:
|
1941
|
-
# * IP address or IPAdmin::CIDR object.
|
1942
|
-
#
|
1943
|
-
# Example:
|
1944
|
-
# puts cidr4.last() --> 192.168.1.255
|
1945
|
-
#
|
1946
|
-
def last(options=nil)
|
1947
|
-
objectify = false
|
1948
|
-
short = false
|
1949
|
-
|
1950
|
-
if (options)
|
1951
|
-
unless(options.kind_of?(Hash))
|
1952
|
-
raise Argumenterror, "Expected Hash, but " +
|
1953
|
-
"#{options.class} provided."
|
1954
|
-
end
|
1955
|
-
|
1956
|
-
if( options.has_key?(:Short) )
|
1957
|
-
short = true
|
1958
|
-
end
|
1959
|
-
|
1960
|
-
if( options.has_key?(:Objectify) )
|
1961
|
-
objectify = true
|
1962
|
-
end
|
1963
|
-
|
1964
|
-
end
|
1965
|
-
|
1966
|
-
packed_ip = @network | @hostmask
|
1967
|
-
if (@version == 4)
|
1968
|
-
ip = IPAdmin.unpack_ipv4_addr(packed_ip)
|
1969
|
-
else
|
1970
|
-
ip = IPAdmin.unpack_ipv6_addr(packed_ip)
|
1971
|
-
ip = IPAdmin.shorten(ip) if (short && !objectify)
|
1972
|
-
end
|
1973
|
-
|
1974
|
-
if (objectify)
|
1975
|
-
ip = IPAdmin::CIDR.new(:CIDR => ip)
|
1976
|
-
end
|
1977
|
-
|
1978
|
-
return(ip)
|
1979
|
-
end
|
1980
|
-
|
1981
|
-
alias :broadcast :last
|
1982
|
-
|
1983
|
-
#======================================#
|
1984
|
-
#
|
1985
|
-
#======================================#
|
1986
|
-
|
1987
|
-
|
1988
|
-
#==============================================================================#
|
1989
|
-
# netmask()
|
1990
|
-
#==============================================================================#
|
1991
|
-
|
1992
|
-
# Provide netmask in cidr format.
|
1993
|
-
#
|
1994
|
-
# - Arguments:
|
1995
|
-
# * none
|
1996
|
-
#
|
1997
|
-
# - Returns:
|
1998
|
-
# * Netmask in CIDR format.
|
1999
|
-
#
|
2000
|
-
# Example:
|
2001
|
-
# puts cidr4.netmask() --> /24
|
2002
|
-
#
|
2003
|
-
def netmask()
|
2004
|
-
if (@version == 4)
|
2005
|
-
bits = IPAdmin.unpack_ipv4_netmask(@netmask)
|
2006
|
-
else
|
2007
|
-
bits = IPAdmin.unpack_ipv6_netmask(@netmask)
|
2008
|
-
end
|
2009
|
-
|
2010
|
-
return("/#{bits}")
|
2011
|
-
end
|
2012
|
-
|
2013
|
-
#======================================#
|
2014
|
-
#
|
2015
|
-
#======================================#
|
2016
|
-
|
2017
|
-
|
2018
|
-
#==============================================================================#
|
2019
|
-
# netmask_ext()
|
2020
|
-
#==============================================================================#
|
2021
|
-
|
2022
|
-
# Provide IPv4 netmask in extended format.
|
2023
|
-
#
|
2024
|
-
# - Arguments:
|
2025
|
-
# * none
|
2026
|
-
#
|
2027
|
-
# - Returns:
|
2028
|
-
# * Netmask in extended (y.y.y.y) format.
|
2029
|
-
#
|
2030
|
-
# Example:
|
2031
|
-
# puts cidr4.netmask_ext() --> 255.255.255.0
|
2032
|
-
#
|
2033
|
-
def netmask_ext()
|
2034
|
-
if (@version == 4)
|
2035
|
-
netmask = IPAdmin.unpack_ipv4_addr(@netmask)
|
2036
|
-
else
|
2037
|
-
raise "IPv6 does not support extended netmask notation. " +
|
2038
|
-
"Use netmask() method instead."
|
2039
|
-
end
|
2040
|
-
|
2041
|
-
return(netmask)
|
2042
|
-
end
|
2043
|
-
|
2044
|
-
#======================================#
|
2045
|
-
#
|
2046
|
-
#======================================#
|
2047
|
-
|
2048
|
-
|
2049
|
-
#==============================================================================#
|
2050
|
-
# network()
|
2051
|
-
#==============================================================================#
|
2052
|
-
|
2053
|
-
# Provide base network address.
|
2054
|
-
#
|
2055
|
-
# - Arguments:
|
2056
|
-
# * Optional Hash with the following fields:
|
2057
|
-
# - :Objectify -- if true, return IPAdmin::CIDR object (optional)
|
2058
|
-
# - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
|
2059
|
-
#
|
2060
|
-
# - Returns:
|
2061
|
-
# * IP address or IPAdmin::CIDR object.
|
2062
|
-
#
|
2063
|
-
# Example:
|
2064
|
-
# puts cidr4.network() --> 192.168.1.0
|
2065
|
-
#
|
2066
|
-
def network(options=nil)
|
2067
|
-
objectify = false
|
2068
|
-
short = false
|
2069
|
-
|
2070
|
-
if (options)
|
2071
|
-
unless(options.kind_of?(Hash))
|
2072
|
-
raise Argumenterror, "Expected Hash, but " +
|
2073
|
-
"#{options.class} provided."
|
2074
|
-
end
|
2075
|
-
|
2076
|
-
if( options.has_key?(:Short) )
|
2077
|
-
short = true
|
2078
|
-
end
|
2079
|
-
|
2080
|
-
if( options.has_key?(:Objectify) )
|
2081
|
-
objectify = true
|
2082
|
-
end
|
2083
|
-
end
|
2084
|
-
|
2085
|
-
if (@version == 4)
|
2086
|
-
ip = IPAdmin.unpack_ipv4_addr(@network)
|
2087
|
-
else
|
2088
|
-
ip = IPAdmin.unpack_ipv6_addr(@network)
|
2089
|
-
ip = IPAdmin.shorten(ip) if (short && !objectify)
|
2090
|
-
end
|
2091
|
-
|
2092
|
-
if (objectify)
|
2093
|
-
ip = IPAdmin::CIDR.new(:CIDR => ip)
|
2094
|
-
end
|
2095
|
-
|
2096
|
-
return(ip)
|
2097
|
-
end
|
2098
|
-
|
2099
|
-
alias :base :network
|
2100
|
-
alias :first :network
|
2101
|
-
|
2102
|
-
#======================================#
|
2103
|
-
#
|
2104
|
-
#======================================#
|
2105
|
-
|
2106
|
-
|
2107
|
-
#==============================================================================#
|
2108
|
-
# next_ip()
|
2109
|
-
#==============================================================================#
|
2110
|
-
|
2111
|
-
# Provide the next IP following the last available IP within this
|
2112
|
-
# CIDR object.
|
2113
|
-
#
|
2114
|
-
# - Arguments:
|
2115
|
-
# * Optional Hash with the following fields:
|
2116
|
-
# - :Bitstep -- step in X sized steps (optional)
|
2117
|
-
# - :Objectify -- if true, return IPAdmin::CIDR object (optional)
|
2118
|
-
# - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
|
2119
|
-
#
|
2120
|
-
# - Returns:
|
2121
|
-
# * IP address or IPAdmin::CIDR object.
|
2122
|
-
#
|
2123
|
-
# Example:
|
2124
|
-
# puts cidr4.next_ip() --> 192.168.2.0
|
2125
|
-
#
|
2126
|
-
def next_ip(options=nil)
|
2127
|
-
bitstep = 1
|
2128
|
-
objectify = false
|
2129
|
-
short = false
|
2130
|
-
|
2131
|
-
if (options)
|
2132
|
-
unless(options.kind_of?(Hash))
|
2133
|
-
raise Argumenterror, "Expected Hash, but " +
|
2134
|
-
"#{options.class} provided."
|
2135
|
-
end
|
2136
|
-
|
2137
|
-
if( options.has_key?(:Bitstep) )
|
2138
|
-
bitstep = options[:Bitstep]
|
2139
|
-
end
|
2140
|
-
|
2141
|
-
if( options.has_key?(:Short) )
|
2142
|
-
short = true
|
2143
|
-
end
|
2144
|
-
|
2145
|
-
if( options.has_key?(:Objectify) )
|
2146
|
-
objectify = true
|
2147
|
-
end
|
2148
|
-
end
|
2149
|
-
|
2150
|
-
next_ip = @network + @hostmask + bitstep
|
2151
|
-
|
2152
|
-
unless (next_ip <= @all_f)
|
2153
|
-
raise "Returned IP is out of bounds for IPv#{@version}."
|
2154
|
-
end
|
2155
|
-
|
2156
|
-
if (@version == 4)
|
2157
|
-
next_ip = IPAdmin.unpack_ipv4_addr(next_ip)
|
2158
|
-
else
|
2159
|
-
next_ip = IPAdmin.unpack_ipv6_addr(next_ip)
|
2160
|
-
next_ip = IPAdmin.shorten(next_ip) if (short && !objectify)
|
2161
|
-
end
|
2162
|
-
|
2163
|
-
if (objectify)
|
2164
|
-
next_ip = IPAdmin::CIDR.new(:CIDR => next_ip)
|
2165
|
-
end
|
2166
|
-
|
2167
|
-
return(next_ip)
|
2168
|
-
end
|
2169
|
-
|
2170
|
-
#======================================#
|
2171
|
-
#
|
2172
|
-
#======================================#
|
2173
|
-
|
2174
|
-
|
2175
|
-
#==============================================================================#
|
2176
|
-
# next_subnet()
|
2177
|
-
#==============================================================================#
|
2178
|
-
|
2179
|
-
# Provide the next subnet following this CIDR object. The next subnet will
|
2180
|
-
# be of the same size as the current CIDR object.
|
2181
|
-
#
|
2182
|
-
# - Arguments:
|
2183
|
-
# * Optional Hash with the following fields:
|
2184
|
-
# - :Bitstep -- step in X sized steps. (optional)
|
2185
|
-
# - :Objectify -- if true, return IPAdmin::CIDR object (optional)
|
2186
|
-
# - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
|
2187
|
-
#
|
2188
|
-
# - Returns:
|
2189
|
-
# * CIDR address or IPAdmin::CIDR object.
|
2190
|
-
#
|
2191
|
-
# Example:
|
2192
|
-
# puts cidr4.next_subnet() --> 192.168.2.0/24
|
2193
|
-
#
|
2194
|
-
def next_subnet(options=nil)
|
2195
|
-
bitstep = 1
|
2196
|
-
objectify = false
|
2197
|
-
short = false
|
2198
|
-
|
2199
|
-
if (options)
|
2200
|
-
unless(options.kind_of?(Hash))
|
2201
|
-
raise Argumenterror, "Expected Hash, but " +
|
2202
|
-
"#{options.class} provided."
|
2203
|
-
end
|
2204
|
-
|
2205
|
-
if( options.has_key?(:Bitstep) )
|
2206
|
-
bitstep = options[:Bitstep]
|
2207
|
-
end
|
2208
|
-
|
2209
|
-
if( options.has_key?(:Short) )
|
2210
|
-
short = true
|
2211
|
-
end
|
2212
|
-
|
2213
|
-
if( options.has_key?(:Objectify) )
|
2214
|
-
objectify = true
|
2215
|
-
end
|
2216
|
-
end
|
2217
|
-
|
2218
|
-
bitstep = bitstep * (2**(@max_bits - self.bits) )
|
2219
|
-
next_sub = @network + bitstep
|
2220
|
-
|
2221
|
-
unless (next_sub <= @all_f)
|
2222
|
-
raise "Returned subnet is out of bounds for IPv#{@version}."
|
2223
|
-
end
|
2224
|
-
|
2225
|
-
if (@version == 4)
|
2226
|
-
next_sub = IPAdmin.unpack_ipv4_addr(next_sub)
|
2227
|
-
else
|
2228
|
-
next_sub = IPAdmin.unpack_ipv6_addr(next_sub)
|
2229
|
-
next_sub = IPAdmin.shorten(next_sub) if (short && !objectify)
|
2230
|
-
end
|
2231
|
-
|
2232
|
-
next_sub = next_sub << "/" << self.bits.to_s
|
2233
|
-
if (objectify)
|
2234
|
-
next_sub = IPAdmin::CIDR.new(:CIDR => next_sub)
|
2235
|
-
end
|
2236
|
-
|
2237
|
-
return(next_sub)
|
2238
|
-
end
|
2239
|
-
|
2240
|
-
#======================================#
|
2241
|
-
#
|
2242
|
-
#======================================#
|
2243
|
-
|
2244
|
-
|
2245
|
-
#==============================================================================#
|
2246
|
-
# nth()
|
2247
|
-
#==============================================================================#
|
2248
|
-
|
2249
|
-
# Provide the nth IP within this object.
|
2250
|
-
#
|
2251
|
-
# - Arguments:
|
2252
|
-
# * Hash with the following fields:
|
2253
|
-
# - :Index -- index number of the IP address to return
|
2254
|
-
# - :Objectify -- if true, return IPAdmin::CIDR objects (optional)
|
2255
|
-
# - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
|
2256
|
-
#
|
2257
|
-
# - Returns:
|
2258
|
-
# * IP address or IPAdmin::CIDR object.
|
2259
|
-
#
|
2260
|
-
# Example:
|
2261
|
-
# puts cidr4.nth(:Index => 1) --> 192.168.1.1
|
2262
|
-
#
|
2263
|
-
def nth(options)
|
2264
|
-
objectify = false
|
2265
|
-
short = false
|
2266
|
-
|
2267
|
-
unless(options.kind_of?(Hash))
|
2268
|
-
raise Argumenterror, "Expected Hash, but " +
|
2269
|
-
"#{options.class} provided."
|
2270
|
-
end
|
2271
|
-
|
2272
|
-
unless( options.has_key?(:Index) )
|
2273
|
-
raise ArgumentError, "Missing argument: Index."
|
2274
|
-
end
|
2275
|
-
index = options[:Index]
|
2276
|
-
|
2277
|
-
if( options.has_key?(:Short) )
|
2278
|
-
short = true
|
2279
|
-
end
|
2280
|
-
|
2281
|
-
if( options.has_key?(:Objectify) )
|
2282
|
-
objectify = true
|
2283
|
-
end
|
2284
|
-
|
2285
|
-
my_ip = @network + index
|
2286
|
-
if ( (@hostmask | my_ip) == (@hostmask | @network) )
|
2287
|
-
|
2288
|
-
if (@version == 4)
|
2289
|
-
my_ip = IPAdmin.unpack_ipv4_addr(my_ip)
|
2290
|
-
if (objectify)
|
2291
|
-
my_ip = IPAdmin::CIDR.new(:CIDR => my_ip)
|
2292
|
-
end
|
2293
|
-
elsif (@version == 6)
|
2294
|
-
my_ip = IPAdmin.unpack_ipv6_addr(my_ip)
|
2295
|
-
my_ip = IPAdmin.shorten(my_ip) if (short && !objectify)
|
2296
|
-
if (objectify)
|
2297
|
-
my_ip = IPAdmin::CIDR.new(:CIDR => my_ip)
|
2298
|
-
end
|
2299
|
-
end
|
2300
|
-
|
2301
|
-
else
|
2302
|
-
raise "Index of #{index} returns IP that is out of " +
|
2303
|
-
"bounds of CIDR network."
|
2304
|
-
end
|
2305
|
-
|
2306
|
-
return(my_ip)
|
2307
|
-
end
|
2308
|
-
|
2309
|
-
#======================================#
|
2310
|
-
#
|
2311
|
-
#======================================#
|
2312
|
-
|
2313
|
-
|
2314
|
-
#==============================================================================#
|
2315
|
-
# packed_hostmask()
|
2316
|
-
#==============================================================================#
|
2317
|
-
|
2318
|
-
# Provide an integer representing the packed Hostmask of this object.
|
2319
|
-
#
|
2320
|
-
# - Arguments:
|
2321
|
-
# * none
|
2322
|
-
#
|
2323
|
-
# - Returns:
|
2324
|
-
# * Byte-packed Hostmask.
|
2325
|
-
#
|
2326
|
-
# Example:
|
2327
|
-
# puts cidr4.packed_hostmask().to_s(16) --> ff
|
2328
|
-
#
|
2329
|
-
def packed_hostmask()
|
2330
|
-
return(@hostmask)
|
2331
|
-
end
|
2332
|
-
|
2333
|
-
#======================================#
|
2334
|
-
#
|
2335
|
-
#======================================#
|
2336
|
-
|
2337
|
-
|
2338
|
-
#==============================================================================#
|
2339
|
-
# packed_ip()
|
2340
|
-
#==============================================================================#
|
2341
|
-
|
2342
|
-
# Provide an integer representing the packed IP of this object.
|
2343
|
-
#
|
2344
|
-
# - Arguments:
|
2345
|
-
# * none
|
2346
|
-
#
|
2347
|
-
# - Returns:
|
2348
|
-
# * Byte-packed IP.
|
2349
|
-
#
|
2350
|
-
# Example:
|
2351
|
-
# puts cidr4.packed_ip().to_s(16) --> c0c80101
|
2352
|
-
#
|
2353
|
-
def packed_ip()
|
2354
|
-
return(@ip)
|
2355
|
-
end
|
2356
|
-
|
2357
|
-
#======================================#
|
2358
|
-
#
|
2359
|
-
#======================================#
|
2360
|
-
|
2361
|
-
|
2362
|
-
#==============================================================================#
|
2363
|
-
# packed_netmask()
|
2364
|
-
#==============================================================================#
|
2365
|
-
|
2366
|
-
# Provide an integer representing the packed Netmask of this object.
|
2367
|
-
#
|
2368
|
-
# - Arguments:
|
2369
|
-
# * none
|
2370
|
-
#
|
2371
|
-
# - Returns:
|
2372
|
-
# * Byte-packed Netmask.
|
2373
|
-
#
|
2374
|
-
# Example:
|
2375
|
-
# puts cidr4.packed_netmask().to_s(16) --> ffffff00
|
2376
|
-
#
|
2377
|
-
def packed_netmask()
|
2378
|
-
return(@netmask)
|
2379
|
-
end
|
2380
|
-
|
2381
|
-
#======================================#
|
2382
|
-
#
|
2383
|
-
#======================================#
|
2384
|
-
|
2385
|
-
|
2386
|
-
#==============================================================================#
|
2387
|
-
# packed_network()
|
2388
|
-
#==============================================================================#
|
2389
|
-
|
2390
|
-
# Provide an integer representing the packed Network address of this object.
|
2391
|
-
#
|
2392
|
-
# - Arguments:
|
2393
|
-
# * none
|
2394
|
-
#
|
2395
|
-
# - Returns:
|
2396
|
-
# * Byte-packed Network Address.
|
2397
|
-
#
|
2398
|
-
# Example:
|
2399
|
-
# packed = cidr4.packed_network().to_s(16) --> c0c80100
|
2400
|
-
#
|
2401
|
-
def packed_network()
|
2402
|
-
return(@network)
|
2403
|
-
end
|
2404
|
-
|
2405
|
-
#======================================#
|
2406
|
-
#
|
2407
|
-
#======================================#
|
2408
|
-
|
2409
|
-
|
2410
|
-
#==============================================================================#
|
2411
|
-
# range()
|
2412
|
-
#==============================================================================#
|
2413
|
-
|
2414
|
-
# Given two Indexes, return all IP addresses within the CIDR that are
|
2415
|
-
# between them (inclusive).
|
2416
|
-
#
|
2417
|
-
# - Arguments:
|
2418
|
-
# * Hash with the following fields:
|
2419
|
-
# - :Bitstep -- enumerate in X sized steps (optional)
|
2420
|
-
# - :Indexes -- array of (2) index numbers of the addresses to use as boundaries
|
2421
|
-
# - :Objectify -- if true, return IPAdmin::CIDR objects (optional)
|
2422
|
-
# - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
|
2423
|
-
#
|
2424
|
-
# - Returns:
|
2425
|
-
# * Array of IP addresses or IPAdmin::CIDR objects
|
2426
|
-
#
|
2427
|
-
# Example:
|
2428
|
-
# list = cidr4.range(:Indexes => [0,1]) --> ['192.168.1.0','192.168.1.1']
|
2429
|
-
#
|
2430
|
-
def range(options)
|
2431
|
-
objectify = false
|
2432
|
-
short = false
|
2433
|
-
bitstep = 1
|
2434
|
-
|
2435
|
-
unless(options.kind_of?(Hash))
|
2436
|
-
raise Argumenterror, "Expected Hash, but #{options.class} provided."
|
2437
|
-
end
|
2438
|
-
|
2439
|
-
unless( options.has_key?(:Indexes) )
|
2440
|
-
raise ArgumentError, "Missing argument: Indexes."
|
2441
|
-
end
|
2442
|
-
indexes = options[:Indexes]
|
2443
|
-
indexes.sort!
|
2444
|
-
|
2445
|
-
unless( (indexes.kind_of?(Array)) && (indexes.length == 2))
|
2446
|
-
raise ArgumentError, "Argument :Index should be an array of (2) index numbers."
|
2447
|
-
end
|
2448
|
-
|
2449
|
-
unless ( (indexes[0] >= 0) && (indexes[0] < self.size) )
|
2450
|
-
raise ArgumentError, "Index #{indexes[0]} is out of bounds for this CIDR."
|
2451
|
-
end
|
2452
|
-
|
2453
|
-
unless (indexes[1] < self.size)
|
2454
|
-
raise ArgumentError, "Index #{indexes[1]} is out of bounds for this CIDR."
|
2455
|
-
end
|
2456
|
-
|
2457
|
-
if( options.has_key?(:Short) )
|
2458
|
-
short = true
|
2459
|
-
end
|
2460
|
-
|
2461
|
-
if( options.has_key?(:Objectify) )
|
2462
|
-
objectify = true
|
2463
|
-
end
|
2464
|
-
|
2465
|
-
if( options.has_key?(:Bitstep) )
|
2466
|
-
bitstep = options[:Bitstep]
|
2467
|
-
end
|
2468
|
-
|
2469
|
-
start_ip = @network + indexes[0]
|
2470
|
-
end_ip = @network + indexes[1]
|
2471
|
-
my_ip = start_ip
|
2472
|
-
list = []
|
2473
|
-
until (my_ip > end_ip)
|
2474
|
-
if (@version == 4)
|
2475
|
-
my_ip_s = IPAdmin.unpack_ipv4_addr(my_ip)
|
2476
|
-
elsif (@version == 6)
|
2477
|
-
my_ip_s = IPAdmin.unpack_ipv6_addr(my_ip)
|
2478
|
-
my_ip_s = IPAdmin.shorten(my_ip_s) if (short && !objectify)
|
2479
|
-
end
|
2480
|
-
|
2481
|
-
if (objectify)
|
2482
|
-
my_ip_s = IPAdmin::CIDR.new(:CIDR => my_ip_s)
|
2483
|
-
end
|
2484
|
-
|
2485
|
-
list.push(my_ip_s)
|
2486
|
-
my_ip += bitstep
|
2487
|
-
end
|
2488
|
-
|
2489
|
-
return(list)
|
2490
|
-
end
|
2491
|
-
|
2492
|
-
#======================================#
|
2493
|
-
#
|
2494
|
-
#======================================#
|
2495
|
-
|
2496
|
-
|
2497
|
-
#==============================================================================#
|
2498
|
-
# remainder()
|
2499
|
-
#==============================================================================#
|
2500
|
-
|
2501
|
-
# Given a subnet of the current CIDR, provide the remaining subnets of
|
2502
|
-
# the CIDR.
|
2503
|
-
#
|
2504
|
-
# - Arguments:
|
2505
|
-
# * Optional hash with the following fields:
|
2506
|
-
# - :Exclude -- IPAdmin::CIDR object to use in calculating the remainder.
|
2507
|
-
# - :Objectify -- if true, return IPAdmin::CIDR objects (optional)
|
2508
|
-
# - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
|
2509
|
-
#
|
2510
|
-
# - Returns:
|
2511
|
-
# * Array of CIDR addresses or IPAdmin::CIDR objects
|
2512
|
-
#
|
2513
|
-
#
|
2514
|
-
# Example:
|
2515
|
-
# cidr4_2 = IPAdmin::CIDR.new(:CIDR => '192.168.1.64/26')
|
2516
|
-
# cidr4.remainder(:Exclude => cidr4_2).each {|x| puts}
|
2517
|
-
#
|
2518
|
-
def remainder(options)
|
2519
|
-
short = nil
|
2520
|
-
objectify = nil
|
2521
|
-
|
2522
|
-
unless (options.kind_of? Hash)
|
2523
|
-
raise ArgumentError, "Expected Hash, but #{options.class} provided."
|
2524
|
-
end
|
2525
|
-
|
2526
|
-
unless ( options.has_key?(:Exclude) )
|
2527
|
-
raise ArgumentError, "Missing argument: Exclude."
|
2528
|
-
end
|
2529
|
-
to_exclude = options[:Exclude]
|
2530
|
-
|
2531
|
-
unless ( to_exclude.kind_of?(IPAdmin::CIDR) )
|
2532
|
-
raise ArgumentError, "Expeced IPAdmin::CIDR, but #{exclude.class} " +
|
2533
|
-
"for option: Exclude."
|
2534
|
-
end
|
2535
|
-
|
2536
|
-
if( options.has_key?(:Short) )
|
2537
|
-
short = true
|
2538
|
-
end
|
2539
|
-
|
2540
|
-
if( options.has_key?(:Objectify) )
|
2541
|
-
objectify = true
|
2542
|
-
end
|
2543
|
-
|
2544
|
-
# make sure 'to_exclude' is the same ip version
|
2545
|
-
unless ( to_exclude.version == @version )
|
2546
|
-
raise "#{to_exclude.desc(:Short => true)} is of a different " +
|
2547
|
-
"IP version than #{self.desc(:Short => true)}."
|
2548
|
-
end
|
2549
|
-
|
2550
|
-
# make sure we contain 'to_exclude'
|
2551
|
-
unless ( self.contains?(to_exclude) == true )
|
2552
|
-
raise "#{to_exclude.desc(:Short => true)} does not fit " +
|
2553
|
-
"within the bounds of #{self.desc(:Short => true)}."
|
2554
|
-
end
|
2555
|
-
|
2556
|
-
# split this cidr in half & see which half 'to_exclude'
|
2557
|
-
# belongs in. take that half & repeat the process. every time
|
2558
|
-
# we repeat, store off the non-matching half
|
2559
|
-
new_mask = self.bits + 1
|
2560
|
-
lower_network = self.packed_network
|
2561
|
-
upper_network = self.packed_network + 2**(@max_bits - new_mask)
|
2562
|
-
|
2563
|
-
new_subnets = []
|
2564
|
-
until(new_mask > to_exclude.bits)
|
2565
|
-
if (to_exclude.packed_network < upper_network)
|
2566
|
-
match = lower_network
|
2567
|
-
non_match = upper_network
|
2568
|
-
else
|
2569
|
-
match = upper_network
|
2570
|
-
non_match = lower_network
|
2571
|
-
end
|
2572
|
-
|
2573
|
-
if (@version == 4)
|
2574
|
-
non_match = IPAdmin.unpack_ipv4_addr(non_match)
|
2575
|
-
else
|
2576
|
-
non_match = IPAdmin.unpack_ipv6_addr(non_match)
|
2577
|
-
non_match = IPAdmin.shorten(non_match) if (short && !objectify)
|
2578
|
-
end
|
2579
|
-
|
2580
|
-
if (!objectify)
|
2581
|
-
new_subnets.unshift("#{non_match}/#{new_mask}")
|
2582
|
-
else
|
2583
|
-
new_subnets.unshift(IPAdmin::CIDR.new(:CIDR => "#{non_match}/#{new_mask}"))
|
2584
|
-
end
|
2585
|
-
|
2586
|
-
new_mask = new_mask + 1
|
2587
|
-
lower_network = match
|
2588
|
-
upper_network = match + 2**(@max_bits - new_mask)
|
2589
|
-
end
|
2590
|
-
|
2591
|
-
return(new_subnets)
|
2592
|
-
end
|
2593
|
-
|
2594
|
-
#======================================#
|
2595
|
-
#
|
2596
|
-
#======================================#
|
2597
|
-
|
2598
|
-
|
2599
|
-
#==============================================================================#
|
2600
|
-
# resize()
|
2601
|
-
#==============================================================================#
|
2602
|
-
|
2603
|
-
# Resize the CIDR by changing the size of the Netmask.
|
2604
|
-
# Return the resulting CIDR as a new object.
|
2605
|
-
#
|
2606
|
-
# - Arguments:
|
2607
|
-
# * Hash with the following fields:
|
2608
|
-
# - :Subnet -- Number of bits of new Netmask (24,26, etc...)
|
2609
|
-
#
|
2610
|
-
# - Returns:
|
2611
|
-
# * IPAdmin::CIDR object
|
2612
|
-
#
|
2613
|
-
# Example:
|
2614
|
-
# new_cidr = cidr4.resize(:Subnet => 23)
|
2615
|
-
# puts new_cidr.desc --> 192.168.1.0/23
|
2616
|
-
#
|
2617
|
-
def resize(options)
|
2618
|
-
unless(options.kind_of?(Hash))
|
2619
|
-
raise Argumenterror, "Expected Hash, but " +
|
2620
|
-
"#{options.class} provided."
|
2621
|
-
end
|
2622
|
-
|
2623
|
-
unless( options.has_key?(:Subnet) )
|
2624
|
-
raise Argumenterror, "Missing argument: Subnet."
|
2625
|
-
end
|
2626
|
-
bits = options[:Subnet]
|
2627
|
-
|
2628
|
-
if (@version == 4)
|
2629
|
-
netmask = IPAdmin.validate_ipv4_netmask(bits)
|
2630
|
-
network = IPAdmin.unpack_ipv4_addr(@network & netmask)
|
2631
|
-
else
|
2632
|
-
netmask = IPAdmin.validate_ipv6_netmask(bits)
|
2633
|
-
network = IPAdmin.unpack_ipv6_addr(@network & netmask)
|
2634
|
-
end
|
2635
|
-
|
2636
|
-
cidr = IPAdmin::CIDR.new(:CIDR => "#{network}/#{bits}")
|
2637
|
-
return(cidr)
|
2638
|
-
end
|
2639
|
-
|
2640
|
-
#======================================#
|
2641
|
-
#
|
2642
|
-
#======================================#
|
2643
|
-
|
2644
|
-
|
2645
|
-
#==============================================================================#
|
2646
|
-
# resize!()
|
2647
|
-
#==============================================================================#
|
2648
|
-
|
2649
|
-
# Resize this object by changing the size of the Netmask.
|
2650
|
-
#
|
2651
|
-
# - Arguments:
|
2652
|
-
# * Hash with the following fields:
|
2653
|
-
# - :Subnet -- Number of bits of new Netmask (24,26, etc...)
|
2654
|
-
#
|
2655
|
-
# - Returns:
|
2656
|
-
# * IPAdmin::CIDR object
|
2657
|
-
#
|
2658
|
-
# Note:
|
2659
|
-
# If CIDR is resized such that the original IP is no longer contained within,
|
2660
|
-
# then that IP will be reset to the base network address.
|
2661
|
-
#
|
2662
|
-
# Example:
|
2663
|
-
# cidr4.resize!(:Subnet => 23)
|
2664
|
-
# puts cidr4.desc --> 192.168.1.0/23
|
2665
|
-
#
|
2666
|
-
def resize!(options)
|
2667
|
-
unless(options.kind_of?(Hash))
|
2668
|
-
raise Argumenterror, "Expected Hash, but " +
|
2669
|
-
"#{options.class} provided."
|
2670
|
-
end
|
2671
|
-
|
2672
|
-
unless( options.has_key?(:Subnet) )
|
2673
|
-
raise Argumenterror, "Missing argument: Subnet."
|
2674
|
-
end
|
2675
|
-
bits = options[:Subnet]
|
2676
|
-
|
2677
|
-
if (@version == 4)
|
2678
|
-
netmask = IPAdmin.validate_ipv4_netmask(bits)
|
2679
|
-
else
|
2680
|
-
netmask = IPAdmin.validate_ipv6_netmask(bits)
|
2681
|
-
end
|
2682
|
-
|
2683
|
-
@netmask = netmask
|
2684
|
-
@network = @network & netmask
|
2685
|
-
@hostmask = @netmask ^ @all_f
|
2686
|
-
|
2687
|
-
# check @ip
|
2688
|
-
if ((@ip & @netmask) != (@network))
|
2689
|
-
@ip = @network
|
2690
|
-
end
|
2691
|
-
|
2692
|
-
return(nil)
|
2693
|
-
end
|
2694
|
-
|
2695
|
-
#======================================#
|
2696
|
-
#
|
2697
|
-
#======================================#
|
2698
|
-
|
2699
|
-
|
2700
|
-
#==============================================================================#
|
2701
|
-
# size()
|
2702
|
-
#==============================================================================#
|
2703
|
-
|
2704
|
-
# Provide number of IP addresses within this object.
|
2705
|
-
#
|
2706
|
-
# - Arguments:
|
2707
|
-
# * none
|
2708
|
-
#
|
2709
|
-
# - Returns:
|
2710
|
-
# * Number of IP addresses in this CIDR block.
|
2711
|
-
#
|
2712
|
-
# Example:
|
2713
|
-
# puts cidr4.size() --> 256
|
2714
|
-
#
|
2715
|
-
def size()
|
2716
|
-
return(@hostmask + 1)
|
2717
|
-
end
|
2718
|
-
|
2719
|
-
#======================================#
|
2720
|
-
#
|
2721
|
-
#======================================#
|
2722
|
-
|
2723
|
-
|
2724
|
-
#==============================================================================#
|
2725
|
-
# subnet()
|
2726
|
-
#==============================================================================#
|
2727
|
-
|
2728
|
-
# Subnet this object. Object will be fully subnetted into X number of subnets
|
2729
|
-
# of specified size. If :MinCount is provided, then method will return at least
|
2730
|
-
# that number of subnets (of size X). The remainder of the new subnets
|
2731
|
-
# will be merged together as tightly as possible. If a size is not provided,
|
2732
|
-
# then the current object will be split in half.
|
2733
|
-
#
|
2734
|
-
# - Arguments:
|
2735
|
-
# * Optional hash with the following fields:
|
2736
|
-
# - :IPCount -- Minimum number of IP's that new subnets should contain (optional)
|
2737
|
-
# - :MinCount -- Minimum number of X sized subnets to return (optional)
|
2738
|
-
# - :Objectify -- if true, return IPAdmin::CIDR objects (optional)
|
2739
|
-
# - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
|
2740
|
-
# - :Subnet -- Netmask (in bits) of new subnets (optional)
|
2741
|
-
#
|
2742
|
-
# - Returns:
|
2743
|
-
# * Array of CIDR addresses or IPAdmin::CIDR objects
|
2744
|
-
#
|
2745
|
-
# Note:
|
2746
|
-
# :Subnet always takes precedence over :IPCount.
|
2747
|
-
#
|
2748
|
-
# Example:
|
2749
|
-
# cidr_list = cidr4.subnet(:Subnet => 28, :MinCount => 3)
|
2750
|
-
# cidr_list = cidr4.subnet(:IPCount => 19)
|
2751
|
-
# puts cidr_list[0] --> 192.168.1.0/27
|
2752
|
-
#
|
2753
|
-
def subnet(options=nil)
|
2754
|
-
my_network = self.packed_network
|
2755
|
-
my_mask = self.bits
|
2756
|
-
subnet_bits = my_mask + 1
|
2757
|
-
min_count = nil
|
2758
|
-
objectify = false
|
2759
|
-
short = false
|
2760
|
-
|
2761
|
-
if (options)
|
2762
|
-
unless (options.kind_of? Hash)
|
2763
|
-
raise ArgumentError, "Expected Hash, but #{options.class} provided."
|
2764
|
-
end
|
2765
|
-
|
2766
|
-
if ( options.has_key?(:IPCount) )
|
2767
|
-
num_ips = options[:IPCount]
|
2768
|
-
bits_needed = 0
|
2769
|
-
until (2**bits_needed >= num_ips)
|
2770
|
-
bits_needed += 1
|
2771
|
-
end
|
2772
|
-
subnet_bits = @max_bits - bits_needed
|
2773
|
-
end
|
2774
|
-
|
2775
|
-
if ( options.has_key?(:Subnet) )
|
2776
|
-
subnet_bits = options[:Subnet]
|
2777
|
-
end
|
2778
|
-
|
2779
|
-
if ( options.has_key?(:MinCount) )
|
2780
|
-
min_count = options[:MinCount]
|
2781
|
-
end
|
2782
|
-
|
2783
|
-
if( options.has_key?(:Short) )
|
2784
|
-
short = true
|
2785
|
-
end
|
2786
|
-
|
2787
|
-
if( options.has_key?(:Objectify) )
|
2788
|
-
objectify = true
|
2789
|
-
end
|
2790
|
-
|
2791
|
-
end
|
2792
|
-
|
2793
|
-
# get number of subnets possible with the requested subnet_bits
|
2794
|
-
num_avail = 2**(subnet_bits - my_mask)
|
2795
|
-
|
2796
|
-
# get the number of bits in the next supernet and
|
2797
|
-
# make sure min_count is a power of 2
|
2798
|
-
bits_needed = 1
|
2799
|
-
min_count = num_avail if (!min_count)
|
2800
|
-
until (2**bits_needed >= min_count)
|
2801
|
-
bits_needed += 1
|
2802
|
-
end
|
2803
|
-
min_count = 2**bits_needed
|
2804
|
-
next_supernet_bits = subnet_bits - bits_needed
|
2805
|
-
|
2806
|
-
|
2807
|
-
# make sure subnet isnt bigger than available bits
|
2808
|
-
if (subnet_bits > @max_bits)
|
2809
|
-
raise "Requested subnet (#{subnet_bits}) does not fit " +
|
2810
|
-
"within the bounds of IPv#{@version}."
|
2811
|
-
end
|
2812
|
-
|
2813
|
-
# make sure subnet is larger than mymask
|
2814
|
-
if (subnet_bits < my_mask)
|
2815
|
-
raise "Requested subnet (#{subnet_bits}) is too large for " +
|
2816
|
-
"current CIDR space."
|
2817
|
-
end
|
2818
|
-
|
2819
|
-
# make sure MinCount is smaller than available subnets
|
2820
|
-
if (min_count > num_avail)
|
2821
|
-
raise "Requested subnet count (#{min_count}) exceeds subnets " +
|
2822
|
-
"available for allocation (#{num_avail})."
|
2823
|
-
end
|
2824
|
-
|
2825
|
-
# list all 'subnet_bits' sized subnets of this cidr block
|
2826
|
-
# with a limit of min_count
|
2827
|
-
bitstep = 2**(@max_bits - subnet_bits)
|
2828
|
-
subnets = self.enumerate(:Bitstep => bitstep, :Limit => min_count)
|
2829
|
-
|
2830
|
-
# turn the first min_count subnets into IPAdmin::CIDR
|
2831
|
-
# objects and push them to new_subnets
|
2832
|
-
new_subnets = []
|
2833
|
-
subnets.each do |subnet|
|
2834
|
-
subnet = IPAdmin.shorten(subnet) if ((@version == 6) && short && !objectify)
|
2835
|
-
if (!objectify)
|
2836
|
-
new_subnets.push("#{subnet}/#{subnet_bits}")
|
2837
|
-
else
|
2838
|
-
new_subnets.push(IPAdmin::CIDR.new(:CIDR => "#{subnet}/#{subnet_bits}"))
|
2839
|
-
end
|
2840
|
-
end
|
2841
|
-
|
2842
|
-
# now go through the rest of the cidr space and make the rest
|
2843
|
-
# of the subnets. we want these to be as tightly merged as possible
|
2844
|
-
next_supernet_bitstep = (bitstep * min_count)
|
2845
|
-
next_supernet_ip = my_network + next_supernet_bitstep
|
2846
|
-
until (next_supernet_bits == my_mask)
|
2847
|
-
if (@version == 4)
|
2848
|
-
next_network = IPAdmin.unpack_ipv4_addr(next_supernet_ip)
|
2849
|
-
else
|
2850
|
-
next_network = IPAdmin.unpack_ipv6_addr(next_supernet_ip)
|
2851
|
-
next_network = IPAdmin.shorten(next_network) if (short && !objectify)
|
2852
|
-
end
|
2853
|
-
|
2854
|
-
if (!objectify)
|
2855
|
-
new_subnets.push("#{next_network}/#{next_supernet_bits}")
|
2856
|
-
else
|
2857
|
-
new_subnets.push(IPAdmin::CIDR.new(:CIDR => "#{next_network}/#{next_supernet_bits}"))
|
2858
|
-
end
|
2859
|
-
|
2860
|
-
next_supernet_bits -= 1
|
2861
|
-
next_supernet_ip = next_supernet_ip + next_supernet_bitstep
|
2862
|
-
next_supernet_bitstep = next_supernet_bitstep << 1
|
2863
|
-
end
|
2864
|
-
|
2865
|
-
return(new_subnets)
|
2866
|
-
end
|
2867
|
-
|
2868
|
-
#======================================#
|
2869
|
-
#
|
2870
|
-
#======================================#
|
2871
|
-
|
2872
|
-
end
|
2873
|
-
|
2874
|
-
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
2875
|
-
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
2876
|
-
#
|
2877
|
-
# END class CIDR
|
2878
|
-
#
|
2879
|
-
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
2880
|
-
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
2881
|
-
|
2882
|
-
|
2883
|
-
|
2884
|
-
|
2885
|
-
|
2886
|
-
|
2887
|
-
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
2888
|
-
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
2889
|
-
#
|
2890
|
-
# BEGIN class Tree
|
2891
|
-
#
|
2892
|
-
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
2893
|
-
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
2894
|
-
|
2895
|
-
=begin rdoc
|
2896
|
-
A class & series of methods for creating and manipulating IP-based
|
2897
|
-
heirarchical trees. Both IPv4 and IPv6 are supported.
|
2898
|
-
|
2899
|
-
Tree's are useful for creating mini 'routing tables' of CIDR address space.
|
2900
|
-
Using a tree, you can quickly determine the 'route' of another CIDR via
|
2901
|
-
the standard longest-match algorithm.
|
2902
|
-
|
2903
|
-
Tree's are not dynamic, in that if you modify any of the CIDR objects
|
2904
|
-
contained within, the Tree will not automatically adjust itself accordingly.
|
2905
|
-
For example if you have a tree like the following:
|
2906
|
-
|
2907
|
-
192.168.0.0/24
|
2908
|
-
192.168.0.0/26
|
2909
|
-
192.168.1.0/24
|
2910
|
-
|
2911
|
-
You decide to resize 192.168.0.0/24 to 192.168.0.0/23. The tree will now
|
2912
|
-
be incorrect:
|
2913
|
-
|
2914
|
-
192.168.0.0/23
|
2915
|
-
192.168.0.0/26
|
2916
|
-
192.168.1.0/24
|
2917
|
-
|
2918
|
-
You would need to remove and re-add the CIDR 192.168.0.0/23 in order for the
|
2919
|
-
tree to rebuild itself properly.
|
2920
|
-
=end
|
2921
|
-
|
2922
|
-
class Tree
|
2923
|
-
|
2924
|
-
# instance variables
|
2925
|
-
# @all_f
|
2926
|
-
# @max_bits
|
2927
|
-
# @root
|
2928
|
-
# @version
|
2929
|
-
|
2930
|
-
#==============================================================================#
|
2931
|
-
# attr_reader / attr_writer
|
2932
|
-
#==============================================================================#
|
2933
|
-
|
2934
|
-
# IP version for this tree (4 or 6)
|
2935
|
-
attr_reader :version
|
2936
|
-
|
2937
|
-
#======================================#
|
2938
|
-
#
|
2939
|
-
#======================================#
|
2940
|
-
|
2941
|
-
|
2942
|
-
#==============================================================================#
|
2943
|
-
# initialize()
|
2944
|
-
#==============================================================================#
|
2945
|
-
|
2946
|
-
# - Arguments:
|
2947
|
-
# * Hash with the following fields:
|
2948
|
-
# - :Version -- IP version for this tree (4 or 6)
|
2949
|
-
#
|
2950
|
-
# Example:
|
2951
|
-
# table = IPAdmin::CIDRTable.new(:Version => 4)
|
2952
|
-
#
|
2953
|
-
def initialize(options)
|
2954
|
-
unless (options.kind_of? Hash)
|
2955
|
-
raise ArgumentError, "Expected Hash, but #{options.class} provided."
|
2956
|
-
end
|
2957
|
-
|
2958
|
-
if ( options.has_key?(:Version) )
|
2959
|
-
@version = options[:Version]
|
2960
|
-
|
2961
|
-
unless ( @version == 4 || @version == 6 )
|
2962
|
-
raise "IP version should be either 4 or 6."
|
2963
|
-
end
|
2964
|
-
else
|
2965
|
-
raise ArgumentError, "Missing argument: Version."
|
2966
|
-
end
|
2967
|
-
|
2968
|
-
if (@version == 4)
|
2969
|
-
@max_bits = 32
|
2970
|
-
@all_f = 2**@max_bits - 1
|
2971
|
-
else
|
2972
|
-
@max_bits = 128
|
2973
|
-
@all_f = 2**@max_bits - 1
|
2974
|
-
end
|
2975
|
-
|
2976
|
-
# root of our ordered IP tree
|
2977
|
-
@root = []
|
2978
|
-
|
2979
|
-
end
|
2980
|
-
|
2981
|
-
#======================================#
|
2982
|
-
#
|
2983
|
-
#======================================#
|
2984
|
-
|
2985
|
-
|
2986
|
-
#==============================================================================#
|
2987
|
-
# add()
|
2988
|
-
#==============================================================================#
|
2989
|
-
|
2990
|
-
# Add an IPAdmin::CIDR object to the tree.
|
2991
|
-
#
|
2992
|
-
# - Arguments:
|
2993
|
-
# * IPAdmin::CIDR object
|
2994
|
-
#
|
2995
|
-
# - Returns:
|
2996
|
-
# * nothing
|
2997
|
-
#
|
2998
|
-
# Example:
|
2999
|
-
# tree.add(cidr)
|
3000
|
-
#
|
3001
|
-
def add(object)
|
3002
|
-
|
3003
|
-
# validate object
|
3004
|
-
unless ( object.kind_of?(IPAdmin::CIDR) )
|
3005
|
-
raise ArgumentError, "IPAdmin::CIDR object " +
|
3006
|
-
"required but #{object.class} provided."
|
3007
|
-
end
|
3008
|
-
|
3009
|
-
unless (object.version == @version )
|
3010
|
-
raise "IP version #{object.version} is incompatible with " +
|
3011
|
-
"Tree IP version #{@version}."
|
3012
|
-
end
|
3013
|
-
|
3014
|
-
net_struct = IPAdmin.create_net_struct(object)
|
3015
|
-
|
3016
|
-
# search tree for it's home branch
|
3017
|
-
search_results = find_home(net_struct, @root)
|
3018
|
-
home_struct = search_results[0] if (search_results)
|
3019
|
-
home_branch = home_struct.subnets if (search_results)
|
3020
|
-
home_branch = @root if (!home_branch)
|
3021
|
-
|
3022
|
-
# make sure we dont have a duplicate
|
3023
|
-
if (home_struct)
|
3024
|
-
if ( (net_struct.network == home_struct.network) &&
|
3025
|
-
(net_struct.netmask == home_struct.netmask) )
|
3026
|
-
raise "Attempted to add #{object.desc()} multiple times."
|
3027
|
-
end
|
3028
|
-
end
|
3029
|
-
|
3030
|
-
# now that we have our branch location, check that other entries
|
3031
|
-
# of this branch are not subnets of our new object. if they are
|
3032
|
-
# then make them a branch of our new object
|
3033
|
-
home_branch.each do |entry|
|
3034
|
-
is_contained = contains(net_struct, entry)
|
3035
|
-
|
3036
|
-
if (is_contained)
|
3037
|
-
net_struct.subnets.push(entry)
|
3038
|
-
end
|
3039
|
-
end
|
3040
|
-
|
3041
|
-
net_struct.subnets.each do |entry|
|
3042
|
-
home_branch.delete(entry)
|
3043
|
-
end
|
3044
|
-
|
3045
|
-
# add new object as an ordered entry to home_branch
|
3046
|
-
add_to_branch(net_struct,home_branch)
|
3047
|
-
|
3048
|
-
return(nil)
|
3049
|
-
end
|
3050
|
-
|
3051
|
-
#======================================#
|
3052
|
-
#
|
3053
|
-
#======================================#
|
3054
|
-
|
3055
|
-
|
3056
|
-
#==============================================================================#
|
3057
|
-
# add_to_branch() private
|
3058
|
-
#==============================================================================#
|
3059
|
-
|
3060
|
-
# Add Netstruct object to an array of NetStruct's
|
3061
|
-
#
|
3062
|
-
# - Arguments:
|
3063
|
-
# * NetStruct, and array of NetStruct's in which to add it
|
3064
|
-
#
|
3065
|
-
# - Returns:
|
3066
|
-
# * none
|
3067
|
-
#
|
3068
|
-
# Example:
|
3069
|
-
# add_to_branch(net_struct,branch)
|
3070
|
-
#
|
3071
|
-
def add_to_branch(net_struct,branch)
|
3072
|
-
# make sure we have appropriate types
|
3073
|
-
unless (net_struct.kind_of?(IPAdmin::NetStruct) &&
|
3074
|
-
branch.kind_of?(Array) )
|
3075
|
-
raise ArgumentError, "Incorrect argument types " +
|
3076
|
-
"#{net_struct.class} and #{branch}."
|
3077
|
-
end
|
3078
|
-
|
3079
|
-
index = 0
|
3080
|
-
branch.each do
|
3081
|
-
if(net_struct.network < (branch[index]).network)
|
3082
|
-
break
|
3083
|
-
end
|
3084
|
-
index += 1
|
3085
|
-
end
|
3086
|
-
|
3087
|
-
branch.insert(index, net_struct)
|
3088
|
-
|
3089
|
-
return()
|
3090
|
-
end
|
3091
|
-
|
3092
|
-
#======================================#
|
3093
|
-
#
|
3094
|
-
#======================================#
|
3095
|
-
|
3096
|
-
|
3097
|
-
#==============================================================================#
|
3098
|
-
# collapse()
|
3099
|
-
#==============================================================================#
|
3100
|
-
|
3101
|
-
# Collapse (supernet) all subnets of the tree,
|
3102
|
-
# and return a new tree. The supernetting of subnets will only occur in
|
3103
|
-
# situations where the newly created supernet will not result in the
|
3104
|
-
# 'creation' of additional space. For example the following blocks
|
3105
|
-
# (192.168.0.0/24, 192.168.1.0/24, and 192.168.2.0/24) would merge into
|
3106
|
-
# 192.168.0.0/23 and 192.168.2.0/24 rather than into 192.168.0.0/22.
|
3107
|
-
#
|
3108
|
-
# - Arguments:
|
3109
|
-
# * none
|
3110
|
-
#
|
3111
|
-
# - Returns:
|
3112
|
-
# * IPAdmin::Tree object
|
3113
|
-
#
|
3114
|
-
# Example:
|
3115
|
-
# new_tree = tree.collapse()
|
3116
|
-
#
|
3117
|
-
def collapse()
|
3118
|
-
tree = IPAdmin::Tree.new(:Version => @version)
|
3119
|
-
branch = IPAdmin.merge(:List => @root, :NetStruct => 1)
|
3120
|
-
dumped = dump_branch(branch)
|
3121
|
-
dumped.each do |entry|
|
3122
|
-
net_struct = entry[:NetStruct]
|
3123
|
-
|
3124
|
-
if (@version == 4)
|
3125
|
-
network = IPAdmin.unpack_ipv4_addr(net_struct.network)
|
3126
|
-
netmask = IPAdmin.unpack_ipv4_netmask(net_struct.netmask)
|
3127
|
-
else
|
3128
|
-
network = IPAdmin.unpack_ipv6_addr(net_struct.network)
|
3129
|
-
netmask = IPAdmin.unpack_ipv6_netmask(net_struct.netmask)
|
3130
|
-
end
|
3131
|
-
cidr = IPAdmin::CIDR.new(:CIDR => "#{network}/#{netmask}")
|
3132
|
-
tree.add(cidr)
|
3133
|
-
end
|
3134
|
-
return(tree)
|
3135
|
-
end
|
3136
|
-
|
3137
|
-
#======================================#
|
3138
|
-
#
|
3139
|
-
#======================================#
|
3140
|
-
|
3141
|
-
|
3142
|
-
#==============================================================================#
|
3143
|
-
# contains() private
|
3144
|
-
#==============================================================================#
|
3145
|
-
|
3146
|
-
# Is the first NetStruct object the supernet of the second?
|
3147
|
-
#
|
3148
|
-
# - Arguments:
|
3149
|
-
# * Two NetStruct objects
|
3150
|
-
#
|
3151
|
-
# - Returns:
|
3152
|
-
# * true if struct1 contains or is equal to struct2, or false if not.
|
3153
|
-
#
|
3154
|
-
# Example:
|
3155
|
-
# is_contained = contains(struct1,struct2)
|
3156
|
-
#
|
3157
|
-
def contains(struct1,struct2)
|
3158
|
-
|
3159
|
-
# make sure we have appropriate types
|
3160
|
-
unless (struct1.kind_of?(IPAdmin::NetStruct) &&
|
3161
|
-
struct2.kind_of?(IPAdmin::NetStruct) )
|
3162
|
-
raise ArgumentError, "Incorrect argument types " +
|
3163
|
-
"#{struct1.class} and #{struct2.class}."
|
3164
|
-
end
|
3165
|
-
|
3166
|
-
network1 = struct1.network
|
3167
|
-
netmask1 = struct1.netmask
|
3168
|
-
network2 = struct2.network
|
3169
|
-
netmask2 = struct2.netmask
|
3170
|
-
hostmask1 = netmask1 ^ @all_f
|
3171
|
-
|
3172
|
-
# if network1 == network2, then we have to go by netmask length
|
3173
|
-
# else we can tell by or'ing network1 and network2 by hostmask1
|
3174
|
-
# and comparing the results
|
3175
|
-
is_contained = false
|
3176
|
-
if (network1 == network2)
|
3177
|
-
if (netmask2 >= netmask1)
|
3178
|
-
is_contained = true
|
3179
|
-
end
|
3180
|
-
else
|
3181
|
-
if ( (network1 | hostmask1) == (network2 | hostmask1) )
|
3182
|
-
is_contained = true
|
3183
|
-
end
|
3184
|
-
end
|
3185
|
-
|
3186
|
-
return(is_contained)
|
3187
|
-
end
|
3188
|
-
|
3189
|
-
#======================================#
|
3190
|
-
#
|
3191
|
-
#======================================#
|
3192
|
-
|
3193
|
-
|
3194
|
-
#==============================================================================#
|
3195
|
-
# dump
|
3196
|
-
#==============================================================================#
|
3197
|
-
|
3198
|
-
# Dump the contents of this tree.
|
3199
|
-
#
|
3200
|
-
# - Arguments:
|
3201
|
-
# * none
|
3202
|
-
#
|
3203
|
-
# - Returns:
|
3204
|
-
# * ordered array of hashes with the following fields:
|
3205
|
-
# - :Object => CIDR object
|
3206
|
-
# - :Depth => (depth level in tree)
|
3207
|
-
# Example:
|
3208
|
-
# dumped = tree.dump()
|
3209
|
-
#
|
3210
|
-
def dump()
|
3211
|
-
list = []
|
3212
|
-
dumped = dump_branch(@root)
|
3213
|
-
|
3214
|
-
dumped.each do |entry|
|
3215
|
-
depth = entry[:Depth]
|
3216
|
-
net_struct = entry[:NetStruct]
|
3217
|
-
object = net_struct.object
|
3218
|
-
list.push({:Depth => depth, :Object => object})
|
3219
|
-
end
|
3220
|
-
|
3221
|
-
return(list)
|
3222
|
-
end
|
3223
|
-
|
3224
|
-
#======================================#
|
3225
|
-
#
|
3226
|
-
#======================================#
|
3227
|
-
|
3228
|
-
|
3229
|
-
#==============================================================================#
|
3230
|
-
# dump_branch() private
|
3231
|
-
#==============================================================================#
|
3232
|
-
|
3233
|
-
# Dump contents of an Array of NetStruct objects
|
3234
|
-
#
|
3235
|
-
# - Arguments:
|
3236
|
-
# * array of NetStruct objects, and an optional depth within the tree
|
3237
|
-
#
|
3238
|
-
# - Returns:
|
3239
|
-
# * array of hashes in form: :NetStruct => IPAdmin::NetStruct object
|
3240
|
-
# :Depth => (depth level in tree)
|
3241
|
-
# Example:
|
3242
|
-
# dumped = dump_branch(branch)
|
3243
|
-
#
|
3244
|
-
def dump_branch(branch,depth=nil)
|
3245
|
-
list = []
|
3246
|
-
depth = 0 if (!depth)
|
3247
|
-
|
3248
|
-
branch.each do |entry|
|
3249
|
-
subnets = entry.subnets
|
3250
|
-
list.push({:NetStruct => entry, :Depth => depth})
|
3251
|
-
|
3252
|
-
if (subnets.length > 0)
|
3253
|
-
list.concat( dump_branch(subnets, (depth+1) ) )
|
3254
|
-
end
|
3255
|
-
|
3256
|
-
end
|
3257
|
-
|
3258
|
-
return(list)
|
3259
|
-
end
|
3260
|
-
|
3261
|
-
#======================================#
|
3262
|
-
#
|
3263
|
-
#======================================#
|
3264
|
-
|
3265
|
-
|
3266
|
-
#==============================================================================#
|
3267
|
-
# exists?()
|
3268
|
-
#==============================================================================#
|
3269
|
-
|
3270
|
-
# Has an IPAdmin::CIDR object already been added to the tree?
|
3271
|
-
#
|
3272
|
-
# - Arguments:
|
3273
|
-
# * IPAdmin::CIDR object
|
3274
|
-
#
|
3275
|
-
# - Returns:
|
3276
|
-
# * true or false
|
3277
|
-
#
|
3278
|
-
# Example:
|
3279
|
-
# added = tree.exists?(cidr)
|
3280
|
-
#
|
3281
|
-
def exists?(object)
|
3282
|
-
|
3283
|
-
# validate object
|
3284
|
-
unless ( object.kind_of?(IPAdmin::CIDR) )
|
3285
|
-
raise ArgumentError, "IPAdmin::CIDR object " +
|
3286
|
-
"required but #{object.class} provided."
|
3287
|
-
end
|
3288
|
-
|
3289
|
-
unless (object.version == @version )
|
3290
|
-
raise "IP version #{object.version} is incompatible with " +
|
3291
|
-
"Tree IP version #{@version}."
|
3292
|
-
end
|
3293
|
-
|
3294
|
-
net_struct = IPAdmin.create_net_struct(object)
|
3295
|
-
home_struct,home_branch = find_home(net_struct, @root)
|
3296
|
-
|
3297
|
-
# check for match
|
3298
|
-
found = false
|
3299
|
-
if (home_struct)
|
3300
|
-
if ( (net_struct.network == home_struct.network) &&
|
3301
|
-
(net_struct.netmask == home_struct.netmask) )
|
3302
|
-
found = true
|
3303
|
-
end
|
3304
|
-
end
|
3305
|
-
|
3306
|
-
|
3307
|
-
return(found)
|
3308
|
-
end
|
3309
|
-
|
3310
|
-
#======================================#
|
3311
|
-
#
|
3312
|
-
#======================================#
|
3313
|
-
|
3314
|
-
|
3315
|
-
#==============================================================================#
|
3316
|
-
# find()
|
3317
|
-
#==============================================================================#
|
3318
|
-
|
3319
|
-
# Find the longest matching branch of our tree to which an
|
3320
|
-
# IPAdmin::CIDR object belongs. Useful for performing 'routing table' lookups.
|
3321
|
-
#
|
3322
|
-
# - Arguments:
|
3323
|
-
# * IPAdmin::CIDR object
|
3324
|
-
#
|
3325
|
-
# - Returns:
|
3326
|
-
# * IPAdmin::CIDR object, or nil if not found
|
3327
|
-
#
|
3328
|
-
# Example:
|
3329
|
-
# longest_match = tree.find(ip)
|
3330
|
-
#
|
3331
|
-
def find(object)
|
3332
|
-
unless ( object.kind_of?(IPAdmin::CIDR) )
|
3333
|
-
raise ArgumentError, "IPAdmin::CIDR object " +
|
3334
|
-
"required but #{object.class} provided."
|
3335
|
-
end
|
3336
|
-
|
3337
|
-
unless (object.version == @version )
|
3338
|
-
raise "IP version #{object.version} is incompatible with " +
|
3339
|
-
"Tree IP version #{@version}."
|
3340
|
-
end
|
3341
|
-
|
3342
|
-
net_struct = IPAdmin.create_net_struct(object)
|
3343
|
-
home_struct,home_branch = find_home(net_struct, @root)
|
3344
|
-
found_obj = home_struct.object if (home_struct)
|
3345
|
-
|
3346
|
-
return(found_obj)
|
3347
|
-
end
|
3348
|
-
|
3349
|
-
#======================================#
|
3350
|
-
#
|
3351
|
-
#======================================#
|
3352
|
-
|
3353
|
-
|
3354
|
-
#==============================================================================#
|
3355
|
-
# find_home() private
|
3356
|
-
#==============================================================================#
|
3357
|
-
|
3358
|
-
# Find the branch to which a NetStruct object belongs.
|
3359
|
-
#
|
3360
|
-
# - Arguments:
|
3361
|
-
# * IPAdmin::NetStruct to find, and Array of
|
3362
|
-
# IPAdmin::NetStruct's to search
|
3363
|
-
#
|
3364
|
-
# - Returns:
|
3365
|
-
# * array of: container IPAdmin::NetStruct object,
|
3366
|
-
# and container branch, or nil
|
3367
|
-
#
|
3368
|
-
# Example:
|
3369
|
-
# branch = find_home(net_struct,branch)
|
3370
|
-
#
|
3371
|
-
def find_home(net_struct,branch)
|
3372
|
-
ret_val = nil
|
3373
|
-
|
3374
|
-
branch.each do |entry|
|
3375
|
-
is_contained = contains(entry,net_struct)
|
3376
|
-
|
3377
|
-
if (is_contained)
|
3378
|
-
home_struct = entry
|
3379
|
-
home_branch = branch
|
3380
|
-
|
3381
|
-
if (home_struct.subnets.length > 0)
|
3382
|
-
search_results = find_home(net_struct,home_struct.subnets)
|
3383
|
-
if (search_results)
|
3384
|
-
home_struct = search_results[0]
|
3385
|
-
home_branch = search_results[1]
|
3386
|
-
end
|
3387
|
-
end
|
3388
|
-
|
3389
|
-
ret_val = [home_struct,home_branch]
|
3390
|
-
break
|
3391
|
-
end
|
3392
|
-
end
|
3393
|
-
|
3394
|
-
return(ret_val)
|
3395
|
-
end
|
3396
|
-
|
3397
|
-
#======================================#
|
3398
|
-
#
|
3399
|
-
#======================================#
|
3400
|
-
|
3401
|
-
|
3402
|
-
#==============================================================================#
|
3403
|
-
# find_space()
|
3404
|
-
#==============================================================================#
|
3405
|
-
|
3406
|
-
# Find Tree entries that can hold a subnet of size X. Returns only the smallest
|
3407
|
-
# matching subnets of each supernet. If neither IPCount or Subnet or provided,
|
3408
|
-
# then method returns all entries that can fit a single IP.
|
3409
|
-
#
|
3410
|
-
# - Arguments:
|
3411
|
-
# * Hash with the following fields:
|
3412
|
-
# - :IPCount -- Minimum number of IP's that should fit within subnet (optional)
|
3413
|
-
# - :Limit -- Maximum entries to return (optional)
|
3414
|
-
# - :Subnet -- Netmask (in bits) of space to find(optional)
|
3415
|
-
#
|
3416
|
-
# - Returns:
|
3417
|
-
# * ordered array of IPAdmin::CIDR objects
|
3418
|
-
#
|
3419
|
-
# Note:
|
3420
|
-
# :Subnet always takes precedence over :IPCount.
|
3421
|
-
#
|
3422
|
-
# Example:
|
3423
|
-
# list = tree.find_space(:Subnet => 27)
|
3424
|
-
# list = tree.find_space(:IPCount => 33, :Limit => 2)
|
3425
|
-
#
|
3426
|
-
def find_space(options)
|
3427
|
-
limit = nil
|
3428
|
-
list = []
|
3429
|
-
|
3430
|
-
# validate options
|
3431
|
-
unless (options.kind_of? Hash)
|
3432
|
-
raise ArgumentError, "Expected Hash, but #{options.class} provided."
|
3433
|
-
end
|
3434
|
-
|
3435
|
-
if ( options.has_key?(:Limit) )
|
3436
|
-
limit = options[:Limit]
|
3437
|
-
end
|
3438
|
-
|
3439
|
-
if ( options.has_key?(:IPCount) )
|
3440
|
-
num_ips = options[:IPCount]
|
3441
|
-
bits_needed = 0
|
3442
|
-
until (2**bits_needed >= num_ips)
|
3443
|
-
bits_needed += 1
|
3444
|
-
end
|
3445
|
-
subnet_size = @max_bits - bits_needed
|
3446
|
-
end
|
3447
|
-
|
3448
|
-
if ( options.has_key?(:Subnet) )
|
3449
|
-
subnet_size = options[:Subnet]
|
3450
|
-
end
|
3451
|
-
|
3452
|
-
# check that subnet_size is a valid size
|
3453
|
-
if (@version == 4)
|
3454
|
-
subnet_size = 32 if (!subnet_size)
|
3455
|
-
unless ( (subnet_size > 0) && (subnet_size < 33) )
|
3456
|
-
raise "#{subnet_size} is out of range for an " +
|
3457
|
-
"IP version #{@version} Tree."
|
3458
|
-
end
|
3459
|
-
|
3460
|
-
else
|
3461
|
-
subnet_size = 128 if (!subnet_size)
|
3462
|
-
unless ( (subnet_size > 0) && (subnet_size < 129) )
|
3463
|
-
raise "#{subnet_size} is out of range for an " +
|
3464
|
-
"IP version #{@version} Tree."
|
3465
|
-
end
|
3466
|
-
end
|
3467
|
-
|
3468
|
-
search_list = dump_branch(@root)
|
3469
|
-
|
3470
|
-
search_list.each do |entry|
|
3471
|
-
depth = entry[:Depth]
|
3472
|
-
net_struct = entry[:NetStruct]
|
3473
|
-
|
3474
|
-
# we only want leaf nodes of type IPAdmin::CIDR
|
3475
|
-
if ( (net_struct.subnets.length == 0) &&
|
3476
|
-
(net_struct.object.kind_of?(IPAdmin::CIDR)) )
|
3477
|
-
|
3478
|
-
if (subnet_size >= net_struct.object.bits)
|
3479
|
-
list.push(net_struct.object)
|
3480
|
-
end
|
3481
|
-
end
|
3482
|
-
|
3483
|
-
if (limit && (list.length >= limit) )
|
3484
|
-
break
|
3485
|
-
end
|
3486
|
-
|
3487
|
-
end
|
3488
|
-
|
3489
|
-
return(list)
|
3490
|
-
|
3491
|
-
end
|
3492
|
-
|
3493
|
-
#======================================#
|
3494
|
-
#
|
3495
|
-
#======================================#
|
3496
|
-
|
3497
|
-
|
3498
|
-
#==============================================================================#
|
3499
|
-
# prune()
|
3500
|
-
#==============================================================================#
|
3501
|
-
|
3502
|
-
# Remove an IPAdmin::CIDR object, and all child
|
3503
|
-
# objects from the tree.
|
3504
|
-
#
|
3505
|
-
# - Arguments:
|
3506
|
-
# * IPAdmin::CIDR object
|
3507
|
-
#
|
3508
|
-
# - Returns:
|
3509
|
-
# * true on success, or false
|
3510
|
-
#
|
3511
|
-
# Example:
|
3512
|
-
# did_prune = tree.prune(ip)
|
3513
|
-
#
|
3514
|
-
def prune(object)
|
3515
|
-
unless ( object.kind_of?(IPAdmin::CIDR) )
|
3516
|
-
raise ArgumentError, "IPAdmin::CIDR object " +
|
3517
|
-
"required but #{object.class} provided."
|
3518
|
-
end
|
3519
|
-
|
3520
|
-
unless (object.version == @version )
|
3521
|
-
raise "IP version #{object.version} is incompatible with " +
|
3522
|
-
"Tree IP version #{@version}."
|
3523
|
-
end
|
3524
|
-
|
3525
|
-
net_struct = IPAdmin.create_net_struct(object)
|
3526
|
-
home_struct,home_branch = find_home(net_struct, @root)
|
3527
|
-
|
3528
|
-
if(!home_struct)
|
3529
|
-
raise "#{object.desc} could not be found."
|
3530
|
-
end
|
3531
|
-
|
3532
|
-
# remove if home_struct.object = object
|
3533
|
-
pruned = false
|
3534
|
-
if (home_struct.object = object)
|
3535
|
-
home_branch.delete(home_struct)
|
3536
|
-
pruned = true
|
3537
|
-
end
|
3538
|
-
|
3539
|
-
return(pruned)
|
3540
|
-
end
|
3541
|
-
|
3542
|
-
#======================================#
|
3543
|
-
#
|
3544
|
-
#======================================#
|
3545
|
-
|
3546
|
-
|
3547
|
-
#==============================================================================#
|
3548
|
-
# remove()
|
3549
|
-
#==============================================================================#
|
3550
|
-
|
3551
|
-
# Remove a single IPAdmin::CIDR object from the tree.
|
3552
|
-
#
|
3553
|
-
# - Arguments:
|
3554
|
-
# * IPAdmin::CIDR object
|
3555
|
-
#
|
3556
|
-
# - Returns:
|
3557
|
-
# * true on success, or false
|
3558
|
-
#
|
3559
|
-
# Example:
|
3560
|
-
# did_remove = tree.remove(ip)
|
3561
|
-
#
|
3562
|
-
def remove(object)
|
3563
|
-
unless ( object.kind_of?(IPAdmin::CIDR) )
|
3564
|
-
raise ArgumentError, "IPAdmin::CIDR object " +
|
3565
|
-
"required but #{object.class} provided."
|
3566
|
-
end
|
3567
|
-
|
3568
|
-
unless (object.version == @version )
|
3569
|
-
raise "IP version #{object.version} is incompatible with " +
|
3570
|
-
"Tree IP version #{@version}."
|
3571
|
-
end
|
3572
|
-
|
3573
|
-
net_struct = IPAdmin.create_net_struct(object)
|
3574
|
-
home_struct,home_branch = find_home(net_struct, @root)
|
3575
|
-
|
3576
|
-
# remove if home_struct.object = object
|
3577
|
-
removed = false
|
3578
|
-
if (home_struct.object = object)
|
3579
|
-
|
3580
|
-
# if we have children subnets, move them up one level
|
3581
|
-
if (home_struct.subnets.length > 0)
|
3582
|
-
home_struct.subnets.each do |entry|
|
3583
|
-
index = 0
|
3584
|
-
home_branch.each do
|
3585
|
-
if(entry.network < (home_branch[index]).network)
|
3586
|
-
break
|
3587
|
-
end
|
3588
|
-
index += 1
|
3589
|
-
end
|
3590
|
-
home_branch.insert(index, entry)
|
3591
|
-
end
|
3592
|
-
end
|
3593
|
-
|
3594
|
-
home_branch.delete(home_struct)
|
3595
|
-
removed = true
|
3596
|
-
end
|
3597
|
-
|
3598
|
-
return(removed)
|
3599
|
-
end
|
3600
|
-
|
3601
|
-
#======================================#
|
3602
|
-
#
|
3603
|
-
#======================================#
|
3604
|
-
|
3605
|
-
|
3606
|
-
#==============================================================================#
|
3607
|
-
# show()
|
3608
|
-
#==============================================================================#
|
3609
|
-
|
3610
|
-
# Print the tree in a nicely formatted string.
|
3611
|
-
#
|
3612
|
-
# - Arguments:
|
3613
|
-
# * none
|
3614
|
-
#
|
3615
|
-
# - Returns:
|
3616
|
-
# * String representing the contents of the tree
|
3617
|
-
#
|
3618
|
-
# Example:
|
3619
|
-
# puts tree.show()
|
3620
|
-
#
|
3621
|
-
def show()
|
3622
|
-
printed = ""
|
3623
|
-
list = dump_branch(@root)
|
3624
|
-
|
3625
|
-
list.each do |entry|
|
3626
|
-
net_struct = entry[:NetStruct]
|
3627
|
-
depth = entry[:Depth]
|
3628
|
-
|
3629
|
-
if (@version == 4)
|
3630
|
-
network = IPAdmin.unpack_ipv4_addr(net_struct.network)
|
3631
|
-
netmask = IPAdmin.unpack_ipv4_netmask(net_struct.netmask)
|
3632
|
-
|
3633
|
-
else
|
3634
|
-
network = IPAdmin.unpack_ipv6_addr(net_struct.network)
|
3635
|
-
netmask = IPAdmin.unpack_ipv6_netmask(net_struct.netmask)
|
3636
|
-
network = IPAdmin.shorten(network)
|
3637
|
-
end
|
3638
|
-
|
3639
|
-
if (depth == 0)
|
3640
|
-
indent = ""
|
3641
|
-
else
|
3642
|
-
indent = " " * (depth*3)
|
3643
|
-
end
|
3644
|
-
|
3645
|
-
printed << "#{indent}#{network}/#{netmask}\n"
|
3646
|
-
end
|
3647
|
-
|
3648
|
-
return(printed)
|
3649
|
-
end
|
3650
|
-
|
3651
|
-
#======================================#
|
3652
|
-
#
|
3653
|
-
#======================================#
|
3654
|
-
|
3655
|
-
private :add_to_branch, :contains, :dump_branch, :find_home
|
3656
|
-
|
3657
|
-
end
|
3658
|
-
|
3659
|
-
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
3660
|
-
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
3661
|
-
#
|
3662
|
-
# END class Tree
|
3663
|
-
#
|
3664
|
-
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
3665
|
-
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
3666
|
-
|
3667
|
-
|
3668
|
-
#==============================================================================#
|
3669
|
-
# NetStruct
|
3670
|
-
#==============================================================================#
|
3671
|
-
|
3672
|
-
# Struct object used internally by IPAdmin. It is not likely directly useful
|
3673
|
-
# to anyone.
|
3674
|
-
#
|
3675
|
-
# Description of fields:
|
3676
|
-
# * network - byte-packed IP network
|
3677
|
-
# * netmask - byte-packed IP netmask
|
3678
|
-
# * version - IP version
|
3679
|
-
# * object - holds an IPAdmin class object
|
3680
|
-
# * subnets - an array of children IPAdmin::NetStruct objects
|
3681
|
-
#
|
3682
|
-
NetStruct = Struct.new(:network, :netmask, :version, :object, :subnets)
|
3683
|
-
|
3684
|
-
#======================================#
|
3685
|
-
#
|
3686
|
-
#======================================#
|
3687
|
-
|
3688
|
-
end # module IPAdmin
|
3689
11
|
__END__
|
3690
|
-
|