ipadmin 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2168 @@
1
+ =begin rdoc
2
+ Copyright (c) 2006 Dustin Spinhirne -
3
+ Licensed under the same terms as Ruby, No Warranty is provided.
4
+ =end
5
+
6
+
7
+
8
+ module IPAdmin
9
+
10
+ #==============================================================================#
11
+ # compare()
12
+ #==============================================================================#
13
+
14
+ # Compare CIDR or NetStruct objects, and determine if one
15
+ # is the supernet of the other.
16
+ #
17
+ # - Arguments:
18
+ # * Two CIDR or NetStruct objects
19
+ #
20
+ # - Returns:
21
+ # * if one object is a subnet of another, then return an array in order of
22
+ # [supernet,subnet]
23
+ # * if both are equal, return 1
24
+ # * if neither is a supernet of the other, return nil
25
+ #
26
+ # Example:
27
+ # supernet,subnet = IPAdmin.compare(cidr1,cidr2)
28
+ #
29
+
30
+ def compare(obj1,obj2)
31
+ ret_val = nil
32
+
33
+ # validate arguments
34
+ if ( !obj1.kind_of?(IPAdmin::CIDR) &&
35
+ !obj1.kind_of?(IPAdmin::NetStruct) )
36
+ raise ArgumentError, "Expected IPAdmin::CIDR or NetStruct " +
37
+ "as first argument, but #{obj1.class} provided."
38
+ end
39
+
40
+ if ( !obj2.kind_of?(IPAdmin::CIDR) &&
41
+ !obj2.kind_of?(IPAdmin::NetStruct) )
42
+ raise ArgumentError, "Expected IPAdmin::CIDR or NetStruct " +
43
+ "as second argument, but #{obj2.class} provided."
44
+ end
45
+
46
+ # make sure both are same version
47
+ if (obj1.version != obj2.version )
48
+ raise ArgumentError, "Provided objects are of different IP versions."
49
+ end
50
+
51
+
52
+ # get network/netmask of each
53
+ objects = [obj1,obj2]
54
+ networks = []
55
+ netmasks = []
56
+ for obj in objects
57
+ if ( obj.kind_of?(IPAdmin::CIDR) )
58
+ networks.push(obj.packed_network)
59
+ netmasks.push(obj.packed_netmask)
60
+
61
+ elsif ( obj.kind_of?(IPAdmin::NetStruct) )
62
+ networks.push(obj.network)
63
+ netmasks.push(obj.netmask)
64
+
65
+ end
66
+ end
67
+
68
+ # return 1's if objects are equal otherwise
69
+ # whichever netmask is smaller will be the supernet.
70
+ # if we '&' both networks by the supernet, and they are
71
+ # equal, then the supernet is the parent of the other network
72
+ if ( (networks[0] == networks[1]) && (netmasks[0] == netmasks[1]) )
73
+ ret_val = 1
74
+ elsif (netmasks[0] < netmasks[1])
75
+ if ( (netmasks[0] & networks[0]) == (netmasks[0] & networks[1]) )
76
+ ret_val = [obj1,obj2]
77
+ end
78
+ elsif (netmasks[1] < netmasks[0])
79
+ if ( (netmasks[1] & networks[0]) == (netmasks[1] & networks[1]) )
80
+ ret_val = [obj2,obj1]
81
+ end
82
+ end
83
+
84
+ return(ret_val)
85
+ end
86
+ module_function :compare
87
+
88
+ #======================================#
89
+ #
90
+ #======================================#
91
+
92
+
93
+ #==============================================================================#
94
+ # create_net_struct()
95
+ #==============================================================================#
96
+
97
+ # Create a NetStruct object from an CIDR or NetStruct object.
98
+ # This type of Struct is used internally for various tasks, and is not likely
99
+ # to be useful to anyone.
100
+ #
101
+ # - Arguments:
102
+ # * CIDR or NetStruct object
103
+ #
104
+ # - Returns:
105
+ # * NetStruct object
106
+ #
107
+ # Example:
108
+ # net_struct = IPAdmin.create_net_struct(object)
109
+ #
110
+ def create_net_struct(object)
111
+
112
+ if ( object.kind_of?(IPAdmin::CIDR) )
113
+ network = object.packed_ip
114
+ netmask = object.packed_netmask
115
+
116
+ elsif ( object.kind_of?(IPAdmin::NetStruct) )
117
+ network = object.network
118
+ netmask = object.netmask
119
+ else
120
+ raise ArgumentError, "Expected IPAdmin::CIDR or NetStruct "+
121
+ "object, but #{object.class} provided."
122
+ end
123
+
124
+ version = object.version
125
+ net_struct = NetStruct.new(network,netmask,version,object,[])
126
+
127
+ return(net_struct)
128
+ end
129
+ module_function :create_net_struct
130
+
131
+ #======================================#
132
+ #
133
+ #======================================#
134
+
135
+
136
+ #==============================================================================#
137
+ # merge()
138
+ #==============================================================================#
139
+
140
+ # Given a list of CIDR or NetStruct objects
141
+ # merge (supernet) them in the most efficient way possible. Supernetting
142
+ # will only occur when the newly created supernet will not result in the
143
+ # 'creation' of additional space. For example the following blocks
144
+ # (192.168.0.0/24, 192.168.1.0/24, and 192.168.2.0/24) would merge into
145
+ # 192.168.0.0/23 and 192.168.2.0/24 rather than into 192.168.0.0/22
146
+ #
147
+ # - Arguments:
148
+ # * Hash with the following fields:
149
+ # - :List -- Array of CIDR or NetStruct objects
150
+ # - :NetStruct -- if true, return IPAdmin::NetStruct objects (optional)
151
+ # - :Objectify -- if true, return IPAdmin::CIDR objects (optional)
152
+ # - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
153
+ #
154
+ # - Returns:
155
+ # * Array of CIDR or NetStruct objects
156
+ #
157
+ # Example:
158
+ # supernets = IPAdmin.merge(:List => list)
159
+ #
160
+ def merge(options)
161
+ version = nil
162
+ short = false
163
+ objectify = false
164
+
165
+ # validate options
166
+ if ( !options.kind_of?(Hash) )
167
+ raise ArgumentError, "Hash expected but #{options.class} provided."
168
+ end
169
+
170
+ if (!options.has_key?(:List))
171
+ raise ArgumentError, "Missing argument: List."
172
+ end
173
+
174
+ if (options.has_key?(:Short))
175
+ short = true
176
+ end
177
+
178
+ if (options.has_key?(:Objectify))
179
+ objectify = true
180
+ end
181
+
182
+ list = options[:List]
183
+ ret_struct = 1 if (options.has_key?(:NetStruct))
184
+
185
+ # make sure list is an array
186
+ if ( !list.kind_of?(Array) )
187
+ raise ArgumentError, "Expected Array for option :List, " +
188
+ "but #{list.class} provided."
189
+ end
190
+
191
+ # make sure all are valid types of the same IP version
192
+ list.each do |obj|
193
+ if (!obj.kind_of?(IPAdmin::CIDR) && !obj.kind_of?(IPAdmin::NetStruct) )
194
+ raise ArgumentError, "Expected IPAdmin::CIDR or NetStruct " +
195
+ "object but #{obj.class} provided."
196
+ end
197
+
198
+ version = obj.version if (!version)
199
+ if (!obj.version == version)
200
+ raise "Provided objects must be of the same IP version."
201
+ end
202
+ end
203
+
204
+ # make all_f
205
+ if (version == 4)
206
+ all_f = 2**32 - 1
207
+ else
208
+ all_f = 2**128 - 1
209
+ end
210
+
211
+ # make sure that our list is in order, and contains no duplicates
212
+ list = IPAdmin.sort(list)
213
+ index = 0
214
+ (list.length).times do
215
+ if ((index > 0)&&(IPAdmin.compare(list[index],list[index-1]) == 1))
216
+ list.delete_at(index)
217
+ else
218
+ index += 1
219
+ end
220
+ end
221
+
222
+ # create supernet_list from list
223
+ supernet_list = []
224
+ list.each do |obj|
225
+ if (obj.kind_of?(IPAdmin::CIDR))
226
+ supernet_list.push(IPAdmin.create_net_struct(obj))
227
+
228
+ elsif ( obj.kind_of?(IPAdmin::NetStruct) )
229
+ if (obj.subnets && obj.subnets.length > 0)
230
+ # get all child subnets of this branch entry
231
+ children = merge(:List => obj.subnets, :NetStruct => 1)
232
+
233
+ # if any child subnets are equal to the parent subnet
234
+ # then copy the grandchild subnets and delete the child
235
+ children.each do |child|
236
+ if ( (obj.network == child.network) &&
237
+ (obj.netmask == child.netmask) )
238
+ if (child.subnets && child.subnets.length > 0)
239
+ grandchildren = child.subnets
240
+ children.concat(grandchildren)
241
+ end
242
+ children.delete(child)
243
+ children = IPAdmin.sort(children)
244
+ break
245
+ end
246
+ end
247
+
248
+ obj.subnets.clear
249
+ obj.subnets.concat(children)
250
+ end
251
+
252
+ supernet_list.push(obj)
253
+ end
254
+ end
255
+
256
+ # merge subnets of this new branch by removing them from 'supernet_list',
257
+ # and categorizing them into hash of arrays (key=netmask)
258
+ # within each categorization we merge contiguous subnets
259
+ # and then remove them from that category & put them back into
260
+ # 'supernet_list'. we do this until our list stops getting any shorter
261
+ categories = {}
262
+ supernet_list_length = 0
263
+ until (supernet_list_length == supernet_list.length)
264
+ supernet_list_length = supernet_list.length
265
+
266
+ # categorize
267
+ supernet_list.each do |entry|
268
+ netmask = entry.netmask
269
+ if (categories.has_key?(netmask) )
270
+ (categories[netmask]).push(entry)
271
+ else
272
+ categories[netmask] = [entry]
273
+ end
274
+ end
275
+ supernet_list.clear
276
+
277
+ categories.each_key do |netmask|
278
+ entries = categories[netmask]
279
+ bitstep = (all_f + 1) - netmask
280
+
281
+ # this entire process depends on the list being in order
282
+ until (entries.length == 0)
283
+ to_merge = []
284
+ multiplier = 1
285
+ first = entries[0]
286
+ num_required = 2**multiplier
287
+ supermask = (netmask << multiplier) & all_f
288
+ supernet = supermask & first.network
289
+ if (first.network == supernet)
290
+ # take first entry and use it to form a base
291
+ # supernet address. this supernet should have
292
+ # x number of subnets in it, so we look for
293
+ # those subnets & if found store them
294
+ expected = first.network
295
+ entries.each do |entry|
296
+ if ( (entry.network == expected) && (first.network == supernet) )
297
+ to_merge.push(entry)
298
+ expected = expected + bitstep
299
+ if ( (to_merge.length == num_required) &&
300
+ (entries.length > num_required ) )
301
+ multiplier += 1
302
+ num_required = 2**multiplier
303
+ supermask = (netmask << multiplier) & all_f
304
+ supernet = supermask & first.network
305
+ end
306
+ else
307
+ # if entry is a duplicate, then kill it
308
+ if (IPAdmin.compare(entry,to_merge.last) == 1)
309
+ entries.delete(entry)
310
+ end
311
+ break
312
+ end
313
+ end
314
+ else
315
+ to_merge.push(first)
316
+ end
317
+
318
+ # make sure we actually have all of our subnets
319
+ # create our new supernet
320
+ if (to_merge.length != num_required)
321
+ multiplier -= 1
322
+ supermask = (netmask << multiplier) & all_f
323
+ supernet = supermask & first.network
324
+ end
325
+ net_struct = NetStruct.new(supernet,supermask,version,nil,[])
326
+
327
+ # now that we have the child members of our new supernet
328
+ # take any grandchild subnets that they may have and
329
+ # add them to the new supernet. then kill the children
330
+ (2**multiplier).times do
331
+ to_kill = to_merge.shift
332
+ net_struct.subnets.concat(to_kill.subnets) if (to_kill.subnets)
333
+ entries.delete(to_kill)
334
+ end
335
+ supernet_list.push(net_struct)
336
+ end
337
+ end
338
+ categories.clear
339
+ supernet_list = IPAdmin.sort(supernet_list)
340
+ end
341
+
342
+ # if '!ret_struct', return CIDR's
343
+ if (!ret_struct)
344
+ supernets = []
345
+ supernet_list.each do |entry|
346
+ if (!objectify)
347
+ network = IPAdmin.unpack_ip_addr(:Integer => entry.network, :Version => version)
348
+ network = IPAdmin.shorten(network) if (short && version == 6)
349
+ netmask = IPAdmin.unpack_ip_netmask(:Integer => entry.netmask, :Version => version)
350
+ supernets.push("#{network}/#{netmask}")
351
+ else
352
+ supernets.push(IPAdmin::CIDR.new(:PackedIP => entry.network,
353
+ :PackedNetmask => entry.netmask,
354
+ :Version => version))
355
+ end
356
+ end
357
+ else
358
+ supernets = supernet_list
359
+ end
360
+
361
+ return(supernets)
362
+ end
363
+ module_function :merge
364
+
365
+ #======================================#
366
+ #
367
+ #======================================#
368
+
369
+
370
+ #==============================================================================#
371
+ # minimum_size()
372
+ #==============================================================================#
373
+
374
+ # Given the number of IP addresses required in a subnet, return the minimum
375
+ # netmask required for that subnet.
376
+ #
377
+ # - Arguments:
378
+ # * Hash with the following fields:
379
+ # - :IPCount -- the number of IP addresses required - Integer
380
+ # - :Version -- IP version - Integer(optional)
381
+ #
382
+ # - Returns:
383
+ # * Integer
384
+ #
385
+ # - Notes:
386
+ # * Version is assumed to be 4 unless specified otherwise.
387
+ #
388
+ # Example:
389
+ # netmask = IPAdmin.minumum_size(:IPCount => 14) -> 28
390
+ #
391
+ def minimum_size(options)
392
+ version = 4
393
+
394
+ if ( !options.kind_of?(Hash) )
395
+ raise ArgumentError, "Hash expected but #{options.class} provided."
396
+ end
397
+
398
+ if ( !options.has_key?(:IPCount) )
399
+ raise ArgumentError, "Missing argument: List."
400
+ end
401
+ ipcount = options[:IPCount]
402
+
403
+ if (options.has_key?(:Version))
404
+ version = options[:Version]
405
+ end
406
+
407
+ if (version == 4)
408
+ max_bits = 32
409
+ else
410
+ max_bits = 128
411
+ end
412
+
413
+
414
+ if (ipcount > 2**max_bits)
415
+ raise "Required IP count exceeds number of IP addresses available " +
416
+ "for IPv#{version}."
417
+ end
418
+
419
+
420
+ bits_needed = 0
421
+ until (2**bits_needed >= ipcount)
422
+ bits_needed += 1
423
+ end
424
+ subnet_bits = max_bits - bits_needed
425
+
426
+ return(subnet_bits)
427
+ end
428
+ module_function :minimum_size
429
+
430
+ #======================================#
431
+ #
432
+ #======================================#
433
+
434
+
435
+ #==============================================================================#
436
+ # pack_ip_addr()
437
+ #==============================================================================#
438
+
439
+ # Convert IP addresses into an integer. No attempt at
440
+ # validation is performed.
441
+ #
442
+ # - Arguments:
443
+ # * Hash with the following fields:
444
+ # - :IP -- IP address - String
445
+ # - :Version -- IP version - Integer
446
+ #
447
+ # - Returns:
448
+ # * Integer
449
+ #
450
+ # - Notes:
451
+ # * Will attempt to auto-detect IP version if not provided
452
+ #
453
+ # Example:
454
+ # pack_ip_addr(IP => '192.168.1.1')
455
+ # pack_ip_addr(IP => 'ffff::1')
456
+ # pack_ip_addr(IP => '::192.168.1.1')
457
+ #
458
+ def pack_ip_addr(options)
459
+
460
+ if (!options.kind_of?(Hash))
461
+ raise ArgumentError, "Expected Hash, but #{options.class} provided."
462
+ end
463
+
464
+ if (!options.has_key?(:IP))
465
+ raise ArgumentError, "Missing argument: IP."
466
+ end
467
+ ip = options[:IP]
468
+
469
+ if (options.has_key?(:Version))
470
+ version = options[:Version]
471
+ if (version != 4 && version != 6)
472
+ raise ArgumentError, ":Version should be 4 or 6, but was '#{version}'."
473
+ end
474
+ end
475
+
476
+
477
+ if ( ip.kind_of?(String) )
478
+
479
+ # determine version if not provided
480
+ if (!version)
481
+ if ( ip =~ /\./ && ip !~ /:/ )
482
+ version = 4
483
+ else
484
+ version = 6
485
+ end
486
+ end
487
+
488
+ packed_ip = 0
489
+ if ( version == 4)
490
+ octets = ip.split('.')
491
+ (0..3).each do |x|
492
+ octet = octets.pop.to_i
493
+ octet = octet << 8*x
494
+ packed_ip = packed_ip | octet
495
+ end
496
+
497
+ else
498
+ # if ipv4-mapped ipv6 addr
499
+ if (ip =~ /\./)
500
+ dotted_dec = true
501
+ end
502
+
503
+ # split up by ':'
504
+ fields = []
505
+ if (ip =~ /::/)
506
+ shrthnd = ip.split( /::/ )
507
+ if (shrthnd.length == 0)
508
+ return(0)
509
+ else
510
+ first_half = shrthnd[0].split( /:/ ) if (shrthnd[0])
511
+ sec_half = shrthnd[1].split( /:/ ) if (shrthnd[1])
512
+ first_half = [] if (!first_half)
513
+ sec_half = [] if (!sec_half)
514
+ end
515
+ missing_fields = 8 - first_half.length - sec_half.length
516
+ missing_fields -= 1 if dotted_dec
517
+ fields = fields.concat(first_half)
518
+ missing_fields.times {fields.push('0')}
519
+ fields = fields.concat(sec_half)
520
+
521
+ else
522
+ fields = ip.split(':')
523
+ end
524
+
525
+ if (dotted_dec)
526
+ ipv4_addr = fields.pop
527
+ packed_v4 = IPAdmin.pack_ip_addr(:IP => ipv4_addr, :Version => 4)
528
+ octets = []
529
+ 2.times do
530
+ octet = packed_v4 & 0xFFFF
531
+ octets.unshift(octet.to_s(16))
532
+ packed_v4 = packed_v4 >> 16
533
+ end
534
+ fields.concat(octets)
535
+ end
536
+
537
+ # pack
538
+ (0..7).each do |x|
539
+ field = fields.pop.to_i(16)
540
+ field = field << 16*x
541
+ packed_ip = packed_ip | field
542
+ end
543
+
544
+ end
545
+
546
+ else
547
+ raise ArgumentError, "Argument :IP should be a String, but is a #{ip.class}."
548
+ end
549
+
550
+ return(packed_ip)
551
+ end
552
+ module_function :pack_ip_addr
553
+
554
+ #======================================#
555
+ #
556
+ #======================================#
557
+
558
+
559
+ #==============================================================================#
560
+ # pack_ip_netmask()
561
+ #==============================================================================#
562
+
563
+ # Convert IP netmask into an integer. No validation is performed. Netmask
564
+ # may be in either CIDR (/yy) or extended (y.y.y.y) format. CIDR formatted
565
+ # netmasks may either be a String or an Integer.
566
+ #
567
+ # - Arguments
568
+ # * Hash with the following fields:
569
+ # - :Netmask -- Netmask - String or Integer
570
+ # - :Version -- IP version - Integer (optional)
571
+ #
572
+ # - Returns:
573
+ # * Integer
574
+ #
575
+ # - Notes:
576
+ # * Version defaults to 4
577
+ #
578
+ # Example:
579
+ # packed = IPAdmin.pack_ip_netmask(Netmask => '255.255.255.0')
580
+ # packed = IPAdmin.pack_ip_netmask(Netmask => '24')
581
+ # packed = IPAdmin.pack_ip_netmask(Netmask => 24)
582
+ # packed = IPAdmin.pack_ip_netmask(Netmask => '/24')
583
+ # packed = IPAdmin.pack_ip_netmask(Netmask => '64', :Version => 6)
584
+ #
585
+ def pack_ip_netmask(options)
586
+ if (!options.kind_of?(Hash))
587
+ raise ArgumentError, "Expected Hash, but #{options.class} provided."
588
+ end
589
+
590
+ if (!options.has_key?(:Netmask))
591
+ raise ArgumentError, "Missing argument: Netmask."
592
+ end
593
+ netmask = options[:Netmask]
594
+
595
+ if (options.has_key?(:Version))
596
+ version = options[:Version]
597
+ if (version != 4 && version != 6)
598
+ raise ArgumentError, ":Version should be 4 or 6, but was '#{version}'."
599
+ elsif (version == 6)
600
+ all_f = 2**128-1
601
+ else
602
+ all_f = 2**32-1
603
+ end
604
+ else
605
+ all_f = 2**32-1
606
+ end
607
+
608
+
609
+ if (netmask.kind_of?(String))
610
+ if(netmask =~ /\./)
611
+ packed_netmask = IPAdmin.pack_ip_addr(:IP => netmask)
612
+
613
+ else
614
+ # remove '/' if present
615
+ if (netmask =~ /^\// )
616
+ netmask[0] = " "
617
+ netmask.lstrip!
618
+ end
619
+ netmask = netmask.to_i
620
+ packed_netmask = all_f ^ (all_f >> netmask)
621
+ end
622
+ elsif (netmask.kind_of?(Integer))
623
+ packed_netmask = all_f ^ (all_f >> netmask)
624
+
625
+ else
626
+ raise ArgumentError, "Argument :Netmask should be a String or Integer, but is a #{netmask.class}."
627
+
628
+ end
629
+
630
+ return(packed_netmask)
631
+ end
632
+ module_function :pack_ip_netmask
633
+
634
+ #======================================#
635
+ #
636
+ #======================================#
637
+
638
+
639
+ #==============================================================================#
640
+ # range()
641
+ #==============================================================================#
642
+
643
+ # Given two IPAdmin::CIDR objects of the same version, return all IP
644
+ # addresses between them (non-inclusive).
645
+ #
646
+ # - Arguments:
647
+ # * Hash with the following fields:
648
+ # - :Bitstep -- enumerate in X sized steps - Integer (optional)
649
+ # - :Boundaries -- array of (2) CIDR objects
650
+ # - :Limit -- limit returned list to X number of items - Integer (optional)
651
+ # - :Objectify -- if true, return CIDR objects (optional)
652
+ # - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
653
+ #
654
+ # - Returns:
655
+ # * Array of Strings or CIDR objects
656
+ #
657
+ # - Notes:
658
+ # * IPAdmin.range will use the original IP address passed during the initialization
659
+ # of the CIDR objects. This IP can be found using the CIDR.ip() method.
660
+ #
661
+ # Example:
662
+ # list = IPAdmin.range(:Boundaries => [cidr1,cidr2])
663
+ #
664
+ def range(options)
665
+ list = []
666
+ bitstep = 1
667
+ objectify = false
668
+ short = false
669
+ limit = nil
670
+
671
+ # check options
672
+ if (options)
673
+ if ( !options.has_key?(:Boundaries) )
674
+ raise ArgumentError, "Missing argument: Boundaries."
675
+ end
676
+
677
+ if (options[:Boundaries].length == 2)
678
+ (cidr1,cidr2) = options[:Boundaries]
679
+ else
680
+ raise ArgumentError, "Two IPAdmin::CIDR ojects are required. " +
681
+ "as Boundaries."
682
+ end
683
+
684
+ if( options.has_key?(:Bitstep) )
685
+ bitstep = options[:Bitstep]
686
+ end
687
+
688
+ if( options.has_key?(:Objectify) )
689
+ objectify = true
690
+ end
691
+
692
+ if( options.has_key?(:Short) )
693
+ short = true
694
+ end
695
+
696
+ if( options.has_key?(:Limit) )
697
+ limit = options[:Limit]
698
+ end
699
+ end
700
+
701
+ # check our objects
702
+ if (!cidr1.kind_of?(IPAdmin::CIDR) && !cidr2.kind_of?(IPAdmin::CIDR))
703
+ raise ArgumentError, "One or more values provided under :Boundaries "+
704
+ "is not a valid IPAdmin::CIDR object."
705
+ end
706
+
707
+ # check version, store & sort
708
+ if (cidr1.version == cidr2.version)
709
+ version = cidr1.version
710
+ boundaries = [cidr1.packed_ip, cidr2.packed_ip]
711
+ boundaries.sort
712
+ else
713
+ raise ArgumentError, "Provided IPAdmin::CIDR objects are of different IP versions."
714
+ end
715
+
716
+ # dump our range
717
+ my_ip = boundaries[0] + 1
718
+ until (my_ip >= boundaries[1])
719
+ if (!objectify)
720
+ my_ip_s = IPAdmin.unpack_ip_addr(:Integer => my_ip, :Version => version)
721
+ my_ips = IPAdmin.shorten(my_ips) if (short && version == 6)
722
+ list.push(my_ip_s)
723
+ else
724
+ list.push( IPAdmin::CIDR.new(:PackedIP => my_ip, :Version => version) )
725
+ end
726
+
727
+ my_ip = my_ip + bitstep
728
+ if (limit)
729
+ limit = limit -1
730
+ break if (limit == 0)
731
+ end
732
+ end
733
+
734
+ return(list)
735
+ end
736
+ module_function :range
737
+
738
+ #======================================#
739
+ #
740
+ #======================================#
741
+
742
+
743
+ #==============================================================================#
744
+ # shorten()
745
+ #==============================================================================#
746
+
747
+ # Take a standard IPv6 address, and format it in short-hand notation.
748
+ # The address should not contain a netmask.
749
+ #
750
+ # - Arguments:
751
+ # * String
752
+ #
753
+ # - Returns:
754
+ # * String
755
+ #
756
+ # Example:
757
+ # short = IPAdmin.shorten('fec0:0000:0000:0000:0000:0000:0000:0001') --> 'fec0::1'
758
+ #
759
+ def shorten(addr)
760
+
761
+ # is this a string?
762
+ if (!addr.kind_of? String)
763
+ raise ArgumentError, "Expected String, but #{addr.class} provided."
764
+ end
765
+
766
+ validate_ip_addr(:IP => addr, :Version => 6)
767
+
768
+ # make sure this isnt already shorthand
769
+ if (addr =~ /::/)
770
+ return(addr)
771
+ end
772
+
773
+ # split into fields
774
+ fields = addr.split(":")
775
+
776
+ # check last field for ipv4-mapped addr
777
+ if (fields.last() =~ /\./ )
778
+ ipv4_mapped = fields.pop()
779
+ end
780
+
781
+ # look for most consecutive '0' fields
782
+ start_field,end_field = nil,nil
783
+ start_end = []
784
+ consecutive,longest = 0,0
785
+
786
+ (0..(fields.length-1)).each do |x|
787
+ fields[x] = fields[x].to_i(16)
788
+
789
+ if (fields[x] == 0)
790
+ if (!start_field)
791
+ start_field = x
792
+ end_field = x
793
+ else
794
+ end_field = x
795
+ end
796
+ consecutive += 1
797
+ else
798
+ if (start_field)
799
+ if (consecutive > longest)
800
+ longest = consecutive
801
+ start_end = [start_field,end_field]
802
+ start_field,end_field = nil,nil
803
+ end
804
+ consecutive = 0
805
+ end
806
+ end
807
+
808
+ fields[x] = fields[x].to_s(16)
809
+ end
810
+
811
+ # if our longest set of 0's is at the end, then start & end fields
812
+ # are already set. if not, then make start & end fields the ones we've
813
+ # stored away in start_end
814
+ if (consecutive > longest)
815
+ longest = consecutive
816
+ else
817
+ start_field = start_end[0]
818
+ end_field = start_end[1]
819
+ end
820
+
821
+ if (longest > 1)
822
+ fields[start_field] = ''
823
+ start_field += 1
824
+ fields.slice!(start_field..end_field)
825
+ end
826
+ fields.push(ipv4_mapped) if (ipv4_mapped)
827
+ short = fields.join(':')
828
+ short << ':' if (short =~ /:$/)
829
+
830
+ return(short)
831
+ end
832
+ module_function :shorten
833
+
834
+ #======================================#
835
+ #
836
+ #======================================#
837
+
838
+
839
+ #==============================================================================#
840
+ # sort()
841
+ #==============================================================================#
842
+
843
+ # Given a list of IPAdmin::CIDR or NetStruct objects
844
+ # sort them from lowest to highest by Network/Netmask.
845
+ #
846
+ # - Arguments:
847
+ # * Array of CIDR or NetStruct objects
848
+ #
849
+ # - Returns:
850
+ # * Array
851
+ #
852
+ # - Notes:
853
+ # * IPAdmin.sort will use the original IP address passed during the initialization
854
+ # of the CIDR objects. This IP can be found using the CIDR.ip() method.
855
+ #
856
+ # Example:
857
+ # sorted = IPAdmin.sort(list)
858
+ #
859
+ def sort(list)
860
+
861
+ # make sure list is an array
862
+ if ( !list.kind_of?(Array) )
863
+ raise ArgumentError, "Array of IPAdmin::CIDR or NetStruct " +
864
+ "objects expected, but #{list.class} provided."
865
+ end
866
+
867
+ # make sure all are valid types of the same IP version
868
+ version = nil
869
+ list.each do |obj|
870
+ unless (obj.kind_of?(IPAdmin::CIDR) || obj.kind_of?(IPAdmin::NetStruct) )
871
+ raise ArgumentError, "Expected IPAdmin::CIDR or NetStruct " +
872
+ "object but #{obj.class} provided."
873
+ end
874
+
875
+ version = obj.version if (!version)
876
+ unless (obj.version == version)
877
+ raise "Provided objects must all be of the same IP version."
878
+ end
879
+ end
880
+
881
+ # create unsorted_list from list
882
+ unsorted_list = []
883
+ list.each do |obj|
884
+ unsorted_list.push(IPAdmin.create_net_struct(obj))
885
+ end
886
+
887
+ # sort by network. if networks are equal, sort by netmask.
888
+ sorted_list = []
889
+ unsorted_list.each do |entry|
890
+ index = 0
891
+ sorted_list.each do
892
+ if(entry.network < (sorted_list[index]).network)
893
+ break
894
+ elsif (entry.network == (sorted_list[index]).network)
895
+ if (entry.netmask < (sorted_list[index]).netmask)
896
+ break
897
+ end
898
+ end
899
+ index += 1
900
+ end
901
+ sorted_list.insert(index, entry)
902
+ end
903
+
904
+ # replace sorted_list entries with their .object
905
+ index = 0
906
+ sorted_list.length.times do
907
+ sorted_list[index] = (sorted_list[index]).object
908
+ index += 1
909
+ end
910
+
911
+ return(sorted_list)
912
+ end
913
+ module_function :sort
914
+
915
+ #======================================#
916
+ #
917
+ #======================================#
918
+
919
+
920
+ #==============================================================================#
921
+ # unpack_ip_addr()
922
+ #==============================================================================#
923
+
924
+ # Unack a packed IP address back into a printable string. No attempt at
925
+ # validation is performed.
926
+ #
927
+ # - Arguments:
928
+ # * Hash with the following fields:
929
+ # - :Integer -- Integer representaion of an IP address
930
+ # - :Version -- IP version - Integer (optional)
931
+ # - :IPv4Mapped -- if true, unpack IPv6 as an IPv4 mapped address (optional)
932
+ #
933
+ # - Returns:
934
+ # * String
935
+ #
936
+ # - Notes:
937
+ # * IP version will attempt to be auto-detected if not provided
938
+ #
939
+ # Example:
940
+ # unpacked = IPAdmin.unpack_ip_addr(:Integer => packed)
941
+ #
942
+ def unpack_ip_addr(options)
943
+ ipv4_mapped = false
944
+
945
+ if (!options.kind_of?(Hash))
946
+ raise ArgumentError, "Expected Hash, but #{options.class} provided."
947
+ end
948
+
949
+ if (!options.has_key?(:Integer))
950
+ raise ArgumentError, "Missing argument: Integer."
951
+ end
952
+ packed_ip = options[:Integer]
953
+
954
+ if (!packed_ip.kind_of?(Integer))
955
+ raise ArgumentError, "Expected Integer, but #{options.class} provided for argument: Integer."
956
+ end
957
+
958
+ if (options.has_key?(:Version))
959
+ version = options[:Version]
960
+ if (version != 4 && version != 6)
961
+ raise ArgumentError, ":Version should be 4 or 6, but was '#{version}'."
962
+ end
963
+ end
964
+
965
+ if (options.has_key?(:IPv4Mapped) && options[:IPv4Mapped] == true)
966
+ ipv4_mapped = true
967
+ end
968
+
969
+ # set version if not set
970
+ if (!version)
971
+ if (packed_ip < 2**32)
972
+ version = 4
973
+ else
974
+ version = 6
975
+ end
976
+ end
977
+
978
+ if (version == 4)
979
+ octets = []
980
+ 4.times do
981
+ octet = packed_ip & 0xFF
982
+ octets.unshift(octet.to_s)
983
+ packed_ip = packed_ip >> 8
984
+ end
985
+ ip = octets.join('.')
986
+ else
987
+ fields = []
988
+ if (!ipv4_mapped)
989
+ loop_count = 8
990
+ else
991
+ loop_count = 6
992
+ packed_v4 = packed_ip & 0xffffffff
993
+ ipv4_addr = IPAdmin.unpack_ip_addr(:Integer => packed_v4, :Version => 4)
994
+ fields.unshift(ipv4_addr)
995
+ packed_ip = packed_ip >> 32
996
+ end
997
+
998
+ loop_count.times do
999
+ octet = packed_ip & 0xFFFF
1000
+ octet = octet.to_s(16)
1001
+ packed_ip = packed_ip >> 16
1002
+
1003
+ # if octet < 4 characters, then pad with 0's
1004
+ (4 - octet.length).times do
1005
+ octet = '0' << octet
1006
+ end
1007
+ fields.unshift(octet)
1008
+ end
1009
+ ip = fields.join(':')
1010
+ end
1011
+
1012
+
1013
+ return(ip)
1014
+ end
1015
+ module_function :unpack_ip_addr
1016
+
1017
+ #======================================#
1018
+ #
1019
+ #======================================#
1020
+
1021
+
1022
+ #==============================================================================#
1023
+ # unpack_ip_netmask()
1024
+ #==============================================================================#
1025
+
1026
+ # Unack a packed IP netmask into a integer representing the number of
1027
+ # bits in the CIDR mask. No attempt at validation is performed.
1028
+ #
1029
+ # - Arguments:
1030
+ # * Hash with the following fields:
1031
+ # - :Integer -- Integer representation of an IP Netmask
1032
+ #
1033
+ # - Returns:
1034
+ # * Integer
1035
+ #
1036
+ # Example:
1037
+ # unpacked = IPAdmin.unpack_ip_netmask(:Integer => packed)
1038
+ #
1039
+ def unpack_ip_netmask(options)
1040
+ if (!options.kind_of?(Hash))
1041
+ raise ArgumentError, "Expected Hash, but #{options.class} provided."
1042
+ end
1043
+
1044
+ if (!options.has_key?(:Integer))
1045
+ raise ArgumentError, "Missing argument: Integer."
1046
+ end
1047
+ packed_netmask = options[:Integer]
1048
+
1049
+ if (!packed_netmask.kind_of?(Integer))
1050
+ raise ArgumentError, "Argument :Integer should be an Integer, but is a #{packed_netmask.class}."
1051
+ end
1052
+
1053
+ if (packed_netmask < 2**32)
1054
+ mask = 32
1055
+ else
1056
+ mask = 128
1057
+ end
1058
+
1059
+ mask.times do
1060
+ if ( (packed_netmask & 1) == 1)
1061
+ break
1062
+ end
1063
+ packed_netmask = packed_netmask >> 1
1064
+ mask = mask - 1
1065
+ end
1066
+
1067
+ return(mask)
1068
+ end
1069
+ module_function :unpack_ip_netmask
1070
+
1071
+ #======================================#
1072
+ #
1073
+ #======================================#
1074
+
1075
+
1076
+ #==============================================================================#
1077
+ # unshorten()
1078
+ #==============================================================================#
1079
+
1080
+ # Take an IPv6 address in short-hand format, and expand it into standard
1081
+ # notation. The address should not contain a netmask.
1082
+ #
1083
+ # - Arguments:
1084
+ # * String
1085
+ #
1086
+ # - Returns:
1087
+ # * String
1088
+ #
1089
+ # Example:
1090
+ # long = IPAdmin.unshorten('fec0::1') --> 'fec0:0000:0000:0000:0000:0000:0000:0001'
1091
+ #
1092
+ def unshorten(addr)
1093
+
1094
+ # is this a string?
1095
+ if (!addr.kind_of? String)
1096
+ raise ArgumentError, "Expected String, but #{addr.class} provided."
1097
+ end
1098
+
1099
+ validate_ip_addr(:IP => addr, :Version => 6)
1100
+ ipv4_mapped = true if (addr =~ /\./)
1101
+
1102
+ packed = pack_ip_addr(:IP => addr, :Version => 6)
1103
+ if (!ipv4_mapped)
1104
+ long = unpack_ip_addr(:Integer => packed, :Version => 6)
1105
+ else
1106
+ long = unpack_ip_addr(:Integer => packed, :Version => 6, :IPv4Mapped => true)
1107
+ end
1108
+
1109
+ return(long)
1110
+ end
1111
+ module_function :unshorten
1112
+
1113
+ #======================================#
1114
+ #
1115
+ #======================================#
1116
+
1117
+
1118
+ #==============================================================================#
1119
+ # validate_ip_addr()
1120
+ #==============================================================================#
1121
+
1122
+ # Validate an IP address. The address should not contain a netmask.
1123
+ #
1124
+ # - Arguments
1125
+ # * Hash with the following fields:
1126
+ # - :IP -- IP address to validate - String or Integer
1127
+ # - :Version -- IP version - Integer (optional)
1128
+ #
1129
+ # - Returns:
1130
+ # * True
1131
+ #
1132
+ # - Notes:
1133
+ # * IP version will attempt to be auto-detected if not provided
1134
+ #
1135
+ # Example:
1136
+ # validate_ip_addr(IP => '192.168.1.1')
1137
+ # validate_ip_addr(IP => 'ffff::1')
1138
+ # validate_ip_addr(IP => '::192.168.1.1')
1139
+ # validate_ip_addr(IP => 0xFFFFFF)
1140
+ # validate_ip_addr(IP => 2**128-1)
1141
+ # validate_ip_addr(IP => 2**32-1, :Version => 4)
1142
+ #
1143
+ def validate_ip_addr(options)
1144
+
1145
+ if (!options.kind_of?(Hash))
1146
+ raise ArgumentError, "Expected Hash, but #{options.class} provided."
1147
+ end
1148
+
1149
+ if (!options.has_key?(:IP))
1150
+ raise ArgumentError, "Missing argument: IP."
1151
+ end
1152
+ ip = options[:IP]
1153
+
1154
+ if (options.has_key?(:Version))
1155
+ version = options[:Version]
1156
+ if (version != 4 && version != 6)
1157
+ raise ArgumentError, ":Version should be 4 or 6, but was '#{version}'."
1158
+ end
1159
+ end
1160
+
1161
+ if ( ip.kind_of?(String) )
1162
+
1163
+ # check validity of charaters
1164
+ if (ip =~ /[^0-9a-fA-F\.:]/)
1165
+ raise "#{ip} is invalid (contains invalid characters)."
1166
+ end
1167
+
1168
+ # determine version if not specified
1169
+ if (!version && (ip =~ /\./ && ip !~ /:/ ) )
1170
+ version = 4
1171
+ elsif (!version && ip =~ /:/)
1172
+ version = 6
1173
+ end
1174
+
1175
+ if (version == 4)
1176
+ octets = ip.split('.')
1177
+ raise "#{ip} is invalid (IPv4 requires (4) octets)." if (octets.length != 4)
1178
+
1179
+ # are octets in range 0..255?
1180
+ octets.each do |octet|
1181
+ raise "#{ip} is invalid (IPv4 dotted-decimal format " +
1182
+ "should not contain non-numeric characters)." if (octet =~ /[^0-9]/ )
1183
+ octet = octet.to_i()
1184
+ if ( (octet < 0) || (octet >= 256) )
1185
+ raise "#{ip} is invalid (IPv4 octets should be between 0 and 255)."
1186
+ end
1187
+ end
1188
+
1189
+ elsif (version == 6)
1190
+ # make sure we only have at most (2) colons in a row, and then only
1191
+ # (1) instance of that
1192
+ if ( (ip =~ /:{3,}/) || (ip.split("::").length > 2) )
1193
+ raise "#{ip} is invalid (IPv6 field separators (:) are bad)."
1194
+ end
1195
+
1196
+ # set flags
1197
+ shorthand = false
1198
+ if (ip =~ /\./)
1199
+ dotted_dec = true
1200
+ else
1201
+ dotted_dec = false
1202
+ end
1203
+
1204
+ # split up by ':'
1205
+ fields = []
1206
+ if (ip =~ /::/)
1207
+ shorthand = true
1208
+ ip.split('::').each do |x|
1209
+ fields.concat( x.split(':') )
1210
+ end
1211
+ else
1212
+ fields.concat( ip.split(':') )
1213
+ end
1214
+
1215
+ # make sure we have the correct number of fields
1216
+ if (shorthand)
1217
+ if ( (dotted_dec && fields.length > 6) || (!dotted_dec && fields.length > 7) )
1218
+ raise "#{ip} is invalid (IPv6 shorthand notation has " +
1219
+ "incorrect number of fields)."
1220
+ end
1221
+ else
1222
+ if ( (dotted_dec && fields.length != 7 ) || (!dotted_dec && fields.length != 8) )
1223
+ raise "#{ip} is invalid (IPv6 address has " +
1224
+ "incorrect number of fields)."
1225
+ end
1226
+ end
1227
+
1228
+ # if dotted_dec then validate the last field
1229
+ if (dotted_dec)
1230
+ dotted = fields.pop()
1231
+ octets = dotted.split('.')
1232
+ raise "#{ip} is invalid (Legacy IPv4 portion of IPv6 " +
1233
+ "address should contain (4) octets)." if (octets.length != 4)
1234
+ octets.each do |x|
1235
+ raise "#{ip} is invalid (egacy IPv4 portion of IPv6 " +
1236
+ "address should not contain non-numeric characters)." if (x =~ /[^0-9]/ )
1237
+ x = x.to_i
1238
+ if ( (x < 0) || (x >= 256) )
1239
+ raise "#{ip} is invalid (Octets of a legacy IPv4 portion of IPv6 " +
1240
+ "address should be between 0 and 255)."
1241
+ end
1242
+ end
1243
+ end
1244
+
1245
+ # validate hex fields
1246
+ fields.each do |x|
1247
+ if (x =~ /[^0-9a-fA-F]/)
1248
+ raise "#{ip} is invalid (IPv6 address contains invalid hex characters)."
1249
+ else
1250
+ x = x.to_i(16)
1251
+ if ( (x < 0) || (x >= 2**16) )
1252
+ raise "#{ip} is invalid (Fields of an IPv6 address " +
1253
+ "should be between 0x0 and 0xFFFF)."
1254
+ end
1255
+ end
1256
+ end
1257
+
1258
+ else
1259
+ raise "#{ip} is invalid (Did you mean to pass an Integer instead of a String?)."
1260
+ end
1261
+
1262
+ elsif ( ip.kind_of?(Integer) )
1263
+ if (version && version == 4)
1264
+ raise "#{ip} is invalid (Integer is out of bounds)." if ( (ip < 0) || (ip > 2**32) )
1265
+ else
1266
+ raise "#{ip} is invalid (Integer is out of bounds)." if ( (ip < 0) || (ip > 2**128) )
1267
+ end
1268
+
1269
+ else
1270
+ raise ArgumentError, "Expected String or Integer, but #{ip.class} provided."
1271
+ end
1272
+
1273
+
1274
+ return(true)
1275
+ end
1276
+ module_function :validate_ip_addr
1277
+
1278
+ #======================================#
1279
+ #
1280
+ #======================================#
1281
+
1282
+
1283
+ #==============================================================================#
1284
+ # validate_ip_netmask()
1285
+ #==============================================================================#
1286
+
1287
+ # Validate IP Netmask.
1288
+ #
1289
+ # - Arguments:
1290
+ # * Hash with the following fields:
1291
+ # - :Netmask -- Netmask to validate - String or Integer
1292
+ # - :Packed -- if true, the provided Netmask is a packed Integer
1293
+ # - :Version -- IP version - Integer (optional)
1294
+ #
1295
+ # - Returns:
1296
+ # * True or exception.
1297
+ #
1298
+ # - Notes:
1299
+ # * Version defaults to 4 if not specified.
1300
+ #
1301
+ # Example:
1302
+ # IPAdmin.validate_ip_netmask(:Netmask => '/32')
1303
+ # IPAdmin.validate_ip_netmask(:Netmask => 32)
1304
+ # IPAdmin.validate_ip_netmask(:Netmask => 0xffffffff, :Packed => true)
1305
+ #
1306
+ def validate_ip_netmask(options)
1307
+ packed = false
1308
+
1309
+ if (!options.kind_of?(Hash))
1310
+ raise ArgumentError, "Expected Hash, but #{options.class} provided."
1311
+ end
1312
+
1313
+ if (!options.has_key?(:Netmask))
1314
+ raise ArgumentError, "Missing argument: Netmask."
1315
+ end
1316
+ netmask = options[:Netmask]
1317
+
1318
+ if (options.has_key?(:Packed) && options[:Packed] == true)
1319
+ packed = true
1320
+ end
1321
+
1322
+ if (options.has_key?(:Version))
1323
+ version = options[:Version]
1324
+ if (version != 4 && version != 6)
1325
+ raise ArgumentError, ":Version should be 4 or 6, but was '#{version}'."
1326
+ elsif (version == 6)
1327
+ max_bits = 128
1328
+ else
1329
+ max_bits = 32
1330
+ end
1331
+ else
1332
+ version = 4
1333
+ max_bits = 32
1334
+ end
1335
+
1336
+
1337
+ if (netmask.kind_of?(String))
1338
+ if(netmask =~ /\./)
1339
+ all_f = 2**32-1
1340
+ packed_netmask = 0
1341
+
1342
+ # validate & pack extended mask
1343
+ begin
1344
+ validate_ip_addr(:IP => netmask)
1345
+ packed_netmask = pack_ip_addr(:IP => netmask)
1346
+ rescue Exception
1347
+ raise "#{netmask} is an improperly formed IPv4 address."
1348
+ end
1349
+
1350
+ # cycle through the bits of hostmask and compare
1351
+ # with packed_mask. when we hit the firt '1' within
1352
+ # packed_mask (our netmask boundary), xor hostmask and
1353
+ # packed_mask. the result should be all 1's. this whole
1354
+ # process is in place to make sure that we dont have
1355
+ # and crazy masks such as 255.254.255.0
1356
+ hostmask = 1
1357
+ 32.times do
1358
+ check = packed_netmask & hostmask
1359
+ if ( check != 0)
1360
+ hostmask = hostmask >> 1
1361
+ unless ( (packed_netmask ^ hostmask) == all_f)
1362
+ raise "#{netmask} contains '1' bits within the host portion of the netmask."
1363
+ end
1364
+ break
1365
+ else
1366
+ hostmask = hostmask << 1
1367
+ hostmask = hostmask | 1
1368
+ end
1369
+ end
1370
+
1371
+ else
1372
+ # remove '/' if present
1373
+ if (netmask =~ /^\// )
1374
+ netmask[0] = " "
1375
+ netmask.lstrip!
1376
+ end
1377
+
1378
+ # check if we have any non numeric characters
1379
+ if (netmask =~ /\D/)
1380
+ raise "#{netmask} contains invalid characters."
1381
+ end
1382
+
1383
+ netmask = netmask.to_i
1384
+ if (netmask > max_bits || netmask == 0 )
1385
+ raise "Netmask, #{netmask}, is out of bounds for IPv#{version}."
1386
+ end
1387
+
1388
+ end
1389
+
1390
+ elsif (netmask.kind_of?(Integer) )
1391
+ if (!packed)
1392
+ if (netmask > max_bits || netmask == 0 )
1393
+ raise "Netmask, #{netmask}, is out of bounds for IPv#{version}."
1394
+ end
1395
+ else
1396
+ if (netmask >= 2**max_bits || netmask == 0 )
1397
+ raise "Packed netmask, #{netmask}, is out of bounds for IPv#{version}."
1398
+ end
1399
+ end
1400
+
1401
+ else
1402
+ raise ArgumentError, "Expected String or Integer, but #{netmask.class} provided."
1403
+ end
1404
+
1405
+ return(true)
1406
+ end
1407
+ module_function :validate_ip_netmask
1408
+
1409
+ #======================================#
1410
+ #
1411
+ #======================================#
1412
+
1413
+
1414
+ #==============================================================================#
1415
+ # NetStruct
1416
+ #==============================================================================#
1417
+
1418
+ # Struct object used internally by IPAdmin. It is not likely directly useful
1419
+ # to anyone.
1420
+ #
1421
+ # Description of fields:
1422
+ # * network - Integer representing an IP address
1423
+ # * netmask - Integer representing IP netmask
1424
+ # * version - Integer representing an IP version
1425
+ # * object - holds an IPAdmin class object
1426
+ # * subnets - an array of children NetStruct objects
1427
+ #
1428
+ NetStruct = Struct.new(:network, :netmask, :version, :object, :subnets)
1429
+
1430
+ #======================================#
1431
+ #
1432
+ #======================================#
1433
+
1434
+
1435
+
1436
+
1437
+
1438
+ # DEPRICATED
1439
+
1440
+ #==============================================================================#
1441
+ # arpa()
1442
+ #==============================================================================#
1443
+
1444
+ # DEPRICATED - USE CIDR.arpa:
1445
+ # Using the provided CIDR object,
1446
+ # return either an in-addr.arpa. or ip6.arpa. string. The netmask will be used
1447
+ # to determine the length of the returned arpa string.
1448
+ #
1449
+ # - Arguments:
1450
+ # * CIDR object
1451
+ #
1452
+ # - Returns:
1453
+ # * String
1454
+ #
1455
+ # - Notes:
1456
+ # * IPAdmin.arpa will use the original IP address passed during the initialization
1457
+ # of the CIDR objects. This IP can be found using the CIDR.ip() method.
1458
+ #
1459
+ # Example:
1460
+ # arpa = IPAdmin.arpa(cidr)
1461
+ #
1462
+ def arpa(object)
1463
+
1464
+ if (!object.kind_of? IPAdmin::CIDR)
1465
+ raise ArgumentError, "Expected IPAdmin::CIDR object, " +
1466
+ "but #{object.class} provided."
1467
+ end
1468
+
1469
+ base = object.ip()
1470
+ netmask = object.bits()
1471
+
1472
+ if (object.version == 4)
1473
+ net = base.split('.')
1474
+
1475
+ if (netmask)
1476
+ while (netmask < 32)
1477
+ net.pop
1478
+ netmask = netmask + 8
1479
+ end
1480
+ end
1481
+
1482
+ arpa = net.reverse.join('.')
1483
+ arpa << ".in-addr.arpa."
1484
+
1485
+ elsif (object.version == 6)
1486
+ fields = base.split(':')
1487
+ net = []
1488
+ fields.each do |field|
1489
+ (field.split("")).each do |x|
1490
+ net.push(x)
1491
+ end
1492
+ end
1493
+
1494
+ if (netmask)
1495
+ while (netmask < 128)
1496
+ net.pop
1497
+ netmask = netmask + 4
1498
+ end
1499
+ end
1500
+
1501
+ arpa = net.reverse.join('.')
1502
+ arpa << ".ip6.arpa."
1503
+
1504
+ end
1505
+
1506
+ return(arpa)
1507
+ end
1508
+ module_function :arpa
1509
+
1510
+ #======================================#
1511
+ #
1512
+ #======================================#
1513
+
1514
+ #==============================================================================#
1515
+ # validate_ipv4_addr()
1516
+ #==============================================================================#
1517
+
1518
+ # DEPRICATED - USE validate_ip_addr INSTEAD
1519
+ # Validate IPv4 addresses. The address should not contain a netmask.
1520
+ #
1521
+ # - Arguments:
1522
+ # * IPv4 address
1523
+ #
1524
+ # - Returns:
1525
+ # * packed IP on valid, or exception on error.
1526
+ #
1527
+ # Example:
1528
+ # IPAdmin.validate_ipv4_addr('192.168.1.1')
1529
+ #
1530
+ def validate_ipv4_addr(ip)
1531
+
1532
+ # is this a string?
1533
+ unless (ip.kind_of? String)
1534
+ raise ArgumentError, "Expected String, but #{ip.class} provided."
1535
+ end
1536
+
1537
+ # check validity of characters in the addr
1538
+ if ( (ip =~ /\.{2,}?/ ) || (ip =~ /[^0-9\.]/) )
1539
+ raise "#{ip} is not a valid IPv4 address."
1540
+ end
1541
+
1542
+ # do we have 4 octets?
1543
+ octets = ip.split( /\./ ).reverse
1544
+ if (octets.length != 4)
1545
+ raise "#{ip} is not a valid IPv4 address."
1546
+ end
1547
+
1548
+ # are octets in range 0..255?
1549
+ packed_ip = 0
1550
+ (0..3).each do |x|
1551
+ octets[x] = octets[x].to_i
1552
+ unless ( (octets[x] >= 0) && (octets[x] < 256 ) )
1553
+ raise "#{ip} is not a valid IPv4 address."
1554
+ end
1555
+ octets[x] = octets[x] << 8*x
1556
+ packed_ip = packed_ip | octets[x]
1557
+ end
1558
+
1559
+ # dont allow first octet to be 0
1560
+ if (octets[3] == 0)
1561
+ raise "#{ip} is not a valid IPv4 address."
1562
+ end
1563
+
1564
+ return(packed_ip)
1565
+
1566
+ end
1567
+ module_function :validate_ipv4_addr
1568
+
1569
+ #======================================#
1570
+ #
1571
+ #======================================#
1572
+
1573
+
1574
+ #==============================================================================#
1575
+ # validate_ipv4_netmask()
1576
+ #==============================================================================#
1577
+
1578
+ # DEPRICATED - USE validate_ip_netmask INSTEAD
1579
+ # Validate IPv4 Netmask.
1580
+ #
1581
+ # - Arguments:
1582
+ # * IPv4 netmask in cidr or extended notation
1583
+ #
1584
+ # - Returns:
1585
+ # * packed netmask on valid, or exception on error.
1586
+ #
1587
+ # Example:
1588
+ # IPAdmin.validate_ipv4_netmask('255.255.255.0')
1589
+ # IPAdmin.validate_ipv4_netmask('24')
1590
+ # IPAdmin.validate_ipv4_netmask('/24')
1591
+ # IPAdmin.validate_ipv4_netmask(24)
1592
+ #
1593
+ def validate_ipv4_netmask(netmask)
1594
+ all_f = 2**32 - 1
1595
+ packed_mask = nil
1596
+
1597
+ # is this a CIDR or Extended mask?
1598
+ if(netmask =~ /\./)
1599
+ # validate & pack extended mask
1600
+ begin
1601
+ validate_ipv4_addr(netmask)
1602
+ packed_netmask = pack_ipv4_addr(netmask)
1603
+
1604
+ rescue Exception
1605
+ raise "#{netmask} is not a valid IPv4 netmask."
1606
+ end
1607
+
1608
+ # cycle through the bits of hostmask and compare
1609
+ # with packed_mask. when we hit the firt '1' within
1610
+ # packed_mask (our netmask boundary), xor hostmask and
1611
+ # packed_mask. the result should be all 1's. this whole
1612
+ # process is in place to make sure that we dont have
1613
+ # and crazy masks such as 255.254.255.0
1614
+ hostmask = 1
1615
+ 32.times do
1616
+ check = packed_netmask & hostmask
1617
+ if ( check != 0)
1618
+ hostmask = hostmask >> 1
1619
+ unless ( (packed_netmask ^ hostmask) == all_f)
1620
+ raise "#{netmask} is not a valid IPv4 netmask."
1621
+ end
1622
+ break
1623
+ else
1624
+ hostmask = hostmask << 1
1625
+ hostmask = hostmask | 1
1626
+ end
1627
+ end
1628
+
1629
+ else
1630
+ # remove '/' if present
1631
+ if (netmask =~ /^\// )
1632
+ netmask[0] = " "
1633
+ netmask.lstrip!
1634
+ end
1635
+
1636
+ # check if we have any non numeric characters
1637
+ if (netmask =~ /\D/)
1638
+ raise "#{netmask} is not a valid IPv4 netmask."
1639
+ end
1640
+
1641
+ # are we between 1 and 32 inclusive
1642
+ if (netmask.kind_of? String)
1643
+ netmask = netmask.to_i
1644
+ end
1645
+
1646
+ if ( (netmask > 32) || (netmask == 0) )
1647
+ raise "#{netmask} is not a valid IPv4 netmask."
1648
+ end
1649
+
1650
+ packed_netmask = all_f ^ (all_f >> netmask)
1651
+ end
1652
+
1653
+ return(packed_netmask)
1654
+ end
1655
+ module_function :validate_ipv4_netmask
1656
+
1657
+ #======================================#
1658
+ #
1659
+ #======================================#
1660
+
1661
+
1662
+ #==============================================================================#
1663
+ # validate_ipv6_addr()
1664
+ #==============================================================================#
1665
+
1666
+ # DEPRICATED - USE validate_ip_addr INSTEAD
1667
+ # Validate IPv6 addresses. The address should not contain a netmask.
1668
+ #
1669
+ # - Arguments:
1670
+ # * IPv6 address
1671
+ #
1672
+ # - Returns:
1673
+ # * packed IP on valid, or exception on error.
1674
+ #
1675
+ # Example:
1676
+ # IPAdmin.validate_ipv6_addr('fec0::')
1677
+ #
1678
+ def validate_ipv6_addr(ip)
1679
+ # is this a string?
1680
+ unless (ip.kind_of? String)
1681
+ raise ArgumentError, "Expected String, but #{ip.class} provided."
1682
+ end
1683
+
1684
+ # check validity of characters in the addr
1685
+ if ( (ip =~ /:{3,}?/ ) || (ip =~ /[^0-9a-fA-F:]/) )
1686
+ raise "#{ip} is not a valid IPv6 address."
1687
+ end
1688
+
1689
+ # look for a '::' to see if this address is in shorthand
1690
+ # if found, split on it & make sure that we have at most
1691
+ # two elements
1692
+ if (ip =~ /::/)
1693
+ shrthnd = ip.split( /::/ )
1694
+ unless ( (shrthnd.length > 0) && (shrthnd.length < 3) )
1695
+ raise "#{ip} is not a valid IPv6 address."
1696
+ end
1697
+ end
1698
+
1699
+ if (shrthnd)
1700
+ # if shorthand, we should have between 1 and 7
1701
+ # hex fields
1702
+ hex_fields = []
1703
+ shrthnd.each do |x|
1704
+ elements = x.split( /:/ )
1705
+ elements.each {|x| hex_fields.push(x)}
1706
+ end
1707
+ if ( (hex_fields.length < 1) || (hex_fields.length > 7) )
1708
+ raise "#{ip} is not a valid IPv6 address."
1709
+ end
1710
+
1711
+ else
1712
+ # since no shorthand notation was detected we should
1713
+ # have exactly 8 hex fields
1714
+ hex_fields = ip.split( /:/ )
1715
+ if (hex_fields.length != 8)
1716
+ raise "#{ip} is not a valid IPv6 address."
1717
+ end
1718
+
1719
+ end
1720
+
1721
+ # check that we have no more than 4 characters in each
1722
+ # hex field
1723
+ hex_fields.each do |x|
1724
+ if (x.length > 4)
1725
+ raise "#{ip} is not a valid IPv6 address."
1726
+ end
1727
+ end
1728
+
1729
+ packed_ip = IPAdmin.pack_ipv6_addr(ip)
1730
+ return(packed_ip)
1731
+ end
1732
+ module_function :validate_ipv6_addr
1733
+
1734
+ #======================================#
1735
+ #
1736
+ #======================================#
1737
+
1738
+
1739
+ #==============================================================================#
1740
+ # validate_ipv6_netmask()
1741
+ #==============================================================================#
1742
+
1743
+ # DEPRICATED - USE validate_ip_netmask INSTEAD
1744
+ # Validate IPv6 netmask.
1745
+ #
1746
+ # - Arguments:
1747
+ # * IPv6 netmask in cidr notation
1748
+ #
1749
+ # - Returns:
1750
+ # * packed netmask on valid, or exception on error.
1751
+ #
1752
+ # Example:
1753
+ # IPAdmin.validate_ipv6_netmask('64')
1754
+ # IPAdmin.validate_ipv6_netmask('/64')
1755
+ # IPAdmin.validate_ipv6_netmask(64)
1756
+ #
1757
+ def validate_ipv6_netmask(netmask)
1758
+ all_f = 2**128 -1
1759
+
1760
+ # remove '/' if present
1761
+ if (netmask =~ /^\// )
1762
+ netmask[0] = " "
1763
+ netmask.lstrip!
1764
+ end
1765
+
1766
+ if (netmask =~ /\D/)
1767
+ raise "#{netmask} is not a valid IPv6 netmask."
1768
+
1769
+ else
1770
+ # are we between 1 and 128 inclusive
1771
+ if (netmask.kind_of? String)
1772
+ netmask = netmask.to_i
1773
+ end
1774
+
1775
+ if ( (netmask > 128) || (netmask == 0) )
1776
+ raise "#{netmask} is not a valid IPv6 netmask."
1777
+ end
1778
+
1779
+ end
1780
+
1781
+ packed_netmask = all_f ^ (all_f >> netmask)
1782
+ return(packed_netmask)
1783
+ end
1784
+ module_function :validate_ipv6_netmask
1785
+
1786
+ #======================================#
1787
+ #
1788
+ #======================================#
1789
+
1790
+
1791
+ #==============================================================================#
1792
+ # pack_ipv4_addr()
1793
+ #==============================================================================#
1794
+
1795
+ # DEPRICATED - USE pack_ip_addr INSTEAD
1796
+ # Convert IPv4 addresses into an integer. No attempt at
1797
+ # validation is performed.
1798
+ #
1799
+ # - Arguments:
1800
+ # * IPv4 address
1801
+ #
1802
+ # - Returns:
1803
+ # * packed IPv4 address or exception on error.
1804
+ #
1805
+ # Example:
1806
+ # packed = IPAdmin.pack_ipv4_addr('192.168.1.1')
1807
+ #
1808
+ def pack_ipv4_addr(ip)
1809
+
1810
+ # is this a string?
1811
+ unless (ip.kind_of? String)
1812
+ raise ArgumentError, "Expected String, but #{ip.class} provided."
1813
+ end
1814
+
1815
+ # pack our ip
1816
+ octets = ip.split( /\./ ).reverse
1817
+ packed_ip = 0
1818
+
1819
+ (0..3).each do |x|
1820
+ octets[x] = (octets[x]).to_i
1821
+ octets[x] = octets[x] << 8*x
1822
+ packed_ip = packed_ip | octets[x]
1823
+ end
1824
+
1825
+ return(packed_ip)
1826
+ end
1827
+ module_function :pack_ipv4_addr
1828
+
1829
+ #======================================#
1830
+ #
1831
+ #======================================#
1832
+
1833
+
1834
+ #==============================================================================#
1835
+ # pack_ipv4_netmask()
1836
+ #==============================================================================#
1837
+
1838
+ # DEPRICATED - USE pack_ip_netmask INSTEAD
1839
+ # Convert IPv4 netmask into an integer. Only very basic
1840
+ # validation is performed.
1841
+ #
1842
+ # - Arguments:
1843
+ # * IPv4 netmask in cidr or extended notation
1844
+ #
1845
+ # - Returns:
1846
+ # * packed IPv4 netmask or exception on error.
1847
+ #
1848
+ # Example:
1849
+ # packed = IPAdmin.pack_ipv4_netmask('255.255.255.0')
1850
+ # packed = IPAdmin.pack_ipv4_netmask('24')
1851
+ # packed = IPAdmin.pack_ipv4_netmask('/24')
1852
+ # packed = IPAdmin.pack_ipv4_netmask(24)
1853
+ #
1854
+ def pack_ipv4_netmask(netmask)
1855
+ all_f = 2**32-1
1856
+
1857
+ # is this a CIDR or Extended mask?
1858
+ if(netmask =~ /\./)
1859
+ # pack extended mask
1860
+ begin
1861
+ packed_netmask = pack_ipv4_addr(netmask)
1862
+ rescue Exception
1863
+ raise "#{netmask} is not a valid IPv4 netmask."
1864
+ end
1865
+
1866
+ else
1867
+ # remove '/' if present
1868
+ if (netmask =~ /^\// )
1869
+ netmask[0] = " "
1870
+ netmask.lstrip!
1871
+ end
1872
+
1873
+ # check if we have any non numeric characters
1874
+ if (netmask =~ /\D/)
1875
+ raise "#{netmask} is not a valid IPv4 netmask."
1876
+ end
1877
+
1878
+ if (netmask.kind_of? String)
1879
+ netmask = netmask.to_i
1880
+ end
1881
+
1882
+ packed_netmask = all_f ^ (all_f >> netmask)
1883
+ end
1884
+
1885
+ return(packed_netmask)
1886
+ end
1887
+ module_function :pack_ipv4_netmask
1888
+
1889
+ #======================================#
1890
+ #
1891
+ #======================================#
1892
+
1893
+
1894
+ #==============================================================================#
1895
+ # pack_ipv6_addr()
1896
+ #==============================================================================#
1897
+
1898
+ # DEPRICATED - USE pack_ip_addr INSTEAD
1899
+ # Convert IPv6 addresses into an integer. No attempt at
1900
+ # validation is performed.
1901
+ #
1902
+ # - Arguments:
1903
+ # * IPv6 address
1904
+ #
1905
+ # - Returns:
1906
+ # * packed IPv6 address or exception on error.
1907
+ #
1908
+ # Example:
1909
+ # packed = IPAdmin.pack_ipv6_addr('fec0::1')
1910
+ #
1911
+ def pack_ipv6_addr(ip)
1912
+ # is this a string?
1913
+ unless (ip.kind_of? String)
1914
+ raise ArgumentError, "Expected String, but #{ip.class} provided."
1915
+ end
1916
+
1917
+ # look for a '::' to see if this address is in shorthand
1918
+ # if found, split on it
1919
+ hex_fields = []
1920
+ if (ip =~ /::/)
1921
+ shrthnd = ip.split( /::/ )
1922
+ if (ip =~ /^::/)
1923
+ sec_half = shrthnd[1].split( /:/ )
1924
+ zero_pads = 8 - sec_half.length
1925
+ (1..zero_pads).each {hex_fields.push('0')}
1926
+ sec_half.each {|field| hex_fields.push(field)}
1927
+ elsif (ip =~ /::$/)
1928
+ hex_fields = shrthnd[0].split( /:/ )
1929
+ zero_pads = 8 - hex_fields.length
1930
+ (1..zero_pads).each {hex_fields.push('0')}
1931
+ else
1932
+ first_half = shrthnd[0].split( /:/ )
1933
+ sec_half = shrthnd[1].split( /:/ )
1934
+ zero_pads = 8 - (first_half.length + sec_half.length)
1935
+ first_half.each {|field| hex_fields.push(field)}
1936
+ (1..zero_pads).each {hex_fields.push('0')}
1937
+ sec_half.each {|field| hex_fields.push(field)}
1938
+ end
1939
+
1940
+ else
1941
+ hex_fields = ip.split( /:/ )
1942
+ end
1943
+
1944
+ # pack
1945
+ hex_fields.reverse!
1946
+ packed_ip = 0
1947
+ (0..7).each do |x|
1948
+ hex = hex_fields[x]
1949
+ base16 = hex.to_i(16)
1950
+
1951
+ base16 = base16 << 16*x
1952
+ packed_ip = packed_ip | base16
1953
+ end
1954
+
1955
+
1956
+ return(packed_ip)
1957
+ end
1958
+ module_function :pack_ipv6_addr
1959
+
1960
+ #======================================#
1961
+ #
1962
+ #======================================#
1963
+
1964
+
1965
+ #==============================================================================#
1966
+ # pack_ipv6_netmask()
1967
+ #==============================================================================#
1968
+
1969
+ # DEPRICATED - USE pack_ip_netmask INSTEAD
1970
+ # Convert IPv6 netmask into an integer. Only very basic
1971
+ # validation is performed.
1972
+ #
1973
+ # - Arguments:
1974
+ # * IPv6 netmask in cidr notation
1975
+ #
1976
+ # - Returns:
1977
+ # * packed IPv6 netmask or exception on error.
1978
+ #
1979
+ # Example:
1980
+ # packed = IPAdmin.pack_ipv6_netmask('64')
1981
+ # packed = IPAdmin.pack_ipv6_netmask('/64')
1982
+ # packed = IPAdmin.pack_ipv6_netmask(64)
1983
+ #
1984
+ def pack_ipv6_netmask(netmask)
1985
+ all_f = 2**128-1
1986
+
1987
+ # remove '/' if present
1988
+ if (netmask =~ /^\// )
1989
+ netmask[0] = " "
1990
+ netmask.lstrip!
1991
+ end
1992
+
1993
+ if (netmask !~ /\D/)
1994
+ # pack
1995
+ if (netmask.kind_of? String)
1996
+ netmask = netmask.to_i
1997
+ end
1998
+
1999
+ packed_netmask = all_f ^ (all_f >> netmask)
2000
+
2001
+ else
2002
+ raise "#{netmask} is not a valid IPv6 netmask."
2003
+
2004
+ end
2005
+
2006
+ return(packed_netmask)
2007
+ end
2008
+ module_function :pack_ipv6_netmask
2009
+
2010
+ #======================================#
2011
+ #
2012
+ #======================================#
2013
+
2014
+
2015
+ #==============================================================================#
2016
+ # unpack_ipv4_addr()
2017
+ #==============================================================================#
2018
+
2019
+ # DEPRICATED - USE unpack_ip_addr INSTEAD
2020
+ # Unack a packed IPv4 address back into a printable string. No attempt at
2021
+ # validation is performed.
2022
+ #
2023
+ # - Arguments:
2024
+ # * Byte-packed IPv4 address
2025
+ #
2026
+ # - Returns:
2027
+ # * IPv4 address.
2028
+ #
2029
+ # Example:
2030
+ # unpacked = IPAdmin.unpack_ipv4_addr(packed)
2031
+ #
2032
+ def unpack_ipv4_addr(packed_ip)
2033
+ octets = []
2034
+ (0..3).each do |x|
2035
+ octets[x] = packed_ip & 0xFF
2036
+ octets[x] = (octets[x]).to_s
2037
+ packed_ip = packed_ip >> 8
2038
+ end
2039
+
2040
+ octets.reverse!
2041
+ ip = octets.join('.')
2042
+
2043
+ return(ip)
2044
+ end
2045
+ module_function :unpack_ipv4_addr
2046
+
2047
+ #======================================#
2048
+ #
2049
+ #======================================#
2050
+
2051
+
2052
+ #==============================================================================#
2053
+ # unpack_ipv4_netmask()
2054
+ #==============================================================================#
2055
+
2056
+ # DEPRICATED - USE unpack_ip_netmask INSTEAD
2057
+ # Unack a packed IPv4 netmask into a integer representing the number of
2058
+ # bits in the CIDR mask. No attempt at validation is performed.
2059
+ #
2060
+ # - Arguments:
2061
+ # * Byte-packed IPv4 netmask
2062
+ #
2063
+ # - Returns:
2064
+ # * IPv4 netmask as number of bits (cidr format).
2065
+ #
2066
+ # Example:
2067
+ # unpacked = IPAdmin.unpack_ipv4_netmask(packed)
2068
+ #
2069
+ def unpack_ipv4_netmask(packed_mask)
2070
+ mask = 32
2071
+ 32.times do
2072
+ if ( (packed_mask & 1) != 0)
2073
+ break
2074
+ end
2075
+ packed_mask = packed_mask >> 1
2076
+ mask = mask - 1
2077
+ end
2078
+
2079
+ return(mask)
2080
+ end
2081
+ module_function :unpack_ipv4_netmask
2082
+
2083
+ #======================================#
2084
+ #
2085
+ #======================================#
2086
+
2087
+
2088
+ #==============================================================================#
2089
+ # unpack_ipv6_addr()
2090
+ #==============================================================================#
2091
+
2092
+ # DEPRICATED - USE unpack_ip_addr INSTEAD
2093
+ # Unack a packed IPv6 address back into a printable string. No attempt at
2094
+ # validation is performed.
2095
+ #
2096
+ # - Arguments:
2097
+ # * Byte-packed IPv6 address
2098
+ #
2099
+ # - Returns:
2100
+ # * IPv6 address.
2101
+ #
2102
+ # Example:
2103
+ # unpacked = IPAdmin.unpack_ipv6_addr(packed)
2104
+ #
2105
+ def unpack_ipv6_addr(packed_ip)
2106
+ hex_fields = []
2107
+ (0..7).each do |x|
2108
+ hex_fields[x] = packed_ip & 0xFFFF
2109
+ hex_fields[x] = (hex_fields[x]).to_s(16)
2110
+ packed_ip = packed_ip >> 16
2111
+
2112
+ # if hex_fields[x] < 4 characters, then pad with 0's
2113
+ (4 - hex_fields[x].length).times do
2114
+ hex_fields[x] = '0' << hex_fields[x]
2115
+ end
2116
+ end
2117
+
2118
+ hex_fields.reverse!
2119
+ ip = hex_fields.join(':')
2120
+
2121
+ return(ip)
2122
+ end
2123
+ module_function :unpack_ipv6_addr
2124
+
2125
+ #======================================#
2126
+ #
2127
+ #======================================#
2128
+
2129
+
2130
+ #==============================================================================#
2131
+ # unpack_ipv6_netmask()
2132
+ #==============================================================================#
2133
+
2134
+ # DEPRICATED - USE unpack_ip_netmask INSTEAD
2135
+ # Unack a packed IPv6 netmask into a integer representing the number of
2136
+ # bits in the CIDR mask. No attempt at validation is performed.
2137
+ #
2138
+ # - Arguments:
2139
+ # * Byte-packed IPv6 netmask
2140
+ #
2141
+ # - Returns:
2142
+ # * IPv6 netmask as number of bits (cidr format).
2143
+ #
2144
+ # Example:
2145
+ # unpacked = IPAdmin.unpack_ipv6_netmask(packed)
2146
+ #
2147
+ def unpack_ipv6_netmask(packed_mask)
2148
+
2149
+ mask = 128
2150
+ 128.times do
2151
+ if ( (packed_mask & 1) == 1)
2152
+ break
2153
+ end
2154
+ mask = mask - 1
2155
+ packed_mask = packed_mask >> 1
2156
+ end
2157
+
2158
+ return(mask)
2159
+ end
2160
+ module_function :unpack_ipv6_netmask
2161
+
2162
+ #======================================#
2163
+ #
2164
+ #======================================#
2165
+
2166
+ end # module IPAdmin
2167
+ __END__
2168
+