ipadmin 0.3.0 → 0.4.0

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/lib/methods.rb CHANGED
@@ -1,68 +1,63 @@
1
- =begin rdoc
2
- Copyright (c) 2006 Dustin Spinhirne -
3
- Licensed under the same terms as Ruby, No Warranty is provided.
4
- =end
5
-
6
-
7
-
8
1
  module IPAdmin
9
2
 
10
3
  #==============================================================================#
11
4
  # compare()
12
5
  #==============================================================================#
13
6
 
14
- # Compare CIDR or NetStruct objects, and determine if one
7
+ # Compare (2) CIDR addresses, and determine if one
15
8
  # is the supernet of the other.
16
9
  #
17
10
  # - Arguments:
18
- # * Two CIDR or NetStruct objects
11
+ # * (2) CIDR addresses or IPAdmin::CIDR objects
19
12
  #
20
13
  # - Returns:
21
- # * if one object is a subnet of another, then return an array in order of
22
- # [supernet,subnet]
14
+ # * if one is a subnet of another, then return original array in order of [supernet,subnet]
23
15
  # * if both are equal, return 1
24
16
  # * if neither is a supernet of the other, return nil
25
17
  #
26
- # Example:
18
+ # Examples:
27
19
  # supernet,subnet = IPAdmin.compare(cidr1,cidr2)
20
+ # supernet,subnet = IPAdmin.compare('192.168.1.0/24','192.168.1.32/27')
28
21
  #
29
22
 
30
- def compare(obj1,obj2)
23
+ def compare(cidr1,cidr2)
31
24
  ret_val = nil
32
-
33
- # validate arguments
34
- if ( !obj1.kind_of?(IPAdmin::CIDR) &&
35
- !obj1.kind_of?(IPAdmin::NetStruct) )
36
- raise ArgumentError, "Expected IPAdmin::CIDR or NetStruct " +
37
- "as first argument, but #{obj1.class} provided."
25
+ orig1 = cidr1
26
+ orig2 = cidr2
27
+
28
+ # if args are not CIDR objects, then attempt to create
29
+ # cidr objects from them
30
+ if ( !cidr1.kind_of?(IPAdmin::CIDR) )
31
+ begin
32
+ cidr1 = IPAdmin::CIDR.new(:CIDR => cidr1)
33
+ rescue Exception => error
34
+ raise ArgumentError, "First provided argument raised the following " +
35
+ "errors: #{error}"
36
+ end
38
37
  end
39
38
 
40
- if ( !obj2.kind_of?(IPAdmin::CIDR) &&
41
- !obj2.kind_of?(IPAdmin::NetStruct) )
42
- raise ArgumentError, "Expected IPAdmin::CIDR or NetStruct " +
43
- "as second argument, but #{obj2.class} provided."
39
+ if ( !cidr2.kind_of?(IPAdmin::CIDR))
40
+ begin
41
+ cidr2 = IPAdmin::CIDR.new(:CIDR => cidr2)
42
+ rescue Exception => error
43
+ raise ArgumentError, "Second provided argument raised the following " +
44
+ "errors: #{error}"
45
+ end
44
46
  end
45
47
 
46
48
  # make sure both are same version
47
- if (obj1.version != obj2.version )
48
- raise ArgumentError, "Provided objects are of different IP versions."
49
+ if (cidr1.version != cidr2.version )
50
+ raise ArgumentError, "Provided CIDR addresses are of different IP versions."
49
51
  end
50
52
 
51
53
 
52
54
  # get network/netmask of each
53
- objects = [obj1,obj2]
55
+ objects = [cidr1,cidr2]
54
56
  networks = []
55
57
  netmasks = []
56
58
  for obj in objects
57
- if ( obj.kind_of?(IPAdmin::CIDR) )
58
- networks.push(obj.packed_network)
59
- netmasks.push(obj.packed_netmask)
60
-
61
- elsif ( obj.kind_of?(IPAdmin::NetStruct) )
62
- networks.push(obj.network)
63
- netmasks.push(obj.netmask)
64
-
65
- end
59
+ networks.push(obj.packed_network)
60
+ netmasks.push(obj.packed_netmask)
66
61
  end
67
62
 
68
63
  # return 1's if objects are equal otherwise
@@ -73,11 +68,11 @@ def compare(obj1,obj2)
73
68
  ret_val = 1
74
69
  elsif (netmasks[0] < netmasks[1])
75
70
  if ( (netmasks[0] & networks[0]) == (netmasks[0] & networks[1]) )
76
- ret_val = [obj1,obj2]
71
+ ret_val = [orig1,orig2]
77
72
  end
78
73
  elsif (netmasks[1] < netmasks[0])
79
74
  if ( (netmasks[1] & networks[0]) == (netmasks[1] & networks[1]) )
80
- ret_val = [obj2,obj1]
75
+ ret_val = [orig2,orig1]
81
76
  end
82
77
  end
83
78
 
@@ -90,69 +85,26 @@ module_function :compare
90
85
  #======================================#
91
86
 
92
87
 
93
- #==============================================================================#
94
- # create_net_struct()
95
- #==============================================================================#
96
-
97
- # Create a NetStruct object from an CIDR or NetStruct object.
98
- # This type of Struct is used internally for various tasks, and is not likely
99
- # to be useful to anyone.
100
- #
101
- # - Arguments:
102
- # * CIDR or NetStruct object
103
- #
104
- # - Returns:
105
- # * NetStruct object
106
- #
107
- # Example:
108
- # net_struct = IPAdmin.create_net_struct(object)
109
- #
110
- def create_net_struct(object)
111
-
112
- if ( object.kind_of?(IPAdmin::CIDR) )
113
- network = object.packed_ip
114
- netmask = object.packed_netmask
115
-
116
- elsif ( object.kind_of?(IPAdmin::NetStruct) )
117
- network = object.network
118
- netmask = object.netmask
119
- else
120
- raise ArgumentError, "Expected IPAdmin::CIDR or NetStruct "+
121
- "object, but #{object.class} provided."
122
- end
123
-
124
- version = object.version
125
- net_struct = NetStruct.new(network,netmask,version,object,[])
126
-
127
- return(net_struct)
128
- end
129
- module_function :create_net_struct
130
-
131
- #======================================#
132
- #
133
- #======================================#
134
-
135
-
136
88
  #==============================================================================#
137
89
  # merge()
138
90
  #==============================================================================#
139
91
 
140
- # Given a list of CIDR or NetStruct objects
141
- # merge (supernet) them in the most efficient way possible. Supernetting
142
- # will only occur when the newly created supernet will not result in the
92
+ # Given a list of CIDR addresses or IPAdmin::CIDR objects of the same version,
93
+ # merge (summarize) them in the most efficient way possible. Summarization
94
+ # will only occur when the newly created supernets will not result in the
143
95
  # 'creation' of additional space. For example the following blocks
144
- # (192.168.0.0/24, 192.168.1.0/24, and 192.168.2.0/24) would merge into
96
+ # (192.168.0.0/24, 192.168.1.0/24, and 192.168.2.0/24) would be summarized into
145
97
  # 192.168.0.0/23 and 192.168.2.0/24 rather than into 192.168.0.0/22
146
98
  #
147
99
  # - Arguments:
148
- # * Hash with the following fields:
149
- # - :List -- Array of CIDR or NetStruct objects
150
- # - :NetStruct -- if true, return IPAdmin::NetStruct objects (optional)
100
+ # * Array of CIDR addresses or IPAdmin::CIDR objects, or a Hash with
101
+ # the following fields:
102
+ # - :List -- Array of CIDR addresses or IPAdmin::CIDR objects
151
103
  # - :Objectify -- if true, return IPAdmin::CIDR objects (optional)
152
104
  # - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
153
105
  #
154
106
  # - Returns:
155
- # * Array of CIDR or NetStruct objects
107
+ # * Array of CIDR address Strings or IPAdmin::CIDR objects
156
108
  #
157
109
  # - Notes:
158
110
  # * I have designed this with enough flexibility that you can pass in CIDR
@@ -160,211 +112,155 @@ module_function :create_net_struct
160
112
  # 10.1.0.0/26, 10.1.0.64/26) and they will be merged properly (ie 192.168.1.0/25,
161
113
  # and 10.1.0.0/25 would be returned).
162
114
  #
163
- # Example:
164
- # supernets = IPAdmin.merge(:List => list)
115
+ # Examples:
116
+ # supernets = IPAdmin.merge(['192.168.1.0/27','192.168.1.32/27'])
117
+ # supernets = IPAdmin.merge(:List => ['192.168.1.0/27','192.168.1.32/27'], :Short => true)
165
118
  #
166
119
  def merge(options)
167
120
  version = nil
121
+ all_f = nil
168
122
  short = false
169
123
  objectify = false
170
124
 
171
125
  # validate options
172
- if ( !options.kind_of?(Hash) )
173
- raise ArgumentError, "Hash expected but #{options.class} provided."
174
- end
175
-
176
- if (!options.has_key?(:List))
177
- raise ArgumentError, "Missing argument: List."
178
- end
126
+ if ( options.kind_of?(Hash) )
127
+ if (!options.has_key?(:List))
128
+ raise ArgumentError, "Missing argument: List."
129
+ end
179
130
 
180
- if (options.has_key?(:Short) && options[:Short] == true)
181
- short = true
182
- end
131
+ if (options.has_key?(:Short) && options[:Short] == true)
132
+ short = true
133
+ end
183
134
 
184
- if (options.has_key?(:Objectify) && options[:Objectify] == true)
185
- objectify = true
186
- end
187
-
188
- list = options[:List]
189
- ret_struct = 1 if (options.has_key?(:NetStruct))
135
+ if (options.has_key?(:Objectify) && options[:Objectify] == true)
136
+ objectify = true
137
+ end
190
138
 
191
- # make sure list is an array
192
- if ( !list.kind_of?(Array) )
193
- raise ArgumentError, "Expected Array for option :List, " +
194
- "but #{list.class} provided."
139
+ # make sure :List is an array
140
+ if ( !options[:List].kind_of?(Array) )
141
+ raise ArgumentError, "Expected Array for option :List, " +
142
+ "but #{list.class} provided."
143
+ end
144
+ list = options[:List]
145
+
146
+ elsif ( options.kind_of?(Array) )
147
+ list = options
148
+
149
+ else
150
+ raise ArgumentError, "Array or Hash expected but #{options.class} provided."
195
151
  end
196
-
152
+
197
153
  # make sure all are valid types of the same IP version
154
+ supernet_list = []
198
155
  list.each do |obj|
199
- if (!obj.kind_of?(IPAdmin::CIDR) && !obj.kind_of?(IPAdmin::NetStruct) )
200
- raise ArgumentError, "Expected IPAdmin::CIDR or NetStruct " +
201
- "object but #{obj.class} provided."
156
+ if (!obj.kind_of?(IPAdmin::CIDR))
157
+ begin
158
+ obj = IPAdmin::CIDR.new(:CIDR => obj)
159
+ rescue Exception => error
160
+ raise ArgumentError, "An array element of :List raised the following " +
161
+ "errors: #{error}"
162
+ end
202
163
  end
203
164
 
204
165
  version = obj.version if (!version)
166
+ all_f = obj.all_f if (!all_f)
205
167
  if (!obj.version == version)
206
168
  raise "Provided objects must be of the same IP version."
207
169
  end
170
+ supernet_list.push(obj)
208
171
  end
209
172
 
210
- # make all_f
211
- if (version == 4)
212
- all_f = 2**32 - 1
213
- else
214
- all_f = 2**128 - 1
215
- end
216
-
217
- # make sure that our list is in order, and contains no duplicates
218
- list = IPAdmin.sort(list)
219
- index = 0
220
- (list.length).times do
221
- if ((index > 0)&&(IPAdmin.compare(list[index],list[index-1]) == 1))
222
- list.delete_at(index)
223
- else
224
- index += 1
225
- end
226
- end
227
-
228
- # create supernet_list from list
229
- supernet_list = []
230
- list.each do |obj|
231
- if (obj.kind_of?(IPAdmin::CIDR))
232
- supernet_list.push(IPAdmin.create_net_struct(obj))
233
-
234
- elsif ( obj.kind_of?(IPAdmin::NetStruct) )
235
- if (obj.subnets && obj.subnets.length > 0)
236
- # get all child subnets of this branch entry
237
- children = merge(:List => obj.subnets, :NetStruct => 1)
238
-
239
- # if any child subnets are equal to the parent subnet
240
- # then copy the grandchild subnets and delete the child
241
- children.each do |child|
242
- if ( (obj.network == child.network) &&
243
- (obj.netmask == child.netmask) )
244
- if (child.subnets && child.subnets.length > 0)
245
- grandchildren = child.subnets
246
- children.concat(grandchildren)
247
- end
248
- children.delete(child)
249
- children = IPAdmin.sort(children)
250
- break
251
- end
252
- end
253
-
254
- obj.subnets.clear
255
- obj.subnets.concat(children)
256
- end
257
-
258
- supernet_list.push(obj)
259
- end
260
- end
261
-
262
- # merge subnets of this new branch by removing them from 'supernet_list',
263
- # and categorizing them into hash of arrays (key=netmask)
173
+ # merge subnets by removing them from 'supernet_list',
174
+ # and categorizing them into hash of arrays ({packed_netmask => [packed_network,packed_network,etc...] )
264
175
  # within each categorization we merge contiguous subnets
265
176
  # and then remove them from that category & put them back into
266
- # 'supernet_list'. we do this until our list stops getting any shorter
177
+ # 'supernet_list'. we do this until supernet_list stops getting any shorter
267
178
  categories = {}
268
179
  supernet_list_length = 0
269
180
  until (supernet_list_length == supernet_list.length)
270
181
  supernet_list_length = supernet_list.length
271
182
 
272
183
  # categorize
273
- supernet_list.each do |entry|
274
- netmask = entry.netmask
184
+ supernet_list.each do |cidr|
185
+ netmask = cidr.packed_netmask
186
+ network = cidr.packed_network
275
187
  if (categories.has_key?(netmask) )
276
- (categories[netmask]).push(entry)
188
+ categories[netmask].push(network)
277
189
  else
278
- categories[netmask] = [entry]
190
+ categories[netmask] = [network]
279
191
  end
280
192
  end
281
193
  supernet_list.clear
282
194
 
283
- categories.each_key do |netmask|
284
- entries = categories[netmask]
195
+ ordered_cats = categories.keys.sort
196
+ ordered_cats.each do |netmask|
197
+ nets = categories[netmask].sort
285
198
  bitstep = (all_f + 1) - netmask
286
-
287
- # this entire process depends on the list being in order
288
- until (entries.length == 0)
199
+
200
+ until (nets.length == 0)
201
+ # take the first network & create its supernet. this
202
+ # supernet will have x number of subnets, so we'll look
203
+ # & see if we have those subnets. if so, keep supernet & delete subnets.
289
204
  to_merge = []
290
205
  multiplier = 1
291
- first = entries[0]
206
+ network1 = nets[0]
292
207
  num_required = 2**multiplier
293
208
  supermask = (netmask << multiplier) & all_f
294
- supernet = supermask & first.network
295
- if (first.network == supernet)
296
- # take first entry and use it to form a base
297
- # supernet address. this supernet should have
298
- # x number of subnets in it, so we look for
299
- # those subnets & if found store them
300
- expected = first.network
301
- entries.each do |entry|
302
- if ( (entry.network == expected) && (first.network == supernet) )
303
- to_merge.push(entry)
209
+ supernet = supermask & network1
210
+ if (network1 == supernet)
211
+ # we have the first half of the new supernet
212
+ expected = network1
213
+ nets.each do |network|
214
+ if (network == expected)
215
+ to_merge.push(network)
304
216
  expected = expected + bitstep
305
- if ( (to_merge.length == num_required) &&
306
- (entries.length > num_required ) )
217
+ if ( (to_merge.length == num_required) && (nets.length > num_required) )
218
+ # we have all of our subnets for this round, but still have
219
+ # more to look at
307
220
  multiplier += 1
308
221
  num_required = 2**multiplier
309
222
  supermask = (netmask << multiplier) & all_f
310
- supernet = supermask & first.network
223
+ supernet = supermask & network1
311
224
  end
312
225
  else
313
- # if entry is a duplicate, then kill it
314
- if (IPAdmin.compare(entry,to_merge.last) == 1)
315
- entries.delete(entry)
316
- end
317
226
  break
318
227
  end
319
228
  end
320
229
  else
321
- to_merge.push(first)
230
+ # we have the second half of the new supernet only
231
+ to_merge.push(network1)
322
232
  end
323
233
 
324
- # make sure we actually have all of our subnets
325
- # create our new supernet
234
+
326
235
  if (to_merge.length != num_required)
236
+ # we dont have all of our subnets, so backstep 1 bit
327
237
  multiplier -= 1
328
238
  supermask = (netmask << multiplier) & all_f
329
- supernet = supermask & first.network
239
+ supernet = supermask & network1
330
240
  end
331
- net_struct = NetStruct.new(supernet,supermask,version,nil,[])
332
-
333
- # now that we have the child members of our new supernet
334
- # take any grandchild subnets that they may have and
335
- # add them to the new supernet. then kill the children
336
- (2**multiplier).times do
337
- to_kill = to_merge.shift
338
- net_struct.subnets.concat(to_kill.subnets) if (to_kill.subnets)
339
- entries.delete(to_kill)
340
- end
341
- supernet_list.push(net_struct)
241
+
242
+ # save new supernet
243
+ supernet_list.push(IPAdmin::CIDR.new(:PackedIP => supernet,
244
+ :PackedNetmask => supermask,
245
+ :Version => version))
246
+
247
+ # delete the subnets of the new supernet
248
+ (2**multiplier).times {nets.delete(to_merge.shift)}
342
249
  end
343
250
  end
344
251
  categories.clear
345
252
  supernet_list = IPAdmin.sort(supernet_list)
346
253
  end
347
254
 
348
- # if '!ret_struct', return CIDR's
349
- if (!ret_struct)
350
- supernets = []
351
- supernet_list.each do |entry|
352
- if (!objectify)
353
- network = IPAdmin.unpack_ip_addr(:Integer => entry.network, :Version => version)
354
- network = IPAdmin.shorten(network) if (short && version == 6)
355
- netmask = IPAdmin.unpack_ip_netmask(:Integer => entry.netmask, :Version => version)
356
- supernets.push("#{network}/#{netmask}")
357
- else
358
- supernets.push(IPAdmin::CIDR.new(:PackedIP => entry.network,
359
- :PackedNetmask => entry.netmask,
360
- :Version => version))
361
- end
362
- end
255
+ # decide what to return
256
+ if (!objectify)
257
+ supernets = []
258
+ supernet_list.each {|entry| supernets.push(entry.desc(:Short => short))}
259
+ return(supernets)
363
260
  else
364
- supernets = supernet_list
261
+ return(supernet_list)
365
262
  end
366
263
 
367
- return(supernets)
368
264
  end
369
265
  module_function :merge
370
266
 
@@ -378,10 +274,10 @@ module_function :merge
378
274
  #==============================================================================#
379
275
 
380
276
  # Given the number of IP addresses required in a subnet, return the minimum
381
- # netmask required for that subnet.
277
+ # netmask (in bits) required for that subnet.
382
278
  #
383
279
  # - Arguments:
384
- # * Hash with the following fields:
280
+ # * Integer or Hash with the following fields:
385
281
  # - :IPCount -- the number of IP addresses required - Integer
386
282
  # - :Version -- IP version - Integer(optional)
387
283
  #
@@ -391,23 +287,26 @@ module_function :merge
391
287
  # - Notes:
392
288
  # * Version is assumed to be 4 unless specified otherwise.
393
289
  #
394
- # Example:
395
- # netmask = IPAdmin.minumum_size(:IPCount => 14) -> 28
290
+ # Examples:
291
+ # netmask = IPAdmin.minumum_size(14)
292
+ # netmask = IPAdmin.minumum_size(:IPCount => 65536, :Version => 6)
396
293
  #
397
294
  def minimum_size(options)
398
295
  version = 4
399
296
 
400
- if ( !options.kind_of?(Hash) )
401
- raise ArgumentError, "Hash expected but #{options.class} provided."
402
- end
403
-
404
- if ( !options.has_key?(:IPCount) )
405
- raise ArgumentError, "Missing argument: List."
406
- end
407
- ipcount = options[:IPCount]
297
+ if ( options.kind_of?(Hash) )
298
+ if ( !options.has_key?(:IPCount) )
299
+ raise ArgumentError, "Missing argument: List."
300
+ end
301
+ ipcount = options[:IPCount]
408
302
 
409
- if (options.has_key?(:Version))
410
- version = options[:Version]
303
+ if (options.has_key?(:Version))
304
+ version = options[:Version]
305
+ end
306
+ elsif ( options.kind_of?(Integer) )
307
+ ipcount = options
308
+ else
309
+ raise ArgumentError, "Integer or Hash expected but #{options.class} provided."
411
310
  end
412
311
 
413
312
  if (version == 4)
@@ -442,11 +341,10 @@ module_function :minimum_size
442
341
  # pack_ip_addr()
443
342
  #==============================================================================#
444
343
 
445
- # Convert IP addresses into an integer. No attempt at
446
- # validation is performed.
344
+ # Convert IP addresses into an integer.
447
345
  #
448
346
  # - Arguments:
449
- # * Hash with the following fields:
347
+ # * IP address as a String, or a Hash with the following fields:
450
348
  # - :IP -- IP address - String
451
349
  # - :Version -- IP version - Integer
452
350
  #
@@ -456,32 +354,39 @@ module_function :minimum_size
456
354
  # - Notes:
457
355
  # * Will attempt to auto-detect IP version if not provided
458
356
  #
459
- # Example:
460
- # pack_ip_addr(IP => '192.168.1.1')
461
- # pack_ip_addr(IP => 'ffff::1')
462
- # pack_ip_addr(IP => '::192.168.1.1')
357
+ # Examples:
358
+ # pack_ip_addr('192.168.1.1')
359
+ # pack_ip_addr(IP => 'ffff::1', :Version => 6)
360
+ # pack_ip_addr(::192.168.1.1')
463
361
  #
464
362
  def pack_ip_addr(options)
363
+ to_validate = {}
465
364
 
466
- if (!options.kind_of?(Hash))
467
- raise ArgumentError, "Expected Hash, but #{options.class} provided."
468
- end
469
-
470
- if (!options.has_key?(:IP))
471
- raise ArgumentError, "Missing argument: IP."
472
- end
473
- ip = options[:IP]
474
-
475
- if (options.has_key?(:Version))
476
- version = options[:Version]
477
- if (version != 4 && version != 6)
478
- raise ArgumentError, ":Version should be 4 or 6, but was '#{version}'."
365
+ if (options.kind_of?(Hash))
366
+ if (!options.has_key?(:IP))
367
+ raise ArgumentError, "Missing argument: IP."
479
368
  end
480
- end
369
+ ip = options[:IP]
370
+
371
+ if (options.has_key?(:Version))
372
+ version = options[:Version]
373
+ to_validate[:Version] = version
374
+ if (version != 4 && version != 6)
375
+ raise ArgumentError, ":Version should be 4 or 6, but was '#{version}'."
376
+ end
377
+ end
378
+ elsif
379
+ ip = options
380
+ else
381
+ raise ArgumentError, "String or Hash expected, but #{options.class} provided."
382
+ end
383
+ to_validate[:IP] = ip
481
384
 
482
-
483
385
  if ( ip.kind_of?(String) )
484
386
 
387
+ # validate
388
+ IPAdmin.validate_ip_addr(to_validate)
389
+
485
390
  # determine version if not provided
486
391
  if (!version)
487
392
  if ( ip =~ /\./ && ip !~ /:/ )
@@ -550,7 +455,7 @@ def pack_ip_addr(options)
550
455
  end
551
456
 
552
457
  else
553
- raise ArgumentError, "Argument :IP should be a String, but is a #{ip.class}."
458
+ raise ArgumentError, "IP address should be a String, but is a #{ip.class}."
554
459
  end
555
460
 
556
461
  return(packed_ip)
@@ -566,12 +471,12 @@ module_function :pack_ip_addr
566
471
  # pack_ip_netmask()
567
472
  #==============================================================================#
568
473
 
569
- # Convert IP netmask into an integer. No validation is performed. Netmask
570
- # may be in either CIDR (/yy) or extended (y.y.y.y) format. CIDR formatted
571
- # netmasks may either be a String or an Integer.
474
+ # Convert IP netmask into an integer.Netmask may be in either CIDR (/yy) or
475
+ # extended (y.y.y.y) format. CIDR formatted netmasks may either
476
+ # be a String or an Integer.
572
477
  #
573
478
  # - Arguments
574
- # * Hash with the following fields:
479
+ # * Netmask as a String, or a Integer, or a Hash with the following fields:
575
480
  # - :Netmask -- Netmask - String or Integer
576
481
  # - :Version -- IP version - Integer (optional)
577
482
  #
@@ -581,38 +486,45 @@ module_function :pack_ip_addr
581
486
  # - Notes:
582
487
  # * Version defaults to 4
583
488
  #
584
- # Example:
585
- # packed = IPAdmin.pack_ip_netmask(Netmask => '255.255.255.0')
586
- # packed = IPAdmin.pack_ip_netmask(Netmask => '24')
587
- # packed = IPAdmin.pack_ip_netmask(Netmask => 24)
588
- # packed = IPAdmin.pack_ip_netmask(Netmask => '/24')
489
+ # Examples:
490
+ # packed = IPAdmin.pack_ip_netmask('255.255.255.0')
491
+ # packed = IPAdmin.pack_ip_netmask('24')
492
+ # packed = IPAdmin.pack_ip_netmask(24)
493
+ # packed = IPAdmin.pack_ip_netmask('/24')
589
494
  # packed = IPAdmin.pack_ip_netmask(Netmask => '64', :Version => 6)
590
495
  #
591
496
  def pack_ip_netmask(options)
592
- if (!options.kind_of?(Hash))
593
- raise ArgumentError, "Expected Hash, but #{options.class} provided."
594
- end
497
+ all_f = 2**32-1
595
498
 
596
- if (!options.has_key?(:Netmask))
597
- raise ArgumentError, "Missing argument: Netmask."
598
- end
599
- netmask = options[:Netmask]
499
+ if (options.kind_of?(Hash))
500
+ if (!options.has_key?(:Netmask))
501
+ raise ArgumentError, "Missing argument: Netmask."
502
+ end
503
+ netmask = options[:Netmask]
504
+ to_validate = {:Netmask => netmask}
600
505
 
601
- if (options.has_key?(:Version))
602
- version = options[:Version]
603
- if (version != 4 && version != 6)
604
- raise ArgumentError, ":Version should be 4 or 6, but was '#{version}'."
605
- elsif (version == 6)
606
- all_f = 2**128-1
607
- else
608
- all_f = 2**32-1
506
+ if (options.has_key?(:Version))
507
+ version = options[:Version]
508
+ if (version != 4 && version != 6)
509
+ raise ArgumentError, ":Version should be 4 or 6, but was '#{version}'."
510
+ elsif (version == 6)
511
+ all_f = 2**128-1
512
+ else
513
+ all_f = 2**32-1
514
+ end
515
+ to_validate[:Version] = version
609
516
  end
517
+
518
+ elsif (options.kind_of?(String) || options.kind_of?(Integer))
519
+ netmask = options
520
+ to_validate = {:Netmask => netmask}
610
521
  else
611
- all_f = 2**32-1
522
+ raise ArgumentError, "String, Integer, or Hash expected, but #{options.class} provided."
612
523
  end
613
524
 
614
-
615
525
  if (netmask.kind_of?(String))
526
+ IPAdmin.validate_ip_netmask(to_validate)
527
+
616
528
  if(netmask =~ /\./)
617
529
  packed_netmask = IPAdmin.pack_ip_addr(:IP => netmask)
618
530
 
@@ -625,11 +537,14 @@ def pack_ip_netmask(options)
625
537
  netmask = netmask.to_i
626
538
  packed_netmask = all_f ^ (all_f >> netmask)
627
539
  end
540
+
628
541
  elsif (netmask.kind_of?(Integer))
542
+ to_validate[:Packed] = true
543
+ IPAdmin.validate_ip_netmask(to_validate)
629
544
  packed_netmask = all_f ^ (all_f >> netmask)
630
545
 
631
546
  else
632
- raise ArgumentError, "Argument :Netmask should be a String or Integer, but is a #{netmask.class}."
547
+ raise ArgumentError, "Netmask should be a String or Integer, but is a #{netmask.class}."
633
548
 
634
549
  end
635
550
 
@@ -646,36 +561,41 @@ module_function :pack_ip_netmask
646
561
  # range()
647
562
  #==============================================================================#
648
563
 
649
- # Given two IPAdmin::CIDR objects of the same version, return all IP
650
- # addresses between them (non-inclusive).
564
+ # Given two CIDR addresses or IPAdmin::CIDR objects of the same version,
565
+ # return all IP addresses between them.
651
566
  #
652
567
  # - Arguments:
653
- # * Hash with the following fields:
568
+ # * Array of (2)CIDR addresses or IPAdmin::CIDR objects, or a Hash
569
+ # with the following fields:
654
570
  # - :Bitstep -- enumerate in X sized steps - Integer (optional)
655
- # - :Boundaries -- array of (2) CIDR objects
571
+ # - :Boundaries -- array of (2) CIDR addresses or IPAdmin::CIDR objects
572
+ # - :Inclusive -- if true, include boundaries in returned data
656
573
  # - :Limit -- limit returned list to X number of items - Integer (optional)
657
574
  # - :Objectify -- if true, return CIDR objects (optional)
658
575
  # - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
659
576
  #
660
577
  # - Returns:
661
- # * Array of Strings or CIDR objects
578
+ # * Array of CIDR address Strings or IPAdmin::CIDR objects
662
579
  #
663
580
  # - Notes:
664
581
  # * IPAdmin.range will use the original IP address passed during the initialization
665
- # of the CIDR objects. This IP can be found using the CIDR.ip() method.
582
+ # of the IPAdmin::CIDR objects, or the base address of any CIDR addresses passed.
583
+ # * The default behavior is to be non-inclusive (dont include boundaries as part of returned data)
666
584
  #
667
- # Example:
668
- # list = IPAdmin.range(:Boundaries => [cidr1,cidr2])
585
+ # Examples:
586
+ # list = IPAdmin.range(:Boundaries => [cidr1,cidr2], :Limit => 10)
587
+ # list = IPAdmin.range(['192.168.1.0','192.168.1.10']
669
588
  #
670
589
  def range(options)
671
590
  list = []
672
591
  bitstep = 1
673
592
  objectify = false
674
593
  short = false
594
+ inclusive = false
675
595
  limit = nil
676
596
 
677
597
  # check options
678
- if (options)
598
+ if (options.kind_of?(Hash))
679
599
  if ( !options.has_key?(:Boundaries) )
680
600
  raise ArgumentError, "Missing argument: Boundaries."
681
601
  end
@@ -698,16 +618,39 @@ def range(options)
698
618
  if( options.has_key?(:Short) && options[:Short] == true )
699
619
  short = true
700
620
  end
621
+
622
+ if( options.has_key?(:Inclusive) && options[:Inclusive] == true )
623
+ inclusive = true
624
+ end
701
625
 
702
626
  if( options.has_key?(:Limit) )
703
627
  limit = options[:Limit]
704
628
  end
629
+
630
+ elsif(options.kind_of?(Array))
631
+ (cidr1,cidr2) = options
632
+ else
633
+ raise ArgumentError, "Array or Hash expected but #{options.class} provided."
634
+ end
635
+
636
+ # if cidr1/cidr2 are not CIDR objects, then attempt to create
637
+ # cidr objects from them
638
+ if ( !cidr1.kind_of?(IPAdmin::CIDR) )
639
+ begin
640
+ cidr1 = IPAdmin::CIDR.new(:CIDR => cidr1)
641
+ rescue Exception => error
642
+ raise ArgumentError, "First argument of :Boundaries raised the following " +
643
+ "errors: #{error}"
644
+ end
705
645
  end
706
646
 
707
- # check our objects
708
- if (!cidr1.kind_of?(IPAdmin::CIDR) && !cidr2.kind_of?(IPAdmin::CIDR))
709
- raise ArgumentError, "One or more values provided under :Boundaries "+
710
- "is not a valid IPAdmin::CIDR object."
647
+ if ( !cidr2.kind_of?(IPAdmin::CIDR))
648
+ begin
649
+ cidr2 = IPAdmin::CIDR.new(:CIDR => cidr2)
650
+ rescue Exception => error
651
+ raise ArgumentError, "Second argument of :Boundaries raised the following " +
652
+ "errors: #{error}"
653
+ end
711
654
  end
712
655
 
713
656
  # check version, store & sort
@@ -720,8 +663,15 @@ def range(options)
720
663
  end
721
664
 
722
665
  # dump our range
723
- my_ip = boundaries[0] + 1
724
- until (my_ip >= boundaries[1])
666
+ if (!inclusive)
667
+ my_ip = boundaries[0] + 1
668
+ end_ip = boundaries[1]
669
+ else
670
+ my_ip = boundaries[0]
671
+ end_ip = boundaries[1] + 1
672
+ end
673
+
674
+ until (my_ip >= end_ip)
725
675
  if (!objectify)
726
676
  my_ip_s = IPAdmin.unpack_ip_addr(:Integer => my_ip, :Version => version)
727
677
  my_ips = IPAdmin.shorten(my_ips) if (short && version == 6)
@@ -754,13 +704,13 @@ module_function :range
754
704
  # The address should not contain a netmask.
755
705
  #
756
706
  # - Arguments:
757
- # * String
707
+ # * IP address as a String
758
708
  #
759
709
  # - Returns:
760
710
  # * String
761
711
  #
762
- # Example:
763
- # short = IPAdmin.shorten('fec0:0000:0000:0000:0000:0000:0000:0001') --> 'fec0::1'
712
+ # Examples:
713
+ # short = IPAdmin.shorten('fec0:0000:0000:0000:0000:0000:0000:0001')
764
714
  #
765
715
  def shorten(addr)
766
716
 
@@ -846,21 +796,22 @@ module_function :shorten
846
796
  # sort()
847
797
  #==============================================================================#
848
798
 
849
- # Given a list of IPAdmin::CIDR or NetStruct objects
799
+ # Given a list of CIDR addresses or IPAdmin::CIDR objects,
850
800
  # sort them from lowest to highest by Network/Netmask.
851
801
  #
852
802
  # - Arguments:
853
- # * Array of CIDR or NetStruct objects
803
+ # * Array of CIDR addresses or IPAdmin::CIDR objects
854
804
  #
855
805
  # - Returns:
856
- # * Array
806
+ # * Array of CIDR address Strings or IPAdmin::CIDR objects
857
807
  #
858
808
  # - Notes:
859
809
  # * IPAdmin.sort will use the original IP address passed during the initialization
860
- # of the CIDR objects. This IP can be found using the CIDR.ip() method.
810
+ # of any IPAdmin::CIDR objects, or the base address of any CIDR addresses passed.
861
811
  #
862
- # Example:
863
- # sorted = IPAdmin.sort(list)
812
+ # Examples:
813
+ # sorted = IPAdmin.sort([cidr1,cidr2])
814
+ # sorted = IPAdmin.sort(['192.168.1.32/27','192.168.1.0/27'])
864
815
  #
865
816
  def sort(list)
866
817
 
@@ -872,33 +823,35 @@ def sort(list)
872
823
 
873
824
  # make sure all are valid types of the same IP version
874
825
  version = nil
875
- list.each do |obj|
876
- unless (obj.kind_of?(IPAdmin::CIDR) || obj.kind_of?(IPAdmin::NetStruct) )
877
- raise ArgumentError, "Expected IPAdmin::CIDR or NetStruct " +
878
- "object but #{obj.class} provided."
826
+ cidr_hash = {}
827
+ list.each do |cidr|
828
+ if (!cidr.kind_of?(IPAdmin::CIDR))
829
+ begin
830
+ new_cidr = IPAdmin::CIDR.new(cidr)
831
+ rescue Exception => error
832
+ raise ArgumentError, "An element of the provided Array " +
833
+ "raised the following errors: #{error}"
834
+ end
835
+ else
836
+ new_cidr = cidr
879
837
  end
880
-
881
- version = obj.version if (!version)
882
- unless (obj.version == version)
883
- raise "Provided objects must all be of the same IP version."
838
+ cidr_hash[new_cidr] = cidr
839
+
840
+ version = new_cidr.version if (!version)
841
+ unless (new_cidr.version == version)
842
+ raise "Provided CIDR addresses must all be of the same IP version."
884
843
  end
885
844
  end
886
845
 
887
- # create unsorted_list from list
888
- unsorted_list = []
889
- list.each do |obj|
890
- unsorted_list.push(IPAdmin.create_net_struct(obj))
891
- end
892
-
893
846
  # sort by network. if networks are equal, sort by netmask.
894
847
  sorted_list = []
895
- unsorted_list.each do |entry|
848
+ cidr_hash.each_key do |entry|
896
849
  index = 0
897
850
  sorted_list.each do
898
- if(entry.network < (sorted_list[index]).network)
851
+ if(entry.packed_network < (sorted_list[index]).packed_network)
899
852
  break
900
- elsif (entry.network == (sorted_list[index]).network)
901
- if (entry.netmask < (sorted_list[index]).netmask)
853
+ elsif (entry.packed_network == (sorted_list[index]).packed_network)
854
+ if (entry.packed_netmask < (sorted_list[index]).packed_netmask)
902
855
  break
903
856
  end
904
857
  end
@@ -906,15 +859,12 @@ def sort(list)
906
859
  end
907
860
  sorted_list.insert(index, entry)
908
861
  end
862
+
863
+ # return original values passed
864
+ ret_list = []
865
+ sorted_list.each {|x| ret_list.push(cidr_hash[x])}
909
866
 
910
- # replace sorted_list entries with their .object
911
- index = 0
912
- sorted_list.length.times do
913
- sorted_list[index] = (sorted_list[index]).object
914
- index += 1
915
- end
916
-
917
- return(sorted_list)
867
+ return(ret_list)
918
868
  end
919
869
  module_function :sort
920
870
 
@@ -927,11 +877,10 @@ module_function :sort
927
877
  # unpack_ip_addr()
928
878
  #==============================================================================#
929
879
 
930
- # Unack a packed IP address back into a printable string. No attempt at
931
- # validation is performed.
880
+ # Unack a packed IP address back into a printable string.
932
881
  #
933
882
  # - Arguments:
934
- # * Hash with the following fields:
883
+ # * Integer or a Hash with the following fields:
935
884
  # - :Integer -- Integer representaion of an IP address
936
885
  # - :Version -- IP version - Integer (optional)
937
886
  # - :IPv4Mapped -- if true, unpack IPv6 as an IPv4 mapped address (optional)
@@ -942,35 +891,44 @@ module_function :sort
942
891
  # - Notes:
943
892
  # * IP version will attempt to be auto-detected if not provided
944
893
  #
945
- # Example:
894
+ # Examples:
895
+ # unpacked = IPAdmin.unpack_ip_addr(3232235906)
946
896
  # unpacked = IPAdmin.unpack_ip_addr(:Integer => packed)
947
897
  #
948
898
  def unpack_ip_addr(options)
949
899
  ipv4_mapped = false
900
+ to_validate = {}
950
901
 
951
- if (!options.kind_of?(Hash))
952
- raise ArgumentError, "Expected Hash, but #{options.class} provided."
953
- end
902
+ if (options.kind_of?(Hash))
903
+ if (!options.has_key?(:Integer))
904
+ raise ArgumentError, "Missing argument: Integer."
905
+ end
906
+ packed_ip = options[:Integer]
907
+
908
+ if (options.has_key?(:Version))
909
+ version = options[:Version]
910
+ to_validate[:Version] = version
911
+ if (version != 4 && version != 6)
912
+ raise ArgumentError, ":Version should be 4 or 6, but was '#{version}'."
913
+ end
914
+ end
954
915
 
955
- if (!options.has_key?(:Integer))
956
- raise ArgumentError, "Missing argument: Integer."
916
+ if (options.has_key?(:IPv4Mapped) && options[:IPv4Mapped] == true)
917
+ ipv4_mapped = true
918
+ end
919
+ elsif (options.kind_of?(Integer))
920
+ packed_ip = options
921
+ else
922
+ raise ArgumentError, "Integer or Hash expected, but #{options.class} provided."
957
923
  end
958
- packed_ip = options[:Integer]
959
924
 
960
925
  if (!packed_ip.kind_of?(Integer))
961
- raise ArgumentError, "Expected Integer, but #{options.class} provided for argument: Integer."
962
- end
963
-
964
- if (options.has_key?(:Version))
965
- version = options[:Version]
966
- if (version != 4 && version != 6)
967
- raise ArgumentError, ":Version should be 4 or 6, but was '#{version}'."
968
- end
926
+ raise ArgumentError, "Packed IP should be an Integer, but was a #{options.class}."
969
927
  end
970
928
 
971
- if (options.has_key?(:IPv4Mapped) && options[:IPv4Mapped] == true)
972
- ipv4_mapped = true
973
- end
929
+ # validate
930
+ to_validate[:IP] = packed_ip
931
+ IPAdmin.validate_ip_addr(to_validate)
974
932
 
975
933
  # set version if not set
976
934
  if (!version)
@@ -1030,38 +988,48 @@ module_function :unpack_ip_addr
1030
988
  #==============================================================================#
1031
989
 
1032
990
  # Unack a packed IP netmask into a integer representing the number of
1033
- # bits in the CIDR mask. No attempt at validation is performed.
991
+ # bits in the CIDR mask.
1034
992
  #
1035
993
  # - Arguments:
1036
- # * Hash with the following fields:
994
+ # * Integer or a Hash with the following fields:
1037
995
  # - :Integer -- Integer representation of an IP Netmask
1038
996
  #
1039
997
  # - Returns:
1040
998
  # * Integer
1041
999
  #
1042
- # Example:
1000
+ # Examples:
1001
+ # unpacked = IPAdmin.unpack_ip_netmask(0xfffffffe)
1043
1002
  # unpacked = IPAdmin.unpack_ip_netmask(:Integer => packed)
1044
1003
  #
1045
1004
  def unpack_ip_netmask(options)
1046
- if (!options.kind_of?(Hash))
1047
- raise ArgumentError, "Expected Hash, but #{options.class} provided."
1048
- end
1049
1005
 
1050
- if (!options.has_key?(:Integer))
1051
- raise ArgumentError, "Missing argument: Integer."
1052
- end
1053
- packed_netmask = options[:Integer]
1006
+ if (options.kind_of?(Hash))
1007
+ if (!options.has_key?(:Integer))
1008
+ raise ArgumentError, "Missing argument: Integer."
1009
+ end
1010
+ packed_netmask = options[:Integer]
1011
+
1012
+ if (!packed_netmask.kind_of?(Integer))
1013
+ raise ArgumentError, "Packed netmask should be an Integer, but is a #{packed_netmask.class}."
1014
+ end
1015
+
1016
+ elsif (options.kind_of?(Integer))
1017
+ packed_netmask = options
1018
+
1019
+ else
1020
+ raise ArgumentError, "Integer or Hash expected, but #{options.class} provided."
1021
+ end
1054
1022
 
1055
- if (!packed_netmask.kind_of?(Integer))
1056
- raise ArgumentError, "Argument :Integer should be an Integer, but is a #{packed_netmask.class}."
1057
- end
1058
1023
 
1059
1024
  if (packed_netmask < 2**32)
1060
1025
  mask = 32
1026
+ IPAdmin.validate_ip_netmask(:Netmask => packed_netmask, :Packed => true, :Version => 4)
1061
1027
  else
1028
+ IPAdmin.validate_ip_netmask(:Netmask => packed_netmask, :Packed => true, :Version => 6)
1062
1029
  mask = 128
1063
1030
  end
1064
1031
 
1032
+
1065
1033
  mask.times do
1066
1034
  if ( (packed_netmask & 1) == 1)
1067
1035
  break
@@ -1087,13 +1055,13 @@ module_function :unpack_ip_netmask
1087
1055
  # notation. The address should not contain a netmask.
1088
1056
  #
1089
1057
  # - Arguments:
1090
- # * String
1058
+ # * IP address as a String
1091
1059
  #
1092
1060
  # - Returns:
1093
1061
  # * String
1094
1062
  #
1095
- # Example:
1096
- # long = IPAdmin.unshorten('fec0::1') --> 'fec0:0000:0000:0000:0000:0000:0000:0001'
1063
+ # Examples:
1064
+ # long = IPAdmin.unshorten('fec0::1')
1097
1065
  #
1098
1066
  def unshorten(addr)
1099
1067
 
@@ -1128,24 +1096,27 @@ module_function :unshorten
1128
1096
  # Validate an EUI-48 or EUI-64 address.
1129
1097
  #
1130
1098
  # - Arguments
1131
- # * Hash with the following fields:
1099
+ # * EUI address as a String, or a Hash with the following fields:
1132
1100
  # - :EUI -- Address to validate - String
1133
1101
  #
1134
1102
  # - Returns:
1135
1103
  # * True
1136
1104
  #
1137
- # - Example:
1105
+ # Examples:
1106
+ # * IPAdmin.validate_eui('01-00-5e-12-34-56')
1138
1107
  # * IPAdmin.validate_eui(:EUI => '01-00-5e-12-34-56')
1139
1108
  #
1140
1109
  def validate_eui(options)
1141
- if (!options.kind_of? Hash)
1142
- raise ArgumentError, "Expected Hash, but #{options.class} provided."
1143
- end
1144
-
1145
- if (!options.has_key?(:EUI))
1146
- raise ArgumentError, "Missing argument: EUI."
1110
+ if (options.kind_of? Hash)
1111
+ if (!options.has_key?(:EUI))
1112
+ raise ArgumentError, "Missing argument: EUI."
1113
+ end
1114
+ eui = options[:EUI]
1115
+ elsif (options.kind_of? String)
1116
+ eui = options
1117
+ else
1118
+ raise ArgumentError, "String or Hash expected, but #{options.class} provided."
1147
1119
  end
1148
- eui = options[:EUI]
1149
1120
 
1150
1121
  if (eui.kind_of?(String))
1151
1122
  # check for invalid characters
@@ -1174,8 +1145,7 @@ def validate_eui(options)
1174
1145
  end
1175
1146
 
1176
1147
  else
1177
- raise ArgumentError, "Expected String, but #{eui.class} " +
1178
- "provided for argument :EUI."
1148
+ raise ArgumentError, "EUI address should be a String, but was a#{eui.class}."
1179
1149
  end
1180
1150
  return(true)
1181
1151
  end
@@ -1193,7 +1163,7 @@ module_function :validate_eui
1193
1163
  # Validate an IP address. The address should not contain a netmask.
1194
1164
  #
1195
1165
  # - Arguments
1196
- # * Hash with the following fields:
1166
+ # * IP address as a String or Integer, or a Hash with the following fields:
1197
1167
  # - :IP -- IP address to validate - String or Integer
1198
1168
  # - :Version -- IP version - Integer (optional)
1199
1169
  #
@@ -1203,9 +1173,9 @@ module_function :validate_eui
1203
1173
  # - Notes:
1204
1174
  # * IP version will attempt to be auto-detected if not provided
1205
1175
  #
1206
- # Example:
1207
- # validate_ip_addr(IP => '192.168.1.1')
1208
- # validate_ip_addr(IP => 'ffff::1')
1176
+ # Examples:
1177
+ # validate_ip_addr('192.168.1.1')
1178
+ # validate_ip_addr(IP => 'ffff::1', :Version => 6)
1209
1179
  # validate_ip_addr(IP => '::192.168.1.1')
1210
1180
  # validate_ip_addr(IP => 0xFFFFFF)
1211
1181
  # validate_ip_addr(IP => 2**128-1)
@@ -1213,20 +1183,22 @@ module_function :validate_eui
1213
1183
  #
1214
1184
  def validate_ip_addr(options)
1215
1185
 
1216
- if (!options.kind_of?(Hash))
1217
- raise ArgumentError, "Expected Hash, but #{options.class} provided."
1218
- end
1219
-
1220
- if (!options.has_key?(:IP))
1221
- raise ArgumentError, "Missing argument: IP."
1222
- end
1223
- ip = options[:IP]
1186
+ if (options.kind_of?(Hash))
1187
+ if (!options.has_key?(:IP))
1188
+ raise ArgumentError, "Missing argument: IP."
1189
+ end
1190
+ ip = options[:IP]
1224
1191
 
1225
- if (options.has_key?(:Version))
1226
- version = options[:Version]
1227
- if (version != 4 && version != 6)
1228
- raise ArgumentError, ":Version should be 4 or 6, but was '#{version}'."
1192
+ if (options.has_key?(:Version))
1193
+ version = options[:Version]
1194
+ if (version != 4 && version != 6)
1195
+ raise ArgumentError, ":Version should be 4 or 6, but was '#{version}'."
1196
+ end
1229
1197
  end
1198
+ elsif (options.kind_of?(String) || options.kind_of?(Integer))
1199
+ ip = options
1200
+ else
1201
+ raise ArgumentError, "String or Hash expected, but #{options.class} provided."
1230
1202
  end
1231
1203
 
1232
1204
  if ( ip.kind_of?(String) )
@@ -1358,7 +1330,7 @@ module_function :validate_ip_addr
1358
1330
  # Validate IP Netmask.
1359
1331
  #
1360
1332
  # - Arguments:
1361
- # * Hash with the following fields:
1333
+ # * Netmask as a String or Integer, or a Hash with the following fields:
1362
1334
  # - :Netmask -- Netmask to validate - String or Integer
1363
1335
  # - :Packed -- if true, the provided Netmask is a packed Integer
1364
1336
  # - :Version -- IP version - Integer (optional)
@@ -1369,42 +1341,43 @@ module_function :validate_ip_addr
1369
1341
  # - Notes:
1370
1342
  # * Version defaults to 4 if not specified.
1371
1343
  #
1372
- # Example:
1373
- # IPAdmin.validate_ip_netmask(:Netmask => '/32')
1374
- # IPAdmin.validate_ip_netmask(:Netmask => 32)
1344
+ # Examples:
1345
+ # IPAdmin.validate_ip_netmask('/32')
1346
+ # IPAdmin.validate_ip_netmask(32)
1375
1347
  # IPAdmin.validate_ip_netmask(:Netmask => 0xffffffff, :Packed => true)
1376
1348
  #
1377
1349
  def validate_ip_netmask(options)
1378
1350
  packed = false
1351
+ version = 4
1352
+ max_bits = 32
1379
1353
 
1380
- if (!options.kind_of?(Hash))
1381
- raise ArgumentError, "Expected Hash, but #{options.class} provided."
1382
- end
1383
-
1384
- if (!options.has_key?(:Netmask))
1385
- raise ArgumentError, "Missing argument: Netmask."
1386
- end
1387
- netmask = options[:Netmask]
1354
+ if (options.kind_of?(Hash))
1355
+ if (!options.has_key?(:Netmask))
1356
+ raise ArgumentError, "Missing argument: Netmask."
1357
+ end
1358
+ netmask = options[:Netmask]
1388
1359
 
1389
- if (options.has_key?(:Packed) && options[:Packed] == true)
1390
- packed = true
1391
- end
1360
+ if (options.has_key?(:Packed) && options[:Packed] == true)
1361
+ packed = true
1362
+ end
1392
1363
 
1393
- if (options.has_key?(:Version))
1394
- version = options[:Version]
1395
- if (version != 4 && version != 6)
1396
- raise ArgumentError, ":Version should be 4 or 6, but was '#{version}'."
1397
- elsif (version == 6)
1398
- max_bits = 128
1399
- else
1400
- max_bits = 32
1364
+ if (options.has_key?(:Version))
1365
+ version = options[:Version]
1366
+ if (version != 4 && version != 6)
1367
+ raise ArgumentError, ":Version should be 4 or 6, but was '#{version}'."
1368
+ elsif (version == 6)
1369
+ max_bits = 128
1370
+ else
1371
+ max_bits = 32
1372
+ end
1401
1373
  end
1374
+
1375
+ elsif (options.kind_of?(String) || options.kind_of?(Integer))
1376
+ netmask = options
1402
1377
  else
1403
- version = 4
1404
- max_bits = 32
1378
+ raise ArgumentError, "String, Integer, or Hash expected, but #{options.class} provided."
1405
1379
  end
1406
1380
 
1407
-
1408
1381
  if (netmask.kind_of?(String))
1409
1382
  if(netmask =~ /\./)
1410
1383
  all_f = 2**32-1
@@ -1490,13 +1463,11 @@ module_function :validate_ip_netmask
1490
1463
  # to anyone.
1491
1464
  #
1492
1465
  # Description of fields:
1493
- # * network - Integer representing an IP address
1494
- # * netmask - Integer representing IP netmask
1495
- # * version - Integer representing an IP version
1496
- # * object - holds an IPAdmin class object
1497
- # * subnets - an array of children NetStruct objects
1466
+ # * cidr - IPAdmin::CIDR object
1467
+ # * parent - parent NetStruct in tree
1468
+ # * children - Array of children NetStruct objects
1498
1469
  #
1499
- NetStruct = Struct.new(:network, :netmask, :version, :object, :subnets)
1470
+ NetStruct = Struct.new(:cidr, :parent, :children)
1500
1471
 
1501
1472
  #======================================#
1502
1473
  #