ipadmin 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README 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__