netaddr 1.5.1 → 2.0.1

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.

@@ -1,259 +0,0 @@
1
- module NetAddr
2
- private
3
-
4
-
5
- # IP MATH METHODS
6
-
7
- #==============================================================================#
8
- # binary_mirror()
9
- #==============================================================================#
10
-
11
- # given an integer and number of bits to consider, return its binary mirror
12
- #
13
- def binary_mirror(num, bit_count)
14
- mirror = 0
15
- bit_count.times do # make mirror image of num by capturning lsb and left-shifting it onto mirror
16
- mirror = mirror << 1
17
- lsb = num & 1
18
- num = num >> 1
19
- mirror = mirror | lsb
20
- end
21
- return(mirror)
22
- end
23
- module_function :binary_mirror
24
-
25
- #==============================================================================#
26
- # bits_to_mask()
27
- #==============================================================================#
28
-
29
- # convert a netmask (in bits) to an integer mask
30
- #
31
- def bits_to_mask(netmask,version)
32
- return(0) if (netmask == 0)
33
- all_f = 2**32-1
34
- all_f = 2**128-1 if (version == 6)
35
- return( all_f ^ (all_f >> netmask) )
36
- end
37
- module_function :bits_to_mask
38
-
39
- #==============================================================================#
40
- # detect_ip_version()
41
- #==============================================================================#
42
-
43
- # determine the ip version from ip address string.
44
- #
45
- # return 4, 6, or nil
46
- #
47
- def detect_ip_version(ip)
48
- version = nil
49
- if ( ip =~ /\./ && ip !~ /:/ )
50
- version = 4
51
- elsif (ip =~ /:/)
52
- version = 6
53
- else
54
- raise ValidationError, "Could not auto-detect IP version for '#{ip}'."
55
- end
56
- return(version)
57
- end
58
- module_function :detect_ip_version
59
-
60
- #==============================================================================#
61
- # ip_count_to_size()
62
- #==============================================================================#
63
-
64
- # given an ip count, determine the most appropriate mask (in bits)
65
- #
66
- def ip_count_to_size(ipcount,version,extended=false)
67
- address_len = 32
68
- address_len = 128 if (version == 6 )
69
-
70
- if (ipcount > 2**address_len)
71
- raise BoundaryError, "Required IP count exceeds number of IP addresses available " +
72
- "for IPv#{version}."
73
- end
74
-
75
- bits_needed = 0
76
- until (2**bits_needed >= ipcount)
77
- bits_needed += 1
78
- end
79
- subnet_bits = address_len - bits_needed
80
-
81
- return( ip_int_to_str(bits_to_mask(subnet_bits, 4), 4) ) if (extended && version == 4)
82
- return(subnet_bits)
83
- end
84
- module_function :ip_count_to_size
85
-
86
- #==============================================================================#
87
- # ip_int_to_str()
88
- #==============================================================================#
89
-
90
- # unpack an int into an ip address string
91
- #
92
- def ip_int_to_str(ip_int, version, ipv4_mapped=nil)
93
- ip = nil
94
- version = 4 if (!version && ip_int < 2**32)
95
- if (version == 4)
96
- octets = []
97
- 4.times do
98
- octet = ip_int & 0xFF
99
- octets.unshift(octet.to_s)
100
- ip_int = ip_int >> 8
101
- end
102
- ip = octets.join('.')
103
- else
104
- fields = []
105
- if (!ipv4_mapped)
106
- loop_count = 8
107
- else
108
- loop_count = 6
109
- ipv4_int = ip_int & 0xffffffff
110
- ipv4_addr = ip_int_to_str(ipv4_int, 4)
111
- fields.unshift(ipv4_addr)
112
- ip_int = ip_int >> 32
113
- end
114
-
115
- loop_count.times do
116
- octet = ip_int & 0xFFFF
117
- octet = octet.to_s(16)
118
- ip_int = ip_int >> 16
119
-
120
- # if octet < 4 characters, then pad with 0's
121
- (4 - octet.length).times do
122
- octet = '0' << octet
123
- end
124
- fields.unshift(octet)
125
- end
126
- ip = fields.join(':')
127
- end
128
- return(ip)
129
- end
130
- module_function :ip_int_to_str
131
-
132
- #==============================================================================#
133
- # ip_str_to_int()
134
- #==============================================================================#
135
-
136
- # convert an ip string into an int
137
- #
138
- def ip_str_to_int(ip,version)
139
- ip_int = 0
140
- if ( version == 4)
141
- octets = ip.split('.')
142
- (0..3).each do |x|
143
- octet = octets.pop.to_i
144
- octet = octet << 8*x
145
- ip_int = ip_int | octet
146
- end
147
-
148
- else
149
- # if ipv4-mapped ipv6 addr
150
- if (ip =~ /\./)
151
- dotted_dec = true
152
- end
153
-
154
- # split up by ':'
155
- fields = []
156
- if (ip =~ /::/)
157
- shrthnd = ip.split( /::/ )
158
- if (shrthnd.length == 0)
159
- return(0)
160
- else
161
- first_half = shrthnd[0].split( /:/ ) if (shrthnd[0])
162
- sec_half = shrthnd[1].split( /:/ ) if (shrthnd[1])
163
- first_half = [] if (!first_half)
164
- sec_half = [] if (!sec_half)
165
- end
166
- missing_fields = 8 - first_half.length - sec_half.length
167
- missing_fields -= 1 if dotted_dec
168
- fields = fields.concat(first_half)
169
- missing_fields.times {fields.push('0')}
170
- fields = fields.concat(sec_half)
171
-
172
- else
173
- fields = ip.split(':')
174
- end
175
-
176
- if (dotted_dec)
177
- ipv4_addr = fields.pop
178
- ipv4_int = NetAddr.ip_to_i(ipv4_addr, :Version => 4)
179
- octets = []
180
- 2.times do
181
- octet = ipv4_int & 0xFFFF
182
- octets.unshift(octet.to_s(16))
183
- ipv4_int = ipv4_int >> 16
184
- end
185
- fields.concat(octets)
186
- end
187
-
188
- # pack
189
- (0..7).each do |x|
190
- field = fields.pop.to_i(16)
191
- field = field << 16*x
192
- ip_int = ip_int | field
193
- end
194
-
195
- end
196
- return(ip_int)
197
- end
198
- module_function :ip_str_to_int
199
-
200
- #==============================================================================#
201
- # mask_to_bits()
202
- #==============================================================================#
203
-
204
- # convert integer into a cidr formatted netmask (bits)
205
- #
206
- def mask_to_bits(netmask_int)
207
- return(netmask_int) if (netmask_int == 0)
208
-
209
- mask = nil
210
- if (netmask_int < 2**32)
211
- mask = 32
212
- validate_netmask_int(netmask_int, 4, true)
213
- else
214
- mask = 128
215
- validate_netmask_int(netmask_int, 6, true)
216
- end
217
-
218
- mask.times do
219
- if ( (netmask_int & 1) == 1)
220
- break
221
- end
222
- netmask_int = netmask_int >> 1
223
- mask = mask - 1
224
- end
225
- return(mask)
226
- end
227
- module_function :mask_to_bits
228
-
229
- #==============================================================================#
230
- # netmask_str_to_int()
231
- #==============================================================================#
232
-
233
- # convert string into integer mask
234
- #
235
- def netmask_str_to_int(netmask,version)
236
- netmask_int = nil
237
- all_f = 2**32-1
238
- all_f = 2**128-1 if (version == 6)
239
- if(netmask =~ /\./)
240
- netmask_int = NetAddr.ip_to_i(netmask)
241
- else
242
- # remove '/' if present
243
- if (netmask =~ /^\// )
244
- netmask[0] = " "
245
- netmask.lstrip!
246
- end
247
- netmask = netmask.to_i
248
- netmask_int = all_f ^ (all_f >> netmask)
249
- end
250
- return(netmask_int)
251
- end
252
- module_function :netmask_str_to_int
253
-
254
-
255
-
256
- end # module NetAddr
257
-
258
- __END__
259
-
@@ -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
-