ipadmin 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/cidr.rb CHANGED
@@ -1,9 +1,3 @@
1
- =begin rdoc
2
- Copyright (c) 2006 Dustin Spinhirne -
3
- Licensed under the same terms as Ruby, No Warranty is provided.
4
- =end
5
-
6
-
7
1
  module IPAdmin
8
2
  class CIDR
9
3
 
@@ -26,6 +20,9 @@ class CIDR
26
20
 
27
21
  # Hash of custom tags. Should be in the format tag => value.
28
22
  attr_reader :tag
23
+
24
+ # Integer of either 32 or 128 bits in length, with all bits set to 1
25
+ attr_reader :all_f
29
26
 
30
27
  # Hash of custom tags. Should be in the format tag => value.
31
28
  #
@@ -50,7 +47,7 @@ class CIDR
50
47
  #==============================================================================#
51
48
 
52
49
  # - Arguments:
53
- # * Hash with the following fields:
50
+ # * CIDR address as a string, or a Hash with the following fields:
54
51
  # - :CIDR -- IP address in CIDR notation - String (optional)
55
52
  # - :Netmask -- IP Netmask - String or Integer (optional)
56
53
  # - :PackedIP -- Integer representation of an IP address (optional)
@@ -66,7 +63,8 @@ class CIDR
66
63
  # * Netmask within :CIDR takes precedence over :Netmask.
67
64
  # * Version will be auto-detected if not specified
68
65
  #
69
- # Example:
66
+ # Examples:
67
+ # cidr4 = IPAdmin::CIDR.new('192.168.1.1/24')
70
68
  # cidr4 = IPAdmin::CIDR.new(:CIDR => '192.168.1.1/24')
71
69
  # cidr4_2 = IPAdmin::CIDR.new(:PackedIP => 0x0a010001,
72
70
  # :PackedNetmask => 0xffffff00
@@ -76,47 +74,51 @@ class CIDR
76
74
  # cidr6_2 = IPAdmin::CIDR.new(:CIDR => '::ffff:192.168.1.1/96')
77
75
  #
78
76
  def initialize(options)
79
- if (!options.kind_of? Hash)
80
- raise ArgumentError, "Expected Hash, but #{options.class} provided."
77
+ @tag = {}
78
+ if (!options.kind_of?(Hash) && !options.kind_of?(String))
79
+ raise ArgumentError, "Expected Hash or String, but #{options.class} provided."
81
80
  end
82
81
 
83
-
84
- if (options.has_key?(:PackedIP))
85
- packed_ip = options[:PackedIP]
86
- raise ArgumentError, "Expected Integer, but #{packed_ip.class} " +
87
- "provided for option :PackedIP." if (!packed_ip.kind_of?(Integer))
88
- elsif (options.has_key?(:CIDR))
89
- cidr = options[:CIDR]
90
- raise ArgumentError, "Expected String, but #{cidr.class} " +
91
- "provided for option :CIDR." if (!cidr.kind_of?(String))
92
- else
93
- raise ArgumentError, "Missing argument: CIDR or PackedIP."
94
- end
95
-
82
+ if (options.kind_of? String)
83
+ cidr = options
84
+ end
96
85
 
97
- if (options.has_key?(:PackedNetmask))
98
- packed_netmask = options[:PackedNetmask]
99
- raise ArgumentError, "Expected Integer, but #{packed_netmask.class} " +
100
- "provided for option :PackedNetmask." if (!packed_netmask.kind_of?(Integer))
101
-
102
- elsif (options.has_key?(:Netmask))
103
- netmask = options[:Netmask]
104
- raise ArgumentError, "Expected String or Integer, but #{netmask.class} " +
105
- "provided for option :Netmask." if (!netmask.kind_of?(String) && !netmask.kind_of?(Integer))
106
- end
86
+ if (options.kind_of? Hash)
87
+ if (options.has_key?(:PackedIP))
88
+ packed_ip = options[:PackedIP]
89
+ raise ArgumentError, "Expected Integer, but #{packed_ip.class} " +
90
+ "provided for option :PackedIP." if (!packed_ip.kind_of?(Integer))
91
+ elsif (options.has_key?(:CIDR))
92
+ cidr = options[:CIDR]
93
+ raise ArgumentError, "Expected String, but #{cidr.class} " +
94
+ "provided for option :CIDR." if (!cidr.kind_of?(String))
95
+ else
96
+ raise ArgumentError, "Missing argument: CIDR or PackedIP."
97
+ end
107
98
 
99
+ if (options.has_key?(:PackedNetmask))
100
+ packed_netmask = options[:PackedNetmask]
101
+ raise ArgumentError, "Expected Integer, but #{packed_netmask.class} " +
102
+ "provided for option :PackedNetmask." if (!packed_netmask.kind_of?(Integer))
103
+
104
+ elsif (options.has_key?(:Netmask))
105
+ netmask = options[:Netmask]
106
+ raise ArgumentError, "Expected String or Integer, but #{netmask.class} " +
107
+ "provided for option :Netmask." if (!netmask.kind_of?(String) && !netmask.kind_of?(Integer))
108
+ end
108
109
 
109
- if (options.has_key?(:Version))
110
- @version = options[:Version]
111
- if (@version != 4 && @version != 6)
112
- raise ArgumentError, ":Version should be 4 or 6, but was '#{version}'."
110
+ if (options.has_key?(:Version))
111
+ @version = options[:Version]
112
+ if (@version != 4 && @version != 6)
113
+ raise ArgumentError, ":Version should be 4 or 6, but was '#{version}'."
114
+ end
113
115
  end
114
- end
115
116
 
116
- if (options.has_key?(:Tag))
117
- @tag = options[:Tag]
118
- if (!@tag.kind_of? Hash)
119
- raise ArgumentError, "Expected Hash, but #{@tag.class} provided for option :Tag."
117
+ if (options.has_key?(:Tag))
118
+ @tag = options[:Tag]
119
+ if (!@tag.kind_of? Hash)
120
+ raise ArgumentError, "Expected Hash, but #{@tag.class} provided for option :Tag."
121
+ end
120
122
  end
121
123
  end
122
124
 
@@ -156,8 +158,7 @@ class CIDR
156
158
  @netmask = 2**128-1 if (!netmask && !packed_netmask)
157
159
  end
158
160
 
159
- # validate & pack ip
160
- IPAdmin.validate_ip_addr(:IP => ip, :Version => @version)
161
+ # pack ip
161
162
  @ip = IPAdmin.pack_ip_addr(:IP => ip, :Version => @version)
162
163
 
163
164
  end
@@ -208,8 +209,8 @@ class CIDR
208
209
  # - Returns:
209
210
  # * String
210
211
  #
211
- # Example:
212
- # arpa = cidr4.arpa() --> '1.168.192.in-addr.arpa.'
212
+ # Examples:
213
+ # arpa = cidr4.arpa()
213
214
  #
214
215
  def arpa()
215
216
 
@@ -270,8 +271,8 @@ class CIDR
270
271
  # - Returns:
271
272
  # * Integer.
272
273
  #
273
- # Example:
274
- # puts cidr4.bits() --> 24
274
+ # Examples:
275
+ # puts cidr4.bits()
275
276
  #
276
277
  def bits()
277
278
  return(IPAdmin.unpack_ip_netmask(:Integer => @netmask))
@@ -287,33 +288,35 @@ class CIDR
287
288
  #==============================================================================#
288
289
 
289
290
  # Determines if this CIDR contains (is supernet of)
290
- # the provided CIDR object.
291
+ # the provided CIDR addresss or IPAdmin::CIDR object.
291
292
  #
292
293
  # - Arguments:
293
- # * CIDR object
294
+ # * CIDR addresss or IPAdmin::CIDR object
294
295
  #
295
296
  # - Returns:
296
297
  # * true or false
297
298
  #
298
- # Example:
299
- # cidr4_2 = IPAdmin::CIDR.new(:CIDR => '192.168.1.0/26')
300
- # contains? = cidr4.contains(cidr4_2) --> true
299
+ # Examples:
300
+ # contains? = cidr4.contains('192.168.1.32/27')
301
301
  #
302
- def contains?(object)
302
+ def contains?(cidr)
303
303
  is_contained = false
304
304
 
305
- if (object.kind_of?(IPAdmin::CIDR))
306
- network = object.packed_network
307
- netmask = object.packed_netmask
308
-
309
- else
310
- raise ArgumentError, "Expected IPAdmin::CIDR " +
311
- " object but #{object.class} provided."
305
+ if (!cidr.kind_of?(IPAdmin::CIDR))
306
+ begin
307
+ cidr = IPAdmin::CIDR.new(:CIDR => cidr)
308
+ rescue Exception => error
309
+ raise ArgumentError, "Provided argument raised the following " +
310
+ "errors: #{error}"
311
+ end
312
312
  end
313
+
314
+ network = cidr.packed_network
315
+ netmask = cidr.packed_netmask
313
316
 
314
317
 
315
- if (object.version != @version)
316
- raise ArgumentError, "Attempted to compare a version #{object.version} CIDR " +
318
+ if (cidr.version != @version)
319
+ raise ArgumentError, "Attempted to compare a version #{cidr.version} CIDR " +
317
320
  "with a version #{@version} CIDR."
318
321
  end
319
322
 
@@ -349,11 +352,11 @@ class CIDR
349
352
  # - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
350
353
  #
351
354
  # - Returns:
352
- # * String
355
+ # * CIDR address as a String
353
356
  #
354
- # Example:
355
- # puts cidr4.desc() --> 192.168.1.0/24
356
- # puts cidr4.desc(:IP => true) --> 192.168.1.1/24
357
+ # Examples:
358
+ # puts cidr4.desc()
359
+ # puts cidr4.desc(:IP => true)
357
360
  #
358
361
  def desc(options=nil)
359
362
  short = false
@@ -404,10 +407,10 @@ class CIDR
404
407
  # - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
405
408
  #
406
409
  # - Returns:
407
- # * Array of Strings or CIDR objects
410
+ # * Array of IP address Strings or IPAdmin::CIDR objects
408
411
  #
409
- # Example:
410
- # ip_list = cidr4.enumerate(:Bitstep => 2, :Limit => 2) --> ['192.168.1.0','192.168.1.1']
412
+ # Examples:
413
+ # ip_list = cidr4.enumerate(:Bitstep => 2, :Limit => 2)
411
414
  #
412
415
  def enumerate(options=nil)
413
416
  bitstep = 1
@@ -465,6 +468,127 @@ class CIDR
465
468
  #======================================#
466
469
 
467
470
 
471
+ #==============================================================================#
472
+ # fill_in()
473
+ #==============================================================================#
474
+
475
+ # Given a list of subnets of the current CIDR, return a new list with any
476
+ # holes (missing subnets) filled in.
477
+ #
478
+ # - Arguments:
479
+ # * Array of CIDR addresses or IPAdmin::CIDR objects,
480
+ # or a Hash with the following fields:
481
+ # - :List -- Array of CIDR addresses or IPAdmin::CIDR objects
482
+ # - :Objectify -- if true, return IPAdmin::CIDR objects (optional)
483
+ # - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
484
+ #
485
+ # - Returns:
486
+ # * Array of CIDR address Strings or IPAdmin::CIDR objects
487
+ #
488
+ # Examples:
489
+ # subnets = cidr4.fill_in(['192.168.1.0/27','192.168.1.64/26','192.168.1.128/25'])
490
+ # subnets = cidr4.fill_in(:List => ['192.168.1.0/27','192.168.1.64/26'], :Objectify => true)
491
+ #
492
+
493
+ def fill_in(options)
494
+ short = false
495
+ objectify = false
496
+
497
+ # validate options
498
+ if ( options.kind_of?(Hash) )
499
+ if (!options.has_key?(:List))
500
+ raise ArgumentError, "Missing argument: List."
501
+ end
502
+
503
+ if (options.has_key?(:Short) && options[:Short] == true)
504
+ short = true
505
+ end
506
+
507
+ if (options.has_key?(:Objectify) && options[:Objectify] == true)
508
+ objectify = true
509
+ end
510
+
511
+ # make sure :List is an array
512
+ if ( !options[:List].kind_of?(Array) )
513
+ raise ArgumentError, "Expected Array for option :List, " +
514
+ "but #{list.class} provided."
515
+ end
516
+ list = options[:List]
517
+
518
+ elsif ( options.kind_of?(Array) )
519
+ list = options
520
+ else
521
+ raise ArgumentError, "Array or Hash expected but #{options.class} provided."
522
+ end
523
+
524
+ # validate each cidr and store in cidr_list
525
+ cidr_list = []
526
+ list.each do |obj|
527
+ if (!obj.kind_of?(IPAdmin::CIDR))
528
+ begin
529
+ obj = IPAdmin::CIDR.new(:CIDR => obj)
530
+ rescue Exception => error
531
+ aise ArgumentError, "A provided CIDR raised the following " +
532
+ "errors: #{error}"
533
+ end
534
+ end
535
+
536
+ if (!obj.version == self.version)
537
+ raise "#{obj.desc(:Short => true)} is not a version #{self.version} address."
538
+ end
539
+
540
+ # make sure we contain the cidr
541
+ if ( self.contains?(obj) == false )
542
+ raise "#{obj.desc(:Short => true)} does not fit " +
543
+ "within the bounds of #{self.desc(:Short => true)}."
544
+ end
545
+ cidr_list.push(obj)
546
+ end
547
+
548
+ # sort our cidr's and see what is missing
549
+ complete_list = []
550
+ expected = self.packed_network
551
+ IPAdmin.sort(cidr_list).each do |cidr|
552
+ network = cidr.packed_network
553
+ bitstep = (@all_f + 1) - cidr.packed_netmask
554
+
555
+ if (network > expected)
556
+ num_ips_missing = network - expected
557
+ sub_list = make_subnets_from_base_and_ip_count(expected,num_ips_missing)
558
+ complete_list.concat(sub_list)
559
+ elsif (network < expected)
560
+ next
561
+ end
562
+ complete_list.push(IPAdmin::CIDR.new(:PackedIP => network,
563
+ :PackedNetmask => cidr.packed_netmask,
564
+ :Version => self.version))
565
+ expected = network + bitstep
566
+ end
567
+
568
+ # if expected is not the next subnet, then we're missing subnets
569
+ # at the end of the cidr
570
+ next_sub = self.next_subnet(:Objectify => true).packed_network
571
+ if (expected != next_sub)
572
+ num_ips_missing = next_sub - expected
573
+ sub_list = make_subnets_from_base_and_ip_count(expected,num_ips_missing)
574
+ complete_list.concat(sub_list)
575
+ end
576
+
577
+ # decide what to return
578
+ if (!objectify)
579
+ subnets = []
580
+ complete_list.each {|entry| subnets.push(entry.desc(:Short => short))}
581
+ return(subnets)
582
+ else
583
+ return(complete_list)
584
+ end
585
+ end
586
+
587
+ #======================================#
588
+ #
589
+ #======================================#
590
+
591
+
468
592
  #==============================================================================#
469
593
  # hostmask_ext()
470
594
  #==============================================================================#
@@ -477,8 +601,8 @@ class CIDR
477
601
  # - Returns:
478
602
  # * String
479
603
  #
480
- # Example:
481
- # puts cidr4.hostmask_ext() --> 0.0.0.255
604
+ # Examples:
605
+ # puts cidr4.hostmask_ext()
482
606
  #
483
607
  def hostmask_ext()
484
608
  if (@version == 4)
@@ -507,10 +631,10 @@ class CIDR
507
631
  # - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
508
632
  #
509
633
  # - Returns:
510
- # * String or CIDR object.
634
+ # * IP address String or IPAdmin::CIDR object.
511
635
  #
512
- # Example:
513
- # puts cidr4.ip() --> 192.168.1.1
636
+ # Examples:
637
+ # puts cidr4.ip()
514
638
  #
515
639
  def ip(options=nil)
516
640
  objectify = false
@@ -560,15 +684,15 @@ class CIDR
560
684
  # - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
561
685
  #
562
686
  # - Returns:
563
- # * String or CIDR object.
687
+ # * IP address String or IPAdmin::CIDR object.
564
688
  #
565
689
  # - Notes:
566
690
  # * The broadcast() method is aliased to this method, and thus works for
567
691
  # IPv6 despite the fact that the IPv6 protocol does not support IP
568
692
  # broadcasting.
569
693
  #
570
- # Example:
571
- # puts cidr4.last() --> 192.168.1.255
694
+ # Examples:
695
+ # puts cidr4.last()
572
696
  #
573
697
  def last(options=nil)
574
698
  objectify = false
@@ -620,14 +744,14 @@ class CIDR
620
744
  # - :Objectify -- if true, return EUI objects (optional)
621
745
  #
622
746
  # - Returns:
623
- # * EUI object
747
+ # * IPAdmin::EUI48 object
624
748
  #
625
749
  # - Note:
626
750
  # * MAC address is based on original IP address passed during initialization.
627
751
  #
628
- # Example:
752
+ # Examples:
629
753
  # mcast = IPAdmin::CIDR.new(:CIDR => '224.0.0.6')
630
- # puts mcast.multicast_mac.address --> 01-00-5e-00-00-06
754
+ # puts mcast.multicast_mac.address
631
755
  #
632
756
  def multicast_mac(options=nil)
633
757
  objectify = false
@@ -660,7 +784,7 @@ class CIDR
660
784
  end
661
785
  end
662
786
 
663
- eui = IPAdmin::EUI.new(:PackedEUI => mac)
787
+ eui = IPAdmin::EUI48.new(:PackedEUI => mac)
664
788
  eui = eui.address if (!objectify)
665
789
 
666
790
  return(eui)
@@ -683,8 +807,8 @@ class CIDR
683
807
  # - Returns:
684
808
  # * String
685
809
  #
686
- # Example:
687
- # puts cidr4.netmask() --> /24
810
+ # Examples:
811
+ # puts cidr4.netmask()
688
812
  #
689
813
  def netmask()
690
814
  bits = IPAdmin.unpack_ip_netmask(:Integer => @netmask)
@@ -708,8 +832,8 @@ class CIDR
708
832
  # - Returns:
709
833
  # * String
710
834
  #
711
- # Example:
712
- # puts cidr4.netmask_ext() --> 255.255.255.0
835
+ # Examples:
836
+ # puts cidr4.netmask_ext()
713
837
  #
714
838
  def netmask_ext()
715
839
  if (@version == 4)
@@ -739,10 +863,10 @@ class CIDR
739
863
  # - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
740
864
  #
741
865
  # - Returns:
742
- # * String or CIDR object.
866
+ # * CIDR address String or IPAdmin::CIDR object.
743
867
  #
744
- # Example:
745
- # puts cidr4.network() --> 192.168.1.0
868
+ # Examples:
869
+ # puts cidr4.network()
746
870
  #
747
871
  def network(options=nil)
748
872
  objectify = false
@@ -796,10 +920,10 @@ class CIDR
796
920
  # - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
797
921
  #
798
922
  # - Returns:
799
- # * String or CIDR object.
923
+ # * IP address String or IPAdmin::CIDR object.
800
924
  #
801
- # Example:
802
- # puts cidr4.next_ip() --> 192.168.2.0
925
+ # Examples:
926
+ # puts cidr4.next_ip()
803
927
  #
804
928
  def next_ip(options=nil)
805
929
  bitstep = 1
@@ -861,10 +985,10 @@ class CIDR
861
985
  # - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
862
986
  #
863
987
  # - Returns:
864
- # * String or CIDR object.
988
+ # * CIDR address String or IPAdmin::CIDR object.
865
989
  #
866
- # Example:
867
- # puts cidr4.next_subnet() --> 192.168.2.0/24
990
+ # Examples:
991
+ # puts cidr4.next_subnet()
868
992
  #
869
993
  def next_subnet(options=nil)
870
994
  bitstep = 1
@@ -921,39 +1045,42 @@ class CIDR
921
1045
  # Provide the nth IP within this object.
922
1046
  #
923
1047
  # - Arguments:
924
- # * Hash with the following fields:
1048
+ # * Integer or a Hash with the following fields:
925
1049
  # - :Index -- index number of the IP address to return - Integer
926
1050
  # - :Objectify -- if true, return IPAdmin::CIDR objects (optional)
927
1051
  # - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
928
1052
  #
929
1053
  # - Returns:
930
- # * String or CIDR object.
1054
+ # * IP address String or IPAdmin::CIDR object.
931
1055
  #
932
- # Example:
933
- # puts cidr4.nth(:Index => 1) --> 192.168.1.1
1056
+ # Examples:
1057
+ # puts cidr4.nth(1)
1058
+ # puts cidr4.nth(:Index => 1, :Objectify => true)
934
1059
  #
935
1060
  def nth(options)
936
1061
  objectify = false
937
1062
  short = false
938
1063
 
939
- if (!options.kind_of?(Hash))
940
- raise ArgumentError, "Expected Hash, but " +
941
- "#{options.class} provided."
942
- end
943
-
944
- if ( !options.has_key?(:Index) )
945
- raise ArgumentError, "Missing argument: Index."
946
- end
947
- index = options[:Index]
1064
+ if (options.kind_of?(Hash))
1065
+ if ( !options.has_key?(:Index) )
1066
+ raise ArgumentError, "Missing argument: Index."
1067
+ end
1068
+ index = options[:Index]
948
1069
 
949
- if( options.has_key?(:Short) && options[:Short] == true )
950
- short = true
951
- end
1070
+ if( options.has_key?(:Short) && options[:Short] == true )
1071
+ short = true
1072
+ end
952
1073
 
953
- if( options.has_key?(:Objectify) && options[:Objectify] == true )
954
- objectify = true
1074
+ if( options.has_key?(:Objectify) && options[:Objectify] == true )
1075
+ objectify = true
1076
+ end
1077
+ elsif (options.kind_of?(Integer))
1078
+ index = options
1079
+ else
1080
+ raise ArgumentError, "Integer or Hash expected, but " +
1081
+ "#{options.class} provided."
955
1082
  end
956
-
1083
+
957
1084
  my_ip = @network + index
958
1085
  if ( (@hostmask | my_ip) == (@hostmask | @network) )
959
1086
 
@@ -989,8 +1116,8 @@ class CIDR
989
1116
  # - Returns:
990
1117
  # * Integer
991
1118
  #
992
- # Example:
993
- # puts cidr4.packed_hostmask().to_s(16) --> 0xff
1119
+ # Examples:
1120
+ # puts cidr4.packed_hostmask().to_s(16)
994
1121
  #
995
1122
  def packed_hostmask()
996
1123
  return(@hostmask)
@@ -1013,8 +1140,8 @@ class CIDR
1013
1140
  # - Returns:
1014
1141
  # * Integer
1015
1142
  #
1016
- # Example:
1017
- # puts cidr4.packed_ip().to_s(16) --> 0xc0c80101
1143
+ # Examples:
1144
+ # puts cidr4.packed_ip().to_s(16)
1018
1145
  #
1019
1146
  def packed_ip()
1020
1147
  return(@ip)
@@ -1037,8 +1164,8 @@ class CIDR
1037
1164
  # - Returns:
1038
1165
  # * Integer
1039
1166
  #
1040
- # Example:
1041
- # puts cidr4.packed_netmask().to_s(16) --> 0xffffff00
1167
+ # Examples:
1168
+ # puts cidr4.packed_netmask().to_s(16)
1042
1169
  #
1043
1170
  def packed_netmask()
1044
1171
  return(@netmask)
@@ -1061,8 +1188,8 @@ class CIDR
1061
1188
  # - Returns:
1062
1189
  # * Integer
1063
1190
  #
1064
- # Example:
1065
- # packed = cidr4.packed_network().to_s(16) --> 0xc0c80100
1191
+ # Examples:
1192
+ # packed = cidr4.packed_network().to_s(16)
1066
1193
  #
1067
1194
  def packed_network()
1068
1195
  return(@network)
@@ -1081,37 +1208,53 @@ class CIDR
1081
1208
  # between them (inclusive).
1082
1209
  #
1083
1210
  # - Arguments:
1084
- # * Hash with the following fields:
1211
+ # * Array of (2) Integers, or a Hash with the following fields:
1085
1212
  # - :Bitstep -- enumerate in X sized steps - Integer (optional)
1086
1213
  # - :Indexes -- index numbers of the addresses to use as boundaries - Array of (2) Integers
1087
1214
  # - :Objectify -- if true, return IPAdmin::CIDR objects (optional)
1088
1215
  # - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
1089
1216
  #
1090
1217
  # - Returns:
1091
- # * Array Strings or CIDR objects
1218
+ # * Array IP address Strings or IPAdmin::CIDR objects
1092
1219
  #
1093
- # Example:
1094
- # list = cidr4.range(:Indexes => [0,1]) --> ['192.168.1.0','192.168.1.1']
1220
+ # Examples:
1221
+ # list = cidr4.range([0,1])
1222
+ # list = cidr4.range(:Indexes => [0,1], :Objectify => true)
1095
1223
  #
1096
1224
  def range(options)
1097
1225
  objectify = false
1098
1226
  short = false
1099
1227
  bitstep = 1
1100
1228
 
1101
- if (!options.kind_of?(Hash))
1102
- raise Argumenterror, "Expected Hash, but #{options.class} provided."
1103
- end
1229
+ if (options.kind_of?(Hash))
1230
+ if ( !options.has_key?(:Indexes) )
1231
+ raise ArgumentError, "Missing argument: Indexes."
1232
+ end
1233
+ indexes = options[:Indexes]
1234
+ raise "Array expected but #{indexes.class} provided for argument: Indexes" if (!indexes.kind_of?(Array))
1235
+
1236
+ if( options.has_key?(:Short) && options[:Short] == true )
1237
+ short = true
1238
+ end
1239
+
1240
+ if( options.has_key?(:Objectify) && options[:Objectify] == true )
1241
+ objectify = true
1242
+ end
1104
1243
 
1105
- if ( !options.has_key?(:Indexes) )
1106
- raise ArgumentError, "Missing argument: Indexes."
1244
+ if( options.has_key?(:Bitstep) )
1245
+ bitstep = options[:Bitstep]
1246
+ end
1247
+ elsif (options.kind_of?(Array))
1248
+ indexes = options
1249
+ else
1250
+ raise Argumenterror, "Array or Hash expected, but #{options.class} provided."
1107
1251
  end
1108
- indexes = options[:Indexes]
1109
- indexes.sort!
1110
1252
 
1111
- if( (!indexes.kind_of?(Array)) || (indexes.length != 2))
1112
- raise ArgumentError, "Argument :Index should be an array of (2) index numbers."
1253
+ # validate & sort indexes
1254
+ indexes.sort!
1255
+ if (indexes.length != 2)
1256
+ raise "(2) index numbers are required."
1113
1257
  end
1114
-
1115
1258
  if ( (indexes[0] < 0) || (indexes[0] > self.size) )
1116
1259
  raise ArgumentError, "Index #{indexes[0]} is out of bounds for this CIDR."
1117
1260
  end
@@ -1120,18 +1263,7 @@ class CIDR
1120
1263
  raise ArgumentError, "Index #{indexes[1]} is out of bounds for this CIDR."
1121
1264
  end
1122
1265
 
1123
- if( options.has_key?(:Short) && options[:Short] == true )
1124
- short = true
1125
- end
1126
-
1127
- if( options.has_key?(:Objectify) && options[:Objectify] == true )
1128
- objectify = true
1129
- end
1130
-
1131
- if( options.has_key?(:Bitstep) )
1132
- bitstep = options[:Bitstep]
1133
- end
1134
-
1266
+ # make range
1135
1267
  start_ip = @network + indexes[0]
1136
1268
  end_ip = @network + indexes[1]
1137
1269
  my_ip = start_ip
@@ -1160,51 +1292,59 @@ class CIDR
1160
1292
  # remainder()
1161
1293
  #==============================================================================#
1162
1294
 
1163
- # Given a portion of the current CIDR, provide the remainder of
1164
- # the CIDR. For example if the original CIDR is 192.168.0.0/24 and you
1295
+ # Given a single subnet of the current CIDR, provide the remainder of
1296
+ # the subnets. For example if the original CIDR is 192.168.0.0/24 and you
1165
1297
  # provide 192.168.0.64/26 as the portion to exclude, then 192.168.0.0/26,
1166
- # and 192.168.0.128/25 will be returned as the remainder.
1298
+ # and 192.168.0.128/25 will be returned as the remainders.
1167
1299
  #
1168
1300
  # - Arguments:
1169
- # * Optional hash with the following fields:
1170
- # - :Exclude -- CIDR object to use in calculating the remainder.
1301
+ # * CIDR address or IPAdmin::CIDR object, or a Hash with the following fields:
1302
+ # - :Exclude -- CIDR address or IPAdmin::CIDR object.
1171
1303
  # - :Objectify -- if true, return IPAdmin::CIDR objects (optional)
1172
1304
  # - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
1173
1305
  #
1174
1306
  # - Returns:
1175
- # * Array of Strings or CIDR objects
1307
+ # * Array of CIDR address Strings or IPAdmin::CIDR objects
1176
1308
  #
1177
1309
  #
1178
- # Example:
1179
- # cidr4_2 = IPAdmin::CIDR.new(:CIDR => '192.168.1.64/26')
1180
- # cidr4.remainder(:Exclude => cidr4_2).each {|x| puts}
1310
+ # Examples:
1311
+ # cidr4.remainder('192.168.1.32/27').each {|x| puts x}
1312
+ # cidr4.remainder(:Exclude => '192.168.1.32/27', :Objectify => true).each {|x| puts x.desc}
1181
1313
  #
1182
1314
  def remainder(options)
1183
1315
  short = nil
1184
1316
  objectify = nil
1185
1317
 
1186
- if (!options.kind_of? Hash)
1187
- raise ArgumentError, "Expected Hash, but #{options.class} provided."
1188
- end
1189
-
1190
- if ( !options.has_key?(:Exclude) )
1191
- raise ArgumentError, "Missing argument: Exclude."
1192
- end
1193
- to_exclude = options[:Exclude]
1318
+ if (options.kind_of? Hash)
1319
+ if ( !options.has_key?(:Exclude) )
1320
+ raise ArgumentError, "Missing argument: Exclude."
1321
+ end
1322
+ to_exclude = options[:Exclude]
1194
1323
 
1195
- if ( !to_exclude.kind_of?(IPAdmin::CIDR) )
1196
- raise ArgumentError, "Expeced IPAdmin::CIDR, but #{exclude.class} " +
1197
- "for option: Exclude."
1198
- end
1324
+ if( options.has_key?(:Short) && options[:Short] == true )
1325
+ short = true
1326
+ end
1199
1327
 
1200
- if( options.has_key?(:Short) && options[:Short] == true )
1201
- short = true
1328
+ if( options.has_key?(:Objectify) && options[:Objectify] == true )
1329
+ objectify = true
1330
+ end
1331
+
1332
+ elsif
1333
+ to_exclude = options
1334
+ else
1335
+ raise ArgumentError, "CIDR address or Hash expected, but #{options.class} provided."
1202
1336
  end
1203
1337
 
1204
- if( options.has_key?(:Objectify) && options[:Objectify] == true )
1205
- objectify = true
1338
+ if ( !to_exclude.kind_of?(IPAdmin::CIDR) )
1339
+ begin
1340
+ to_exclude = IPAdmin::CIDR.new(:CIDR => to_exclude)
1341
+ rescue Exception => error
1342
+ raise ArgumentError, "Argument :Exclude raised the following " +
1343
+ "errors: #{error}"
1344
+ end
1206
1345
  end
1207
1346
 
1347
+
1208
1348
  # make sure 'to_exclude' is the same ip version
1209
1349
  if ( to_exclude.version != @version )
1210
1350
  raise "#{to_exclude.desc(:Short => true)} is of a different " +
@@ -1266,26 +1406,29 @@ class CIDR
1266
1406
  # Return the resulting CIDR as a new object.
1267
1407
  #
1268
1408
  # - Arguments:
1269
- # * Hash with the following fields:
1270
- # - :Subnet -- Number of bits of new Netmask - Integer
1409
+ # * Integer, or a Hash with the following fields:
1410
+ # - :Netmask -- Number of bits of new Netmask - Integer
1271
1411
  #
1272
1412
  # - Returns:
1273
- # * CIDR object
1413
+ # * IPAdmin::CIDR object
1274
1414
  #
1275
- # Example:
1276
- # new_cidr = cidr4.resize(:Subnet => 23)
1277
- # puts new_cidr.desc --> 192.168.1.0/23
1415
+ # Examples:
1416
+ # new_cidr = cidr4.resize(23)
1417
+ # new_cidr = cidr4.resize(:Netmask => 23)
1418
+ # puts new_cidr.desc
1278
1419
  #
1279
1420
  def resize(options)
1280
- if (!options.kind_of?(Hash))
1281
- raise Argumenterror, "Expected Hash, but " +
1421
+ if (options.kind_of?(Hash))
1422
+ if ( !options.has_key?(:Netmask) )
1423
+ raise Argumenterror, "Missing argument: Netmask."
1424
+ end
1425
+ bits = options[:Netmask]
1426
+ elsif (options.kind_of?(Integer))
1427
+ bits = options
1428
+ else
1429
+ raise Argumenterror, "Integer or Hash expected, but " +
1282
1430
  "#{options.class} provided."
1283
1431
  end
1284
-
1285
- if ( !options.has_key?(:Subnet) )
1286
- raise Argumenterror, "Missing argument: Subnet."
1287
- end
1288
- bits = options[:Subnet]
1289
1432
 
1290
1433
  IPAdmin.validate_ip_netmask(:Netmask => bits, :Version => @version)
1291
1434
  netmask = IPAdmin.pack_ip_netmask(:Netmask => bits, :Version => @version)
@@ -1307,30 +1450,32 @@ class CIDR
1307
1450
  # Resize this object by changing the size of the Netmask.
1308
1451
  #
1309
1452
  # - Arguments:
1310
- # * Hash with the following fields:
1311
- # - :Subnet -- Number of bits of new Netmask - Integer
1453
+ # * Integer, or a Hash with the following fields:
1454
+ # - :Netmask -- Number of bits of new Netmask - Integer
1312
1455
  #
1313
1456
  # - Returns:
1314
- # * nil
1457
+ # * True
1315
1458
  #
1316
1459
  # - Notes:
1317
1460
  # * If CIDR is resized such that the original IP is no longer contained within,
1318
1461
  # then that IP will be reset to the base network address.
1319
1462
  #
1320
- # Example:
1321
- # cidr4.resize!(:Subnet => 23)
1322
- # puts cidr4.desc --> 192.168.1.0/23
1463
+ # Examples:
1464
+ # cidr4.resize!(23)
1465
+ # puts cidr4.desc
1323
1466
  #
1324
1467
  def resize!(options)
1325
- if (!options.kind_of?(Hash))
1326
- raise Argumenterror, "Expected Hash, but " +
1468
+ if (options.kind_of?(Hash))
1469
+ if ( !options.has_key?(:Netmask) )
1470
+ raise Argumenterror, "Missing argument: Netmask."
1471
+ end
1472
+ bits = options[:Netmask]
1473
+ elsif (options.kind_of?(Integer))
1474
+ bits = options
1475
+ else
1476
+ raise Argumenterror, "Integer or Hash expected, but " +
1327
1477
  "#{options.class} provided."
1328
1478
  end
1329
-
1330
- if ( !options.has_key?(:Subnet) )
1331
- raise Argumenterror, "Missing argument: Subnet."
1332
- end
1333
- bits = options[:Subnet]
1334
1479
 
1335
1480
  IPAdmin.validate_ip_netmask(:Netmask => bits, :Version => @version)
1336
1481
  netmask = IPAdmin.pack_ip_netmask(:Netmask => bits, :Version => @version)
@@ -1344,7 +1489,7 @@ class CIDR
1344
1489
  @ip = @network
1345
1490
  end
1346
1491
 
1347
- return(nil)
1492
+ return(true)
1348
1493
  end
1349
1494
 
1350
1495
  #======================================#
@@ -1364,8 +1509,8 @@ class CIDR
1364
1509
  # - Returns:
1365
1510
  # * Integer
1366
1511
  #
1367
- # Example:
1368
- # puts cidr4.size() --> 256
1512
+ # Examples:
1513
+ # puts cidr4.size()
1369
1514
  #
1370
1515
  def size()
1371
1516
  return(@hostmask + 1)
@@ -1380,11 +1525,14 @@ class CIDR
1380
1525
  # subnet()
1381
1526
  #==============================================================================#
1382
1527
 
1383
- # Subnet this object. Object will be fully subnetted into X number of subnets
1384
- # of specified size. If :MinCount is provided, then method will return at least
1385
- # that number of subnets (of size X) and the remainder of the new subnets
1386
- # will be merged together as tightly as possible. If a size is not provided,
1387
- # then the current CIDR will be split in half.
1528
+ # Subnet this object. There are 2 ways to subnet:
1529
+ # * By providing the netmask of the new subnets in :Subnet.
1530
+ # * By providing the number of IP addresses needed in the new subnets in :IPCount
1531
+ #
1532
+ # If :Mincount is not provided, then the CIDR will be fully subnetted. Otherwise,
1533
+ # if provided then :Mincount number of subnets of requested size will be returned and
1534
+ # the remainder of the subnets will be summarized as much as possible. If neither :Subnet
1535
+ # or :IPCount is provided, then the current CIDR will be split in half.
1388
1536
  #
1389
1537
  # - Arguments:
1390
1538
  # * Optional hash with the following fields:
@@ -1395,15 +1543,14 @@ class CIDR
1395
1543
  # - :Subnet -- Netmask (in bits) of new subnets - Integer (optional)
1396
1544
  #
1397
1545
  # - Returns:
1398
- # * Array of CIDR addresses or IPAdmin::CIDR objects
1546
+ # * Array of CIDR address Strings or IPAdmin::CIDR objects
1399
1547
  #
1400
1548
  # - Notes:
1401
1549
  # * :Subnet always takes precedence over :IPCount.
1402
1550
  #
1403
- # Example:
1551
+ # Examples:
1404
1552
  # cidr_list = cidr4.subnet(:Subnet => 28, :MinCount => 3)
1405
1553
  # cidr_list = cidr4.subnet(:IPCount => 19)
1406
- # puts cidr_list[0] --> 192.168.1.0/27
1407
1554
  #
1408
1555
  def subnet(options=nil)
1409
1556
  my_network = self.packed_network
@@ -1516,6 +1663,61 @@ class CIDR
1516
1663
  #
1517
1664
  #======================================#
1518
1665
 
1666
+
1667
+ # PRIVATE INSTANCE METHODS
1668
+ private
1669
+
1670
+
1671
+ #==============================================================================#
1672
+ # make_subnets_from_base_and_ip_count()
1673
+ #==============================================================================#
1674
+
1675
+ # Make CIDR addresses from a base addr and an number of ip's to encapsulate.
1676
+ #
1677
+ # - Arguments:
1678
+ # * base ip as packed integer
1679
+ # * number of ip's required
1680
+ #
1681
+ # - Returns:
1682
+ # * array of IPAdmin::CIDR objects
1683
+ #
1684
+ def make_subnets_from_base_and_ip_count(base_addr,ip_count)
1685
+ list = []
1686
+ until (ip_count == 0)
1687
+ mask = @all_f
1688
+ multiplier = 0
1689
+ bitstep = 0
1690
+ last_addr = base_addr
1691
+ done = false
1692
+ until (done == true)
1693
+ if (bitstep < ip_count && (base_addr & mask == last_addr & mask) )
1694
+ multiplier += 1
1695
+ elsif (bitstep > ip_count || (base_addr & mask != last_addr & mask) )
1696
+ multiplier -= 1
1697
+ done = true
1698
+ else
1699
+ done = true
1700
+ end
1701
+ bitstep = 2**multiplier
1702
+ mask = @all_f << multiplier & @all_f
1703
+ last_addr = base_addr + bitstep - 1
1704
+ end
1705
+
1706
+ list.push(IPAdmin::CIDR.new(:PackedIP => base_addr,
1707
+ :PackedNetmask => mask,
1708
+ :Version => self.version))
1709
+ ip_count -= bitstep
1710
+ base_addr += bitstep
1711
+ end
1712
+
1713
+ return(list)
1714
+ end
1715
+
1716
+ #======================================#
1717
+ #
1718
+ #======================================#
1719
+
1720
+
1519
1721
  end
1520
1722
 
1521
1723
  end # module IPAdmin