ipadmin 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- Copyright (c) 2006 Dustin Spinhirne
2
+ Copyright (c) 2006 Dustin Spinhirne - http://www.spinhirne.com
3
3
  Licensed under the same terms as Ruby, No Warranty is provided.
4
4
 
5
5
 
@@ -8,6 +8,64 @@
8
8
 
9
9
  Dustin Spinhirne
10
10
 
11
+
12
+ =CIDR
13
+
14
+ A class & series of methods for creating and manipulating CIDR network
15
+ addresses. Both IPv4 and IPv6 are supported.
16
+
17
+ This class accepts a CIDR address in (x.x.x.x/yy or xxxx::/yy) format for
18
+ IPv4 and IPv6, or (x.x.x.x/y.y.y.y) for IPv4. An optional tag hash may be
19
+ provided with each CIDR as a way of adding custom labels to the object.
20
+
21
+ Upon initialization, the IP version is auto-detected and assigned to the
22
+ object. The original IP/Netmask passed within the CIDR is stored and then
23
+ used to determine the confines of the CIDR block. Various properties of the
24
+ CIDR block are accessible via several different methods. There are also
25
+ methods for modifying the CIDR or creating new derivative CIDR's.
26
+
27
+ An example CIDR object is as follows:
28
+ IPAdmin::CIDR.new(:CIDR => '192.168.1.20/24')
29
+
30
+ This would create a CIDR object (192.168.1.0/24) with the following properties:
31
+ version = 4
32
+ base network = 192.168.1.0
33
+ ip address = 192.168.1.20
34
+ netmask = /24 (255.255.255.0)
35
+ size = 256 IP addresses
36
+ broadcast = 192.168.1.255
37
+
38
+ You can see how the CIDR object is based around the entire IP space
39
+ defined by the provided IP/Netmask pair, and not necessarily the individual
40
+ IP address itself.
41
+
42
+
43
+ =Tree
44
+
45
+ A class & series of methods for creating and manipulating IP-based
46
+ heirarchical trees. Both IPv4 and IPv6 are supported.
47
+
48
+ Tree's are useful for creating mini 'routing tables' of CIDR address space.
49
+ Using a tree, you can quickly determine the 'route' of another CIDR via
50
+ the standard longest-match algorithm.
51
+
52
+ Tree's are not dynamic, in that if you modify any of the CIDR objects
53
+ contained within, the Tree will not automatically adjust itself accordingly.
54
+ For example if you have a tree like the following:
55
+
56
+ 192.168.0.0/24
57
+ 192.168.0.0/26
58
+ 192.168.1.0/24
59
+
60
+ You decide to resize 192.168.0.0/24 to 192.168.0.0/23. The tree will now
61
+ be incorrect:
62
+
63
+ 192.168.0.0/23
64
+ 192.168.0.0/26
65
+ 192.168.1.0/24
66
+
67
+ You would need to remove and re-add the CIDR 192.168.0.0/23 in order for the
68
+ tree to rebuild itself properly.
11
69
 
12
70
 
13
71
 
@@ -42,8 +100,16 @@
42
100
  cidr4.tag['test'] = 'modified cidr4 tag'
43
101
  puts "updated cidr4 tag '#{cidr4.tag['test']}'"
44
102
  puts "cidr4 version #{cidr4.version}"
103
+ puts "cidr6 version #{cidr6.version}"
45
104
  print "\n"
46
105
 
106
+
107
+ # arpa
108
+ puts "arpa"
109
+ puts "arpa for #{cidr4.desc()} is #{cidr4.arpa}"
110
+ puts "arpa for #{cidr6.desc(:Short => true)} is #{cidr6.arpa}"
111
+ print "\n"
112
+
47
113
  # bits
48
114
  puts "bits"
49
115
  puts "cidr4 netmask in bits #{cidr4.bits()}"
@@ -53,7 +119,7 @@
53
119
  # contains?
54
120
  puts "contains"
55
121
  puts "#{cidr4.desc} contains #{cidr4_2.desc}" if ( cidr4.contains?(cidr4_2) )
56
- puts "#{cidr6.desc} contains #{cidr6_2.desc}" if ( cidr6.contains?(cidr6_2) )
122
+ puts "#{cidr6.desc} contains #{cidr6_2.desc(:Short => true)}" if ( cidr6.contains?(cidr6_2) )
57
123
  print "\n"
58
124
 
59
125
  # desc
@@ -151,6 +217,7 @@
151
217
  # remainder
152
218
  puts "remainder"
153
219
  cidr4_2 = IPAdmin::CIDR.new(:CIDR => '192.168.1.64/26')
220
+ puts "The remainder of #{cidr4.desc} after subtracting #{cidr4_2.desc} is:"
154
221
  cidr4.remainder(:Exclude => cidr4_2).each {|x| puts x}
155
222
  print "\n"
156
223
 
@@ -304,6 +371,7 @@
304
371
  #=====================================#
305
372
  #
306
373
  #=====================================#
374
+
307
375
 
308
376
  #============================================================================#
309
377
  # IPAdmin Methods
@@ -313,15 +381,16 @@
313
381
 
314
382
  # validate ip
315
383
  puts "validate ip"
316
- puts "192.168.1.0 is valid" if ( IPAdmin.validate_ipv4_addr('192.168.1.0') )
317
- puts "fec0::0 is valid" if ( IPAdmin.validate_ipv6_addr('fec0::0') )
384
+ puts "192.168.1.0 is valid" if ( IPAdmin.validate_ip_addr(:IP => '192.168.1.0') )
385
+ puts "fec0::0 is valid" if ( IPAdmin.validate_ip_addr(:IP => 'fec0::0') )
386
+ puts "::ffff:10.1.0.1 is valid" if ( IPAdmin.validate_ip_addr(:IP => '::ffff:10.1.0.1') )
318
387
  print "\n"
319
388
 
320
389
  # validate netmask
321
390
  puts "validate netmask"
322
- puts "255.255.255.0 is valid" if (IPAdmin.validate_ipv4_netmask('255.255.255.0') )
323
- puts "/24 is valid" if ( IPAdmin.validate_ipv4_netmask(24) )
324
- puts "/64 is valid" if ( IPAdmin.validate_ipv6_netmask(64) )
391
+ puts "255.255.255.0 is valid" if (IPAdmin.validate_ip_netmask(:Netmask => '255.255.255.0') )
392
+ puts "/24 is valid" if ( IPAdmin.validate_ip_netmask(:Netmask => '/24') )
393
+ puts "/64 is valid" if ( IPAdmin.validate_ip_netmask(:Netmask => 64, :Version => 6) )
325
394
 
326
395
  cidr4_1 = IPAdmin::CIDR.new(:CIDR => '192.168.1.0/24')
327
396
  cidr4_2 = IPAdmin::CIDR.new(:CIDR => '192.168.1.0/25')
@@ -351,12 +420,6 @@
351
420
  puts " #{x}"
352
421
  end
353
422
  print "\n"
354
-
355
- # arpa
356
- puts "arpa"
357
- puts "arpa for #{cidr4_1.desc()} is #{IPAdmin.arpa(cidr4_1)}"
358
- puts "arpa for #{cidr6_1.desc()} is #{IPAdmin.arpa(cidr6_1)}"
359
- print "\n"
360
423
 
361
424
  # shorten
362
425
  puts "shorten"
@@ -370,3 +433,4 @@
370
433
  #=====================================#
371
434
  #
372
435
  #=====================================#
436
+
@@ -0,0 +1,1459 @@
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
+ module IPAdmin
8
+ class CIDR
9
+
10
+ # instance variables
11
+ # @all_f
12
+ # @hostmask
13
+ # @ip
14
+ # @max_bits
15
+ # @netmask
16
+ # @network
17
+ # @tag
18
+ # @version
19
+
20
+ #==============================================================================#
21
+ # attr_reader/attr_writer
22
+ #==============================================================================#
23
+
24
+ # IP version 4 or 6.
25
+ attr_reader :version
26
+
27
+ # Hash of custom tags. Should be in the format tag => value.
28
+ attr_reader :tag
29
+
30
+ # Hash of custom tags. Should be in the format tag => value.
31
+ #
32
+ # Example:
33
+ # cidr4.tag['test'] = 'modified cidr4 tag'
34
+ # puts cidr4.tag['test'] --> modified cidr4 tag
35
+ #
36
+ def tag=(new_tag)
37
+ if (!new_tag.kind_of? Hash)
38
+ raise ArgumentError, "Expected Hash, but #{new_tag.class} provided."
39
+ end
40
+ @tag = new_tag
41
+ end
42
+
43
+ #======================================#
44
+ #
45
+ #======================================#
46
+
47
+
48
+ #==============================================================================#
49
+ # initialize()
50
+ #==============================================================================#
51
+
52
+ # - Arguments:
53
+ # * Hash with the following fields:
54
+ # - :CIDR -- IP address in CIDR notation - String (optional)
55
+ # - :Netmask -- IP Netmask - String or Integer (optional)
56
+ # - :PackedIP -- Integer representation of an IP address (optional)
57
+ # - :PackedNetmask -- Integer representation of an IP Netmask (optional)
58
+ # - :Version -- IP version - Integer (optional)
59
+ # - :Tag -- Custom descriptor tag - Hash, tag => value. (optional)
60
+ #
61
+ # - Notes:
62
+ # * At a minimum, either :CIDR or PackedIP must be provided.
63
+ # * Netmask defaults to /32 or /128 if not provided.
64
+ # * :PackedIP takes precedence over :CIDR
65
+ # * :PackedNetmask always takes precedence over any other provided netmasks.
66
+ # * Netmask within :CIDR takes precedence over :Netmask.
67
+ # * Version will be auto-detected if not specified
68
+ #
69
+ # Example:
70
+ # cidr4 = IPAdmin::CIDR.new(:CIDR => '192.168.1.1/24')
71
+ # cidr4_2 = IPAdmin::CIDR.new(:PackedIP => 0x0a010001,
72
+ # :PackedNetmask => 0xffffff00
73
+ # :Version => 4)
74
+ # cidr6 = IPAdmin::CIDR.new(:CIDR => 'fec0::/64',
75
+ # :Tag => {'interface' => 'g0/1'})
76
+ # cidr6_2 = IPAdmin::CIDR.new(:CIDR => '::ffff:192.168.1.1/96')
77
+ #
78
+ def initialize(options)
79
+ if (!options.kind_of? Hash)
80
+ raise ArgumentError, "Expected Hash, but #{options.class} provided."
81
+ end
82
+
83
+
84
+ if (options.has_key?(:PackedIP))
85
+ packed_ip = options[:PackedIP]
86
+ raise ArgumentError, "Expected Integer, but #{packed_ip.class} " +
87
+ "provided for option :PackedIP." if (!packed_ip.kind_of?(Integer))
88
+ elsif (options.has_key?(:CIDR))
89
+ cidr = options[:CIDR]
90
+ raise ArgumentError, "Expected String, but #{cidr.class} " +
91
+ "provided for option :CIDR." if (!cidr.kind_of?(String))
92
+ else
93
+ raise ArgumentError, "Missing argument: CIDR or PackedIP."
94
+ end
95
+
96
+
97
+ if (options.has_key?(:PackedNetmask))
98
+ packed_netmask = options[:PackedNetmask]
99
+ raise ArgumentError, "Expected Integer, but #{packed_netmask.class} " +
100
+ "provided for option :PackedNetmask." if (!packed_netmask.kind_of?(Integer))
101
+
102
+ elsif (options.has_key?(:Netmask))
103
+ netmask = options[:Netmask]
104
+ raise ArgumentError, "Expected String or Integer, but #{netmask.class} " +
105
+ "provided for option :Netmask." if (!netmask.kind_of?(String) && !netmask.kind_of?(Integer))
106
+ end
107
+
108
+
109
+ if (options.has_key?(:Version))
110
+ @version = options[:Version]
111
+ if (@version != 4 && @version != 6)
112
+ raise ArgumentError, ":Version should be 4 or 6, but was '#{version}'."
113
+ end
114
+ end
115
+
116
+ if (options.has_key?(:Tag))
117
+ @tag = options[:Tag]
118
+ if (!@tag.kind_of? Hash)
119
+ raise ArgumentError, "Expected Hash, but #{@tag.class} provided for option :Tag."
120
+ end
121
+ end
122
+
123
+
124
+ if (packed_ip)
125
+ if (packed_ip < 2**32)
126
+ @version = 4 if (!@version)
127
+ @netmask = 2**32-1 if (!netmask && !packed_netmask)
128
+ else
129
+ @version = 6 if (!@version)
130
+ @netmask = 2**128-1 if (!netmask && !packed_netmask)
131
+ end
132
+
133
+ # validate & store packed_ip
134
+ IPAdmin.validate_ip_addr(:IP => packed_ip, :Version => @version)
135
+ @ip = packed_ip
136
+
137
+ else
138
+ # determine version if not set
139
+ if (!@version)
140
+ if (cidr =~ /\./ && cidr !~ /:/)
141
+ @version = 4
142
+ elsif (cidr =~ /:/)
143
+ @version = 6
144
+ end
145
+ end
146
+
147
+ # get ip/netmask
148
+ if (cidr =~ /\//) # if netmask part of cidr
149
+ ip,netmask = cidr.split(/\//)
150
+ raise ArgumentError, ":CIDR is improperly formatted." if (!ip || !netmask)
151
+ elsif (@version == 4)
152
+ ip = cidr
153
+ @netmask = 2**32-1 if (!netmask && !packed_netmask)
154
+ elsif (@version == 6)
155
+ ip = cidr
156
+ @netmask = 2**128-1 if (!netmask && !packed_netmask)
157
+ end
158
+
159
+ # validate & pack ip
160
+ IPAdmin.validate_ip_addr(:IP => ip, :Version => @version)
161
+ @ip = IPAdmin.pack_ip_addr(:IP => ip, :Version => @version)
162
+
163
+ end
164
+
165
+
166
+ # validate & store netmask || packed_netmask if needed
167
+ if (!@netmask)
168
+ if (packed_netmask)
169
+ IPAdmin.validate_ip_netmask(:Netmask => packed_netmask, :Packed => true, :Version => @version)
170
+ @netmask = packed_netmask
171
+ else
172
+ IPAdmin.validate_ip_netmask(:Netmask => netmask, :Version => @version)
173
+ @netmask = IPAdmin.pack_ip_netmask(:Netmask => netmask, :Version => @version)
174
+ end
175
+ end
176
+
177
+ # set vars based on version
178
+ if (@version == 4)
179
+ @max_bits = 32
180
+ @all_f = 2**@max_bits - 1
181
+ else
182
+ @max_bits = 128
183
+ @all_f = 2**@max_bits - 1
184
+ end
185
+
186
+ # get @network & @hostmask
187
+ @network = (@ip & @netmask)
188
+ @hostmask = @netmask ^ @all_f
189
+
190
+ end
191
+
192
+ #======================================#
193
+ #
194
+ #======================================#
195
+
196
+
197
+ #==============================================================================#
198
+ # arpa()
199
+ #==============================================================================#
200
+
201
+ # Depending on the IP version of the current CIDR,
202
+ # return either an in-addr.arpa. or ip6.arpa. string. The netmask will be used
203
+ # to determine the length of the returned string.
204
+ #
205
+ # - Arguments:
206
+ # * none
207
+ #
208
+ # - Returns:
209
+ # * String
210
+ #
211
+ # Example:
212
+ # arpa = cidr4.arpa() --> '1.168.192.in-addr.arpa.'
213
+ #
214
+ def arpa()
215
+
216
+ base = self.ip()
217
+ netmask = self.bits()
218
+
219
+ if (@version == 4)
220
+ net = base.split('.')
221
+
222
+ if (netmask)
223
+ while (netmask < 32)
224
+ net.pop
225
+ netmask = netmask + 8
226
+ end
227
+ end
228
+
229
+ arpa = net.reverse.join('.')
230
+ arpa << ".in-addr.arpa."
231
+
232
+ elsif (@version == 6)
233
+ fields = base.split(':')
234
+ net = []
235
+ fields.each do |field|
236
+ (field.split("")).each do |x|
237
+ net.push(x)
238
+ end
239
+ end
240
+
241
+ if (netmask)
242
+ while (netmask < 128)
243
+ net.pop
244
+ netmask = netmask + 4
245
+ end
246
+ end
247
+
248
+ arpa = net.reverse.join('.')
249
+ arpa << ".ip6.arpa."
250
+
251
+ end
252
+
253
+ return(arpa)
254
+ end
255
+
256
+ #======================================#
257
+ #
258
+ #======================================#
259
+
260
+
261
+ #==============================================================================#
262
+ # bits()
263
+ #==============================================================================#
264
+
265
+ # Provide number of bits in Netmask.
266
+ #
267
+ # - Arguments:
268
+ # * none
269
+ #
270
+ # - Returns:
271
+ # * Integer.
272
+ #
273
+ # Example:
274
+ # puts cidr4.bits() --> 24
275
+ #
276
+ def bits()
277
+ return(IPAdmin.unpack_ip_netmask(:Integer => @netmask))
278
+ end
279
+
280
+ #======================================#
281
+ #
282
+ #======================================#
283
+
284
+
285
+ #==============================================================================#
286
+ # contains?()
287
+ #==============================================================================#
288
+
289
+ # Determines if this CIDR contains (is supernet of)
290
+ # the provided CIDR object.
291
+ #
292
+ # - Arguments:
293
+ # * CIDR object
294
+ #
295
+ # - Returns:
296
+ # * true or false
297
+ #
298
+ # Example:
299
+ # cidr4_2 = IPAdmin::CIDR.new(:CIDR => '192.168.1.0/26')
300
+ # contains? = cidr4.contains(cidr4_2) --> true
301
+ #
302
+ def contains?(object)
303
+ is_contained = false
304
+
305
+ if (object.kind_of?(IPAdmin::CIDR))
306
+ network = object.packed_network
307
+ netmask = object.packed_netmask
308
+
309
+ else
310
+ raise ArgumentError, "Expected IPAdmin::CIDR " +
311
+ " object but #{object.class} provided."
312
+ end
313
+
314
+
315
+ if (object.version != @version)
316
+ raise ArgumentError, "Attempted to compare a version #{object.version} CIDR " +
317
+ "with a version #{@version} CIDR."
318
+ end
319
+
320
+ # if network == @network, then we have to go by netmask length
321
+ # else we can tell by or'ing network and @network by @hostmask
322
+ # and comparing the results
323
+ if (network == @network)
324
+ is_contained = true if (netmask > @netmask)
325
+
326
+ else
327
+ if ( (network | @hostmask) == (@network | @hostmask) )
328
+ is_contained = true
329
+ end
330
+ end
331
+
332
+ return(is_contained)
333
+ end
334
+
335
+ #======================================#
336
+ #
337
+ #======================================#
338
+
339
+
340
+ #==============================================================================#
341
+ # desc()
342
+ #==============================================================================#
343
+
344
+ # Returns network/netmask in CIDR format.
345
+ #
346
+ # - Arguments:
347
+ # * Optional hash with the following fields:
348
+ # - :IP -- if true, return the original ip/netmask passed during initialization (optional)
349
+ # - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
350
+ #
351
+ # - Returns:
352
+ # * String
353
+ #
354
+ # Example:
355
+ # puts cidr4.desc() --> 192.168.1.0/24
356
+ # puts cidr4.desc(:IP => true) --> 192.168.1.1/24
357
+ #
358
+ def desc(options=nil)
359
+ short = false
360
+ orig_ip = false
361
+
362
+ if (options)
363
+ if (!options.kind_of? Hash)
364
+ raise ArgumentError, "Expected Hash, but #{options.class} provided."
365
+ end
366
+
367
+ if (options.has_key?(:Short))
368
+ short = true
369
+ end
370
+
371
+ if (options.has_key?(:IP))
372
+ orig_ip = true
373
+ end
374
+ end
375
+
376
+ if (!orig_ip)
377
+ ip = IPAdmin.unpack_ip_addr(:Integer => @network, :Version => @version)
378
+ else
379
+ ip = IPAdmin.unpack_ip_addr(:Integer => @ip, :Version => @version)
380
+ end
381
+ ip = IPAdmin.shorten(ip) if (short && @version == 6)
382
+ mask = IPAdmin.unpack_ip_netmask(:Integer => @netmask)
383
+
384
+ return("#{ip}/#{mask}")
385
+ end
386
+
387
+ #======================================#
388
+ #
389
+ #======================================#
390
+
391
+
392
+ #==============================================================================#
393
+ # enumerate()
394
+ #==============================================================================#
395
+
396
+ # Provide all IP addresses contained within the IP space of this CIDR.
397
+ # (warning: this can be quite large for big blocks)
398
+ #
399
+ # - Arguments:
400
+ # * Optional Hash with the following fields:
401
+ # - :Bitstep -- enumerate in X sized steps 0 - Integer (optional)
402
+ # - :Limit -- limit returned list to X number of items - Integer (optional)
403
+ # - :Objectify -- if true, return IPAdmin::CIDR objects (optional)
404
+ # - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
405
+ #
406
+ # - Returns:
407
+ # * Array of Strings or CIDR objects
408
+ #
409
+ # Example:
410
+ # ip_list = cidr4.enumerate(:Bitstep => 2, :Limit => 2) --> ['192.168.1.0','192.168.1.1']
411
+ #
412
+ def enumerate(options=nil)
413
+ bitstep = 1
414
+ objectify = false
415
+ limit = nil
416
+ short = false
417
+
418
+ if (options)
419
+ if (!options.kind_of? Hash)
420
+ raise ArgumentError, "Expected Hash, but #{options.class} provided."
421
+ end
422
+
423
+ if( options.has_key?(:Bitstep) )
424
+ bitstep = options[:Bitstep]
425
+ end
426
+
427
+ if( options.has_key?(:Objectify) )
428
+ objectify = true
429
+ end
430
+
431
+ if( options.has_key?(:Limit) )
432
+ limit = options[:Limit]
433
+ end
434
+
435
+ if( options.has_key?(:Short) )
436
+ short = true
437
+ end
438
+ end
439
+
440
+ list = []
441
+ my_ip = @network
442
+ change_mask = @hostmask | my_ip
443
+
444
+ until ( change_mask != (@hostmask | @network) )
445
+ if (!objectify)
446
+ my_ip_s = IPAdmin.unpack_ip_addr(:Integer => my_ip, :Version => @version)
447
+ my_ip_s = IPAdmin.shorten(my_ip_s) if (short && @version == 6)
448
+ list.push( my_ip_s )
449
+ else
450
+ list.push( IPAdmin::CIDR.new(:PackedIP => my_ip, :Version => @version) )
451
+ end
452
+ my_ip = my_ip + bitstep
453
+ change_mask = @hostmask | my_ip
454
+ if (limit)
455
+ limit = limit -1
456
+ break if (limit == 0)
457
+ end
458
+ end
459
+
460
+ return(list)
461
+ end
462
+
463
+ #======================================#
464
+ #
465
+ #======================================#
466
+
467
+
468
+ #==============================================================================#
469
+ # hostmask_ext()
470
+ #==============================================================================#
471
+
472
+ # Provide IPv4 Hostmask in extended format (y.y.y.y).
473
+ #
474
+ # - Arguments:
475
+ # * none
476
+ #
477
+ # - Returns:
478
+ # * String
479
+ #
480
+ # Example:
481
+ # puts cidr4.hostmask_ext() --> 0.0.0.255
482
+ #
483
+ def hostmask_ext()
484
+ if (@version == 4)
485
+ hostmask = IPAdmin.unpack_ip_addr(:Integer => @hostmask, :Version => @version)
486
+ else
487
+ raise "IPv6 does not support extended hostmask notation."
488
+ end
489
+
490
+ return(hostmask)
491
+ end
492
+
493
+ #======================================#
494
+ #
495
+ #======================================#
496
+
497
+
498
+ #==============================================================================#
499
+ # ip()
500
+ #==============================================================================#
501
+
502
+ # Provide original IP address passed during initialization.
503
+ #
504
+ # - Arguments:
505
+ # * Optional Hash with the following fields:
506
+ # - :Objectify -- if true, return IPAdmin::CIDR object (optional)
507
+ # - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
508
+ #
509
+ # - Returns:
510
+ # * String or CIDR object.
511
+ #
512
+ # Example:
513
+ # puts cidr4.ip() --> 192.168.1.1
514
+ #
515
+ def ip(options=nil)
516
+ objectify = false
517
+ short = false
518
+
519
+ if (options)
520
+ if (!options.kind_of?(Hash))
521
+ raise Argumenterror, "Expected Hash, but " +
522
+ "#{options.class} provided."
523
+ end
524
+
525
+ if( options.has_key?(:Short) )
526
+ short = true
527
+ end
528
+
529
+ if( options.has_key?(:Objectify) )
530
+ objectify = true
531
+ end
532
+ end
533
+
534
+
535
+
536
+ if (!objectify)
537
+ ip = IPAdmin.unpack_ip_addr(:Integer => @ip, :Version => @version)
538
+ ip = IPAdmin.shorten(ip) if (short && @version == 6)
539
+ else
540
+ ip = IPAdmin::CIDR.new(:PackedIP => @ip, :Version => @version)
541
+ end
542
+
543
+ return(ip)
544
+ end
545
+
546
+ #======================================#
547
+ #
548
+ #======================================#
549
+
550
+
551
+ #==============================================================================#
552
+ # last()
553
+ #==============================================================================#
554
+
555
+ # Provide last IP address in this CIDR object.
556
+ #
557
+ # - Arguments:
558
+ # * Optional Hash with the following fields:
559
+ # - :Objectify -- if true, return IPAdmin::CIDR object (optional)
560
+ # - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
561
+ #
562
+ # - Returns:
563
+ # * String or CIDR object.
564
+ #
565
+ # - Notes:
566
+ # * The broadcast() method is liased to this method, and thus works for
567
+ # IPv6 despite the fact that the IPv6 protocol does not support IP
568
+ # broadcasting.
569
+ #
570
+ # Example:
571
+ # puts cidr4.last() --> 192.168.1.255
572
+ #
573
+ def last(options=nil)
574
+ objectify = false
575
+ short = false
576
+
577
+ if (options)
578
+ if (!options.kind_of?(Hash))
579
+ raise Argumenterror, "Expected Hash, but " +
580
+ "#{options.class} provided."
581
+ end
582
+
583
+ if( options.has_key?(:Short) )
584
+ short = true
585
+ end
586
+
587
+ if( options.has_key?(:Objectify) )
588
+ objectify = true
589
+ end
590
+
591
+ end
592
+
593
+ packed_ip = @network | @hostmask
594
+ if (!objectify)
595
+ ip = IPAdmin.unpack_ip_addr(:Integer => packed_ip, :Version => @version)
596
+ ip = IPAdmin.shorten(ip) if (short && !objectify && @version == 6)
597
+ else
598
+ ip = IPAdmin::CIDR.new(:PackedIP => packed_ip, :Version => @version)
599
+ end
600
+
601
+ return(ip)
602
+ end
603
+
604
+ alias :broadcast :last
605
+
606
+ #======================================#
607
+ #
608
+ #======================================#
609
+
610
+
611
+ #==============================================================================#
612
+ # netmask()
613
+ #==============================================================================#
614
+
615
+ # Provide netmask in CIDR format.
616
+ #
617
+ # - Arguments:
618
+ # * none
619
+ #
620
+ # - Returns:
621
+ # * String
622
+ #
623
+ # Example:
624
+ # puts cidr4.netmask() --> /24
625
+ #
626
+ def netmask()
627
+ bits = IPAdmin.unpack_ip_netmask(:Integer => @netmask)
628
+ return("/#{bits}")
629
+ end
630
+
631
+ #======================================#
632
+ #
633
+ #======================================#
634
+
635
+
636
+ #==============================================================================#
637
+ # netmask_ext()
638
+ #==============================================================================#
639
+
640
+ # Provide IPv4 netmask in extended format (y.y.y.y).
641
+ #
642
+ # - Arguments:
643
+ # * none
644
+ #
645
+ # - Returns:
646
+ # * String
647
+ #
648
+ # Example:
649
+ # puts cidr4.netmask_ext() --> 255.255.255.0
650
+ #
651
+ def netmask_ext()
652
+ if (@version == 4)
653
+ netmask = IPAdmin.unpack_ip_addr(:Integer => @netmask)
654
+ else
655
+ raise "IPv6 does not support extended netmask notation. " +
656
+ "Use netmask() method instead."
657
+ end
658
+
659
+ return(netmask)
660
+ end
661
+
662
+ #======================================#
663
+ #
664
+ #======================================#
665
+
666
+
667
+ #==============================================================================#
668
+ # network()
669
+ #==============================================================================#
670
+
671
+ # Provide base network address.
672
+ #
673
+ # - Arguments:
674
+ # * Optional Hash with the following fields:
675
+ # - :Objectify -- if true, return IPAdmin::CIDR object (optional)
676
+ # - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
677
+ #
678
+ # - Returns:
679
+ # * String or CIDR object.
680
+ #
681
+ # Example:
682
+ # puts cidr4.network() --> 192.168.1.0
683
+ #
684
+ def network(options=nil)
685
+ objectify = false
686
+ short = false
687
+
688
+ if (options)
689
+ if (!options.kind_of?(Hash))
690
+ raise Argumenterror, "Expected Hash, but " +
691
+ "#{options.class} provided."
692
+ end
693
+
694
+ if( options.has_key?(:Short) )
695
+ short = true
696
+ end
697
+
698
+ if( options.has_key?(:Objectify) )
699
+ objectify = true
700
+ end
701
+ end
702
+
703
+
704
+ if (!objectify)
705
+ ip = IPAdmin.unpack_ip_addr(:Integer => @network, :Version => @version)
706
+ ip = IPAdmin.shorten(ip) if (short && @version == 6)
707
+ else
708
+ ip = IPAdmin::CIDR.new(:PackedIP => @network, :Version => @version)
709
+ end
710
+
711
+ return(ip)
712
+ end
713
+
714
+ alias :base :network
715
+ alias :first :network
716
+
717
+ #======================================#
718
+ #
719
+ #======================================#
720
+
721
+
722
+ #==============================================================================#
723
+ # next_ip()
724
+ #==============================================================================#
725
+
726
+ # Provide the next IP following the last available IP within this
727
+ # CIDR object.
728
+ #
729
+ # - Arguments:
730
+ # * Optional Hash with the following fields:
731
+ # - :Bitstep -- step in X sized steps - Integer (optional)
732
+ # - :Objectify -- if true, return IPAdmin::CIDR object (optional)
733
+ # - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
734
+ #
735
+ # - Returns:
736
+ # * String or CIDR object.
737
+ #
738
+ # Example:
739
+ # puts cidr4.next_ip() --> 192.168.2.0
740
+ #
741
+ def next_ip(options=nil)
742
+ bitstep = 1
743
+ objectify = false
744
+ short = false
745
+
746
+ if (options)
747
+ if (!options.kind_of?(Hash))
748
+ raise Argumenterror, "Expected Hash, but " +
749
+ "#{options.class} provided."
750
+ end
751
+
752
+ if( options.has_key?(:Bitstep) )
753
+ bitstep = options[:Bitstep]
754
+ end
755
+
756
+ if( options.has_key?(:Short) )
757
+ short = true
758
+ end
759
+
760
+ if( options.has_key?(:Objectify) )
761
+ objectify = true
762
+ end
763
+ end
764
+
765
+ next_ip = @network + @hostmask + bitstep
766
+
767
+ if (next_ip > @all_f)
768
+ raise "Returned IP is out of bounds for IPv#{@version}."
769
+ end
770
+
771
+
772
+ if (!objectify)
773
+ next_ip = IPAdmin.unpack_ip_addr(:Integer => next_ip, :Version => @version)
774
+ next_ip = IPAdmin.shorten(next_ip) if (short && @version == 6)
775
+ else
776
+ next_ip = IPAdmin::CIDR.new(:PackedIP => next_ip, :Version => @version)
777
+ end
778
+
779
+ return(next_ip)
780
+ end
781
+
782
+ #======================================#
783
+ #
784
+ #======================================#
785
+
786
+
787
+ #==============================================================================#
788
+ # next_subnet()
789
+ #==============================================================================#
790
+
791
+ # Provide the next subnet following this CIDR object. The next subnet will
792
+ # be of the same size as the current CIDR object.
793
+ #
794
+ # - Arguments:
795
+ # * Optional Hash with the following fields:
796
+ # - :Bitstep -- step in X sized steps. - Integer (optional)
797
+ # - :Objectify -- if true, return IPAdmin::CIDR object (optional)
798
+ # - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
799
+ #
800
+ # - Returns:
801
+ # * String or CIDR object.
802
+ #
803
+ # Example:
804
+ # puts cidr4.next_subnet() --> 192.168.2.0/24
805
+ #
806
+ def next_subnet(options=nil)
807
+ bitstep = 1
808
+ objectify = false
809
+ short = false
810
+
811
+ if (options)
812
+ if (!options.kind_of?(Hash))
813
+ raise Argumenterror, "Expected Hash, but " +
814
+ "#{options.class} provided."
815
+ end
816
+
817
+ if( options.has_key?(:Bitstep) )
818
+ bitstep = options[:Bitstep]
819
+ end
820
+
821
+ if( options.has_key?(:Short) )
822
+ short = true
823
+ end
824
+
825
+ if( options.has_key?(:Objectify) )
826
+ objectify = true
827
+ end
828
+ end
829
+
830
+ bitstep = bitstep * (2**(@max_bits - self.bits) )
831
+ next_sub = @network + bitstep
832
+
833
+ if (next_sub > @all_f)
834
+ raise "Returned subnet is out of bounds for IPv#{@version}."
835
+ end
836
+
837
+ netmask = self.bits
838
+ if (!objectify)
839
+ next_sub = IPAdmin.unpack_ip_addr(:Integer => next_sub, :Version => @version)
840
+ next_sub = IPAdmin.shorten(next_sub) if (short && @version == 6)
841
+ next_sub = next_sub << "/" << netmask.to_s
842
+ else
843
+ next_sub = IPAdmin::CIDR.new(:PackedIP => next_sub, :Netmask => netmask, :Version => @version)
844
+ end
845
+
846
+ return(next_sub)
847
+ end
848
+
849
+ #======================================#
850
+ #
851
+ #======================================#
852
+
853
+
854
+ #==============================================================================#
855
+ # nth()
856
+ #==============================================================================#
857
+
858
+ # Provide the nth IP within this object.
859
+ #
860
+ # - Arguments:
861
+ # * Hash with the following fields:
862
+ # - :Index -- index number of the IP address to return - Integer
863
+ # - :Objectify -- if true, return IPAdmin::CIDR objects (optional)
864
+ # - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
865
+ #
866
+ # - Returns:
867
+ # * String or CIDR object.
868
+ #
869
+ # Example:
870
+ # puts cidr4.nth(:Index => 1) --> 192.168.1.1
871
+ #
872
+ def nth(options)
873
+ objectify = false
874
+ short = false
875
+
876
+ if (!options.kind_of?(Hash))
877
+ raise ArgumentError, "Expected Hash, but " +
878
+ "#{options.class} provided."
879
+ end
880
+
881
+ if ( !options.has_key?(:Index) )
882
+ raise ArgumentError, "Missing argument: Index."
883
+ end
884
+ index = options[:Index]
885
+
886
+ if( options.has_key?(:Short) )
887
+ short = true
888
+ end
889
+
890
+ if( options.has_key?(:Objectify) )
891
+ objectify = true
892
+ end
893
+
894
+ my_ip = @network + index
895
+ if ( (@hostmask | my_ip) == (@hostmask | @network) )
896
+
897
+ if (!objectify)
898
+ my_ip = IPAdmin.unpack_ip_addr(:Integer => my_ip, :Version => @version)
899
+ my_ip = IPAdmin.shorten(my_ip) if (short && @version == 6)
900
+ else
901
+ my_ip = IPAdmin::CIDR.new(:PackedIP => my_ip, :Version => @version)
902
+ end
903
+
904
+ else
905
+ raise "Index of #{index} returns IP that is out of " +
906
+ "bounds of CIDR network."
907
+ end
908
+
909
+ return(my_ip)
910
+ end
911
+
912
+ #======================================#
913
+ #
914
+ #======================================#
915
+
916
+
917
+ #==============================================================================#
918
+ # packed_hostmask()
919
+ #==============================================================================#
920
+
921
+ # Provide an integer representation of the Hostmask of this object.
922
+ #
923
+ # - Arguments:
924
+ # * none
925
+ #
926
+ # - Returns:
927
+ # * Integer
928
+ #
929
+ # Example:
930
+ # puts cidr4.packed_hostmask().to_s(16) --> 0xff
931
+ #
932
+ def packed_hostmask()
933
+ return(@hostmask)
934
+ end
935
+
936
+ #======================================#
937
+ #
938
+ #======================================#
939
+
940
+
941
+ #==============================================================================#
942
+ # packed_ip()
943
+ #==============================================================================#
944
+
945
+ # Provide an integer representation of the IP address of this object.
946
+ #
947
+ # - Arguments:
948
+ # * none
949
+ #
950
+ # - Returns:
951
+ # * Integer
952
+ #
953
+ # Example:
954
+ # puts cidr4.packed_ip().to_s(16) --> 0xc0c80101
955
+ #
956
+ def packed_ip()
957
+ return(@ip)
958
+ end
959
+
960
+ #======================================#
961
+ #
962
+ #======================================#
963
+
964
+
965
+ #==============================================================================#
966
+ # packed_netmask()
967
+ #==============================================================================#
968
+
969
+ # Provide an integer representation of the Netmask of this object.
970
+ #
971
+ # - Arguments:
972
+ # * none
973
+ #
974
+ # - Returns:
975
+ # * Integer
976
+ #
977
+ # Example:
978
+ # puts cidr4.packed_netmask().to_s(16) --> 0xffffff00
979
+ #
980
+ def packed_netmask()
981
+ return(@netmask)
982
+ end
983
+
984
+ #======================================#
985
+ #
986
+ #======================================#
987
+
988
+
989
+ #==============================================================================#
990
+ # packed_network()
991
+ #==============================================================================#
992
+
993
+ # Provide an integer representation of the Network address of this object.
994
+ #
995
+ # - Arguments:
996
+ # * none
997
+ #
998
+ # - Returns:
999
+ # * Integer
1000
+ #
1001
+ # Example:
1002
+ # packed = cidr4.packed_network().to_s(16) --> 0xc0c80100
1003
+ #
1004
+ def packed_network()
1005
+ return(@network)
1006
+ end
1007
+
1008
+ #======================================#
1009
+ #
1010
+ #======================================#
1011
+
1012
+
1013
+ #==============================================================================#
1014
+ # range()
1015
+ #==============================================================================#
1016
+
1017
+ # Given two Indexes, return all IP addresses within the CIDR that are
1018
+ # between them (inclusive).
1019
+ #
1020
+ # - Arguments:
1021
+ # * Hash with the following fields:
1022
+ # - :Bitstep -- enumerate in X sized steps - Integer (optional)
1023
+ # - :Indexes -- index numbers of the addresses to use as boundaries - Array of (2) Integers
1024
+ # - :Objectify -- if true, return IPAdmin::CIDR objects (optional)
1025
+ # - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
1026
+ #
1027
+ # - Returns:
1028
+ # * Array Strings or CIDR objects
1029
+ #
1030
+ # Example:
1031
+ # list = cidr4.range(:Indexes => [0,1]) --> ['192.168.1.0','192.168.1.1']
1032
+ #
1033
+ def range(options)
1034
+ objectify = false
1035
+ short = false
1036
+ bitstep = 1
1037
+
1038
+ if (!options.kind_of?(Hash))
1039
+ raise Argumenterror, "Expected Hash, but #{options.class} provided."
1040
+ end
1041
+
1042
+ if ( !options.has_key?(:Indexes) )
1043
+ raise ArgumentError, "Missing argument: Indexes."
1044
+ end
1045
+ indexes = options[:Indexes]
1046
+ indexes.sort!
1047
+
1048
+ if( (!indexes.kind_of?(Array)) || (indexes.length != 2))
1049
+ raise ArgumentError, "Argument :Index should be an array of (2) index numbers."
1050
+ end
1051
+
1052
+ if ( (indexes[0] < 0) || (indexes[0] > self.size) )
1053
+ raise ArgumentError, "Index #{indexes[0]} is out of bounds for this CIDR."
1054
+ end
1055
+
1056
+ if (indexes[1] >= self.size)
1057
+ raise ArgumentError, "Index #{indexes[1]} is out of bounds for this CIDR."
1058
+ end
1059
+
1060
+ if( options.has_key?(:Short) )
1061
+ short = true
1062
+ end
1063
+
1064
+ if( options.has_key?(:Objectify) )
1065
+ objectify = true
1066
+ end
1067
+
1068
+ if( options.has_key?(:Bitstep) )
1069
+ bitstep = options[:Bitstep]
1070
+ end
1071
+
1072
+ start_ip = @network + indexes[0]
1073
+ end_ip = @network + indexes[1]
1074
+ my_ip = start_ip
1075
+ list = []
1076
+ until (my_ip > end_ip)
1077
+ if (!objectify)
1078
+ ip = IPAdmin.unpack_ip_addr(:Integer => my_ip, :Version => @version)
1079
+ ip = IPAdmin.shorten(ip) if (short && @version == 6)
1080
+ else
1081
+ ip = IPAdmin::CIDR.new(:PackedIP => my_ip, :Version => @version)
1082
+ end
1083
+
1084
+ list.push(ip)
1085
+ my_ip += bitstep
1086
+ end
1087
+
1088
+ return(list)
1089
+ end
1090
+
1091
+ #======================================#
1092
+ #
1093
+ #======================================#
1094
+
1095
+
1096
+ #==============================================================================#
1097
+ # remainder()
1098
+ #==============================================================================#
1099
+
1100
+ # Given a portion of the current CIDR, provide the remainder of
1101
+ # the CIDR. For example if the original CIDR is 192.168.0.0/24 and you
1102
+ # provide 192.168.0.64/26 as the portion to exclude, then 192.168.0.0/26,
1103
+ # 192.168.0.128/26, and 192.168.0.192/26 will be returned as the remainder.
1104
+ #
1105
+ # - Arguments:
1106
+ # * Optional hash with the following fields:
1107
+ # - :Exclude -- CIDR object to use in calculating the remainder.
1108
+ # - :Objectify -- if true, return IPAdmin::CIDR objects (optional)
1109
+ # - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
1110
+ #
1111
+ # - Returns:
1112
+ # * Array of Strings or CIDR objects
1113
+ #
1114
+ #
1115
+ # Example:
1116
+ # cidr4_2 = IPAdmin::CIDR.new(:CIDR => '192.168.1.64/26')
1117
+ # cidr4.remainder(:Exclude => cidr4_2).each {|x| puts}
1118
+ #
1119
+ def remainder(options)
1120
+ short = nil
1121
+ objectify = nil
1122
+
1123
+ if (!options.kind_of? Hash)
1124
+ raise ArgumentError, "Expected Hash, but #{options.class} provided."
1125
+ end
1126
+
1127
+ if ( !options.has_key?(:Exclude) )
1128
+ raise ArgumentError, "Missing argument: Exclude."
1129
+ end
1130
+ to_exclude = options[:Exclude]
1131
+
1132
+ if ( !to_exclude.kind_of?(IPAdmin::CIDR) )
1133
+ raise ArgumentError, "Expeced IPAdmin::CIDR, but #{exclude.class} " +
1134
+ "for option: Exclude."
1135
+ end
1136
+
1137
+ if( options.has_key?(:Short) )
1138
+ short = true
1139
+ end
1140
+
1141
+ if( options.has_key?(:Objectify) )
1142
+ objectify = true
1143
+ end
1144
+
1145
+ # make sure 'to_exclude' is the same ip version
1146
+ if ( to_exclude.version != @version )
1147
+ raise "#{to_exclude.desc(:Short => true)} is of a different " +
1148
+ "IP version than #{self.desc(:Short => true)}."
1149
+ end
1150
+
1151
+ # make sure we contain 'to_exclude'
1152
+ if ( self.contains?(to_exclude) != true )
1153
+ raise "#{to_exclude.desc(:Short => true)} does not fit " +
1154
+ "within the bounds of #{self.desc(:Short => true)}."
1155
+ end
1156
+
1157
+ # split this cidr in half & see which half 'to_exclude'
1158
+ # belongs in. take that half & repeat the process. every time
1159
+ # we repeat, store off the non-matching half
1160
+ new_mask = self.bits + 1
1161
+ lower_network = self.packed_network
1162
+ upper_network = self.packed_network + 2**(@max_bits - new_mask)
1163
+
1164
+ new_subnets = []
1165
+ until(new_mask > to_exclude.bits)
1166
+ if (to_exclude.packed_network < upper_network)
1167
+ match = lower_network
1168
+ non_match = upper_network
1169
+ else
1170
+ match = upper_network
1171
+ non_match = lower_network
1172
+ end
1173
+
1174
+
1175
+ if (!objectify)
1176
+ non_match = IPAdmin.unpack_ip_addr(:Integer => non_match, :Version => @version)
1177
+ non_match = IPAdmin.shorten(non_match) if (short && @version == 6)
1178
+ new_subnets.unshift("#{non_match}/#{new_mask}")
1179
+ else
1180
+ new_subnets.unshift(IPAdmin::CIDR.new(:PackedIP => non_match,
1181
+ :Netmask => new_mask,
1182
+ :Version => @version))
1183
+ end
1184
+
1185
+ new_mask = new_mask + 1
1186
+ lower_network = match
1187
+ upper_network = match + 2**(@max_bits - new_mask)
1188
+ end
1189
+
1190
+ return(new_subnets)
1191
+ end
1192
+
1193
+ #======================================#
1194
+ #
1195
+ #======================================#
1196
+
1197
+
1198
+ #==============================================================================#
1199
+ # resize()
1200
+ #==============================================================================#
1201
+
1202
+ # Resize the CIDR by changing the size of the Netmask.
1203
+ # Return the resulting CIDR as a new object.
1204
+ #
1205
+ # - Arguments:
1206
+ # * Hash with the following fields:
1207
+ # - :Subnet -- Number of bits of new Netmask - Integer
1208
+ #
1209
+ # - Returns:
1210
+ # * CIDR object
1211
+ #
1212
+ # Example:
1213
+ # new_cidr = cidr4.resize(:Subnet => 23)
1214
+ # puts new_cidr.desc --> 192.168.1.0/23
1215
+ #
1216
+ def resize(options)
1217
+ if (!options.kind_of?(Hash))
1218
+ raise Argumenterror, "Expected Hash, but " +
1219
+ "#{options.class} provided."
1220
+ end
1221
+
1222
+ if ( !options.has_key?(:Subnet) )
1223
+ raise Argumenterror, "Missing argument: Subnet."
1224
+ end
1225
+ bits = options[:Subnet]
1226
+
1227
+ IPAdmin.validate_ip_netmask(:Netmask => bits, :Version => @version)
1228
+ netmask = IPAdmin.pack_ip_netmask(:Netmask => bits, :Version => @version)
1229
+ network = @network & netmask
1230
+
1231
+ cidr = IPAdmin::CIDR.new(:PackedIP => network, :PackedNetmask => netmask, :Version => @version)
1232
+ return(cidr)
1233
+ end
1234
+
1235
+ #======================================#
1236
+ #
1237
+ #======================================#
1238
+
1239
+
1240
+ #==============================================================================#
1241
+ # resize!()
1242
+ #==============================================================================#
1243
+
1244
+ # Resize this object by changing the size of the Netmask.
1245
+ #
1246
+ # - Arguments:
1247
+ # * Hash with the following fields:
1248
+ # - :Subnet -- Number of bits of new Netmask - Integer
1249
+ #
1250
+ # - Returns:
1251
+ # * nil
1252
+ #
1253
+ # - Notes:
1254
+ # * If CIDR is resized such that the original IP is no longer contained within,
1255
+ # then that IP will be reset to the base network address.
1256
+ #
1257
+ # Example:
1258
+ # cidr4.resize!(:Subnet => 23)
1259
+ # puts cidr4.desc --> 192.168.1.0/23
1260
+ #
1261
+ def resize!(options)
1262
+ if (!options.kind_of?(Hash))
1263
+ raise Argumenterror, "Expected Hash, but " +
1264
+ "#{options.class} provided."
1265
+ end
1266
+
1267
+ if ( !options.has_key?(:Subnet) )
1268
+ raise Argumenterror, "Missing argument: Subnet."
1269
+ end
1270
+ bits = options[:Subnet]
1271
+
1272
+ IPAdmin.validate_ip_netmask(:Netmask => bits, :Version => @version)
1273
+ netmask = IPAdmin.pack_ip_netmask(:Netmask => bits, :Version => @version)
1274
+
1275
+ @netmask = netmask
1276
+ @network = @network & netmask
1277
+ @hostmask = @netmask ^ @all_f
1278
+
1279
+ # check @ip
1280
+ if ((@ip & @netmask) != (@network))
1281
+ @ip = @network
1282
+ end
1283
+
1284
+ return(nil)
1285
+ end
1286
+
1287
+ #======================================#
1288
+ #
1289
+ #======================================#
1290
+
1291
+
1292
+ #==============================================================================#
1293
+ # size()
1294
+ #==============================================================================#
1295
+
1296
+ # Provide number of IP addresses within this object.
1297
+ #
1298
+ # - Arguments:
1299
+ # * none
1300
+ #
1301
+ # - Returns:
1302
+ # * Integer
1303
+ #
1304
+ # Example:
1305
+ # puts cidr4.size() --> 256
1306
+ #
1307
+ def size()
1308
+ return(@hostmask + 1)
1309
+ end
1310
+
1311
+ #======================================#
1312
+ #
1313
+ #======================================#
1314
+
1315
+
1316
+ #==============================================================================#
1317
+ # subnet()
1318
+ #==============================================================================#
1319
+
1320
+ # Subnet this object. Object will be fully subnetted into X number of subnets
1321
+ # of specified size. If :MinCount is provided, then method will return at least
1322
+ # that number of subnets (of size X) and the remainder of the new subnets
1323
+ # will be merged together as tightly as possible. If a size is not provided,
1324
+ # then the current object will be split in half.
1325
+ #
1326
+ # - Arguments:
1327
+ # * Optional hash with the following fields:
1328
+ # - :IPCount -- Minimum number of IP's that new subnets should contain - Integer (optional)
1329
+ # - :MinCount -- Minimum number of X sized subnets to return - Integer (optional)
1330
+ # - :Objectify -- if true, return IPAdmin::CIDR objects (optional)
1331
+ # - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
1332
+ # - :Subnet -- Netmask (in bits) of new subnets - Integer (optional)
1333
+ #
1334
+ # - Returns:
1335
+ # * Array of CIDR addresses or IPAdmin::CIDR objects
1336
+ #
1337
+ # - Notes:
1338
+ # * :Subnet always takes precedence over :IPCount.
1339
+ #
1340
+ # Example:
1341
+ # cidr_list = cidr4.subnet(:Subnet => 28, :MinCount => 3)
1342
+ # cidr_list = cidr4.subnet(:IPCount => 19)
1343
+ # puts cidr_list[0] --> 192.168.1.0/27
1344
+ #
1345
+ def subnet(options=nil)
1346
+ my_network = self.packed_network
1347
+ my_mask = self.bits
1348
+ subnet_bits = my_mask + 1
1349
+ min_count = nil
1350
+ objectify = false
1351
+ short = false
1352
+
1353
+ if (options)
1354
+ if (!options.kind_of? Hash)
1355
+ raise ArgumentError, "Expected Hash, but #{options.class} provided."
1356
+ end
1357
+
1358
+ if ( options.has_key?(:IPCount) )
1359
+ subnet_bits = IPAdmin.minimum_size(:IPCount => options[:IPCount],
1360
+ :Version => @version)
1361
+ end
1362
+
1363
+ if ( options.has_key?(:Subnet) )
1364
+ subnet_bits = options[:Subnet]
1365
+ end
1366
+
1367
+ if ( options.has_key?(:MinCount) )
1368
+ min_count = options[:MinCount]
1369
+ end
1370
+
1371
+ if( options.has_key?(:Short) )
1372
+ short = true
1373
+ end
1374
+
1375
+ if( options.has_key?(:Objectify) )
1376
+ objectify = true
1377
+ end
1378
+
1379
+ end
1380
+
1381
+ # get number of subnets possible with the requested subnet_bits
1382
+ num_avail = 2**(subnet_bits - my_mask)
1383
+
1384
+ # get the number of bits in the next supernet and
1385
+ # make sure min_count is a power of 2
1386
+ bits_needed = 1
1387
+ min_count = num_avail if (!min_count)
1388
+ until (2**bits_needed >= min_count)
1389
+ bits_needed += 1
1390
+ end
1391
+ min_count = 2**bits_needed
1392
+ next_supernet_bits = subnet_bits - bits_needed
1393
+
1394
+
1395
+ # make sure subnet isnt bigger than available bits
1396
+ if (subnet_bits > @max_bits)
1397
+ raise "Requested subnet (#{subnet_bits}) does not fit " +
1398
+ "within the bounds of IPv#{@version}."
1399
+ end
1400
+
1401
+ # make sure subnet is larger than mymask
1402
+ if (subnet_bits < my_mask)
1403
+ raise "Requested subnet (#{subnet_bits}) is too large for " +
1404
+ "current CIDR space."
1405
+ end
1406
+
1407
+ # make sure MinCount is smaller than available subnets
1408
+ if (min_count > num_avail)
1409
+ raise "Requested subnet count (#{min_count}) exceeds subnets " +
1410
+ "available for allocation (#{num_avail})."
1411
+ end
1412
+
1413
+ # list all 'subnet_bits' sized subnets of this cidr block
1414
+ # with a limit of min_count
1415
+ bitstep = 2**(@max_bits - subnet_bits)
1416
+ subnets = self.enumerate(:Bitstep => bitstep, :Limit => min_count)
1417
+
1418
+ # save our subnets
1419
+ new_subnets = []
1420
+ subnets.each do |subnet|
1421
+ if (!objectify)
1422
+ subnet = IPAdmin.shorten(subnet) if (short && @version == 6)
1423
+ new_subnets.push("#{subnet}/#{subnet_bits}")
1424
+ else
1425
+ new_subnets.push(IPAdmin::CIDR.new(:CIDR => "#{subnet}/#{subnet_bits}", :Version => @version))
1426
+ end
1427
+ end
1428
+
1429
+ # now go through the rest of the cidr space and make the rest
1430
+ # of the subnets. we want these to be as tightly merged as possible
1431
+ next_supernet_bitstep = (bitstep * min_count)
1432
+ next_supernet_ip = my_network + next_supernet_bitstep
1433
+ until (next_supernet_bits == my_mask)
1434
+ if (!objectify)
1435
+ next_network = IPAdmin.unpack_ip_addr(:Integer => next_supernet_ip, :Version => @version)
1436
+ next_network = IPAdmin.shorten(next_network) if (short && @version == 6)
1437
+ new_subnets.push("#{next_network}/#{next_supernet_bits}")
1438
+ else
1439
+ new_subnets.push(IPAdmin::CIDR.new(:PackedIP => next_supernet_ip,
1440
+ :Netmask =>next_supernet_bits,
1441
+ :Version => @version))
1442
+ end
1443
+
1444
+ next_supernet_bits -= 1
1445
+ next_supernet_ip = next_supernet_ip + next_supernet_bitstep
1446
+ next_supernet_bitstep = next_supernet_bitstep << 1
1447
+ end
1448
+
1449
+ return(new_subnets)
1450
+ end
1451
+
1452
+ #======================================#
1453
+ #
1454
+ #======================================#
1455
+
1456
+ end
1457
+
1458
+ end # module IPAdmin
1459
+ __END__