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