ipadmin 0.2.1 → 0.2.2

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