netaddr 1.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of netaddr might be problematic. Click here for more details.

data/Errors ADDED
@@ -0,0 +1,13 @@
1
+ =Error Classes
2
+
3
+ Exception -
4
+ |
5
+ --StandardError -
6
+ |
7
+ --BoundaryError => CIDR or EUI is out of bounds for a valid address
8
+ |
9
+ --ValidationError => CIDR or EUI is not a valid address
10
+ |
11
+ --VersionError => CIDR or EUI is of improper version for requested operation
12
+ |
13
+ |
data/README ADDED
@@ -0,0 +1,202 @@
1
+ =Introduction
2
+
3
+ NetAddr arose through my need as a network engineer for a
4
+ back-end module that could easily handle such advanced tasks
5
+ as automating the subnetting/supernetting
6
+ of IP space, performing calculations on IP CIDR blocks, and other
7
+ various items. At that time there were no modules that could
8
+ do any of the things that I needed, so I set out to create my own.
9
+ Since it proved to be fairly useful to me, I decided to share the
10
+ code with the Ruby community.
11
+
12
+ I have added things that I find immediately useful for me. I am
13
+ open to suggestions if there is something that I could add to make
14
+ your life easier. Comments are also welcome (positive ones in particular).
15
+
16
+ Dustin Spinhirne
17
+
18
+
19
+ =Example Script
20
+
21
+ #!/usr/bin/ruby
22
+
23
+ # A script to parse static routes from a Cisco router.
24
+ # Performs the following tasks:
25
+ # - organizes statics by directly connected next-hop interface
26
+ # - corrects recursive static routes
27
+ # - reports statics with no directly connected next-hop
28
+ # - reports duplicate static routes
29
+ # - reports static routes that overlap directly connected interfaces
30
+ #
31
+ # Change the variable 'infile' to that of the name of the router configuration
32
+ # file that should be parsed.
33
+
34
+ require 'rubygems'
35
+ require_gem 'netaddr'
36
+
37
+ # change this variable to that of the router config file
38
+ infile = 'router_config.txt'
39
+
40
+
41
+ ip_interface_count = 0
42
+ static_route_count = 0
43
+ connected_routes = []
44
+ static_routes = []
45
+ statics = {}
46
+ grouped_routes = {}
47
+ duplicates = []
48
+ recursives = []
49
+ corrected_statics = []
50
+ overlapping_statics = []
51
+
52
+
53
+ # sort through router config. put connected ip interfaces into 'connected_routes'
54
+ # and static routes into 'statics'. Set aside duplicate static routes.
55
+ File.open(infile, 'r') do |file|
56
+ while line = file.gets
57
+ if (line =~/^interface .+/)
58
+ interface = line.chomp
59
+ elsif (line =~ /^\s*ip address/) # connected interface
60
+ ip_interface_count += 1
61
+ addr = line.split('ip address ').join # ['x.x.x.x y.y.y.y']
62
+ cidr = NetAddr::CIDR.create(addr, :Tag => {:interface => interface})
63
+ connected_routes.push(cidr)
64
+ elsif (line =~/^ip route/) # static route
65
+ static_route_count += 1
66
+ elements = line.split(' ') # ['ip','route','x.x.x.x','y.y.y.y','x.x.x.x',]
67
+ ip = elements[2]
68
+ netmask = elements[3]
69
+ nexthop = elements[4]
70
+
71
+ # keep all but loopbacks, nulls, or defaults
72
+ if ( (nexthop !~ /Loopback/) && (nexthop !~ /Null/) && (ip != '0.0.0.0') )
73
+ nexthop_cidr = NetAddr::CIDR.create(nexthop)
74
+ cidr = NetAddr::CIDR.create("#{ip} #{netmask}",
75
+ :Tag => {:route => line, :nexthop => nexthop_cidr})
76
+
77
+ if (!statics.has_key?(cidr.desc))
78
+ statics[cidr.desc] = cidr
79
+ else
80
+ msg = '! overlaps with - ' + statics[cidr.desc].tag[:route] + line + "!\n"
81
+ duplicates.push(msg)
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+
89
+ # look for statics that overlap with a connected interface, and
90
+ # group static routes with their next-hop interface.
91
+ statics.each_key do |desc|
92
+ cidr = statics[desc]
93
+ nexthop = cidr.tag[:nexthop]
94
+ route = cidr.tag[:route]
95
+
96
+ overlaps_with = nil
97
+ nexthop_int = nil
98
+ connected_routes.each do |interface|
99
+ if (interface.contains?(cidr)) # overlapping static
100
+ overlaps_with = interface
101
+ break
102
+ elsif (interface.contains?(nexthop)) # next-hop directly connected
103
+ nexthop_int = interface
104
+ break
105
+ end
106
+ end
107
+
108
+ if (nexthop_int)
109
+ key = "#{nexthop_int.tag[:interface]} -> #{nexthop_int.desc}"
110
+ if (grouped_routes.has_key?(key))
111
+ grouped_routes[key].push(route)
112
+ else
113
+ grouped_routes[key] = [route]
114
+ end
115
+ static_routes.push(cidr)
116
+ elsif (overlaps_with)
117
+ overlap = "! overlaps with: #{overlaps_with.tag[:interface]} -> #{overlaps_with.desc}"
118
+ overlap << "\n#{route}!\n"
119
+ overlapping_statics.push(overlap)
120
+ else
121
+ recursives.push(cidr)
122
+ end
123
+ end
124
+
125
+
126
+ # process recursive routes. update next-hop so that it points
127
+ # to next hop of a directly connected ip. remove any that do not point to a
128
+ # directly connected ip. We must continually cycle through the list, as the
129
+ # 'static_routes' tree is constantly updated. We do this until our list of
130
+ # recursive static routes stops getting any shorter
131
+ recursives_count = 0
132
+ until (recursives_count == recursives.length)
133
+ recursives_count = recursives.length
134
+ recursives.each do |cidr|
135
+ nexthop = cidr.tag[:nexthop]
136
+ route = cidr.tag[:route]
137
+
138
+ found = nil
139
+ static_routes.each do |static|
140
+ if (static.contains?(nexthop))
141
+ found = static
142
+ break
143
+ end
144
+ end
145
+
146
+ if (found)
147
+ updated = 'no ' + route
148
+ updated << "ip route #{cidr.network} #{cidr.netmask_ext} #{found.tag[:nexthop].ip}\n"
149
+ updated << "!\n"
150
+ corrected_statics.push(updated)
151
+ static_routes.push(cidr)
152
+ recursives.delete(cidr)
153
+ end
154
+ end
155
+ end
156
+
157
+
158
+ # print results.
159
+ puts "--- STATISTICS ---"
160
+ puts "#{ip_interface_count} Connected IP Interfaces\n#{static_route_count} IP Static Routes\n"
161
+
162
+ print "\n\n"
163
+
164
+ puts "--- OVERLAPPING STATIC ROUTES ---"
165
+ overlapping_statics.each do |overlap|
166
+ puts overlap
167
+ end
168
+
169
+ print "\n\n"
170
+
171
+ puts "--- DUPLICATE STATIC ROUTES ---"
172
+ duplicates.each do |route|
173
+ puts route
174
+ end
175
+
176
+ print "\n\n"
177
+
178
+ puts "--- STATIC ROUTES WITH UPDATED NEXT-HOP ---"
179
+ corrected_statics.each do |route|
180
+ puts route
181
+ end
182
+
183
+ print "\n\n"
184
+
185
+ puts "--- STATIC ROUTES WITH UNKNOWN NEXT-HOP ---"
186
+ recursives.each do |cidr|
187
+ puts cidr.tag[:route]
188
+ end
189
+
190
+ print "\n\n"
191
+
192
+ puts "--- STATIC ROUTES GROUPED BY NEXT-HOP INTERFACE ---"
193
+ grouped_routes.each_key do |interface|
194
+ routes = grouped_routes[interface]
195
+ puts interface
196
+ routes.each do |route|
197
+ puts route
198
+ end
199
+ print "\n"
200
+ end
201
+
202
+ __END__
data/lib/cidr.rb ADDED
@@ -0,0 +1,1925 @@
1
+ =begin rdoc
2
+ Copyright (c) 2006 Dustin Spinhirne (www.spinhirne.com)
3
+
4
+ Licensed under the same terms as Ruby, No Warranty is provided.
5
+ =end
6
+
7
+ module NetAddr
8
+
9
+ #=CIDR - Classless Inter-Domain Routing
10
+ #
11
+ #A class & series of methods for creating and manipulating CIDR network
12
+ #addresses. Both IPv4 and IPv6 are supported.
13
+ #
14
+ #This class accepts a CIDR address, via the CIDR.create method,
15
+ #in (x.x.x.x/yy or xxxx::/yy) format for IPv4 and IPv6, or (x.x.x.x/y.y.y.y) for IPv4.
16
+ #CIDR.create then creates either a CIDRv4 or CIDRv6 object. An optional tag hash may be
17
+ #provided with each CIDR as a way of adding custom labels to the object.
18
+ #
19
+ #Upon initialization, the IP version is auto-detected and assigned to the
20
+ #object. The original IP/Netmask passed within the CIDR is stored and then
21
+ #used to determine the confines of the CIDR block. Various properties of the
22
+ #CIDR block are accessible via several different methods. There are also
23
+ #methods for modifying the CIDR or creating new derivative CIDR's.
24
+ #
25
+ #An example CIDR object is as follows:
26
+ # NetAddr::CIDR.create('192.168.1.20/24')
27
+ #
28
+ #This would create a CIDR object (192.168.1.0/24) with the following properties:
29
+ # version = 4
30
+ # base network = 192.168.1.0
31
+ # ip address = 192.168.1.20
32
+ # netmask = /24 (255.255.255.0)
33
+ # size = 256 IP addresses
34
+ # broadcast = 192.168.1.255
35
+ #
36
+ #You can see how the CIDR object is based around the entire IP space
37
+ #defined by the provided IP/Netmask pair, and not necessarily the individual
38
+ #IP address itself.
39
+ #
40
+ class CIDR
41
+
42
+ private_class_method :new
43
+
44
+ #==============================================================================#
45
+ # attr_reader/attr_writer
46
+ #==============================================================================#
47
+
48
+ # IP version 4 or 6.
49
+ attr_reader :version
50
+
51
+ # Hash of custom tags. Should be in the format tag => value.
52
+ attr_reader :tag
53
+
54
+ # Integer of either 32 or 128 bits in length, with all bits set to 1
55
+ attr_reader :all_f
56
+
57
+ # Hash of custom tags. Should be in the format tag => value.
58
+ #
59
+ # Example:
60
+ # cidr4.tag[:name] = 'IPv4 CIDR'
61
+ # puts cidr4.tag[:name]
62
+ #
63
+ def tag=(new_tag)
64
+ if (!new_tag.kind_of? Hash)
65
+ raise ArgumentError, "Expected Hash, but #{new_tag.class} provided."
66
+ end
67
+ @tag = new_tag
68
+ end
69
+
70
+ #==============================================================================#
71
+ # create()
72
+ #==============================================================================#
73
+
74
+ #===Synopsis
75
+ #Create a new CIDRv4 or CIDRv6 object.
76
+ #CIDR formatted netmasks take precedence over extended formatted ones.
77
+ #CIDR address defaults to a host network (/32 or /128) if netmask not provided.
78
+ #:PackedNetmask takes precedence over netmask given within CIDR addresses.
79
+ #Version will be auto-detected if not specified.
80
+ #
81
+ # cidr4 = NetAddr::CIDR.create('192.168.1.1/24')
82
+ # cidr4 = NetAddr::CIDR.create('192.168.1.1 255.255.255.0')
83
+ # cidr4_2 = NetAddr::CIDR.create(0x0a010001,
84
+ # :PackedNetmask => 0xffffff00
85
+ # :Version => 4)
86
+ # cidr6 = NetAddr::CIDR.create('fec0::/64')
87
+ # cidr6 = NetAddr::CIDR.create('fec0::/64',
88
+ # :Tag => {'interface' => 'g0/1'})
89
+ # cidr6_2 = NetAddr::CIDR.create('::ffff:192.168.1.1/96')
90
+ #
91
+ #===Arguments:
92
+ #* CIDR address as a String, or a packed IP address as an Integer
93
+ #* Optional Hash with the following keys:
94
+ # :PackedNetmask -- Integer representation of an IP Netmask (optional)
95
+ # :Version -- IP version - Integer (optional)
96
+ # :Tag -- Custom descriptor tag - Hash, tag => value. (optional)
97
+ #
98
+ def CIDR.create(addr, options=nil)
99
+ known_args = [:PackedNetmask, :Version, :Tag]
100
+ cidr = nil
101
+ packed_ip = nil
102
+ version = nil
103
+
104
+ # validate addr arg
105
+ if (addr.kind_of?(String))
106
+ cidr = addr
107
+ elsif (addr.kind_of?(Integer))
108
+ packed_ip = addr
109
+ else
110
+ raise ArgumentError, "String or Integer expected for argument 'addr' but #{addr.class} provided."
111
+ end
112
+
113
+ # validate options
114
+ if (options)
115
+ raise ArgumentError, "Hash expected for argument 'options' but " +
116
+ "#{options.class} provided." if (!options.kind_of?(Hash) )
117
+ NetAddr.validate_args(options.keys,known_args)
118
+ if (options.has_key?(:Version))
119
+ version = options[:Version]
120
+ options.delete(:Version)
121
+ if (version != 4 && version != 6)
122
+ raise VersionError, ":Version should be 4 or 6, but was '#{version}'."
123
+ end
124
+ end
125
+ end
126
+
127
+ # attempt to determine version if not provided
128
+ if (!version)
129
+ if (packed_ip)
130
+ if (packed_ip < 2**32)
131
+ version = 4
132
+ else
133
+ version = 6
134
+ end
135
+ else
136
+ if (cidr =~ /\./ && cidr !~ /:/)
137
+ version = 4
138
+ elsif (cidr =~ /:/)
139
+ version = 6
140
+ end
141
+ end
142
+ end
143
+
144
+ # create CIDRvX object
145
+ if (version == 4)
146
+ return(NetAddr::CIDRv4.new(addr, options))
147
+ elsif (version == 6)
148
+ return(NetAddr::CIDRv6.new(addr, options))
149
+ else
150
+ raise ArgumentError, "IP version omitted or could not be detected."
151
+ end
152
+
153
+ end
154
+
155
+ #==============================================================================#
156
+ # arpa()
157
+ #==============================================================================#
158
+
159
+ #===Synopsis
160
+ #Depending on the IP version of the current CIDR,
161
+ #return either an in-addr.arpa. or ip6.arpa. string. The netmask will be used
162
+ #to determine the length of the returned string.
163
+ #
164
+ # cidr4 = NetAddr::CIDR.create('192.168.1.1/24')
165
+ # cidr6 = NetAddr::CIDR.create('fec0::/64')
166
+ # puts "arpa for #{cidr4.desc()} is #{cidr4.arpa}"
167
+ # puts "arpa for #{cidr6.desc(:Short => true)} is #{cidr6.arpa}"
168
+ #
169
+ #===Arguments:
170
+ #* none
171
+ #
172
+ #===Returns:
173
+ #* String
174
+ #
175
+ def arpa()
176
+
177
+ base = self.ip()
178
+ netmask = self.bits()
179
+
180
+ if (@version == 4)
181
+ net = base.split('.')
182
+
183
+ if (netmask)
184
+ while (netmask < 32)
185
+ net.pop
186
+ netmask = netmask + 8
187
+ end
188
+ end
189
+
190
+ arpa = net.reverse.join('.')
191
+ arpa << ".in-addr.arpa."
192
+
193
+ elsif (@version == 6)
194
+ fields = base.split(':')
195
+ net = []
196
+ fields.each do |field|
197
+ (field.split("")).each do |x|
198
+ net.push(x)
199
+ end
200
+ end
201
+
202
+ if (netmask)
203
+ while (netmask < 128)
204
+ net.pop
205
+ netmask = netmask + 4
206
+ end
207
+ end
208
+
209
+ arpa = net.reverse.join('.')
210
+ arpa << ".ip6.arpa."
211
+
212
+ end
213
+
214
+ return(arpa)
215
+ end
216
+
217
+ #==============================================================================#
218
+ # bits()
219
+ #==============================================================================#
220
+
221
+ #===Synopsis
222
+ #Provide number of bits in Netmask.
223
+ #
224
+ # cidr4 = NetAddr::CIDR.create('192.168.1.1/24')
225
+ # cidr6 = NetAddr::CIDR.create('fec0::/64')
226
+ # puts "cidr4 netmask in bits #{cidr4.bits()}"
227
+ # puts "cidr6 netmask in bits #{cidr6.bits()}"
228
+ #
229
+ #===Arguments:
230
+ #* none
231
+ #
232
+ #===Returns:
233
+ #* Integer.
234
+ #
235
+ def bits()
236
+ return(NetAddr.unpack_ip_netmask(@netmask))
237
+ end
238
+
239
+ #==============================================================================#
240
+ # cmp()
241
+ #==============================================================================#
242
+
243
+ #===Synopsis
244
+ #Compare the current CIDR with a provided CIDR and return:
245
+ #* 1 if the current CIDR contains (is supernet of) the provided CIDR
246
+ #* 0 if the current CIDR and the provided CIDR are equal (base address and netmask are equal)
247
+ #* -1 if the current CIDR is contained by (is subnet of) the provided CIDR
248
+ #* nil if the two CIDR addresses are unrelated
249
+ #
250
+ # comp1 = cidr4.cmp(cidr4_2)
251
+ # comp2 = cidr4.cmp('192.168.1.0/26')
252
+ #
253
+ #===Arguments:
254
+ #* CIDR address or NetAddr::CIDR object
255
+ #
256
+ #===Returns:
257
+ #* Integer or nil
258
+ #
259
+ def cmp(cidr)
260
+ if (!cidr.kind_of?(NetAddr::CIDR))
261
+ begin
262
+ cidr = NetAddr::CIDR.create(cidr)
263
+ rescue Exception => error
264
+ raise ArgumentError, "Provided argument raised the following " +
265
+ "errors: #{error}"
266
+ end
267
+ end
268
+
269
+ if (cidr.version != @version)
270
+ raise VersionError, "Attempted to compare a version #{cidr.version} CIDR " +
271
+ "with a version #{@version} CIDR."
272
+ end
273
+
274
+ # compare
275
+ comparasin = nil
276
+ if ( (@network == cidr.packed_network))
277
+ if (@netmask == cidr.packed_netmask)
278
+ comparasin = 0
279
+ elsif(@netmask < cidr.packed_netmask)
280
+ comparasin = 1
281
+ elsif(@netmask > cidr.packed_netmask)
282
+ comparasin = -1
283
+ end
284
+
285
+ elsif( (cidr.packed_network | @hostmask) == (@network | @hostmask) )
286
+ comparasin = 1
287
+ elsif( (cidr.packed_network | cidr.packed_hostmask) == (@network | cidr.packed_hostmask) )
288
+ comparasin = -1
289
+ end
290
+
291
+ return(comparasin)
292
+ end
293
+
294
+ #==============================================================================#
295
+ # contains?()
296
+ #==============================================================================#
297
+
298
+ #===Synopsis
299
+ #Determines if this CIDR contains (is supernet of)
300
+ #the provided CIDR address or NetAddr::CIDR object.
301
+ #
302
+ # cidr4 = NetAddr::CIDR.create('192.168.1.1/24')
303
+ # cidr6 = NetAddr::CIDR.create('fec0::/64')
304
+ # cidr6_2 = NetAddr::CIDR.create('fec0::/96')
305
+ # puts "#{cidr4.desc} contains 192.168.1.2" if ( cidr4.contains?('192.168.1.2') )
306
+ # puts "#{cidr6.desc} contains #{cidr6_2.desc(:Short => true)}" if ( cidr6.contains?(cidr6_2) )
307
+ #
308
+ #===Arguments:
309
+ #* CIDR address or NetAddr::CIDR object
310
+ #
311
+ #===Returns:
312
+ #* true or false
313
+ #
314
+ def contains?(cidr)
315
+ contains = false
316
+
317
+ if (!cidr.kind_of?(NetAddr::CIDR))
318
+ begin
319
+ cidr = NetAddr::CIDR.create(cidr)
320
+ rescue Exception => error
321
+ raise ArgumentError, "Provided argument raised the following " +
322
+ "errors: #{error}"
323
+ end
324
+ end
325
+
326
+ network = cidr.packed_network
327
+ netmask = cidr.packed_netmask
328
+
329
+
330
+ if (cidr.version != @version)
331
+ raise VersionError, "Attempted to compare a version #{cidr.version} CIDR " +
332
+ "with a version #{@version} CIDR."
333
+ end
334
+
335
+ # if network == @network, then we have to go by netmask length
336
+ # else we can tell by or'ing network and @network by @hostmask
337
+ # and comparing the results
338
+ if (network == @network)
339
+ contains = true if (netmask > @netmask)
340
+
341
+ else
342
+ if ( (network | @hostmask) == (@network | @hostmask) )
343
+ contains = true
344
+ end
345
+ end
346
+
347
+ return(contains)
348
+ end
349
+
350
+ #==============================================================================#
351
+ # desc()
352
+ #==============================================================================#
353
+
354
+ #===Synopsis
355
+ #Returns network/netmask in CIDR format.
356
+ #
357
+ # cidr4 = NetAddr::CIDR.create('192.168.1.1/24')
358
+ # cidr6 = NetAddr::CIDR.create('fec0::/64')
359
+ # puts cidr4.desc(:IP => true)
360
+ # puts "cidr4 description #{cidr4.desc()}"
361
+ # puts "cidr6 description #{cidr6.desc()}"
362
+ # puts "cidr6 short-hand description #{cidr6.desc(:Short => true)}"
363
+ #
364
+ #===Arguments:
365
+ #* Optional hash with the following keys:
366
+ # :IP -- if true, return the original ip/netmask passed during initialization (optional)
367
+ # :Short -- if true, return IPv6 addresses in short-hand notation (optional)
368
+ #
369
+ #===Returns:
370
+ #* String
371
+ #
372
+ def desc(options=nil)
373
+ known_args = [:IP, :Short]
374
+ short = false
375
+ orig_ip = false
376
+
377
+ if (options)
378
+ if (!options.kind_of? Hash)
379
+ raise ArgumentError, "Expected Hash, but #{options.class} provided."
380
+ end
381
+ NetAddr.validate_args(options.keys,known_args)
382
+
383
+ if (options.has_key?(:Short) && options[:Short] == true)
384
+ short = true
385
+ end
386
+
387
+ if (options.has_key?(:IP) && options[:IP] == true)
388
+ orig_ip = true
389
+ end
390
+ end
391
+
392
+ if (!orig_ip)
393
+ ip = NetAddr.unpack_ip_addr(@network, :Version => @version)
394
+ else
395
+ ip = NetAddr.unpack_ip_addr(@ip, :Version => @version)
396
+ end
397
+ ip = NetAddr.shorten(ip) if (short && @version == 6)
398
+ mask = NetAddr.unpack_ip_netmask(@netmask)
399
+
400
+ return("#{ip}/#{mask}")
401
+ end
402
+
403
+ #==============================================================================#
404
+ # enumerate()
405
+ #==============================================================================#
406
+
407
+ #===Synopsis
408
+ #Provide all IP addresses contained within the IP space of this CIDR.
409
+ #
410
+ # cidr4 = NetAddr::CIDR.create('192.168.1.1/24')
411
+ # cidr6 = NetAddr::CIDR.create('fec0::/64')
412
+ # puts "first 4 cidr4 addresses (bitstep 32)"
413
+ # cidr4.enumerate(:Limit => 4, :Bitstep => 32).each {|x| puts " #{x}"}
414
+ # puts "first 4 cidr6 addresses (bitstep 32)"
415
+ # cidr6.enumerate(:Limit => 4, :Bitstep => 32, :Objectify => true).each {|x| puts " #{x.desc}"}
416
+ #
417
+ #===Arguments:
418
+ #* Optional Hash with the following keys:
419
+ # :Bitstep -- enumerate in X sized steps - Integer (optional)
420
+ # :Limit -- limit returned list to X number of items - Integer (optional)
421
+ # :Objectify -- if true, return NetAddr::CIDR objects (optional)
422
+ # :Short -- if true, return IPv6 addresses in short-hand notation (optional)
423
+ #
424
+ #===Returns:
425
+ #* Array of Strings, or Array of NetAddr::CIDR objects
426
+ #
427
+ def enumerate(options=nil)
428
+ known_args = [:Bitstep, :Limit, :Objectify, :Short]
429
+ bitstep = 1
430
+ objectify = false
431
+ limit = nil
432
+ short = false
433
+
434
+ if (options)
435
+ if (!options.kind_of? Hash)
436
+ raise ArgumentError, "Expected Hash, but #{options.class} provided."
437
+ end
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?(:Limit) )
449
+ limit = options[:Limit]
450
+ end
451
+
452
+ if( options.has_key?(:Short) && options[:Short] == true )
453
+ short = true
454
+ end
455
+ end
456
+
457
+ list = []
458
+ my_ip = @network
459
+ change_mask = @hostmask | my_ip
460
+
461
+ until ( change_mask != (@hostmask | @network) )
462
+ if (!objectify)
463
+ my_ip_s = NetAddr.unpack_ip_addr(my_ip, :Version => @version)
464
+ my_ip_s = NetAddr.shorten(my_ip_s) if (short && @version == 6)
465
+ list.push( my_ip_s )
466
+ else
467
+ list.push( NetAddr::CIDR.create(my_ip, :Version => @version) )
468
+ end
469
+ my_ip = my_ip + bitstep
470
+ change_mask = @hostmask | my_ip
471
+ if (limit)
472
+ limit = limit -1
473
+ break if (limit == 0)
474
+ end
475
+ end
476
+
477
+ return(list)
478
+ end
479
+
480
+ #==============================================================================#
481
+ # eql?
482
+ #==============================================================================#
483
+
484
+ #===Synopsis
485
+ #Return true if the current CIDR and the provided CIDR are equal
486
+ #(base address and netmask are equal).
487
+ #
488
+ # cidr4.eql?('192.168.1.1/24')
489
+ #
490
+ #===Arguments:
491
+ #* CIDR address or NetAddr::CIDR object
492
+ #
493
+ #===Returns:
494
+ #* true or false
495
+ #
496
+ def eql?(cidr)
497
+ is_eql = false
498
+
499
+ if (!cidr.kind_of?(NetAddr::CIDR))
500
+ begin
501
+ cidr = NetAddr::CIDR.create(cidr)
502
+ rescue Exception => error
503
+ raise ArgumentError, "Provided argument raised the following " +
504
+ "errors: #{error}"
505
+ end
506
+ end
507
+
508
+ if (cidr.version != @version)
509
+ raise VersionError, "Attempted to compare a version #{cidr.version} CIDR " +
510
+ "with a version #{@version} CIDR."
511
+ end
512
+
513
+ is_eql = true if (self.packed_network == cidr.packed_network && self.packed_netmask == cidr.packed_netmask)
514
+
515
+ return(is_eql)
516
+ end
517
+
518
+ #==============================================================================#
519
+ # fill_in()
520
+ #==============================================================================#
521
+
522
+ #===Synopsis
523
+ #Given a list of subnets of the current CIDR, return a new list with any
524
+ #holes (missing subnets) filled in.
525
+ #
526
+ # subnets = cidr4.fill_in(['192.168.1.0/27','192.168.1.64/26','192.168.1.128/25'])
527
+ # subnets = cidr4.fill_in(:List => ['192.168.1.0/27','192.168.1.64/26'], :Objectify => true)
528
+ #
529
+ #===Arguments:
530
+ #* Array of CIDR addresses, or Array of NetAddr::CIDR objects
531
+ #* Optional Hash with the following keys:
532
+ # :Objectify -- if true, return NetAddr::CIDR objects (optional)
533
+ # :Short -- if true, return IPv6 addresses in short-hand notation (optional)
534
+ #
535
+ #===Returns:
536
+ #* Array of CIDR Strings, or an Array of NetAddr::CIDR objects
537
+ #
538
+
539
+ def fill_in(list, options=nil)
540
+ known_args = [:Objectify, :Short]
541
+ short = false
542
+ objectify = false
543
+
544
+ # validate list
545
+ raise ArgumentError, "Array expected for argument 'list' but #{list.class} provided." if (!list.kind_of?(Array) )
546
+
547
+ # validate options
548
+ if (options)
549
+ raise ArgumentError, "Hash expected for argument 'options' but " +
550
+ "#{options.class} provided." if (!options.kind_of?(Hash) )
551
+ NetAddr.validate_args(options.keys,known_args)
552
+
553
+ if (options.has_key?(:Short) && options[:Short] == true)
554
+ short = true
555
+ end
556
+
557
+ if (options.has_key?(:Objectify) && options[:Objectify] == true)
558
+ objectify = true
559
+ end
560
+ end
561
+
562
+ # validate each cidr and store in cidr_list
563
+ cidr_list = []
564
+ list.each do |obj|
565
+ if (!obj.kind_of?(NetAddr::CIDR))
566
+ begin
567
+ obj = NetAddr::CIDR.create(obj)
568
+ rescue Exception => error
569
+ aise ArgumentError, "A provided CIDR raised the following " +
570
+ "errors: #{error}"
571
+ end
572
+ end
573
+
574
+ if (!obj.version == self.version)
575
+ raise VersionError, "#{obj.desc(:Short => true)} is not a version #{self.version} address."
576
+ end
577
+
578
+ # make sure we contain the cidr
579
+ if ( self.contains?(obj) == false )
580
+ raise "#{obj.desc(:Short => true)} does not fit " +
581
+ "within the bounds of #{self.desc(:Short => true)}."
582
+ end
583
+ cidr_list.push(obj)
584
+ end
585
+
586
+ # sort our cidr's and see what is missing
587
+ complete_list = []
588
+ expected = self.packed_network
589
+ NetAddr.sort(cidr_list).each do |cidr|
590
+ network = cidr.packed_network
591
+ bitstep = (@all_f + 1) - cidr.packed_netmask
592
+
593
+ if (network > expected)
594
+ num_ips_missing = network - expected
595
+ sub_list = make_subnets_from_base_and_ip_count(expected,num_ips_missing)
596
+ complete_list.concat(sub_list)
597
+ elsif (network < expected)
598
+ next
599
+ end
600
+ complete_list.push(NetAddr::CIDR.create(network,
601
+ :PackedNetmask => cidr.packed_netmask,
602
+ :Version => self.version))
603
+ expected = network + bitstep
604
+ end
605
+
606
+ # if expected is not the next subnet, then we're missing subnets
607
+ # at the end of the cidr
608
+ next_sub = self.next_subnet(:Objectify => true).packed_network
609
+ if (expected != next_sub)
610
+ num_ips_missing = next_sub - expected
611
+ sub_list = make_subnets_from_base_and_ip_count(expected,num_ips_missing)
612
+ complete_list.concat(sub_list)
613
+ end
614
+
615
+ # decide what to return
616
+ if (!objectify)
617
+ subnets = []
618
+ complete_list.each {|entry| subnets.push(entry.desc(:Short => short))}
619
+ return(subnets)
620
+ else
621
+ return(complete_list)
622
+ end
623
+ end
624
+
625
+ #==============================================================================#
626
+ # hostmask_ext()
627
+ #==============================================================================#
628
+
629
+ #===Synopsis
630
+ #Provide IPv4 Hostmask in extended format (y.y.y.y).
631
+ #
632
+ # puts cidr4.hostmask_ext(
633
+ #
634
+ #===Arguments:
635
+ #* none
636
+ #
637
+ #===Returns:
638
+ #* String
639
+ #
640
+ def hostmask_ext()
641
+ if (@version == 4)
642
+ return(NetAddr.unpack_ip_addr(@hostmask, :Version => @version))
643
+ else
644
+ return(nil)
645
+ end
646
+ end
647
+
648
+ #==============================================================================#
649
+ # ip()
650
+ #==============================================================================#
651
+
652
+ #===Synopsis
653
+ #Provide original IP address passed during initialization.
654
+ #
655
+ # puts cidr4.ip()
656
+ # puts cidr4.ip(:Objectify => true).desc
657
+ #
658
+ #===Arguments:
659
+ #* Optional Hash with the following keys:
660
+ # :Objectify -- if true, return NetAddr::CIDR object (optional)
661
+ # :Short -- if true, return IPv6 addresses in short-hand notation (optional)
662
+ #
663
+ #===Returns:
664
+ #* String or NetAddr::CIDR object.
665
+ #
666
+ def ip(options=nil)
667
+ known_args = [:Objectify, :Short]
668
+ objectify = false
669
+ short = false
670
+
671
+ if (options)
672
+ if (!options.kind_of?(Hash))
673
+ raise Argumenterror, "Expected Hash, but " +
674
+ "#{options.class} provided."
675
+ end
676
+ NetAddr.validate_args(options.keys,known_args)
677
+
678
+ if( options.has_key?(:Short) && options[:Short] == true )
679
+ short = true
680
+ end
681
+
682
+ if( options.has_key?(:Objectify) && options[:Objectify] == true )
683
+ objectify = true
684
+ end
685
+ end
686
+
687
+
688
+
689
+ if (!objectify)
690
+ ip = NetAddr.unpack_ip_addr(@ip, :Version => @version)
691
+ ip = NetAddr.shorten(ip) if (short && @version == 6)
692
+ else
693
+ ip = NetAddr::CIDR.create(@ip, :Version => @version)
694
+ end
695
+
696
+ return(ip)
697
+ end
698
+
699
+ #==============================================================================#
700
+ # is_contained?()
701
+ #==============================================================================#
702
+
703
+ #===Synopsis
704
+ #Determines if this CIDR is contained within (is subnet of)
705
+ #the provided CIDR address or NetAddr::CIDR object.
706
+ #
707
+ # cidr4 = NetAddr::CIDR.create('192.168.1.1/24')
708
+ # puts "#{cidr4.desc} is contained within 192.168.0.0/23" if ( cidr4.is_contained?('192.168.0.0/23') )
709
+ #
710
+ #===Arguments:
711
+ #* CIDR address or NetAddr::CIDR object
712
+ #
713
+ #===Returns:
714
+ #* true or false
715
+ #
716
+ def is_contained?(cidr)
717
+ is_contained = false
718
+
719
+ if (!cidr.kind_of?(NetAddr::CIDR))
720
+ begin
721
+ cidr = NetAddr::CIDR.create(cidr)
722
+ rescue Exception => error
723
+ raise ArgumentError, "Provided argument raised the following " +
724
+ "errors: #{error}"
725
+ end
726
+ end
727
+
728
+ if (cidr.version != @version)
729
+ raise VersionError, "Attempted to compare a version #{cidr.version} CIDR " +
730
+ "with a version #{@version} CIDR."
731
+ end
732
+
733
+ network = cidr.packed_network
734
+ netmask = cidr.packed_netmask
735
+ hostmask = cidr.packed_hostmask
736
+
737
+ # if network == @network, then we have to go by netmask length
738
+ # else we can tell by or'ing network and @network by hostmask
739
+ # and comparing the results
740
+ if (network == @network)
741
+ is_contained = true if (@netmask > netmask)
742
+
743
+ else
744
+ if ( (network | hostmask) == (@network | hostmask) )
745
+ is_contained = true
746
+ end
747
+ end
748
+
749
+ return(is_contained)
750
+ end
751
+
752
+ #==============================================================================#
753
+ # last()
754
+ #==============================================================================#
755
+
756
+ #===Synopsis
757
+ #Provide last IP address in this CIDR object. The broadcast() method is
758
+ #aliased to this method, and thus works for IPv6 despite the fact that the
759
+ #IPv6 protocol does not support IP broadcasting
760
+ #
761
+ # puts cidr4.last()
762
+ #
763
+ #===Arguments:
764
+ #* Optional Hash with the following keys:
765
+ # :Objectify -- if true, return NetAddr::CIDR object (optional)
766
+ # :Short -- if true, return IPv6 addresses in short-hand notation (optional)
767
+ #
768
+ #===Returns:
769
+ #* String or NetAddr::CIDR object.
770
+ #
771
+ def last(options=nil)
772
+ known_args = [:Objectify, :Short]
773
+ objectify = false
774
+ short = false
775
+
776
+ if (options)
777
+ if (!options.kind_of?(Hash))
778
+ raise Argumenterror, "Expected Hash, but " +
779
+ "#{options.class} provided."
780
+ end
781
+ NetAddr.validate_args(options.keys,known_args)
782
+
783
+ if( options.has_key?(:Short) && options[:Short] == true )
784
+ short = true
785
+ end
786
+
787
+ if( options.has_key?(:Objectify) && options[:Objectify] == true )
788
+ objectify = true
789
+ end
790
+
791
+ end
792
+
793
+ packed_ip = @network | @hostmask
794
+ if (!objectify)
795
+ ip = NetAddr.unpack_ip_addr(packed_ip, :Version => @version)
796
+ ip = NetAddr.shorten(ip) if (short && !objectify && @version == 6)
797
+ else
798
+ ip = NetAddr::CIDR.create(packed_ip, :Version => @version)
799
+ end
800
+
801
+ return(ip)
802
+ end
803
+
804
+ alias :broadcast :last
805
+
806
+ #==============================================================================#
807
+ # multicast_mac()
808
+ #==============================================================================#
809
+
810
+ #===Synopsis
811
+ #Assuming this CIDR is a valid multicast address (224.0.0.0/4 for IPv4
812
+ #and ff00::/8 for IPv6), return its ethernet MAC address (EUI-48) mapping.
813
+ #MAC address is based on original IP address passed during initialization.
814
+ #
815
+ # mcast = NetAddr::CIDR.create('224.0.0.6')
816
+ # puts mcast.multicast_mac.address
817
+ #
818
+ #===Arguments:
819
+ #* Optional Hash with the following keys:
820
+ # :Objectify -- if true, return EUI objects (optional)
821
+ #
822
+ #===Returns:
823
+ #* String or NetAddr::EUI48 object
824
+ #
825
+ def multicast_mac(options=nil)
826
+ known_args = [:Objectify]
827
+ objectify = false
828
+
829
+ if (options)
830
+ if (!options.kind_of? Hash)
831
+ raise ArgumentError, "Expected Hash, but #{options.class} provided."
832
+ end
833
+ NetAddr.validate_args(options.keys,known_args)
834
+
835
+ if (options.has_key?(:Objectify) && options[:Objectify] == true)
836
+ objectify = true
837
+ end
838
+ end
839
+
840
+ if (@version == 4)
841
+ if (@ip & 0xf0000000 == 0xe0000000)
842
+ # map low order 23-bits of ip to 01:00:5e:00:00:00
843
+ mac = @ip & 0x007fffff | 0x01005e000000
844
+ else
845
+ raise ValidationError, "#{self.ip} is not a valid multicast address. IPv4 multicast " +
846
+ "addresses should be in the range 224.0.0.0/4."
847
+ end
848
+ else
849
+ if (@ip & (0xff << 120) == 0xff << 120)
850
+ # map low order 32-bits of ip to 33:33:00:00:00:00
851
+ mac = @ip & (2**32-1) | 0x333300000000
852
+ else
853
+ raise ValidationError, "#{self.ip} is not a valid multicast address. IPv6 multicast " +
854
+ "addresses should be in the range ff00::/8."
855
+ end
856
+ end
857
+
858
+ eui = NetAddr::EUI48.new(mac)
859
+ eui = eui.address if (!objectify)
860
+
861
+ return(eui)
862
+ end
863
+
864
+ #==============================================================================#
865
+ # netmask()
866
+ #==============================================================================#
867
+
868
+ #===Synopsis
869
+ #Provide netmask in CIDR format (/yy).
870
+ #
871
+ # puts cidr4.netmask()
872
+ #
873
+ #===Arguments:
874
+ #* none
875
+ #
876
+ #===Returns:
877
+ #* String
878
+ #
879
+ def netmask()
880
+ bits = NetAddr.unpack_ip_netmask(@netmask)
881
+ return("/#{bits}")
882
+ end
883
+
884
+ #==============================================================================#
885
+ # netmask_ext()
886
+ #==============================================================================#
887
+
888
+ #===Synopsis
889
+ #Provide IPv4 netmask in extended format (y.y.y.y).
890
+ #
891
+ # puts cidr4.netmask_ext()
892
+ #
893
+ #===Arguments:
894
+ #* none
895
+ #
896
+ #===Returns:
897
+ #* String
898
+ #
899
+ def netmask_ext()
900
+ if (@version == 4)
901
+ return(NetAddr.unpack_ip_addr(@netmask))
902
+ else
903
+ return(nil)
904
+ end
905
+ end
906
+
907
+ #==============================================================================#
908
+ # network()
909
+ #==============================================================================#
910
+
911
+ #===Synopsis
912
+ #Provide base network address.
913
+ #
914
+ # puts cidr4.network()
915
+ #
916
+ #===Arguments:
917
+ #* Optional Hash with the following fields:
918
+ # :Objectify -- if true, return NetAddr::CIDR object (optional)
919
+ # :Short -- if true, return IPv6 addresses in short-hand notation (optional)
920
+ #
921
+ #===Returns:
922
+ #* String or NetAddr::CIDR object.
923
+ #
924
+ def network(options=nil)
925
+ known_args = [:Objectify, :Short]
926
+ objectify = false
927
+ short = false
928
+
929
+ if (options)
930
+ if (!options.kind_of?(Hash))
931
+ raise Argumenterror, "Expected Hash, but " +
932
+ "#{options.class} provided."
933
+ end
934
+ NetAddr.validate_args(options.keys,known_args)
935
+
936
+ if( options.has_key?(:Short) && options[:Short] == true )
937
+ short = true
938
+ end
939
+
940
+ if( options.has_key?(:Objectify) && options[:Objectify] == true )
941
+ objectify = true
942
+ end
943
+ end
944
+
945
+
946
+ if (!objectify)
947
+ ip = NetAddr.unpack_ip_addr(@network, :Version => @version)
948
+ ip = NetAddr.shorten(ip) if (short && @version == 6)
949
+ else
950
+ ip = NetAddr::CIDR.create(@network, :Version => @version)
951
+ end
952
+
953
+ return(ip)
954
+ end
955
+
956
+ alias :base :network
957
+ alias :first :network
958
+
959
+ #==============================================================================#
960
+ # next_ip()
961
+ #==============================================================================#
962
+
963
+ #===Synopsis
964
+ #Provide the next IP following the last available IP within this CIDR object.
965
+ #
966
+ # cidr4 = NetAddr::CIDR.create('192.168.1.1/24')
967
+ # cidr6 = NetAddr::CIDR.create('fec0::/64')
968
+ # puts "cidr4 next subnet #{cidr4.next_subnet()}"
969
+ # puts "cidr6 next subnet #{cidr6.next_subnet(:Short => true)}"
970
+ #
971
+ #===Arguments:
972
+ #* Optional Hash with the following keys:
973
+ # :Bitstep -- step in X sized steps - Integer (optional)
974
+ # :Objectify -- if true, return NetAddr::CIDR object (optional)
975
+ # :Short -- if true, return IPv6 addresses in short-hand notation (optional)
976
+ #
977
+ #===Returns:
978
+ #* String or NetAddr::CIDR object.
979
+ #
980
+ def next_ip(options=nil)
981
+ known_args = [:Bitstep, :Objectify, :Short]
982
+ bitstep = 1
983
+ objectify = false
984
+ short = false
985
+
986
+ if (options)
987
+ if (!options.kind_of?(Hash))
988
+ raise Argumenterror, "Expected Hash, but " +
989
+ "#{options.class} provided."
990
+ end
991
+ NetAddr.validate_args(options.keys,known_args)
992
+
993
+ if( options.has_key?(:Bitstep) )
994
+ bitstep = options[:Bitstep]
995
+ end
996
+
997
+ if( options.has_key?(:Short) && options[:Short] == true )
998
+ short = true
999
+ end
1000
+
1001
+ if( options.has_key?(:Objectify) && options[:Objectify] == true )
1002
+ objectify = true
1003
+ end
1004
+ end
1005
+
1006
+ next_ip = @network + @hostmask + bitstep
1007
+
1008
+ if (next_ip > @all_f)
1009
+ raise BoundaryError, "Returned IP is out of bounds for IPv#{@version}."
1010
+ end
1011
+
1012
+
1013
+ if (!objectify)
1014
+ next_ip = NetAddr.unpack_ip_addr(next_ip, :Version => @version)
1015
+ next_ip = NetAddr.shorten(next_ip) if (short && @version == 6)
1016
+ else
1017
+ next_ip = NetAddr::CIDR.create(next_ip, :Version => @version)
1018
+ end
1019
+
1020
+ return(next_ip)
1021
+ end
1022
+
1023
+ #==============================================================================#
1024
+ # next_subnet()
1025
+ #==============================================================================#
1026
+
1027
+ #===Synopsis
1028
+ #Provide the next subnet following this CIDR object. The next subnet will
1029
+ #be of the same size as the current CIDR object.
1030
+ #
1031
+ # cidr4 = NetAddr::CIDR.create('192.168.1.1/24')
1032
+ # cidr6 = NetAddr::CIDR.create('fec0::/64')
1033
+ # puts "cidr4 next subnet #{cidr4.next_subnet()}"
1034
+ # puts "cidr6 next subnet #{cidr6.next_subnet(:Short => true)}"
1035
+ #
1036
+ #===Arguments:
1037
+ #* Optional Hash with the following keys:
1038
+ # :Bitstep -- step in X sized steps. - Integer (optional)
1039
+ # :Objectify -- if true, return NetAddr::CIDR object (optional)
1040
+ # :Short -- if true, return IPv6 addresses in short-hand notation (optional)
1041
+ #
1042
+ #===Returns:
1043
+ #* String or NetAddr::CIDR object.
1044
+ #
1045
+ def next_subnet(options=nil)
1046
+ known_args = [:Bitstep, :Objectify, :Short]
1047
+ bitstep = 1
1048
+ objectify = false
1049
+ short = false
1050
+
1051
+ if (options)
1052
+ if (!options.kind_of?(Hash))
1053
+ raise Argumenterror, "Expected Hash, but " +
1054
+ "#{options.class} provided."
1055
+ end
1056
+ NetAddr.validate_args(options.keys,known_args)
1057
+
1058
+ if( options.has_key?(:Bitstep) )
1059
+ bitstep = options[:Bitstep]
1060
+ end
1061
+
1062
+ if( options.has_key?(:Short) && options[:Short] == true )
1063
+ short = true
1064
+ end
1065
+
1066
+ if( options.has_key?(:Objectify) && options[:Objectify] == true )
1067
+ objectify = true
1068
+ end
1069
+ end
1070
+
1071
+ bitstep = bitstep * (2**(@max_bits - self.bits) )
1072
+ next_sub = @network + bitstep
1073
+
1074
+ if (next_sub > @all_f)
1075
+ raise BoundaryError, "Returned subnet is out of bounds for IPv#{@version}."
1076
+ end
1077
+
1078
+ if (!objectify)
1079
+ next_sub = NetAddr.unpack_ip_addr(next_sub, :Version => @version)
1080
+ next_sub = NetAddr.shorten(next_sub) if (short && @version == 6)
1081
+ next_sub = next_sub << "/" << self.bits.to_s
1082
+ else
1083
+ next_sub = NetAddr::CIDR.create(next_sub,
1084
+ :PackedNetmask => self.packed_netmask,
1085
+ :Version => @version)
1086
+ end
1087
+
1088
+ return(next_sub)
1089
+ end
1090
+
1091
+ #==============================================================================#
1092
+ # nth()
1093
+ #==============================================================================#
1094
+
1095
+ #===Synopsis
1096
+ #Provide the nth IP within this object.
1097
+ #
1098
+ # cidr4 = NetAddr::CIDR.create('192.168.1.1/24')
1099
+ # puts cidr4.nth(1)
1100
+ # puts cidr4.nth(:Index => 1, :Objectify => true
1101
+ #
1102
+ #===Arguments:
1103
+ #* Index number as an Integer
1104
+ #* Optional Hash with the following keys:
1105
+ # :Objectify -- if true, return NetAddr::CIDR objects (optional)
1106
+ # :Short -- if true, return IPv6 addresses in short-hand notation (optional)
1107
+ #
1108
+ #===Returns:
1109
+ #* String or NetAddr::CIDR object.
1110
+ #
1111
+ def nth(index, options=nil)
1112
+ known_args = [:Objectify, :Short]
1113
+ objectify = false
1114
+ short = false
1115
+
1116
+ # validate list
1117
+ raise ArgumentError, "Integer expected for argument 'index' but " +
1118
+ "#{index.class} provided." if (!index.kind_of?(Integer) )
1119
+
1120
+ # validate options
1121
+ if (options)
1122
+ raise ArgumentError, "Hash expected for argument 'options' but #{options.class} provided." if (!options.kind_of?(Hash) )
1123
+ NetAddr.validate_args(options.keys,known_args)
1124
+
1125
+ if( options.has_key?(:Short) && options[:Short] == true )
1126
+ short = true
1127
+ end
1128
+
1129
+ if( options.has_key?(:Objectify) && options[:Objectify] == true )
1130
+ objectify = true
1131
+ end
1132
+ end
1133
+
1134
+ my_ip = @network + index
1135
+ if ( (@hostmask | my_ip) == (@hostmask | @network) )
1136
+
1137
+ if (!objectify)
1138
+ my_ip = NetAddr.unpack_ip_addr(my_ip, :Version => @version)
1139
+ my_ip = NetAddr.shorten(my_ip) if (short && @version == 6)
1140
+ else
1141
+ my_ip = NetAddr::CIDR.create(my_ip, :Version => @version)
1142
+ end
1143
+
1144
+ else
1145
+ raise BoundaryError, "Index of #{index} returns IP that is out of " +
1146
+ "bounds of CIDR network."
1147
+ end
1148
+
1149
+ return(my_ip)
1150
+ end
1151
+
1152
+ #==============================================================================#
1153
+ # packed_hostmask()
1154
+ #==============================================================================#
1155
+
1156
+ #===Synopsis
1157
+ #Provide an Integer representation of the Hostmask of this object.
1158
+ #
1159
+ # puts cidr4.packed_hostmask().to_s(16)
1160
+ #
1161
+ #===Arguments:
1162
+ #* none
1163
+ #
1164
+ #===Returns:
1165
+ #* Integer
1166
+ #
1167
+ def packed_hostmask()
1168
+ return(@hostmask)
1169
+ end
1170
+
1171
+ #==============================================================================#
1172
+ # packed_ip()
1173
+ #==============================================================================#
1174
+
1175
+ #===Synopsis
1176
+ #Provide an Integer representation of the IP address of this object.
1177
+ #
1178
+ # puts cidr4.packed_ip().to_s(16)
1179
+ #
1180
+ #===Arguments:
1181
+ #* none
1182
+ #
1183
+ #===Returns:
1184
+ #* Integer
1185
+ #
1186
+ def packed_ip()
1187
+ return(@ip)
1188
+ end
1189
+
1190
+ #==============================================================================#
1191
+ # packed_netmask()
1192
+ #==============================================================================#
1193
+
1194
+ #===Synopsis
1195
+ #Provide an Integer representation of the Netmask of this object.
1196
+ #
1197
+ # puts cidr4.packed_netmask().to_s(16)
1198
+ #
1199
+ #===Arguments:
1200
+ #* none
1201
+ #
1202
+ #===Returns:
1203
+ #* Integer
1204
+ #
1205
+ def packed_netmask()
1206
+ return(@netmask)
1207
+ end
1208
+
1209
+ #==============================================================================#
1210
+ # packed_network()
1211
+ #==============================================================================#
1212
+
1213
+ #===Synopsis
1214
+ #Provide an Integer representation of the Network address of this object.
1215
+ #
1216
+ # packed = cidr4.packed_network().to_s(16)
1217
+ #
1218
+ #===Arguments:
1219
+ #* none
1220
+ #
1221
+ #===Returns:
1222
+ #* Integer
1223
+ #
1224
+ def packed_network()
1225
+ return(@network)
1226
+ end
1227
+
1228
+ #==============================================================================#
1229
+ # range()
1230
+ #==============================================================================#
1231
+
1232
+ #===Synopsis
1233
+ #Given a set of index numbers for this CIDR, return all IP addresses within the
1234
+ #CIDR that are between them (inclusive). If an upper bound is not provided, then
1235
+ #all addresses from the lower bound up will be returned.
1236
+ #
1237
+ # cidr4 = NetAddr::CIDR.create('192.168.1.1/24')
1238
+ # list = cidr4.range(0, 1)
1239
+ # list = cidr4.range(0, 1, :Objectify => true)
1240
+ # list = cidr4.range(0, nil, :Objectify => true)
1241
+ #
1242
+ #===Arguments:
1243
+ #* Lower range boundary index as an Integer
1244
+ #* Upper range boundary index as an Integer
1245
+ #* Optional Hash with the following keys:
1246
+ # :Bitstep -- enumerate in X sized steps - Integer (optional)
1247
+ # :Objectify -- if true, return NetAddr::CIDR objects (optional)
1248
+ # :Short -- if true, return IPv6 addresses in short-hand notation (optional)
1249
+ #
1250
+ #===Returns:
1251
+ #* Array of Strings, or Array of NetAddr::CIDR objects
1252
+ #
1253
+ def range(lower, upper=nil, options=nil)
1254
+ known_args = [:Bitstep, :Objectify, :Short]
1255
+ objectify = false
1256
+ short = false
1257
+ bitstep = 1
1258
+
1259
+ # validate indexes
1260
+ raise ArgumentError, "Integer expected for argument 'lower' " +
1261
+ "but #{lower.class} provided." if (!lower.kind_of?(Integer))
1262
+
1263
+ raise ArgumentError, "Integer expected for argument 'upper' " +
1264
+ "but #{upper.class} provided." if (upper && !upper.kind_of?(Integer))
1265
+
1266
+ upper = @hostmask if (upper.nil?)
1267
+ indexes = [lower,upper]
1268
+ indexes.sort!
1269
+ if ( (indexes[0] < 0) || (indexes[0] > self.size) )
1270
+ raise BoundaryError, "Index #{indexes[0]} is out of bounds for this CIDR."
1271
+ end
1272
+
1273
+ if (indexes[1] >= self.size)
1274
+ raise BoundaryError, "Index #{indexes[1]} is out of bounds for this CIDR."
1275
+ end
1276
+
1277
+ # validate options
1278
+ if (options)
1279
+ raise ArgumentError, "Hash expected for argument 'options' but #{options.class} provided." if (!options.kind_of?(Hash) )
1280
+ NetAddr.validate_args(options.keys,known_args)
1281
+
1282
+ if( options.has_key?(:Short) && options[:Short] == true )
1283
+ short = true
1284
+ end
1285
+
1286
+ if( options.has_key?(:Objectify) && options[:Objectify] == true )
1287
+ objectify = true
1288
+ end
1289
+
1290
+ if( options.has_key?(:Bitstep) )
1291
+ bitstep = options[:Bitstep]
1292
+ end
1293
+ end
1294
+
1295
+ # make range
1296
+ start_ip = @network + indexes[0]
1297
+ end_ip = @network + indexes[1]
1298
+ my_ip = start_ip
1299
+ list = []
1300
+ until (my_ip > end_ip)
1301
+ if (!objectify)
1302
+ ip = NetAddr.unpack_ip_addr(my_ip, :Version => @version)
1303
+ ip = NetAddr.shorten(ip) if (short && @version == 6)
1304
+ else
1305
+ ip = NetAddr::CIDR.create(:PackedIP => my_ip, :Version => @version)
1306
+ end
1307
+
1308
+ list.push(ip)
1309
+ my_ip += bitstep
1310
+ end
1311
+
1312
+ return(list)
1313
+ end
1314
+
1315
+ #==============================================================================#
1316
+ # remainder()
1317
+ #==============================================================================#
1318
+
1319
+ #===Synopsis
1320
+ #Given a single subnet of the current CIDR, provide the remainder of
1321
+ #the subnets. For example if the original CIDR is 192.168.0.0/24 and you
1322
+ #provide 192.168.0.64/26 as the portion to exclude, then 192.168.0.0/26,
1323
+ #and 192.168.0.128/25 will be returned as the remainders.
1324
+ #
1325
+ # cidr4.remainder('192.168.1.32/27').each {|x| puts x}
1326
+ # cidr4.remainder(:Exclude => '192.168.1.32/27', :Objectify => true).each {|x| puts x.desc}
1327
+ #
1328
+ #===Arguments:
1329
+ #* CIDR address or NetAddr::CIDR object
1330
+ #* Optional Hash with the following keys:
1331
+ # :Objectify -- if true, return NetAddr::CIDR objects (optional)
1332
+ # :Short -- if true, return IPv6 addresses in short-hand notation (optional)
1333
+ #
1334
+ #===Returns:
1335
+ #* Array of Strings, or Array of NetAddr::CIDR objects
1336
+ #
1337
+ def remainder(addr, options=nil)
1338
+ known_args = [:Objectify, :Short]
1339
+ short = nil
1340
+ objectify = nil
1341
+
1342
+ # validate options
1343
+ if (options)
1344
+ raise ArgumentError, "Hash expected for argument 'options' but " +
1345
+ "#{options.class} provided." if (!options.kind_of?(Hash) )
1346
+ NetAddr.validate_args(options.keys,known_args)
1347
+
1348
+ if( options.has_key?(:Short) && options[:Short] == true )
1349
+ short = true
1350
+ end
1351
+
1352
+ if( options.has_key?(:Objectify) && options[:Objectify] == true )
1353
+ objectify = true
1354
+ end
1355
+ end
1356
+
1357
+ if ( !addr.kind_of?(NetAddr::CIDR) )
1358
+ begin
1359
+ addr = NetAddr::CIDR.create(addr)
1360
+ rescue Exception => error
1361
+ raise ArgumentError, "Argument 'addr' raised the following " +
1362
+ "errors: #{error}"
1363
+ end
1364
+ end
1365
+
1366
+
1367
+ # make sure 'addr' is the same ip version
1368
+ if ( addr.version != @version )
1369
+ raise VersionError, "#{addr.desc(:Short => true)} is of a different " +
1370
+ "IP version than #{self.desc(:Short => true)}."
1371
+ end
1372
+
1373
+ # make sure we contain 'to_exclude'
1374
+ if ( self.contains?(addr) != true )
1375
+ raise BoundaryError, "#{addr.desc(:Short => true)} does not fit " +
1376
+ "within the bounds of #{self.desc(:Short => true)}."
1377
+ end
1378
+
1379
+ # split this cidr in half & see which half 'to_exclude'
1380
+ # belongs in. take that half & repeat the process. every time
1381
+ # we repeat, store the non-matching half
1382
+ new_mask = self.bits + 1
1383
+ lower_network = self.packed_network
1384
+ upper_network = self.packed_network + 2**(@max_bits - new_mask)
1385
+
1386
+ new_subnets = []
1387
+ until(new_mask > addr.bits)
1388
+ if (addr.packed_network < upper_network)
1389
+ match = lower_network
1390
+ non_match = upper_network
1391
+ else
1392
+ match = upper_network
1393
+ non_match = lower_network
1394
+ end
1395
+
1396
+
1397
+ if (!objectify)
1398
+ non_match = NetAddr.unpack_ip_addr(non_match, :Version => @version)
1399
+ non_match = NetAddr.shorten(non_match) if (short && @version == 6)
1400
+ new_subnets.unshift("#{non_match}/#{new_mask}")
1401
+ else
1402
+ new_subnets.unshift(NetAddr::CIDR.create(non_match,
1403
+ :PackedNetmask => NetAddr.pack_ip_netmask(new_mask),
1404
+ :Version => @version))
1405
+ end
1406
+
1407
+ new_mask = new_mask + 1
1408
+ lower_network = match
1409
+ upper_network = match + 2**(@max_bits - new_mask)
1410
+ end
1411
+
1412
+ return(new_subnets)
1413
+ end
1414
+
1415
+ #==============================================================================#
1416
+ # resize()
1417
+ #==============================================================================#
1418
+
1419
+ #===Synopsis
1420
+ #Resize the CIDR by changing the size of the Netmask.
1421
+ #Return the resulting CIDR as a new object.
1422
+ #
1423
+ # cidr4 = NetAddr::CIDR.create('192.168.1.1/24')
1424
+ # new_cidr = cidr4.resize(23)
1425
+ # puts new_cidr.desc
1426
+ #
1427
+ #===Arguments:
1428
+ #* Netmask as an Integer
1429
+ #
1430
+ #===Returns:
1431
+ #* NetAddr::CIDR object
1432
+ #
1433
+ def resize(bits)
1434
+ raise Argumenterror, "Integer or Hash expected, but " +
1435
+ "#{bits.class} provided." if (!bits.kind_of?(Integer))
1436
+
1437
+ NetAddr.validate_ip_netmask(bits, :Version => @version)
1438
+ netmask = NetAddr.pack_ip_netmask(bits, :Version => @version)
1439
+ network = @network & netmask
1440
+
1441
+ cidr = NetAddr::CIDR.create(network, :PackedNetmask => netmask, :Version => @version)
1442
+ return(cidr)
1443
+ end
1444
+
1445
+ #==============================================================================#
1446
+ # resize!()
1447
+ #==============================================================================#
1448
+
1449
+ #===Synopsis
1450
+ #Resize the current CIDR by changing the size of the Netmask. The original IP
1451
+ #passed during initialization will be set to the base network address if
1452
+ #it no longer falls within the bounds of the CIDR.
1453
+ #
1454
+ # cidr4 = NetAddr::CIDR.create('192.168.1.1/24')
1455
+ # new_cidr = cidr4.resize(23)
1456
+ # puts new_cidr.desc
1457
+ #
1458
+ #===Arguments:
1459
+ #* Netmask as an Integer
1460
+ #
1461
+ #===Returns:
1462
+ #* True
1463
+ #
1464
+ def resize!(bits)
1465
+ raise Argumenterror, "Integer or Hash expected, but " +
1466
+ "#{bits.class} provided." if (!bits.kind_of?(Integer))
1467
+
1468
+ NetAddr.validate_ip_netmask(bits, :Version => @version)
1469
+ netmask = NetAddr.pack_ip_netmask(bits, :Version => @version)
1470
+
1471
+ @netmask = netmask
1472
+ @network = @network & netmask
1473
+ @hostmask = @netmask ^ @all_f
1474
+
1475
+ # check @ip
1476
+ if ((@ip & @netmask) != (@network))
1477
+ @ip = @network
1478
+ end
1479
+
1480
+ return(true)
1481
+ end
1482
+
1483
+ #==============================================================================#
1484
+ # size()
1485
+ #==============================================================================#
1486
+
1487
+ #===Synopsis
1488
+ #Provide number of IP addresses within this object.
1489
+ #
1490
+ # puts cidr4.size()
1491
+ #
1492
+ #===Arguments:
1493
+ #* none
1494
+ #
1495
+ #===Returns:
1496
+ #* Integer
1497
+ #
1498
+ def size()
1499
+ return(@hostmask + 1)
1500
+ end
1501
+
1502
+ #==============================================================================#
1503
+ # subnet()
1504
+ #==============================================================================#
1505
+
1506
+ #===Synopsis
1507
+ #Subnet this object. There are 2 ways to subnet:
1508
+ # * By providing the netmask (in bits) of the new subnets in :Bits.
1509
+ # * By providing the number of IP addresses needed in the new subnets in :IPCount
1510
+ #
1511
+ #:NumSubnets is used to determine how the CIDR is subnetted. For example, if I request
1512
+ #the following operation:
1513
+ #
1514
+ # NetAddr::CIDR.create('192.168.1.0/24').subnet(:Bits => 26, :NumSubnets => 1)
1515
+ #
1516
+ #then I would get back the first /26 subnet of 192.168.1.0/24 and the remainder of the IP
1517
+ #space as summary CIDR addresses (e.g. 192.168.1.0/26, 192.168.1.64/26, and 192.168.1.128/25).
1518
+ #If I were to perform the same operation without the :NumSubnets directive, then 192.168.1.0/24
1519
+ #will be fully subnetted into X number of /26 subnets (e.g. 192.168.1.0/26, 192.168.1.64/26,
1520
+ #192.168.1.128/26, and 192.168.1.192/26).
1521
+ #
1522
+ #If neither :Bits nor :IPCount is provided, then the current CIDR will be split in half.
1523
+ #If both :Bits and :IPCount are provided, then :Bits takes precedence.
1524
+ #
1525
+ # cidr_list = cidr4.subnet(:Bits => 28, :NumSubnets => 3).each {|x| puts " #{x}"}
1526
+ # cidr_list = cidr4.subnet(:IPCount => 19).each {|x| puts " #{x}"}
1527
+ # cidr4.subnet(:Bits => 28).each {|x| puts " #{x}"} "
1528
+ # cidr6.subnet(:Bits => 67, :NumSubnets => 4, :Short => true).each {|x| puts " #{x}"}
1529
+ #
1530
+ #===Arguments:
1531
+ #* Optional Hash with the following keys:
1532
+ # :Bits -- Netmask (in bits) of new subnets - Integer (optional)
1533
+ # :IPCount -- Minimum number of IP's that new subnets should contain - Integer (optional)
1534
+ # :NumSubnets -- Number of X sized subnets to return - Integer (optional)
1535
+ # :Objectify -- if true, return NetAddr::CIDR objects (optional)
1536
+ # :Short -- if true, return IPv6 addresses in short-hand notation (optional)
1537
+ #
1538
+ #===Returns:
1539
+ #* Array of Strings, or Array of NetAddr::CIDR objects
1540
+ #
1541
+ def subnet(options=nil)
1542
+ known_args = [:Bits, :IPCount, :NumSubnets, :Objectify, :Short]
1543
+ my_network = self.packed_network
1544
+ my_mask = self.bits
1545
+ subnet_bits = my_mask + 1
1546
+ min_count = nil
1547
+ objectify = false
1548
+ short = false
1549
+
1550
+ if (options)
1551
+ if (!options.kind_of? Hash)
1552
+ raise ArgumentError, "Expected Hash, but #{options.class} provided."
1553
+ end
1554
+ NetAddr.validate_args(options.keys,known_args)
1555
+
1556
+ if ( options.has_key?(:IPCount) )
1557
+ subnet_bits = NetAddr.minimum_size(options[:IPCount], :Version => @version)
1558
+ end
1559
+
1560
+ if ( options.has_key?(:Bits) )
1561
+ subnet_bits = options[:Bits]
1562
+ end
1563
+
1564
+ if ( options.has_key?(:NumSubnets) )
1565
+ num_subnets = options[:NumSubnets]
1566
+ end
1567
+
1568
+ if( options.has_key?(:Short) && options[:Short] == true )
1569
+ short = true
1570
+ end
1571
+
1572
+ if( options.has_key?(:Objectify) && options[:Objectify] == true )
1573
+ objectify = true
1574
+ end
1575
+
1576
+ end
1577
+
1578
+ # get number of subnets possible with the requested subnet_bits
1579
+ num_avail = 2**(subnet_bits - my_mask)
1580
+
1581
+ # get the number of bits in the next supernet and
1582
+ # make sure num_subnets is a power of 2
1583
+ bits_needed = 1
1584
+ num_subnets = num_avail if (!num_subnets)
1585
+ until (2**bits_needed >= num_subnets)
1586
+ bits_needed += 1
1587
+ end
1588
+ num_subnets = 2**bits_needed
1589
+ next_supernet_bits = subnet_bits - bits_needed
1590
+
1591
+
1592
+ # make sure subnet isnt bigger than available bits
1593
+ if (subnet_bits > @max_bits)
1594
+ raise BoundaryError, "Requested subnet (#{subnet_bits}) does not fit " +
1595
+ "within the bounds of IPv#{@version}."
1596
+ end
1597
+
1598
+ # make sure subnet is larger than mymask
1599
+ if (subnet_bits < my_mask)
1600
+ raise BoundaryError, "Requested subnet (#{subnet_bits}) is too large for " +
1601
+ "current CIDR space."
1602
+ end
1603
+
1604
+ # make sure MinCount is smaller than available subnets
1605
+ if (num_subnets > num_avail)
1606
+ raise "Requested subnet count (#{num_subnets}) exceeds subnets " +
1607
+ "available for allocation (#{num_avail})."
1608
+ end
1609
+
1610
+ # list all 'subnet_bits' sized subnets of this cidr block
1611
+ # with a limit of num_subnets
1612
+ bitstep = 2**(@max_bits - subnet_bits)
1613
+ subnets = self.enumerate(:Bitstep => bitstep, :Limit => num_subnets)
1614
+
1615
+ # save our subnets
1616
+ new_subnets = []
1617
+ subnets.each do |subnet|
1618
+ if (!objectify)
1619
+ subnet = NetAddr.shorten(subnet) if (short && @version == 6)
1620
+ new_subnets.push("#{subnet}/#{subnet_bits}")
1621
+ else
1622
+ new_subnets.push(NetAddr::CIDR.create("#{subnet}/#{subnet_bits}", :Version => @version))
1623
+ end
1624
+ end
1625
+
1626
+ # now go through the rest of the cidr space and make the rest
1627
+ # of the subnets. we want these to be as tightly merged as possible
1628
+ next_supernet_bitstep = (bitstep * num_subnets)
1629
+ next_supernet_ip = my_network + next_supernet_bitstep
1630
+ until (next_supernet_bits == my_mask)
1631
+ if (!objectify)
1632
+ next_network = NetAddr.unpack_ip_addr(next_supernet_ip, :Version => @version)
1633
+ next_network = NetAddr.shorten(next_network) if (short && @version == 6)
1634
+ new_subnets.push("#{next_network}/#{next_supernet_bits}")
1635
+ else
1636
+ new_subnets.push(NetAddr::CIDR.create(next_supernet_ip,
1637
+ :PackedNetmask => NetAddr.pack_ip_netmask(next_supernet_bits),
1638
+ :Version => @version))
1639
+ end
1640
+
1641
+ next_supernet_bits -= 1
1642
+ next_supernet_ip = next_supernet_ip + next_supernet_bitstep
1643
+ next_supernet_bitstep = next_supernet_bitstep << 1
1644
+ end
1645
+
1646
+ return(new_subnets)
1647
+ end
1648
+
1649
+
1650
+ # PRIVATE INSTANCE METHODS
1651
+ private
1652
+
1653
+
1654
+ #==============================================================================#
1655
+ # make_subnets_from_base_and_ip_count()
1656
+ #==============================================================================#
1657
+
1658
+ # Make CIDR addresses from a base addr and an number of ip's to encapsulate.
1659
+ #
1660
+ #===Arguments:
1661
+ # * base ip as packed integer
1662
+ # * number of ip's required
1663
+ #
1664
+ #===Returns:
1665
+ # * array of NetAddr::CIDR objects
1666
+ #
1667
+ def make_subnets_from_base_and_ip_count(base_addr,ip_count)
1668
+ list = []
1669
+ until (ip_count == 0)
1670
+ mask = @all_f
1671
+ multiplier = 0
1672
+ bitstep = 0
1673
+ last_addr = base_addr
1674
+ done = false
1675
+ until (done == true)
1676
+ if (bitstep < ip_count && (base_addr & mask == last_addr & mask) )
1677
+ multiplier += 1
1678
+ elsif (bitstep > ip_count || (base_addr & mask != last_addr & mask) )
1679
+ multiplier -= 1
1680
+ done = true
1681
+ else
1682
+ done = true
1683
+ end
1684
+ bitstep = 2**multiplier
1685
+ mask = @all_f << multiplier & @all_f
1686
+ last_addr = base_addr + bitstep - 1
1687
+ end
1688
+
1689
+ list.push(NetAddr::CIDR.create(base_addr,
1690
+ :PackedNetmask => mask,
1691
+ :Version => self.version))
1692
+ ip_count -= bitstep
1693
+ base_addr += bitstep
1694
+ end
1695
+
1696
+ return(list)
1697
+ end
1698
+
1699
+ end # end class CIDR
1700
+
1701
+
1702
+
1703
+ # IPv4 CIDR address - Inherits all methods from NetAddr::CIDR.
1704
+ # Addresses of this class are composed of a 32-bit address space.
1705
+ class CIDRv4 < CIDR
1706
+
1707
+ public_class_method :new
1708
+
1709
+ #==============================================================================#
1710
+ # initialize()
1711
+ #==============================================================================#
1712
+
1713
+ #===Synopsis
1714
+ #Return a CIDRv4 object.
1715
+ #CIDR formatted netmasks take precedence over extended formatted ones.
1716
+ #CIDR address defaults to a host network (/32) if netmask not provided.
1717
+ #:PackedNetmask takes precedence over netmask given within CIDR address.
1718
+ #
1719
+ # cidr4 = NetAddr::CIDRv4.new('192.168.1.1/24')
1720
+ # cidr4 = NetAddr::CIDRv4.new('192.168.1.1 255.255.255.0')
1721
+ # cidr4_2 = NetAddr::CIDRv4.new(0x0a010001,
1722
+ # :PackedNetmask => 0xffffff00)
1723
+ #
1724
+ #===Arguments:
1725
+ #* CIDR address as a String, or a packed IP address as an Integer
1726
+ #* Optional Hash with the following keys:
1727
+ # :PackedNetmask -- Integer representation of an IP Netmask (optional)
1728
+ # :Tag -- Custom descriptor tag - Hash, tag => value. (optional)
1729
+ #
1730
+ def initialize(addr, options=nil)
1731
+ known_args = [:PackedNetmask, :Tag]
1732
+ @tag = {}
1733
+ @version = 4
1734
+ @max_bits = 32
1735
+ @all_f = 2**@max_bits - 1
1736
+
1737
+ # validate addr arg
1738
+ if (addr.kind_of?(String))
1739
+ cidr = addr
1740
+ elsif (addr.kind_of?(Integer))
1741
+ packed_ip = addr
1742
+ else
1743
+ raise ArgumentError, "String or Integer expected for argument 'addr' but #{addr.class} provided."
1744
+ end
1745
+
1746
+ # validate options
1747
+ if (options)
1748
+ raise ArgumentError, "Hash expected for argument 'options' but " +
1749
+ "#{options.class} provided." if (!options.kind_of?(Hash) )
1750
+ NetAddr.validate_args(options.keys,known_args)
1751
+
1752
+ if (options.has_key?(:PackedNetmask))
1753
+ packed_netmask = options[:PackedNetmask]
1754
+ raise ArgumentError, "Expected Integer, but #{packed_netmask.class} " +
1755
+ "provided for option :PackedNetmask." if (!packed_netmask.kind_of?(Integer))
1756
+ end
1757
+
1758
+ if (options.has_key?(:Tag))
1759
+ @tag = options[:Tag]
1760
+ if (!@tag.kind_of? Hash)
1761
+ raise ArgumentError, "Expected Hash, but #{@tag.class} provided for option :Tag."
1762
+ end
1763
+ end
1764
+ end
1765
+
1766
+
1767
+ if (packed_ip)
1768
+ # validate & store packed_ip
1769
+ NetAddr.validate_ip_addr(packed_ip, :Version => @version)
1770
+ @ip = packed_ip
1771
+
1772
+ else
1773
+ # if extended netmask provided. should only apply to ipv4
1774
+ if (cidr =~ /.+\s+.+/ )
1775
+ cidr,netmask = cidr.split(' ')
1776
+ end
1777
+
1778
+ # if netmask part of cidr, then over-ride any provided extended netmask.
1779
+ if (cidr =~ /\//)
1780
+ ip,netmask = cidr.split(/\//)
1781
+ if (!ip || !netmask)
1782
+ raise ArgumentError, "CIDR address is improperly formatted. Missing netmask after '/' character."
1783
+ end
1784
+ else
1785
+ ip = cidr
1786
+ end
1787
+
1788
+ # pack ip
1789
+ @ip = NetAddr.pack_ip_addr(ip, :Version => @version)
1790
+ end
1791
+
1792
+ # if no netmask or packed_netmask, then set
1793
+ # else validate. packed_netmask takes precedence over netmask
1794
+ if (!netmask && !packed_netmask)
1795
+ @netmask = @all_f
1796
+ else
1797
+ if (packed_netmask)
1798
+ NetAddr.validate_ip_netmask(packed_netmask, :Packed => true, :Version => @version)
1799
+ @netmask = packed_netmask
1800
+ else
1801
+ NetAddr.validate_ip_netmask(netmask, :Version => @version)
1802
+ @netmask = NetAddr.pack_ip_netmask(netmask, :Version => @version)
1803
+ end
1804
+ end
1805
+
1806
+ # set @network & @hostmask
1807
+ @network = (@ip & @netmask)
1808
+ @hostmask = @netmask ^ @all_f
1809
+
1810
+ end
1811
+
1812
+ end
1813
+
1814
+
1815
+ # IPv6 CIDR address - Inherits all methods from NetAddr::CIDR.
1816
+ # Addresses of this class are composed of a 128-bit address space.
1817
+ class CIDRv6 < CIDR
1818
+
1819
+ public_class_method :new
1820
+
1821
+ #==============================================================================#
1822
+ # initialize()
1823
+ #==============================================================================#
1824
+
1825
+ #===Synopsis
1826
+ #Return a CIDRv6 object.
1827
+ #CIDR formatted netmasks take precedence over extended formatted ones.
1828
+ #CIDR address defaults to a host network (/128) if netmask not provided.
1829
+ #:PackedNetmask takes precedence over netmask given within CIDR address.
1830
+ #
1831
+ # cidr6 = NetAddr::CIDRv6.new('fec0::/64')
1832
+ # cidr6 = NetAddr::CIDRv6.new('fec0::/64',
1833
+ # :Tag => {'interface' => 'g0/1'})
1834
+ # cidr6_2 = NetAddr::CIDRv6.new('::ffff:192.168.1.1/96')
1835
+ #
1836
+ #===Arguments:
1837
+ #* CIDR address as a String, or a packed IP address as an Integer
1838
+ #* Optional Hash with the following keys:
1839
+ # :PackedNetmask -- Integer representation of an IP Netmask (optional)
1840
+ # :Tag -- Custom descriptor tag - Hash, tag => value. (optional)
1841
+ #
1842
+ def initialize(addr, options=nil)
1843
+ known_args = [:PackedNetmask, :Tag]
1844
+ @tag = {}
1845
+ @version = 6
1846
+ @max_bits = 128
1847
+ @all_f = 2**@max_bits - 1
1848
+
1849
+ # validate addr arg
1850
+ if (addr.kind_of?(String))
1851
+ cidr = addr
1852
+ elsif (addr.kind_of?(Integer))
1853
+ packed_ip = addr
1854
+ else
1855
+ raise ArgumentError, "String or Integer expected for argument 'addr' but #{addr.class} provided."
1856
+ end
1857
+
1858
+ # validate options
1859
+ if (options)
1860
+ raise ArgumentError, "Hash expected for argument 'options' but " +
1861
+ "#{options.class} provided." if (!options.kind_of?(Hash) )
1862
+ NetAddr.validate_args(options.keys,known_args)
1863
+
1864
+ if (options.has_key?(:PackedNetmask))
1865
+ packed_netmask = options[:PackedNetmask]
1866
+ raise ArgumentError, "Expected Integer, but #{packed_netmask.class} " +
1867
+ "provided for option :PackedNetmask." if (!packed_netmask.kind_of?(Integer))
1868
+ end
1869
+
1870
+ if (options.has_key?(:Tag))
1871
+ @tag = options[:Tag]
1872
+ if (!@tag.kind_of? Hash)
1873
+ raise ArgumentError, "Expected Hash, but #{@tag.class} provided for option :Tag."
1874
+ end
1875
+ end
1876
+ end
1877
+
1878
+
1879
+ if (packed_ip)
1880
+ # validate & store packed_ip
1881
+ NetAddr.validate_ip_addr(packed_ip, :Version => @version)
1882
+ @ip = packed_ip
1883
+
1884
+ else
1885
+ # if ipv6 and extended netmask was provided, then raise exception
1886
+ raise ArgumentError, "Garbage provided at end of IPv6 address." if (cidr =~ /.+\s+.+/ )
1887
+
1888
+ # get ip and netmask
1889
+ if (cidr =~ /\//)
1890
+ ip,netmask = cidr.split(/\//)
1891
+ if (!ip || !netmask)
1892
+ raise ArgumentError, "CIDR address is improperly formatted. Missing netmask after '/' character."
1893
+ end
1894
+ else
1895
+ ip = cidr
1896
+ end
1897
+
1898
+ # pack ip
1899
+ @ip = NetAddr.pack_ip_addr(ip, :Version => @version)
1900
+ end
1901
+
1902
+ # if no netmask or packed_netmask, then set
1903
+ # else validate. packed_netmask takes precedence over netmask
1904
+ if (!netmask && !packed_netmask)
1905
+ @netmask = @all_f
1906
+ else
1907
+ if (packed_netmask)
1908
+ NetAddr.validate_ip_netmask(packed_netmask, :Packed => true, :Version => @version)
1909
+ @netmask = packed_netmask
1910
+ else
1911
+ NetAddr.validate_ip_netmask(netmask, :Version => @version)
1912
+ @netmask = NetAddr.pack_ip_netmask(netmask, :Version => @version)
1913
+ end
1914
+ end
1915
+
1916
+ # set @network & @hostmask
1917
+ @network = (@ip & @netmask)
1918
+ @hostmask = @netmask ^ @all_f
1919
+
1920
+ end
1921
+
1922
+ end
1923
+
1924
+ end # module NetAddr
1925
+ __END__