netaddr 1.5.0 → 2.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/lib/methods.rb DELETED
@@ -1,1079 +0,0 @@
1
- =begin rdoc
2
- Copyleft (c) 2006 Dustin Spinhirne
3
-
4
- Licensed under the same terms as Ruby, No Warranty is provided.
5
- =end
6
-
7
- module NetAddr
8
-
9
- #==============================================================================#
10
- # i_to_bits()
11
- #==============================================================================#
12
-
13
- #===Synopsis
14
- #Convert an Integer representing a binary netmask into an Integer representing
15
- #the number of bits in that netmask.
16
- #
17
- # Example:
18
- # NetAddr.i_to_bits(0xfffffffe) => 31
19
- # NetAddr.i_to_bits(0xffffffffffffffff0000000000000000) => 64
20
- #
21
- #===Arguments:
22
- #* netmask_int = Integer representing a binary netmask
23
- #
24
- #===Returns:
25
- #* Integer
26
- #
27
- def i_to_bits(netmask_int)
28
-
29
- # validate netmask_int
30
- raise ArgumentError, "Integer expected for argument 'netmask_int', " +
31
- "but #{netmask_int.class} provided." if (!netmask_int.kind_of?(Integer))
32
-
33
-
34
- return( mask_to_bits(netmask_int) )
35
- end
36
- module_function :i_to_bits
37
-
38
- #==============================================================================#
39
- # i_to_ip()
40
- #==============================================================================#
41
-
42
- #===Synopsis
43
- #Convert an Integer into an IP address. This method will attempt to auto-detect the IP version
44
- #if not provided, however, a slight speed increase is realized if version is provided.
45
- #
46
- # Example:
47
- # NetAddr.i_to_ip(3232235906) => "192.168.1.130"
48
- # NetAddr.i_to_ip(0xffff0000000000000000000000000001, :Version => 6) => "ffff:0000:0000:0000:0000:0000:0000:0001"
49
- #
50
- #===Arguments:
51
- #* ip_int = IP address as an Integer
52
- #* options = Hash with the following keys:
53
- # :Version -- IP version - Integer (optional)
54
- # :IPv4Mapped -- if true, unpack IPv6 as an IPv4 mapped address (optional)
55
- #
56
- #===Returns:
57
- #* String
58
- #
59
- def i_to_ip(ip_int, options=nil)
60
- known_args = [:Version, :IPv4Mapped]
61
- ipv4_mapped = false
62
- version = nil
63
-
64
- # validate options
65
- if (options)
66
- raise ArgumentError, "Hash expected for argument 'options' but #{options.class} provided." if (!options.kind_of?(Hash))
67
- NetAddr.validate_args(options.keys,known_args)
68
-
69
- if (options.has_key?(:Version))
70
- version = options[:Version]
71
- if (version != 4 && version != 6)
72
- raise VersionError, ":Version should be 4 or 6, but was '#{version}'."
73
- end
74
- end
75
-
76
- if (options.has_key?(:IPv4Mapped) && options[:IPv4Mapped] == true)
77
- ipv4_mapped = true
78
- end
79
- end
80
-
81
- # validate & unpack
82
- raise ArgumentError, "Integer expected for argument 'ip_int', " +
83
- "but #{ip_int.class} provided." if (!ip_int.kind_of?(Integer))
84
- version = validate_ip_int(ip_int, version)
85
- ip = ip_int_to_str(ip_int, version, ipv4_mapped)
86
-
87
- return(ip)
88
- end
89
- module_function :i_to_ip
90
-
91
- #==============================================================================#
92
- # ip_to_i()
93
- #==============================================================================#
94
-
95
- #===Synopsis
96
- #Convert IP addresses into an Integer. This method will attempt to auto-detect the IP version
97
- #if not provided, however a slight speed increase is realized if version is provided.
98
- #
99
- # Example:
100
- # NetAddr.ip_to_i('192.168.1.1') => 3232235777
101
- # NetAddr.ip_to_i('ffff::1', :Version => 6) => 340277174624079928635746076935438991361
102
- # NetAddr.ip_to_i('::192.168.1.1') => 3232235777
103
- #
104
- #===Arguments:
105
- #* ip = IP address as a String
106
- #* options = Hash with the following keys:
107
- # :Version -- IP version - Integer
108
- #
109
- #===Returns:
110
- #* Integer
111
- #
112
- def ip_to_i(ip, options=nil)
113
- known_args = [:Version]
114
- to_validate = {}
115
- version = nil
116
-
117
- # validate options
118
- if (options)
119
- raise ArgumentError, "Hash expected for argument 'options' but #{options.class} provided." if (!options.kind_of?(Hash))
120
- validate_args(options.keys,known_args)
121
-
122
- if (options.has_key?(:Version))
123
- version = options[:Version]
124
- to_validate[:Version] = version
125
- if (version != 4 && version != 6)
126
- raise VersionError, ":Version should be 4 or 6, but was '#{version}'."
127
- end
128
- end
129
- end
130
-
131
- if ( ip.kind_of?(String) )
132
- version = detect_ip_version(ip) if (!version)
133
- validate_ip_str(ip,version)
134
- ip_int = ip_str_to_int(ip,version)
135
-
136
- else
137
- raise ArgumentError, "String expected for argument 'ip' but #{ip.class} provided."
138
- end
139
-
140
- return(ip_int)
141
- end
142
- module_function :ip_to_i
143
-
144
- #==============================================================================#
145
- # merge()
146
- #==============================================================================#
147
-
148
- #===Synopsis
149
- #Given a list of CIDR addresses or NetAddr::CIDR objects,
150
- #merge (summarize) them in the most efficient way possible. Summarization
151
- #will only occur when the newly created supernets will not result in the
152
- #'creation' of new IP space. For example the following blocks
153
- #(192.168.0.0/24, 192.168.1.0/24, and 192.168.2.0/24) would be summarized into
154
- #192.168.0.0/23 and 192.168.2.0/24 rather than into 192.168.0.0/22
155
- #
156
- #I have designed this with enough flexibility so that you can pass in CIDR
157
- #addresses that arent even related (ex. 192.168.1.0/26, 192.168.1.64/27, 192.168.1.96/27
158
- #10.1.0.0/26, 10.1.0.64/26) and they will be merged properly (ie 192.168.1.0/25,
159
- #and 10.1.0.0/25 would be returned).
160
- #
161
- #If the :Objectify option is enabled, then any summary addresses returned will
162
- #contain the original CIDRs used to create them within the tag value :Subnets
163
- #(ie. cidr_x.tag[:Subnets] would be an Array of the CIDRs used to create cidr_x)
164
- #
165
- # Example:
166
- # cidr1 = NetAddr::CIDR.create('192.168.1.0/27')
167
- # cidr2 = NetAddr::CIDR.create('192.168.1.32/27')
168
- # NetAddr.merge([cidr1,cidr2])
169
- # ip_net_range = NetAddr.range('192.168.35.0','192.168.39.255',:Inclusive => true, :Objectify => true)
170
- # NetAddr.merge(ip_net_range, :Objectify => true)
171
- #
172
- #===Arguments:
173
- #* list = Array of CIDR addresses as Strings, or an Array of NetAddr::CIDR objects
174
- #* options = Hash with the following keys:
175
- # :Objectify -- if true, return NetAddr::CIDR objects
176
- # :Short -- if true, return IPv6 addresses in short-hand notation
177
- #
178
- #===Returns:
179
- #* Array of CIDR addresses or NetAddr::CIDR objects
180
- #
181
- def merge(list,options=nil)
182
- known_args = [:Objectify, :Short]
183
- short = false
184
- objectify = false
185
- verbose = false
186
-
187
- # validate list
188
- raise ArgumentError, "Array expected for argument 'list' but #{list.class} provided." if (!list.kind_of?(Array) )
189
-
190
- # validate options
191
- if (options)
192
- raise ArgumentError, "Hash expected for argument 'options' but #{options.class} provided." if (!options.kind_of?(Hash) )
193
- NetAddr.validate_args(options.keys,known_args)
194
-
195
- if (options.has_key?(:Objectify) && options[:Objectify] == true)
196
- objectify = true
197
- end
198
-
199
- if (options.has_key?(:Short) && options[:Short] == true)
200
- short = true
201
- end
202
- end
203
-
204
- # make sure all are valid types of the same IP version
205
- v4_list = []
206
- v6_list = []
207
- list.each do |obj|
208
- if (!obj.kind_of?(NetAddr::CIDR))
209
- begin
210
- obj = NetAddr::CIDR.create(obj)
211
- rescue Exception => error
212
- raise ArgumentError, "One of the provided CIDR addresses raised the following " +
213
- "errors: #{error}"
214
- end
215
- end
216
-
217
- if (obj.version == 4)
218
- v4_list.push(obj)
219
- else
220
- v6_list.push(obj)
221
- end
222
- end
223
-
224
- # summarize
225
- v4_summary = []
226
- v6_summary = []
227
- if (v4_list.length != 0)
228
- v4_summary = NetAddr.cidr_summarize(v4_list)
229
- end
230
-
231
- if (v6_list.length != 0)
232
- v6_summary = NetAddr.cidr_summarize(v6_list)
233
- end
234
-
235
- # decide what to return
236
- summarized_list = []
237
- if (!objectify)
238
- summarized_list = []
239
- if (v4_summary.length != 0)
240
- v4_summary.each {|x| summarized_list.push(x.desc())}
241
- end
242
-
243
- if (v6_summary.length != 0)
244
- v6_summary.each {|x| summarized_list.push(x.desc(:Short => short))}
245
- end
246
-
247
- else
248
- summarized_list.concat(v4_summary) if (v4_summary.length != 0)
249
- summarized_list.concat(v6_summary) if (v6_summary.length != 0)
250
- end
251
-
252
- return(summarized_list)
253
- end
254
- module_function :merge
255
-
256
- #==============================================================================#
257
- # minimum_size()
258
- #==============================================================================#
259
-
260
- #===Synopsis
261
- #Given the number of IP addresses required in a subnet, return the minimum
262
- #netmask (bits by default) required for that subnet. IP version is assumed to be 4 unless specified otherwise.
263
- #
264
- # Example:
265
- # NetAddr.minimum_size(14) => 28
266
- # NetAddr.minimum_size(65536, :Version => 6) => 112
267
- #
268
- #===Arguments:
269
- #* ipcount = IP count as an Integer
270
- #* options = Hash with the following keys:
271
- # :Extended -- If true, then return the netmask, as a String, in extended format (IPv4 only y.y.y.y)
272
- # :Version -- IP version - Integer
273
- #
274
- #===Returns:
275
- #* Integer or String
276
- #
277
- def minimum_size(ipcount, options=nil)
278
- version = 4
279
- extended = false
280
- known_args = [:Version, :Extended]
281
-
282
- # validate ipcount
283
- raise ArgumentError, "Integer expected for argument 'ipcount' but #{ipcount.class} provided." if (!ipcount.kind_of?(Integer))
284
-
285
- # validate options
286
- if (options)
287
- raise ArgumentError, "Hash expected for argument 'options' but #{options.class} provided." if (!options.kind_of?(Hash))
288
-
289
- NetAddr.validate_args(options.keys,known_args)
290
-
291
- if (options.has_key?(:Version))
292
- version = options[:Version]
293
- end
294
-
295
- if (options.has_key?(:Extended) && options[:Extended] == true)
296
- extended = true
297
- end
298
- end
299
-
300
- return( ip_count_to_size(ipcount,version,extended) )
301
- end
302
- module_function :minimum_size
303
-
304
- #==============================================================================#
305
- # netmask_to_i()
306
- #==============================================================================#
307
-
308
- #===Synopsis
309
- #Convert IP netmask into an Integer. Netmask may be in either CIDR (/yy) or
310
- #extended (y.y.y.y) format. CIDR formatted netmasks may either
311
- #be a String or an Integer. IP version defaults to 4. It may be necessary
312
- #to specify the version if an IPv6 netmask of /32 or smaller is provided.
313
- #
314
- # Example:
315
- # NetAddr.netmask_to_i('255.255.255.0') => 4294967040
316
- # NetAddr.netmask_to_i('24') => 4294967040
317
- # NetAddr.netmask_to_i(24) => 4294967040
318
- # NetAddr.netmask_to_i('/24') => 4294967040
319
- # NetAddr.netmask_to_i('32', :Version => 6) => 340282366841710300949110269838224261120
320
- #
321
- #===Arguments
322
- #* netmask = Netmask as a String or Integer
323
- #* options = Hash with the following keys:
324
- # :Version -- IP version - Integer
325
- #
326
- #===Returns:
327
- #* Integer
328
- #
329
- def netmask_to_i(netmask, options=nil)
330
- known_args = [:Version]
331
- version = 4
332
- netmask_int = nil
333
-
334
- # validate options
335
- if (options)
336
- raise ArgumentError, "Hash expected for argument 'options' but #{options.class} provided." if (!options.kind_of?(Hash))
337
- NetAddr.validate_args(options.keys,known_args)
338
-
339
- if (options.has_key?(:Version))
340
- version = options[:Version]
341
- if (version != 4 && version != 6)
342
- raise VersionError, ":Version should be 4 or 6, but was '#{version}'."
343
- end
344
- end
345
- end
346
-
347
- if (netmask.kind_of?(String))
348
- validate_netmask_str(netmask, version)
349
- netmask_int = netmask_str_to_int(netmask,version)
350
-
351
- elsif (netmask.kind_of?(Integer))
352
- validate_netmask_int(netmask, version, true)
353
- netmask_int = bits_to_mask(netmask,version)
354
-
355
- else
356
- raise ArgumentError, "String or Integer expected for argument 'netmask', " +
357
- "but #{netmask.class} provided." if (!netmask.kind_of?(Integer) && !netmask.kind_of?(String))
358
- end
359
-
360
- return(netmask_int)
361
- end
362
- module_function :netmask_to_i
363
-
364
- #==============================================================================#
365
- # range()
366
- #==============================================================================#
367
-
368
- #===Synopsis
369
- #Given two CIDR addresses or NetAddr::CIDR objects of the same version,
370
- #return all IP addresses between them. NetAddr.range will use the original IP
371
- #address passed during the initialization of the NetAddr::CIDR objects, or the
372
- #IP address portion of any CIDR addresses passed. The default behavior is to be
373
- #non-inclusive (don't include boundaries as part of returned data).
374
- #
375
- # Example:
376
- # lower = NetAddr::CIDR.create('192.168.35.0')
377
- # upper = NetAddr::CIDR.create('192.168.39.255')
378
- # NetAddr.range(lower,upper, :Limit => 10, :Bitstep => 32)
379
- # NetAddr.range('192.168.35.0','192.168.39.255', :Inclusive => true)
380
- # NetAddr.range('192.168.35.0','192.168.39.255', :Inclusive => true, :Size => true)
381
- #
382
- #===Arguments:
383
- #* lower = Lower boundary CIDR as a String or NetAddr::CIDR object
384
- #* upper = Upper boundary CIDR as a String or NetAddr::CIDR object
385
- #* options = Hash with the following keys:
386
- # :Bitstep -- enumerate in X sized steps - Integer
387
- # :Inclusive -- if true, include boundaries in returned data
388
- # :Limit -- limit returned list to X number of items - Integer
389
- # :Objectify -- if true, return CIDR objects
390
- # :Short -- if true, return IPv6 addresses in short-hand notation
391
- # :Size -- if true, return the number of addresses in this range, but not the addresses themselves
392
- #
393
- #===Returns:
394
- #* Array of Strings or NetAddr::CIDR objects, or an Integer
395
- #
396
- #===Note:
397
- #If you do not need all of the fancy options in this method, then please consider
398
- #using the standard Ruby Range class as shown below.
399
- #
400
- # Example:
401
- # start = NetAddr::CIDR.create('192.168.1.0')
402
- # fin = NetAddr::CIDR.create('192.168.2.3')
403
- # (start..fin).each {|addr| puts addr.desc}
404
- #
405
- def range(lower, upper, options=nil)
406
- known_args = [:Bitstep, :Inclusive, :Limit, :Objectify, :Short, :Size]
407
- list = []
408
- bitstep = 1
409
- objectify = false
410
- short = false
411
- size_only = false
412
- inclusive = false
413
- limit = nil
414
-
415
- # if lower/upper are not CIDR objects, then attempt to create
416
- # cidr objects from them
417
- if ( !lower.kind_of?(NetAddr::CIDR) )
418
- begin
419
- lower = NetAddr::CIDR.create(lower)
420
- rescue Exception => error
421
- raise ArgumentError, "Argument 'lower' raised the following " +
422
- "errors: #{error}"
423
- end
424
- end
425
-
426
- if ( !upper.kind_of?(NetAddr::CIDR))
427
- begin
428
- upper = NetAddr::CIDR.create(upper)
429
- rescue Exception => error
430
- raise ArgumentError, "Argument 'upper' raised the following " +
431
- "errors: #{error}"
432
- end
433
- end
434
-
435
- # validate options
436
- if (options)
437
- raise ArgumentError, "Hash expected for argument 'options' but #{options.class} provided." if (!options.kind_of?(Hash))
438
- NetAddr.validate_args(options.keys,known_args)
439
-
440
- if( options.has_key?(:Bitstep) )
441
- bitstep = options[:Bitstep]
442
- end
443
-
444
- if( options.has_key?(:Objectify) && options[:Objectify] == true )
445
- objectify = true
446
- end
447
-
448
- if( options.has_key?(:Short) && options[:Short] == true )
449
- short = true
450
- end
451
-
452
- if( options.has_key?(:Size) && options[:Size] == true )
453
- size_only = true
454
- end
455
-
456
- if( options.has_key?(:Inclusive) && options[:Inclusive] == true )
457
- inclusive = true
458
- end
459
-
460
- if( options.has_key?(:Limit) )
461
- limit = options[:Limit]
462
- end
463
- end
464
-
465
- # check version, store & sort
466
- if (lower.version == upper.version)
467
- version = lower.version
468
- boundaries = [lower.to_i(:ip), upper.to_i(:ip)]
469
- boundaries.sort
470
- else
471
- raise VersionError, "Provided NetAddr::CIDR objects are of different IP versions."
472
- end
473
-
474
- # dump our range
475
- if (!inclusive)
476
- my_ip = boundaries[0] + 1
477
- end_ip = boundaries[1]
478
- else
479
- my_ip = boundaries[0]
480
- end_ip = boundaries[1] + 1
481
- end
482
-
483
- if (!size_only)
484
- until (my_ip >= end_ip)
485
- if (!objectify)
486
- my_ip_s = ip_int_to_str(my_ip, version)
487
- my_ips = shorten(my_ips) if (short && version == 6)
488
- list.push(my_ip_s)
489
- else
490
- list.push( cidr_build(version,my_ip) )
491
- end
492
-
493
- my_ip = my_ip + bitstep
494
- if (limit)
495
- limit = limit -1
496
- break if (limit == 0)
497
- end
498
- end
499
- else
500
- list = end_ip - my_ip
501
- end
502
-
503
- return(list)
504
- end
505
- module_function :range
506
-
507
- #==============================================================================#
508
- # shorten()
509
- #==============================================================================#
510
-
511
- #===Synopsis
512
- #Take a standard IPv6 address and format it in short-hand notation.
513
- #The address should not contain a netmask.
514
- #
515
- # Example:
516
- # NetAddr.shorten('fec0:0000:0000:0000:0000:0000:0000:0001') => "fec0::1"
517
- #
518
- #===Arguments:
519
- #* addr = String
520
- #
521
- #===Returns:
522
- #* String
523
- #
524
- def shorten(addr)
525
-
526
- # is this a string?
527
- if (!addr.kind_of? String)
528
- raise ArgumentError, "Expected String, but #{addr.class} provided."
529
- end
530
-
531
- validate_ip_str(addr, 6)
532
-
533
- # make sure this isnt already shorthand
534
- if (addr =~ /::/)
535
- return(addr)
536
- end
537
-
538
- # split into fields
539
- fields = addr.split(":")
540
-
541
- # check last field for ipv4-mapped addr
542
- if (fields.last() =~ /\./ )
543
- ipv4_mapped = fields.pop()
544
- end
545
-
546
- # look for most consecutive '0' fields
547
- start_field,end_field = nil,nil
548
- start_end = []
549
- consecutive,longest = 0,0
550
-
551
- (0..(fields.length-1)).each do |x|
552
- fields[x] = fields[x].to_i(16)
553
-
554
- if (fields[x] == 0)
555
- if (!start_field)
556
- start_field = x
557
- end_field = x
558
- else
559
- end_field = x
560
- end
561
- consecutive += 1
562
- else
563
- if (start_field)
564
- if (consecutive > longest)
565
- longest = consecutive
566
- start_end = [start_field,end_field]
567
- start_field,end_field = nil,nil
568
- end
569
- consecutive = 0
570
- end
571
- end
572
-
573
- fields[x] = fields[x].to_s(16)
574
- end
575
-
576
- # if our longest set of 0's is at the end, then start & end fields
577
- # are already set. if not, then make start & end fields the ones we've
578
- # stored away in start_end
579
- if (consecutive > longest)
580
- longest = consecutive
581
- else
582
- start_field = start_end[0]
583
- end_field = start_end[1]
584
- end
585
-
586
- if (longest > 1)
587
- fields[start_field] = ''
588
- start_field += 1
589
- fields.slice!(start_field..end_field)
590
- end
591
- fields.push(ipv4_mapped) if (ipv4_mapped)
592
- short = fields.join(':')
593
- short << ':' if (short =~ /:$/)
594
-
595
- return(short)
596
- end
597
- module_function :shorten
598
-
599
- #==============================================================================#
600
- # sort()
601
- #==============================================================================#
602
-
603
- #===Synopsis
604
- #Sort a list of CIDR addresses or NetAddr::CIDR objects,
605
- #
606
- # Example:
607
- # cidr1 = NetAddr::CIDR.create('192.168.1.32/27')
608
- # cidr2 = NetAddr::CIDR.create('192.168.1.0/27')
609
- # NetAddr.sort([cidr1,cidr2])
610
- # NetAddr.sort(['192.168.1.32/27','192.168.1.0/27','192.168.2.0/24'], :Desc => true)
611
- #
612
- #===Arguments:
613
- #* list = Array of CIDR addresses as Strings, or Array of NetAddr::CIDR objects
614
- #* options = Hash with the following keys:
615
- # :ByMask -- if true, sorts based on the netmask length
616
- # :Desc -- if true, return results in descending order
617
- #
618
- #===Returns:
619
- #* Array of Strings, or Array of NetAddr::CIDR objects
620
- #
621
- def sort(list, options=nil)
622
- # make sure list is an array
623
- if ( !list.kind_of?(Array) )
624
- raise ArgumentError, "Array of NetAddr::CIDR or NetStruct " +
625
- "objects expected, but #{list.class} provided."
626
- end
627
-
628
- desc = false
629
- by_mask = false
630
- # validate options
631
- if (options)
632
- known_args = [:Desc, :ByMask]
633
- raise ArgumentError, "Hash expected for argument 'options' but #{options.class} provided." if (!options.kind_of?(Hash))
634
- NetAddr.validate_args(options.keys,known_args)
635
-
636
- if( options.has_key?(:Desc) && options[:Desc] == true )
637
- desc = true
638
- end
639
-
640
- if( options.has_key?(:ByMask) && options[:ByMask] == true )
641
- by_mask = true
642
- end
643
-
644
- end
645
-
646
- # make sure all are valid types of the same IP version
647
- version = nil
648
- cidr_hash = {}
649
- list.each do |cidr|
650
- if (!cidr.kind_of?(NetAddr::CIDR))
651
- begin
652
- new_cidr = NetAddr::CIDR.create(cidr)
653
- rescue Exception => error
654
- raise ArgumentError, "An element of the provided Array " +
655
- "raised the following errors: #{error}"
656
- end
657
- else
658
- new_cidr = cidr
659
- end
660
- cidr_hash[new_cidr] = cidr
661
-
662
- version = new_cidr.version if (!version)
663
- unless (new_cidr.version == version)
664
- raise VersionError, "Provided CIDR addresses must all be of the same IP version."
665
- end
666
- end
667
-
668
- # perform sort
669
- if (by_mask)
670
- sorted_list = netmask_sort(cidr_hash.keys, desc)
671
- else
672
- sorted_list = cidr_sort(cidr_hash.keys, desc)
673
- end
674
-
675
- # return original values passed
676
- ret_list = []
677
- sorted_list.each {|x| ret_list.push(cidr_hash[x])}
678
-
679
- return(ret_list)
680
- end
681
- module_function :sort
682
-
683
- #==============================================================================#
684
- # supernets()
685
- #==============================================================================#
686
-
687
- #===Synopsis
688
- #Given a list of CIDR addresses or NetAddr::CIDR objects,
689
- #return only the top-level supernet CIDR addresses.
690
- #
691
- #
692
- #If the :Objectify option is enabled, then returned CIDR objects will
693
- #store the more specific CIDRs (i.e. subnets of those CIDRs) within the tag value :Subnets
694
- #For example, cidr_x.tag[:Subnets] would be an Array of CIDR subnets of cidr_x.
695
- #
696
- # Example:
697
- # NetAddr.supernets(['192.168.0.0', '192.168.0.1', '192.168.0.0/31'])
698
- #
699
- #===Arguments:
700
- #* list = Array of CIDR addresses as Strings, or an Array of NetAddr::CIDR objects
701
- #* options = Hash with the following keys:
702
- # :Objectify -- if true, return NetAddr::CIDR objects
703
- # :Short -- if true, return IPv6 addresses in short-hand notation
704
- #
705
- #===Returns:
706
- #* Array of CIDR addresses or NetAddr::CIDR objects
707
- #
708
- def supernets(list,options=nil)
709
- known_args = [:Objectify, :Short]
710
- short = false
711
- objectify = false
712
- verbose = false
713
-
714
- # validate list
715
- raise ArgumentError, "Array expected for argument 'list' but #{list.class} provided." if (!list.kind_of?(Array) )
716
-
717
- # validate options
718
- if (options)
719
- raise ArgumentError, "Hash expected for argument 'options' but #{options.class} provided." if (!options.kind_of?(Hash) )
720
- NetAddr.validate_args(options.keys,known_args)
721
-
722
- if (options.has_key?(:Objectify) && options[:Objectify] == true)
723
- objectify = true
724
- end
725
-
726
- if (options.has_key?(:Short) && options[:Short] == true)
727
- short = true
728
- end
729
- end
730
-
731
- # make sure all are valid types of the same IP version
732
- v4_list = []
733
- v6_list = []
734
- list.each do |obj|
735
- if (!obj.kind_of?(NetAddr::CIDR))
736
- begin
737
- obj = NetAddr::CIDR.create(obj)
738
- rescue Exception => error
739
- raise ArgumentError, "One of the provided CIDR addresses raised the following " +
740
- "errors: #{error}"
741
- end
742
- end
743
-
744
- if (obj.version == 4)
745
- v4_list.push(obj)
746
- else
747
- v6_list.push(obj)
748
- end
749
- end
750
-
751
- # do summary calcs
752
- v4_summary = []
753
- v6_summary = []
754
- if (v4_list.length != 0)
755
- v4_summary = NetAddr.cidr_supernets(v4_list)
756
- end
757
-
758
- if (v6_list.length != 0)
759
- v6_summary = NetAddr.cidr_supernets(v6_list)
760
- end
761
-
762
- # decide what to return
763
- summarized_list = []
764
- if (!objectify)
765
- summarized_list = []
766
- if (v4_summary.length != 0)
767
- v4_summary.each {|x| summarized_list.push(x.desc())}
768
- end
769
-
770
- if (v6_summary.length != 0)
771
- v6_summary.each {|x| summarized_list.push(x.desc(:Short => short))}
772
- end
773
-
774
- else
775
- summarized_list.concat(v4_summary) if (v4_summary.length != 0)
776
- summarized_list.concat(v6_summary) if (v6_summary.length != 0)
777
- end
778
-
779
- return(summarized_list)
780
- end
781
- module_function :supernets
782
-
783
- #==============================================================================#
784
- # unshorten()
785
- #==============================================================================#
786
-
787
- #===Synopsis
788
- #Take an IPv6 address in short-hand format, and expand it into standard
789
- #notation. The address should not contain a netmask.
790
- #
791
- # Example:
792
- # NetAddr.unshorten('fec0::1') => "fec0:0000:0000:0000:0000:0000:0000:0001"
793
- #
794
- #===Arguments:
795
- #* ip = CIDR address as a String
796
- #
797
- #===Returns:
798
- #* String
799
- #
800
- def unshorten(ip)
801
-
802
- # is this a string?
803
- if (!ip.kind_of? String)
804
- raise ArgumentError, "Expected String, but #{ip.class} provided."
805
- end
806
-
807
- validate_ip_str(ip, 6)
808
- ipv4_mapped = true if (ip =~ /\./)
809
-
810
- ip_int = ip_to_i(ip, :Version => 6)
811
- if (!ipv4_mapped)
812
- long = ip_int_to_str(ip_int, 6)
813
- else
814
- long = ip_int_to_str(ip_int, 6, true)
815
- end
816
-
817
- return(long)
818
- end
819
- module_function :unshorten
820
-
821
- #==============================================================================#
822
- # validate_eui()
823
- #==============================================================================#
824
-
825
- #===Synopsis
826
- #Validate an EUI-48 or EUI-64 address. Raises NetAddr::ValidationError on validation failure.
827
- #
828
- # Example:
829
- # NetAddr.validate_eui('01-00-5e-12-34-56') => true
830
- #
831
- # - Arguments
832
- #* eui = EUI address as a String
833
- #
834
- #===Returns:
835
- #* True
836
- #
837
- def validate_eui(eui)
838
- if (eui.kind_of?(String))
839
- # check for invalid characters
840
- if (eui =~ /[^0-9a-fA-F\.\-\:]/)
841
- raise ValidationError, "#{eui} is invalid (contains invalid characters)."
842
- end
843
-
844
- # split on formatting characters & check lengths
845
- if (eui =~ /\-/)
846
- fields = eui.split('-')
847
- if (fields.length != 6 && fields.length != 8)
848
- raise ValidationError, "#{eui} is invalid (unrecognized formatting)."
849
- end
850
- fields.each {|x| raise ValidationError, "#{eui} is invalid (missing characters)." if (x.length != 2)}
851
- elsif (eui =~ /\:/)
852
- fields = eui.split(':')
853
- if (fields.length != 6 && fields.length != 8)
854
- raise ValidationError, "#{eui} is invalid (unrecognized formatting)."
855
- end
856
- fields.each {|x| raise ValidationError, "#{eui} is invalid (missing characters)." if (x.length != 2)}
857
- elsif (eui =~ /\./)
858
- fields = eui.split('.')
859
- if (fields.length != 3 && fields.length != 4)
860
- raise ValidationError, "#{eui} is invalid (unrecognized formatting)."
861
- end
862
- fields.each {|x| raise ValidationError, "#{eui} is invalid (missing characters)." if (x.length != 4)}
863
- else
864
- raise ValidationError, "#{eui} is invalid (unrecognized formatting)."
865
- end
866
-
867
- else
868
- raise ArgumentError, "EUI address should be a String, but was a#{eui.class}."
869
- end
870
- return(true)
871
- end
872
- module_function :validate_eui
873
-
874
- #==============================================================================#
875
- # validate_ip_addr()
876
- #==============================================================================#
877
-
878
- #===Synopsis
879
- #Validate an IP address. The address should not contain a netmask.
880
- #This method will attempt to auto-detect the IP version
881
- #if not provided, however a slight speed increase is realized if version is provided.
882
- #Raises NetAddr::ValidationError on validation failure.
883
- #
884
- # Example:
885
- # NetAddr.validate_ip_addr('192.168.1.1') => true
886
- # NetAddr.validate_ip_addr('ffff::1', :Version => 6) => true
887
- # NetAddr.validate_ip_addr('::192.168.1.1') => true
888
- # NetAddr.validate_ip_addr(0xFFFFFF) => true
889
- # NetAddr.validate_ip_addr(2**128-1) => true
890
- # NetAddr.validate_ip_addr(2**32-1, :Version => 4) => true
891
- #
892
- #===Arguments
893
- #* ip = IP address as a String or Integer
894
- #* options = Hash with the following keys:
895
- # :Version -- IP version - Integer (optional)
896
- #
897
- #===Returns:
898
- #* True
899
- #
900
- def validate_ip_addr(ip, options=nil)
901
- known_args = [:Version]
902
- version = nil
903
-
904
- # validate options
905
- if (options)
906
- raise ArgumentError, "Hash expected for argument 'options' but #{options.class} provided." if (!options.kind_of?(Hash))
907
- NetAddr.validate_args(options.keys,known_args)
908
-
909
- if (options.has_key?(:Version))
910
- version = options[:Version]
911
- if (version != 4 && version != 6)
912
- raise ArgumentError, ":Version should be 4 or 6, but was '#{version}'."
913
- end
914
- end
915
- end
916
-
917
- if ( ip.kind_of?(String) )
918
- version = NetAddr.detect_ip_version(ip) if (!version)
919
- NetAddr.validate_ip_str(ip,version)
920
-
921
- elsif ( ip.kind_of?(Integer) )
922
- NetAddr.validate_ip_int(ip,version)
923
-
924
- else
925
- raise ArgumentError, "Integer or String expected for argument 'ip' but " +
926
- "#{ip.class} provided." if (!ip.kind_of?(String) && !ip.kind_of?(Integer))
927
- end
928
-
929
- return(true)
930
- end
931
- module_function :validate_ip_addr
932
-
933
- #==============================================================================#
934
- # validate_ip_netmask()
935
- #==============================================================================#
936
-
937
- #===Synopsis
938
- #Validate IP Netmask. Version defaults to 4 if not specified.
939
- #Raises NetAddr::ValidationError on validation failure.
940
- #
941
- # Examples:
942
- # NetAddr.validate_ip_netmask('/32') => true
943
- # NetAddr.validate_ip_netmask(32) => true
944
- # NetAddr.validate_ip_netmask(0xffffffff, :Integer => true) => true
945
- #
946
- #===Arguments:
947
- #* netmask = Netmask as a String or Integer
948
- #* options = Hash with the following keys:
949
- # :Integer -- if true, the provided Netmask is an Integer mask
950
- # :Version -- IP version - Integer (optional)
951
- #
952
- #===Returns:
953
- #* True
954
- #
955
- def validate_ip_netmask(netmask, options=nil)
956
- known_args = [:Integer, :Version]
957
- is_integer = false
958
- version = 4
959
-
960
- # validate options
961
- if (options)
962
- raise ArgumentError, "Hash expected for argument 'options' but #{options.class} provided." if (!options.kind_of?(Hash))
963
- NetAddr.validate_args(options.keys,known_args)
964
-
965
- if (options.has_key?(:Integer) && options[:Integer] == true)
966
- is_integer = true
967
- end
968
-
969
- if (options.has_key?(:Version))
970
- version = options[:Version]
971
- if (version != 4 && version != 6)
972
- raise ArgumentError, ":Version should be 4 or 6, but was '#{version}'."
973
- end
974
- end
975
- end
976
-
977
- # validate netmask
978
- if (netmask.kind_of?(String))
979
- validate_netmask_str(netmask,version)
980
- elsif (netmask.kind_of?(Integer) )
981
- validate_netmask_int(netmask,version,is_integer)
982
- else
983
- raise ArgumentError, "Integer or String expected for argument 'netmask' but " +
984
- "#{netmask.class} provided." if (!netmask.kind_of?(String) && !netmask.kind_of?(Integer))
985
- end
986
-
987
- return(true)
988
- end
989
- module_function :validate_ip_netmask
990
-
991
- #==============================================================================#
992
- # wildcard()
993
- #==============================================================================#
994
-
995
- #===Synopsis
996
- #Convert a wildcard IP into a valid CIDR address. Wildcards must always be at
997
- #the end of the address. Any data located after the first wildcard will be lost.
998
- #Shorthand notation is prohibited for IPv6 addresses.
999
- #IPv6 encoded IPv4 addresses are not currently supported.
1000
- #
1001
- # Examples:
1002
- # NetAddr.wildcard('192.168.*')
1003
- # NetAddr.wildcard('192.168.1.*')
1004
- # NetAddr.wildcard('fec0:*')
1005
- # NetAddr.wildcard('fec0:1:*')
1006
- #
1007
- #===Arguments:
1008
- #* ip = Wildcard IP address as a String
1009
- #
1010
- #===Returns:
1011
- #* CIDR object
1012
- #
1013
- def wildcard(ip)
1014
- version = 4
1015
-
1016
- # do operations per version of address
1017
- if (ip =~ /\./ && ip !~ /:/)
1018
- octets = []
1019
- mask = 0
1020
-
1021
- ip.split('.').each do |x|
1022
- if (x =~ /\*/)
1023
- break
1024
- end
1025
- octets.push(x)
1026
- end
1027
-
1028
- octets.length.times do
1029
- mask = mask << 8
1030
- mask = mask | 0xff
1031
- end
1032
-
1033
- until (octets.length == 4)
1034
- octets.push('0')
1035
- mask = mask << 8
1036
- end
1037
- ip = octets.join('.')
1038
-
1039
- elsif (ip =~ /:/)
1040
- version = 6
1041
- fields = []
1042
- mask = 0
1043
-
1044
- raise ArgumentError, "IPv6 encoded IPv4 addresses are unsupported." if (ip =~ /\./)
1045
- raise ArgumentError, "Shorthand IPv6 addresses are unsupported." if (ip =~ /::/)
1046
-
1047
- ip.split(':').each do |x|
1048
- if (x =~ /\*/)
1049
- break
1050
- end
1051
- fields.push(x)
1052
- end
1053
-
1054
- fields.length.times do
1055
- mask = mask << 16
1056
- mask = mask | 0xffff
1057
- end
1058
-
1059
- until (fields.length == 8)
1060
- fields.push('0')
1061
- mask = mask << 16
1062
- end
1063
- ip = fields.join(':')
1064
- end
1065
-
1066
- # make & return cidr
1067
- cidr = cidr_build( version, ip_str_to_int(ip,version), mask )
1068
-
1069
- return(cidr)
1070
- end
1071
- module_function :wildcard
1072
-
1073
-
1074
-
1075
-
1076
- end # module NetAddr
1077
-
1078
- __END__
1079
-