ipadmin 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +77 -13
- data/lib/cidr.rb +1459 -0
- data/lib/ip_admin.rb +3 -3682
- data/lib/methods.rb +2168 -0
- data/lib/tree.rb +735 -0
- data/tests/cidr_test.rb +34 -2
- data/tests/methods_test.rb +151 -102
- metadata +5 -2
data/README
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
|
2
|
-
Copyright (c) 2006 Dustin Spinhirne
|
2
|
+
Copyright (c) 2006 Dustin Spinhirne - http://www.spinhirne.com
|
3
3
|
Licensed under the same terms as Ruby, No Warranty is provided.
|
4
4
|
|
5
5
|
|
@@ -8,6 +8,64 @@
|
|
8
8
|
|
9
9
|
Dustin Spinhirne
|
10
10
|
|
11
|
+
|
12
|
+
=CIDR
|
13
|
+
|
14
|
+
A class & series of methods for creating and manipulating CIDR network
|
15
|
+
addresses. Both IPv4 and IPv6 are supported.
|
16
|
+
|
17
|
+
This class accepts a CIDR address in (x.x.x.x/yy or xxxx::/yy) format for
|
18
|
+
IPv4 and IPv6, or (x.x.x.x/y.y.y.y) for IPv4. An optional tag hash may be
|
19
|
+
provided with each CIDR as a way of adding custom labels to the object.
|
20
|
+
|
21
|
+
Upon initialization, the IP version is auto-detected and assigned to the
|
22
|
+
object. The original IP/Netmask passed within the CIDR is stored and then
|
23
|
+
used to determine the confines of the CIDR block. Various properties of the
|
24
|
+
CIDR block are accessible via several different methods. There are also
|
25
|
+
methods for modifying the CIDR or creating new derivative CIDR's.
|
26
|
+
|
27
|
+
An example CIDR object is as follows:
|
28
|
+
IPAdmin::CIDR.new(:CIDR => '192.168.1.20/24')
|
29
|
+
|
30
|
+
This would create a CIDR object (192.168.1.0/24) with the following properties:
|
31
|
+
version = 4
|
32
|
+
base network = 192.168.1.0
|
33
|
+
ip address = 192.168.1.20
|
34
|
+
netmask = /24 (255.255.255.0)
|
35
|
+
size = 256 IP addresses
|
36
|
+
broadcast = 192.168.1.255
|
37
|
+
|
38
|
+
You can see how the CIDR object is based around the entire IP space
|
39
|
+
defined by the provided IP/Netmask pair, and not necessarily the individual
|
40
|
+
IP address itself.
|
41
|
+
|
42
|
+
|
43
|
+
=Tree
|
44
|
+
|
45
|
+
A class & series of methods for creating and manipulating IP-based
|
46
|
+
heirarchical trees. Both IPv4 and IPv6 are supported.
|
47
|
+
|
48
|
+
Tree's are useful for creating mini 'routing tables' of CIDR address space.
|
49
|
+
Using a tree, you can quickly determine the 'route' of another CIDR via
|
50
|
+
the standard longest-match algorithm.
|
51
|
+
|
52
|
+
Tree's are not dynamic, in that if you modify any of the CIDR objects
|
53
|
+
contained within, the Tree will not automatically adjust itself accordingly.
|
54
|
+
For example if you have a tree like the following:
|
55
|
+
|
56
|
+
192.168.0.0/24
|
57
|
+
192.168.0.0/26
|
58
|
+
192.168.1.0/24
|
59
|
+
|
60
|
+
You decide to resize 192.168.0.0/24 to 192.168.0.0/23. The tree will now
|
61
|
+
be incorrect:
|
62
|
+
|
63
|
+
192.168.0.0/23
|
64
|
+
192.168.0.0/26
|
65
|
+
192.168.1.0/24
|
66
|
+
|
67
|
+
You would need to remove and re-add the CIDR 192.168.0.0/23 in order for the
|
68
|
+
tree to rebuild itself properly.
|
11
69
|
|
12
70
|
|
13
71
|
|
@@ -42,8 +100,16 @@
|
|
42
100
|
cidr4.tag['test'] = 'modified cidr4 tag'
|
43
101
|
puts "updated cidr4 tag '#{cidr4.tag['test']}'"
|
44
102
|
puts "cidr4 version #{cidr4.version}"
|
103
|
+
puts "cidr6 version #{cidr6.version}"
|
45
104
|
print "\n"
|
46
105
|
|
106
|
+
|
107
|
+
# arpa
|
108
|
+
puts "arpa"
|
109
|
+
puts "arpa for #{cidr4.desc()} is #{cidr4.arpa}"
|
110
|
+
puts "arpa for #{cidr6.desc(:Short => true)} is #{cidr6.arpa}"
|
111
|
+
print "\n"
|
112
|
+
|
47
113
|
# bits
|
48
114
|
puts "bits"
|
49
115
|
puts "cidr4 netmask in bits #{cidr4.bits()}"
|
@@ -53,7 +119,7 @@
|
|
53
119
|
# contains?
|
54
120
|
puts "contains"
|
55
121
|
puts "#{cidr4.desc} contains #{cidr4_2.desc}" if ( cidr4.contains?(cidr4_2) )
|
56
|
-
puts "#{cidr6.desc} contains #{cidr6_2.desc}" if ( cidr6.contains?(cidr6_2) )
|
122
|
+
puts "#{cidr6.desc} contains #{cidr6_2.desc(:Short => true)}" if ( cidr6.contains?(cidr6_2) )
|
57
123
|
print "\n"
|
58
124
|
|
59
125
|
# desc
|
@@ -151,6 +217,7 @@
|
|
151
217
|
# remainder
|
152
218
|
puts "remainder"
|
153
219
|
cidr4_2 = IPAdmin::CIDR.new(:CIDR => '192.168.1.64/26')
|
220
|
+
puts "The remainder of #{cidr4.desc} after subtracting #{cidr4_2.desc} is:"
|
154
221
|
cidr4.remainder(:Exclude => cidr4_2).each {|x| puts x}
|
155
222
|
print "\n"
|
156
223
|
|
@@ -304,6 +371,7 @@
|
|
304
371
|
#=====================================#
|
305
372
|
#
|
306
373
|
#=====================================#
|
374
|
+
|
307
375
|
|
308
376
|
#============================================================================#
|
309
377
|
# IPAdmin Methods
|
@@ -313,15 +381,16 @@
|
|
313
381
|
|
314
382
|
# validate ip
|
315
383
|
puts "validate ip"
|
316
|
-
puts "192.168.1.0 is valid" if ( IPAdmin.
|
317
|
-
puts "fec0::0 is valid" if ( IPAdmin.
|
384
|
+
puts "192.168.1.0 is valid" if ( IPAdmin.validate_ip_addr(:IP => '192.168.1.0') )
|
385
|
+
puts "fec0::0 is valid" if ( IPAdmin.validate_ip_addr(:IP => 'fec0::0') )
|
386
|
+
puts "::ffff:10.1.0.1 is valid" if ( IPAdmin.validate_ip_addr(:IP => '::ffff:10.1.0.1') )
|
318
387
|
print "\n"
|
319
388
|
|
320
389
|
# validate netmask
|
321
390
|
puts "validate netmask"
|
322
|
-
puts "255.255.255.0 is valid" if (IPAdmin.
|
323
|
-
puts "/24 is valid" if ( IPAdmin.
|
324
|
-
puts "/64 is valid" if ( IPAdmin.
|
391
|
+
puts "255.255.255.0 is valid" if (IPAdmin.validate_ip_netmask(:Netmask => '255.255.255.0') )
|
392
|
+
puts "/24 is valid" if ( IPAdmin.validate_ip_netmask(:Netmask => '/24') )
|
393
|
+
puts "/64 is valid" if ( IPAdmin.validate_ip_netmask(:Netmask => 64, :Version => 6) )
|
325
394
|
|
326
395
|
cidr4_1 = IPAdmin::CIDR.new(:CIDR => '192.168.1.0/24')
|
327
396
|
cidr4_2 = IPAdmin::CIDR.new(:CIDR => '192.168.1.0/25')
|
@@ -351,12 +420,6 @@
|
|
351
420
|
puts " #{x}"
|
352
421
|
end
|
353
422
|
print "\n"
|
354
|
-
|
355
|
-
# arpa
|
356
|
-
puts "arpa"
|
357
|
-
puts "arpa for #{cidr4_1.desc()} is #{IPAdmin.arpa(cidr4_1)}"
|
358
|
-
puts "arpa for #{cidr6_1.desc()} is #{IPAdmin.arpa(cidr6_1)}"
|
359
|
-
print "\n"
|
360
423
|
|
361
424
|
# shorten
|
362
425
|
puts "shorten"
|
@@ -370,3 +433,4 @@
|
|
370
433
|
#=====================================#
|
371
434
|
#
|
372
435
|
#=====================================#
|
436
|
+
|
data/lib/cidr.rb
ADDED
@@ -0,0 +1,1459 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
Copyright (c) 2006 Dustin Spinhirne -
|
3
|
+
Licensed under the same terms as Ruby, No Warranty is provided.
|
4
|
+
=end
|
5
|
+
|
6
|
+
|
7
|
+
module IPAdmin
|
8
|
+
class CIDR
|
9
|
+
|
10
|
+
# instance variables
|
11
|
+
# @all_f
|
12
|
+
# @hostmask
|
13
|
+
# @ip
|
14
|
+
# @max_bits
|
15
|
+
# @netmask
|
16
|
+
# @network
|
17
|
+
# @tag
|
18
|
+
# @version
|
19
|
+
|
20
|
+
#==============================================================================#
|
21
|
+
# attr_reader/attr_writer
|
22
|
+
#==============================================================================#
|
23
|
+
|
24
|
+
# IP version 4 or 6.
|
25
|
+
attr_reader :version
|
26
|
+
|
27
|
+
# Hash of custom tags. Should be in the format tag => value.
|
28
|
+
attr_reader :tag
|
29
|
+
|
30
|
+
# Hash of custom tags. Should be in the format tag => value.
|
31
|
+
#
|
32
|
+
# Example:
|
33
|
+
# cidr4.tag['test'] = 'modified cidr4 tag'
|
34
|
+
# puts cidr4.tag['test'] --> modified cidr4 tag
|
35
|
+
#
|
36
|
+
def tag=(new_tag)
|
37
|
+
if (!new_tag.kind_of? Hash)
|
38
|
+
raise ArgumentError, "Expected Hash, but #{new_tag.class} provided."
|
39
|
+
end
|
40
|
+
@tag = new_tag
|
41
|
+
end
|
42
|
+
|
43
|
+
#======================================#
|
44
|
+
#
|
45
|
+
#======================================#
|
46
|
+
|
47
|
+
|
48
|
+
#==============================================================================#
|
49
|
+
# initialize()
|
50
|
+
#==============================================================================#
|
51
|
+
|
52
|
+
# - Arguments:
|
53
|
+
# * Hash with the following fields:
|
54
|
+
# - :CIDR -- IP address in CIDR notation - String (optional)
|
55
|
+
# - :Netmask -- IP Netmask - String or Integer (optional)
|
56
|
+
# - :PackedIP -- Integer representation of an IP address (optional)
|
57
|
+
# - :PackedNetmask -- Integer representation of an IP Netmask (optional)
|
58
|
+
# - :Version -- IP version - Integer (optional)
|
59
|
+
# - :Tag -- Custom descriptor tag - Hash, tag => value. (optional)
|
60
|
+
#
|
61
|
+
# - Notes:
|
62
|
+
# * At a minimum, either :CIDR or PackedIP must be provided.
|
63
|
+
# * Netmask defaults to /32 or /128 if not provided.
|
64
|
+
# * :PackedIP takes precedence over :CIDR
|
65
|
+
# * :PackedNetmask always takes precedence over any other provided netmasks.
|
66
|
+
# * Netmask within :CIDR takes precedence over :Netmask.
|
67
|
+
# * Version will be auto-detected if not specified
|
68
|
+
#
|
69
|
+
# Example:
|
70
|
+
# cidr4 = IPAdmin::CIDR.new(:CIDR => '192.168.1.1/24')
|
71
|
+
# cidr4_2 = IPAdmin::CIDR.new(:PackedIP => 0x0a010001,
|
72
|
+
# :PackedNetmask => 0xffffff00
|
73
|
+
# :Version => 4)
|
74
|
+
# cidr6 = IPAdmin::CIDR.new(:CIDR => 'fec0::/64',
|
75
|
+
# :Tag => {'interface' => 'g0/1'})
|
76
|
+
# cidr6_2 = IPAdmin::CIDR.new(:CIDR => '::ffff:192.168.1.1/96')
|
77
|
+
#
|
78
|
+
def initialize(options)
|
79
|
+
if (!options.kind_of? Hash)
|
80
|
+
raise ArgumentError, "Expected Hash, but #{options.class} provided."
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
if (options.has_key?(:PackedIP))
|
85
|
+
packed_ip = options[:PackedIP]
|
86
|
+
raise ArgumentError, "Expected Integer, but #{packed_ip.class} " +
|
87
|
+
"provided for option :PackedIP." if (!packed_ip.kind_of?(Integer))
|
88
|
+
elsif (options.has_key?(:CIDR))
|
89
|
+
cidr = options[:CIDR]
|
90
|
+
raise ArgumentError, "Expected String, but #{cidr.class} " +
|
91
|
+
"provided for option :CIDR." if (!cidr.kind_of?(String))
|
92
|
+
else
|
93
|
+
raise ArgumentError, "Missing argument: CIDR or PackedIP."
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
if (options.has_key?(:PackedNetmask))
|
98
|
+
packed_netmask = options[:PackedNetmask]
|
99
|
+
raise ArgumentError, "Expected Integer, but #{packed_netmask.class} " +
|
100
|
+
"provided for option :PackedNetmask." if (!packed_netmask.kind_of?(Integer))
|
101
|
+
|
102
|
+
elsif (options.has_key?(:Netmask))
|
103
|
+
netmask = options[:Netmask]
|
104
|
+
raise ArgumentError, "Expected String or Integer, but #{netmask.class} " +
|
105
|
+
"provided for option :Netmask." if (!netmask.kind_of?(String) && !netmask.kind_of?(Integer))
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
if (options.has_key?(:Version))
|
110
|
+
@version = options[:Version]
|
111
|
+
if (@version != 4 && @version != 6)
|
112
|
+
raise ArgumentError, ":Version should be 4 or 6, but was '#{version}'."
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
if (options.has_key?(:Tag))
|
117
|
+
@tag = options[:Tag]
|
118
|
+
if (!@tag.kind_of? Hash)
|
119
|
+
raise ArgumentError, "Expected Hash, but #{@tag.class} provided for option :Tag."
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
if (packed_ip)
|
125
|
+
if (packed_ip < 2**32)
|
126
|
+
@version = 4 if (!@version)
|
127
|
+
@netmask = 2**32-1 if (!netmask && !packed_netmask)
|
128
|
+
else
|
129
|
+
@version = 6 if (!@version)
|
130
|
+
@netmask = 2**128-1 if (!netmask && !packed_netmask)
|
131
|
+
end
|
132
|
+
|
133
|
+
# validate & store packed_ip
|
134
|
+
IPAdmin.validate_ip_addr(:IP => packed_ip, :Version => @version)
|
135
|
+
@ip = packed_ip
|
136
|
+
|
137
|
+
else
|
138
|
+
# determine version if not set
|
139
|
+
if (!@version)
|
140
|
+
if (cidr =~ /\./ && cidr !~ /:/)
|
141
|
+
@version = 4
|
142
|
+
elsif (cidr =~ /:/)
|
143
|
+
@version = 6
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# get ip/netmask
|
148
|
+
if (cidr =~ /\//) # if netmask part of cidr
|
149
|
+
ip,netmask = cidr.split(/\//)
|
150
|
+
raise ArgumentError, ":CIDR is improperly formatted." if (!ip || !netmask)
|
151
|
+
elsif (@version == 4)
|
152
|
+
ip = cidr
|
153
|
+
@netmask = 2**32-1 if (!netmask && !packed_netmask)
|
154
|
+
elsif (@version == 6)
|
155
|
+
ip = cidr
|
156
|
+
@netmask = 2**128-1 if (!netmask && !packed_netmask)
|
157
|
+
end
|
158
|
+
|
159
|
+
# validate & pack ip
|
160
|
+
IPAdmin.validate_ip_addr(:IP => ip, :Version => @version)
|
161
|
+
@ip = IPAdmin.pack_ip_addr(:IP => ip, :Version => @version)
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
# validate & store netmask || packed_netmask if needed
|
167
|
+
if (!@netmask)
|
168
|
+
if (packed_netmask)
|
169
|
+
IPAdmin.validate_ip_netmask(:Netmask => packed_netmask, :Packed => true, :Version => @version)
|
170
|
+
@netmask = packed_netmask
|
171
|
+
else
|
172
|
+
IPAdmin.validate_ip_netmask(:Netmask => netmask, :Version => @version)
|
173
|
+
@netmask = IPAdmin.pack_ip_netmask(:Netmask => netmask, :Version => @version)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# set vars based on version
|
178
|
+
if (@version == 4)
|
179
|
+
@max_bits = 32
|
180
|
+
@all_f = 2**@max_bits - 1
|
181
|
+
else
|
182
|
+
@max_bits = 128
|
183
|
+
@all_f = 2**@max_bits - 1
|
184
|
+
end
|
185
|
+
|
186
|
+
# get @network & @hostmask
|
187
|
+
@network = (@ip & @netmask)
|
188
|
+
@hostmask = @netmask ^ @all_f
|
189
|
+
|
190
|
+
end
|
191
|
+
|
192
|
+
#======================================#
|
193
|
+
#
|
194
|
+
#======================================#
|
195
|
+
|
196
|
+
|
197
|
+
#==============================================================================#
|
198
|
+
# arpa()
|
199
|
+
#==============================================================================#
|
200
|
+
|
201
|
+
# Depending on the IP version of the current CIDR,
|
202
|
+
# return either an in-addr.arpa. or ip6.arpa. string. The netmask will be used
|
203
|
+
# to determine the length of the returned string.
|
204
|
+
#
|
205
|
+
# - Arguments:
|
206
|
+
# * none
|
207
|
+
#
|
208
|
+
# - Returns:
|
209
|
+
# * String
|
210
|
+
#
|
211
|
+
# Example:
|
212
|
+
# arpa = cidr4.arpa() --> '1.168.192.in-addr.arpa.'
|
213
|
+
#
|
214
|
+
def arpa()
|
215
|
+
|
216
|
+
base = self.ip()
|
217
|
+
netmask = self.bits()
|
218
|
+
|
219
|
+
if (@version == 4)
|
220
|
+
net = base.split('.')
|
221
|
+
|
222
|
+
if (netmask)
|
223
|
+
while (netmask < 32)
|
224
|
+
net.pop
|
225
|
+
netmask = netmask + 8
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
arpa = net.reverse.join('.')
|
230
|
+
arpa << ".in-addr.arpa."
|
231
|
+
|
232
|
+
elsif (@version == 6)
|
233
|
+
fields = base.split(':')
|
234
|
+
net = []
|
235
|
+
fields.each do |field|
|
236
|
+
(field.split("")).each do |x|
|
237
|
+
net.push(x)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
if (netmask)
|
242
|
+
while (netmask < 128)
|
243
|
+
net.pop
|
244
|
+
netmask = netmask + 4
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
arpa = net.reverse.join('.')
|
249
|
+
arpa << ".ip6.arpa."
|
250
|
+
|
251
|
+
end
|
252
|
+
|
253
|
+
return(arpa)
|
254
|
+
end
|
255
|
+
|
256
|
+
#======================================#
|
257
|
+
#
|
258
|
+
#======================================#
|
259
|
+
|
260
|
+
|
261
|
+
#==============================================================================#
|
262
|
+
# bits()
|
263
|
+
#==============================================================================#
|
264
|
+
|
265
|
+
# Provide number of bits in Netmask.
|
266
|
+
#
|
267
|
+
# - Arguments:
|
268
|
+
# * none
|
269
|
+
#
|
270
|
+
# - Returns:
|
271
|
+
# * Integer.
|
272
|
+
#
|
273
|
+
# Example:
|
274
|
+
# puts cidr4.bits() --> 24
|
275
|
+
#
|
276
|
+
def bits()
|
277
|
+
return(IPAdmin.unpack_ip_netmask(:Integer => @netmask))
|
278
|
+
end
|
279
|
+
|
280
|
+
#======================================#
|
281
|
+
#
|
282
|
+
#======================================#
|
283
|
+
|
284
|
+
|
285
|
+
#==============================================================================#
|
286
|
+
# contains?()
|
287
|
+
#==============================================================================#
|
288
|
+
|
289
|
+
# Determines if this CIDR contains (is supernet of)
|
290
|
+
# the provided CIDR object.
|
291
|
+
#
|
292
|
+
# - Arguments:
|
293
|
+
# * CIDR object
|
294
|
+
#
|
295
|
+
# - Returns:
|
296
|
+
# * true or false
|
297
|
+
#
|
298
|
+
# Example:
|
299
|
+
# cidr4_2 = IPAdmin::CIDR.new(:CIDR => '192.168.1.0/26')
|
300
|
+
# contains? = cidr4.contains(cidr4_2) --> true
|
301
|
+
#
|
302
|
+
def contains?(object)
|
303
|
+
is_contained = false
|
304
|
+
|
305
|
+
if (object.kind_of?(IPAdmin::CIDR))
|
306
|
+
network = object.packed_network
|
307
|
+
netmask = object.packed_netmask
|
308
|
+
|
309
|
+
else
|
310
|
+
raise ArgumentError, "Expected IPAdmin::CIDR " +
|
311
|
+
" object but #{object.class} provided."
|
312
|
+
end
|
313
|
+
|
314
|
+
|
315
|
+
if (object.version != @version)
|
316
|
+
raise ArgumentError, "Attempted to compare a version #{object.version} CIDR " +
|
317
|
+
"with a version #{@version} CIDR."
|
318
|
+
end
|
319
|
+
|
320
|
+
# if network == @network, then we have to go by netmask length
|
321
|
+
# else we can tell by or'ing network and @network by @hostmask
|
322
|
+
# and comparing the results
|
323
|
+
if (network == @network)
|
324
|
+
is_contained = true if (netmask > @netmask)
|
325
|
+
|
326
|
+
else
|
327
|
+
if ( (network | @hostmask) == (@network | @hostmask) )
|
328
|
+
is_contained = true
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
return(is_contained)
|
333
|
+
end
|
334
|
+
|
335
|
+
#======================================#
|
336
|
+
#
|
337
|
+
#======================================#
|
338
|
+
|
339
|
+
|
340
|
+
#==============================================================================#
|
341
|
+
# desc()
|
342
|
+
#==============================================================================#
|
343
|
+
|
344
|
+
# Returns network/netmask in CIDR format.
|
345
|
+
#
|
346
|
+
# - Arguments:
|
347
|
+
# * Optional hash with the following fields:
|
348
|
+
# - :IP -- if true, return the original ip/netmask passed during initialization (optional)
|
349
|
+
# - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
|
350
|
+
#
|
351
|
+
# - Returns:
|
352
|
+
# * String
|
353
|
+
#
|
354
|
+
# Example:
|
355
|
+
# puts cidr4.desc() --> 192.168.1.0/24
|
356
|
+
# puts cidr4.desc(:IP => true) --> 192.168.1.1/24
|
357
|
+
#
|
358
|
+
def desc(options=nil)
|
359
|
+
short = false
|
360
|
+
orig_ip = false
|
361
|
+
|
362
|
+
if (options)
|
363
|
+
if (!options.kind_of? Hash)
|
364
|
+
raise ArgumentError, "Expected Hash, but #{options.class} provided."
|
365
|
+
end
|
366
|
+
|
367
|
+
if (options.has_key?(:Short))
|
368
|
+
short = true
|
369
|
+
end
|
370
|
+
|
371
|
+
if (options.has_key?(:IP))
|
372
|
+
orig_ip = true
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
if (!orig_ip)
|
377
|
+
ip = IPAdmin.unpack_ip_addr(:Integer => @network, :Version => @version)
|
378
|
+
else
|
379
|
+
ip = IPAdmin.unpack_ip_addr(:Integer => @ip, :Version => @version)
|
380
|
+
end
|
381
|
+
ip = IPAdmin.shorten(ip) if (short && @version == 6)
|
382
|
+
mask = IPAdmin.unpack_ip_netmask(:Integer => @netmask)
|
383
|
+
|
384
|
+
return("#{ip}/#{mask}")
|
385
|
+
end
|
386
|
+
|
387
|
+
#======================================#
|
388
|
+
#
|
389
|
+
#======================================#
|
390
|
+
|
391
|
+
|
392
|
+
#==============================================================================#
|
393
|
+
# enumerate()
|
394
|
+
#==============================================================================#
|
395
|
+
|
396
|
+
# Provide all IP addresses contained within the IP space of this CIDR.
|
397
|
+
# (warning: this can be quite large for big blocks)
|
398
|
+
#
|
399
|
+
# - Arguments:
|
400
|
+
# * Optional Hash with the following fields:
|
401
|
+
# - :Bitstep -- enumerate in X sized steps 0 - Integer (optional)
|
402
|
+
# - :Limit -- limit returned list to X number of items - Integer (optional)
|
403
|
+
# - :Objectify -- if true, return IPAdmin::CIDR objects (optional)
|
404
|
+
# - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
|
405
|
+
#
|
406
|
+
# - Returns:
|
407
|
+
# * Array of Strings or CIDR objects
|
408
|
+
#
|
409
|
+
# Example:
|
410
|
+
# ip_list = cidr4.enumerate(:Bitstep => 2, :Limit => 2) --> ['192.168.1.0','192.168.1.1']
|
411
|
+
#
|
412
|
+
def enumerate(options=nil)
|
413
|
+
bitstep = 1
|
414
|
+
objectify = false
|
415
|
+
limit = nil
|
416
|
+
short = false
|
417
|
+
|
418
|
+
if (options)
|
419
|
+
if (!options.kind_of? Hash)
|
420
|
+
raise ArgumentError, "Expected Hash, but #{options.class} provided."
|
421
|
+
end
|
422
|
+
|
423
|
+
if( options.has_key?(:Bitstep) )
|
424
|
+
bitstep = options[:Bitstep]
|
425
|
+
end
|
426
|
+
|
427
|
+
if( options.has_key?(:Objectify) )
|
428
|
+
objectify = true
|
429
|
+
end
|
430
|
+
|
431
|
+
if( options.has_key?(:Limit) )
|
432
|
+
limit = options[:Limit]
|
433
|
+
end
|
434
|
+
|
435
|
+
if( options.has_key?(:Short) )
|
436
|
+
short = true
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
list = []
|
441
|
+
my_ip = @network
|
442
|
+
change_mask = @hostmask | my_ip
|
443
|
+
|
444
|
+
until ( change_mask != (@hostmask | @network) )
|
445
|
+
if (!objectify)
|
446
|
+
my_ip_s = IPAdmin.unpack_ip_addr(:Integer => my_ip, :Version => @version)
|
447
|
+
my_ip_s = IPAdmin.shorten(my_ip_s) if (short && @version == 6)
|
448
|
+
list.push( my_ip_s )
|
449
|
+
else
|
450
|
+
list.push( IPAdmin::CIDR.new(:PackedIP => my_ip, :Version => @version) )
|
451
|
+
end
|
452
|
+
my_ip = my_ip + bitstep
|
453
|
+
change_mask = @hostmask | my_ip
|
454
|
+
if (limit)
|
455
|
+
limit = limit -1
|
456
|
+
break if (limit == 0)
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
return(list)
|
461
|
+
end
|
462
|
+
|
463
|
+
#======================================#
|
464
|
+
#
|
465
|
+
#======================================#
|
466
|
+
|
467
|
+
|
468
|
+
#==============================================================================#
|
469
|
+
# hostmask_ext()
|
470
|
+
#==============================================================================#
|
471
|
+
|
472
|
+
# Provide IPv4 Hostmask in extended format (y.y.y.y).
|
473
|
+
#
|
474
|
+
# - Arguments:
|
475
|
+
# * none
|
476
|
+
#
|
477
|
+
# - Returns:
|
478
|
+
# * String
|
479
|
+
#
|
480
|
+
# Example:
|
481
|
+
# puts cidr4.hostmask_ext() --> 0.0.0.255
|
482
|
+
#
|
483
|
+
def hostmask_ext()
|
484
|
+
if (@version == 4)
|
485
|
+
hostmask = IPAdmin.unpack_ip_addr(:Integer => @hostmask, :Version => @version)
|
486
|
+
else
|
487
|
+
raise "IPv6 does not support extended hostmask notation."
|
488
|
+
end
|
489
|
+
|
490
|
+
return(hostmask)
|
491
|
+
end
|
492
|
+
|
493
|
+
#======================================#
|
494
|
+
#
|
495
|
+
#======================================#
|
496
|
+
|
497
|
+
|
498
|
+
#==============================================================================#
|
499
|
+
# ip()
|
500
|
+
#==============================================================================#
|
501
|
+
|
502
|
+
# Provide original IP address passed during initialization.
|
503
|
+
#
|
504
|
+
# - Arguments:
|
505
|
+
# * Optional Hash with the following fields:
|
506
|
+
# - :Objectify -- if true, return IPAdmin::CIDR object (optional)
|
507
|
+
# - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
|
508
|
+
#
|
509
|
+
# - Returns:
|
510
|
+
# * String or CIDR object.
|
511
|
+
#
|
512
|
+
# Example:
|
513
|
+
# puts cidr4.ip() --> 192.168.1.1
|
514
|
+
#
|
515
|
+
def ip(options=nil)
|
516
|
+
objectify = false
|
517
|
+
short = false
|
518
|
+
|
519
|
+
if (options)
|
520
|
+
if (!options.kind_of?(Hash))
|
521
|
+
raise Argumenterror, "Expected Hash, but " +
|
522
|
+
"#{options.class} provided."
|
523
|
+
end
|
524
|
+
|
525
|
+
if( options.has_key?(:Short) )
|
526
|
+
short = true
|
527
|
+
end
|
528
|
+
|
529
|
+
if( options.has_key?(:Objectify) )
|
530
|
+
objectify = true
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
|
535
|
+
|
536
|
+
if (!objectify)
|
537
|
+
ip = IPAdmin.unpack_ip_addr(:Integer => @ip, :Version => @version)
|
538
|
+
ip = IPAdmin.shorten(ip) if (short && @version == 6)
|
539
|
+
else
|
540
|
+
ip = IPAdmin::CIDR.new(:PackedIP => @ip, :Version => @version)
|
541
|
+
end
|
542
|
+
|
543
|
+
return(ip)
|
544
|
+
end
|
545
|
+
|
546
|
+
#======================================#
|
547
|
+
#
|
548
|
+
#======================================#
|
549
|
+
|
550
|
+
|
551
|
+
#==============================================================================#
|
552
|
+
# last()
|
553
|
+
#==============================================================================#
|
554
|
+
|
555
|
+
# Provide last IP address in this CIDR object.
|
556
|
+
#
|
557
|
+
# - Arguments:
|
558
|
+
# * Optional Hash with the following fields:
|
559
|
+
# - :Objectify -- if true, return IPAdmin::CIDR object (optional)
|
560
|
+
# - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
|
561
|
+
#
|
562
|
+
# - Returns:
|
563
|
+
# * String or CIDR object.
|
564
|
+
#
|
565
|
+
# - Notes:
|
566
|
+
# * The broadcast() method is liased to this method, and thus works for
|
567
|
+
# IPv6 despite the fact that the IPv6 protocol does not support IP
|
568
|
+
# broadcasting.
|
569
|
+
#
|
570
|
+
# Example:
|
571
|
+
# puts cidr4.last() --> 192.168.1.255
|
572
|
+
#
|
573
|
+
def last(options=nil)
|
574
|
+
objectify = false
|
575
|
+
short = false
|
576
|
+
|
577
|
+
if (options)
|
578
|
+
if (!options.kind_of?(Hash))
|
579
|
+
raise Argumenterror, "Expected Hash, but " +
|
580
|
+
"#{options.class} provided."
|
581
|
+
end
|
582
|
+
|
583
|
+
if( options.has_key?(:Short) )
|
584
|
+
short = true
|
585
|
+
end
|
586
|
+
|
587
|
+
if( options.has_key?(:Objectify) )
|
588
|
+
objectify = true
|
589
|
+
end
|
590
|
+
|
591
|
+
end
|
592
|
+
|
593
|
+
packed_ip = @network | @hostmask
|
594
|
+
if (!objectify)
|
595
|
+
ip = IPAdmin.unpack_ip_addr(:Integer => packed_ip, :Version => @version)
|
596
|
+
ip = IPAdmin.shorten(ip) if (short && !objectify && @version == 6)
|
597
|
+
else
|
598
|
+
ip = IPAdmin::CIDR.new(:PackedIP => packed_ip, :Version => @version)
|
599
|
+
end
|
600
|
+
|
601
|
+
return(ip)
|
602
|
+
end
|
603
|
+
|
604
|
+
alias :broadcast :last
|
605
|
+
|
606
|
+
#======================================#
|
607
|
+
#
|
608
|
+
#======================================#
|
609
|
+
|
610
|
+
|
611
|
+
#==============================================================================#
|
612
|
+
# netmask()
|
613
|
+
#==============================================================================#
|
614
|
+
|
615
|
+
# Provide netmask in CIDR format.
|
616
|
+
#
|
617
|
+
# - Arguments:
|
618
|
+
# * none
|
619
|
+
#
|
620
|
+
# - Returns:
|
621
|
+
# * String
|
622
|
+
#
|
623
|
+
# Example:
|
624
|
+
# puts cidr4.netmask() --> /24
|
625
|
+
#
|
626
|
+
def netmask()
|
627
|
+
bits = IPAdmin.unpack_ip_netmask(:Integer => @netmask)
|
628
|
+
return("/#{bits}")
|
629
|
+
end
|
630
|
+
|
631
|
+
#======================================#
|
632
|
+
#
|
633
|
+
#======================================#
|
634
|
+
|
635
|
+
|
636
|
+
#==============================================================================#
|
637
|
+
# netmask_ext()
|
638
|
+
#==============================================================================#
|
639
|
+
|
640
|
+
# Provide IPv4 netmask in extended format (y.y.y.y).
|
641
|
+
#
|
642
|
+
# - Arguments:
|
643
|
+
# * none
|
644
|
+
#
|
645
|
+
# - Returns:
|
646
|
+
# * String
|
647
|
+
#
|
648
|
+
# Example:
|
649
|
+
# puts cidr4.netmask_ext() --> 255.255.255.0
|
650
|
+
#
|
651
|
+
def netmask_ext()
|
652
|
+
if (@version == 4)
|
653
|
+
netmask = IPAdmin.unpack_ip_addr(:Integer => @netmask)
|
654
|
+
else
|
655
|
+
raise "IPv6 does not support extended netmask notation. " +
|
656
|
+
"Use netmask() method instead."
|
657
|
+
end
|
658
|
+
|
659
|
+
return(netmask)
|
660
|
+
end
|
661
|
+
|
662
|
+
#======================================#
|
663
|
+
#
|
664
|
+
#======================================#
|
665
|
+
|
666
|
+
|
667
|
+
#==============================================================================#
|
668
|
+
# network()
|
669
|
+
#==============================================================================#
|
670
|
+
|
671
|
+
# Provide base network address.
|
672
|
+
#
|
673
|
+
# - Arguments:
|
674
|
+
# * Optional Hash with the following fields:
|
675
|
+
# - :Objectify -- if true, return IPAdmin::CIDR object (optional)
|
676
|
+
# - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
|
677
|
+
#
|
678
|
+
# - Returns:
|
679
|
+
# * String or CIDR object.
|
680
|
+
#
|
681
|
+
# Example:
|
682
|
+
# puts cidr4.network() --> 192.168.1.0
|
683
|
+
#
|
684
|
+
def network(options=nil)
|
685
|
+
objectify = false
|
686
|
+
short = false
|
687
|
+
|
688
|
+
if (options)
|
689
|
+
if (!options.kind_of?(Hash))
|
690
|
+
raise Argumenterror, "Expected Hash, but " +
|
691
|
+
"#{options.class} provided."
|
692
|
+
end
|
693
|
+
|
694
|
+
if( options.has_key?(:Short) )
|
695
|
+
short = true
|
696
|
+
end
|
697
|
+
|
698
|
+
if( options.has_key?(:Objectify) )
|
699
|
+
objectify = true
|
700
|
+
end
|
701
|
+
end
|
702
|
+
|
703
|
+
|
704
|
+
if (!objectify)
|
705
|
+
ip = IPAdmin.unpack_ip_addr(:Integer => @network, :Version => @version)
|
706
|
+
ip = IPAdmin.shorten(ip) if (short && @version == 6)
|
707
|
+
else
|
708
|
+
ip = IPAdmin::CIDR.new(:PackedIP => @network, :Version => @version)
|
709
|
+
end
|
710
|
+
|
711
|
+
return(ip)
|
712
|
+
end
|
713
|
+
|
714
|
+
alias :base :network
|
715
|
+
alias :first :network
|
716
|
+
|
717
|
+
#======================================#
|
718
|
+
#
|
719
|
+
#======================================#
|
720
|
+
|
721
|
+
|
722
|
+
#==============================================================================#
|
723
|
+
# next_ip()
|
724
|
+
#==============================================================================#
|
725
|
+
|
726
|
+
# Provide the next IP following the last available IP within this
|
727
|
+
# CIDR object.
|
728
|
+
#
|
729
|
+
# - Arguments:
|
730
|
+
# * Optional Hash with the following fields:
|
731
|
+
# - :Bitstep -- step in X sized steps - Integer (optional)
|
732
|
+
# - :Objectify -- if true, return IPAdmin::CIDR object (optional)
|
733
|
+
# - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
|
734
|
+
#
|
735
|
+
# - Returns:
|
736
|
+
# * String or CIDR object.
|
737
|
+
#
|
738
|
+
# Example:
|
739
|
+
# puts cidr4.next_ip() --> 192.168.2.0
|
740
|
+
#
|
741
|
+
def next_ip(options=nil)
|
742
|
+
bitstep = 1
|
743
|
+
objectify = false
|
744
|
+
short = false
|
745
|
+
|
746
|
+
if (options)
|
747
|
+
if (!options.kind_of?(Hash))
|
748
|
+
raise Argumenterror, "Expected Hash, but " +
|
749
|
+
"#{options.class} provided."
|
750
|
+
end
|
751
|
+
|
752
|
+
if( options.has_key?(:Bitstep) )
|
753
|
+
bitstep = options[:Bitstep]
|
754
|
+
end
|
755
|
+
|
756
|
+
if( options.has_key?(:Short) )
|
757
|
+
short = true
|
758
|
+
end
|
759
|
+
|
760
|
+
if( options.has_key?(:Objectify) )
|
761
|
+
objectify = true
|
762
|
+
end
|
763
|
+
end
|
764
|
+
|
765
|
+
next_ip = @network + @hostmask + bitstep
|
766
|
+
|
767
|
+
if (next_ip > @all_f)
|
768
|
+
raise "Returned IP is out of bounds for IPv#{@version}."
|
769
|
+
end
|
770
|
+
|
771
|
+
|
772
|
+
if (!objectify)
|
773
|
+
next_ip = IPAdmin.unpack_ip_addr(:Integer => next_ip, :Version => @version)
|
774
|
+
next_ip = IPAdmin.shorten(next_ip) if (short && @version == 6)
|
775
|
+
else
|
776
|
+
next_ip = IPAdmin::CIDR.new(:PackedIP => next_ip, :Version => @version)
|
777
|
+
end
|
778
|
+
|
779
|
+
return(next_ip)
|
780
|
+
end
|
781
|
+
|
782
|
+
#======================================#
|
783
|
+
#
|
784
|
+
#======================================#
|
785
|
+
|
786
|
+
|
787
|
+
#==============================================================================#
|
788
|
+
# next_subnet()
|
789
|
+
#==============================================================================#
|
790
|
+
|
791
|
+
# Provide the next subnet following this CIDR object. The next subnet will
|
792
|
+
# be of the same size as the current CIDR object.
|
793
|
+
#
|
794
|
+
# - Arguments:
|
795
|
+
# * Optional Hash with the following fields:
|
796
|
+
# - :Bitstep -- step in X sized steps. - Integer (optional)
|
797
|
+
# - :Objectify -- if true, return IPAdmin::CIDR object (optional)
|
798
|
+
# - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
|
799
|
+
#
|
800
|
+
# - Returns:
|
801
|
+
# * String or CIDR object.
|
802
|
+
#
|
803
|
+
# Example:
|
804
|
+
# puts cidr4.next_subnet() --> 192.168.2.0/24
|
805
|
+
#
|
806
|
+
def next_subnet(options=nil)
|
807
|
+
bitstep = 1
|
808
|
+
objectify = false
|
809
|
+
short = false
|
810
|
+
|
811
|
+
if (options)
|
812
|
+
if (!options.kind_of?(Hash))
|
813
|
+
raise Argumenterror, "Expected Hash, but " +
|
814
|
+
"#{options.class} provided."
|
815
|
+
end
|
816
|
+
|
817
|
+
if( options.has_key?(:Bitstep) )
|
818
|
+
bitstep = options[:Bitstep]
|
819
|
+
end
|
820
|
+
|
821
|
+
if( options.has_key?(:Short) )
|
822
|
+
short = true
|
823
|
+
end
|
824
|
+
|
825
|
+
if( options.has_key?(:Objectify) )
|
826
|
+
objectify = true
|
827
|
+
end
|
828
|
+
end
|
829
|
+
|
830
|
+
bitstep = bitstep * (2**(@max_bits - self.bits) )
|
831
|
+
next_sub = @network + bitstep
|
832
|
+
|
833
|
+
if (next_sub > @all_f)
|
834
|
+
raise "Returned subnet is out of bounds for IPv#{@version}."
|
835
|
+
end
|
836
|
+
|
837
|
+
netmask = self.bits
|
838
|
+
if (!objectify)
|
839
|
+
next_sub = IPAdmin.unpack_ip_addr(:Integer => next_sub, :Version => @version)
|
840
|
+
next_sub = IPAdmin.shorten(next_sub) if (short && @version == 6)
|
841
|
+
next_sub = next_sub << "/" << netmask.to_s
|
842
|
+
else
|
843
|
+
next_sub = IPAdmin::CIDR.new(:PackedIP => next_sub, :Netmask => netmask, :Version => @version)
|
844
|
+
end
|
845
|
+
|
846
|
+
return(next_sub)
|
847
|
+
end
|
848
|
+
|
849
|
+
#======================================#
|
850
|
+
#
|
851
|
+
#======================================#
|
852
|
+
|
853
|
+
|
854
|
+
#==============================================================================#
|
855
|
+
# nth()
|
856
|
+
#==============================================================================#
|
857
|
+
|
858
|
+
# Provide the nth IP within this object.
|
859
|
+
#
|
860
|
+
# - Arguments:
|
861
|
+
# * Hash with the following fields:
|
862
|
+
# - :Index -- index number of the IP address to return - Integer
|
863
|
+
# - :Objectify -- if true, return IPAdmin::CIDR objects (optional)
|
864
|
+
# - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
|
865
|
+
#
|
866
|
+
# - Returns:
|
867
|
+
# * String or CIDR object.
|
868
|
+
#
|
869
|
+
# Example:
|
870
|
+
# puts cidr4.nth(:Index => 1) --> 192.168.1.1
|
871
|
+
#
|
872
|
+
def nth(options)
|
873
|
+
objectify = false
|
874
|
+
short = false
|
875
|
+
|
876
|
+
if (!options.kind_of?(Hash))
|
877
|
+
raise ArgumentError, "Expected Hash, but " +
|
878
|
+
"#{options.class} provided."
|
879
|
+
end
|
880
|
+
|
881
|
+
if ( !options.has_key?(:Index) )
|
882
|
+
raise ArgumentError, "Missing argument: Index."
|
883
|
+
end
|
884
|
+
index = options[:Index]
|
885
|
+
|
886
|
+
if( options.has_key?(:Short) )
|
887
|
+
short = true
|
888
|
+
end
|
889
|
+
|
890
|
+
if( options.has_key?(:Objectify) )
|
891
|
+
objectify = true
|
892
|
+
end
|
893
|
+
|
894
|
+
my_ip = @network + index
|
895
|
+
if ( (@hostmask | my_ip) == (@hostmask | @network) )
|
896
|
+
|
897
|
+
if (!objectify)
|
898
|
+
my_ip = IPAdmin.unpack_ip_addr(:Integer => my_ip, :Version => @version)
|
899
|
+
my_ip = IPAdmin.shorten(my_ip) if (short && @version == 6)
|
900
|
+
else
|
901
|
+
my_ip = IPAdmin::CIDR.new(:PackedIP => my_ip, :Version => @version)
|
902
|
+
end
|
903
|
+
|
904
|
+
else
|
905
|
+
raise "Index of #{index} returns IP that is out of " +
|
906
|
+
"bounds of CIDR network."
|
907
|
+
end
|
908
|
+
|
909
|
+
return(my_ip)
|
910
|
+
end
|
911
|
+
|
912
|
+
#======================================#
|
913
|
+
#
|
914
|
+
#======================================#
|
915
|
+
|
916
|
+
|
917
|
+
#==============================================================================#
|
918
|
+
# packed_hostmask()
|
919
|
+
#==============================================================================#
|
920
|
+
|
921
|
+
# Provide an integer representation of the Hostmask of this object.
|
922
|
+
#
|
923
|
+
# - Arguments:
|
924
|
+
# * none
|
925
|
+
#
|
926
|
+
# - Returns:
|
927
|
+
# * Integer
|
928
|
+
#
|
929
|
+
# Example:
|
930
|
+
# puts cidr4.packed_hostmask().to_s(16) --> 0xff
|
931
|
+
#
|
932
|
+
def packed_hostmask()
|
933
|
+
return(@hostmask)
|
934
|
+
end
|
935
|
+
|
936
|
+
#======================================#
|
937
|
+
#
|
938
|
+
#======================================#
|
939
|
+
|
940
|
+
|
941
|
+
#==============================================================================#
|
942
|
+
# packed_ip()
|
943
|
+
#==============================================================================#
|
944
|
+
|
945
|
+
# Provide an integer representation of the IP address of this object.
|
946
|
+
#
|
947
|
+
# - Arguments:
|
948
|
+
# * none
|
949
|
+
#
|
950
|
+
# - Returns:
|
951
|
+
# * Integer
|
952
|
+
#
|
953
|
+
# Example:
|
954
|
+
# puts cidr4.packed_ip().to_s(16) --> 0xc0c80101
|
955
|
+
#
|
956
|
+
def packed_ip()
|
957
|
+
return(@ip)
|
958
|
+
end
|
959
|
+
|
960
|
+
#======================================#
|
961
|
+
#
|
962
|
+
#======================================#
|
963
|
+
|
964
|
+
|
965
|
+
#==============================================================================#
|
966
|
+
# packed_netmask()
|
967
|
+
#==============================================================================#
|
968
|
+
|
969
|
+
# Provide an integer representation of the Netmask of this object.
|
970
|
+
#
|
971
|
+
# - Arguments:
|
972
|
+
# * none
|
973
|
+
#
|
974
|
+
# - Returns:
|
975
|
+
# * Integer
|
976
|
+
#
|
977
|
+
# Example:
|
978
|
+
# puts cidr4.packed_netmask().to_s(16) --> 0xffffff00
|
979
|
+
#
|
980
|
+
def packed_netmask()
|
981
|
+
return(@netmask)
|
982
|
+
end
|
983
|
+
|
984
|
+
#======================================#
|
985
|
+
#
|
986
|
+
#======================================#
|
987
|
+
|
988
|
+
|
989
|
+
#==============================================================================#
|
990
|
+
# packed_network()
|
991
|
+
#==============================================================================#
|
992
|
+
|
993
|
+
# Provide an integer representation of the Network address of this object.
|
994
|
+
#
|
995
|
+
# - Arguments:
|
996
|
+
# * none
|
997
|
+
#
|
998
|
+
# - Returns:
|
999
|
+
# * Integer
|
1000
|
+
#
|
1001
|
+
# Example:
|
1002
|
+
# packed = cidr4.packed_network().to_s(16) --> 0xc0c80100
|
1003
|
+
#
|
1004
|
+
def packed_network()
|
1005
|
+
return(@network)
|
1006
|
+
end
|
1007
|
+
|
1008
|
+
#======================================#
|
1009
|
+
#
|
1010
|
+
#======================================#
|
1011
|
+
|
1012
|
+
|
1013
|
+
#==============================================================================#
|
1014
|
+
# range()
|
1015
|
+
#==============================================================================#
|
1016
|
+
|
1017
|
+
# Given two Indexes, return all IP addresses within the CIDR that are
|
1018
|
+
# between them (inclusive).
|
1019
|
+
#
|
1020
|
+
# - Arguments:
|
1021
|
+
# * Hash with the following fields:
|
1022
|
+
# - :Bitstep -- enumerate in X sized steps - Integer (optional)
|
1023
|
+
# - :Indexes -- index numbers of the addresses to use as boundaries - Array of (2) Integers
|
1024
|
+
# - :Objectify -- if true, return IPAdmin::CIDR objects (optional)
|
1025
|
+
# - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
|
1026
|
+
#
|
1027
|
+
# - Returns:
|
1028
|
+
# * Array Strings or CIDR objects
|
1029
|
+
#
|
1030
|
+
# Example:
|
1031
|
+
# list = cidr4.range(:Indexes => [0,1]) --> ['192.168.1.0','192.168.1.1']
|
1032
|
+
#
|
1033
|
+
def range(options)
|
1034
|
+
objectify = false
|
1035
|
+
short = false
|
1036
|
+
bitstep = 1
|
1037
|
+
|
1038
|
+
if (!options.kind_of?(Hash))
|
1039
|
+
raise Argumenterror, "Expected Hash, but #{options.class} provided."
|
1040
|
+
end
|
1041
|
+
|
1042
|
+
if ( !options.has_key?(:Indexes) )
|
1043
|
+
raise ArgumentError, "Missing argument: Indexes."
|
1044
|
+
end
|
1045
|
+
indexes = options[:Indexes]
|
1046
|
+
indexes.sort!
|
1047
|
+
|
1048
|
+
if( (!indexes.kind_of?(Array)) || (indexes.length != 2))
|
1049
|
+
raise ArgumentError, "Argument :Index should be an array of (2) index numbers."
|
1050
|
+
end
|
1051
|
+
|
1052
|
+
if ( (indexes[0] < 0) || (indexes[0] > self.size) )
|
1053
|
+
raise ArgumentError, "Index #{indexes[0]} is out of bounds for this CIDR."
|
1054
|
+
end
|
1055
|
+
|
1056
|
+
if (indexes[1] >= self.size)
|
1057
|
+
raise ArgumentError, "Index #{indexes[1]} is out of bounds for this CIDR."
|
1058
|
+
end
|
1059
|
+
|
1060
|
+
if( options.has_key?(:Short) )
|
1061
|
+
short = true
|
1062
|
+
end
|
1063
|
+
|
1064
|
+
if( options.has_key?(:Objectify) )
|
1065
|
+
objectify = true
|
1066
|
+
end
|
1067
|
+
|
1068
|
+
if( options.has_key?(:Bitstep) )
|
1069
|
+
bitstep = options[:Bitstep]
|
1070
|
+
end
|
1071
|
+
|
1072
|
+
start_ip = @network + indexes[0]
|
1073
|
+
end_ip = @network + indexes[1]
|
1074
|
+
my_ip = start_ip
|
1075
|
+
list = []
|
1076
|
+
until (my_ip > end_ip)
|
1077
|
+
if (!objectify)
|
1078
|
+
ip = IPAdmin.unpack_ip_addr(:Integer => my_ip, :Version => @version)
|
1079
|
+
ip = IPAdmin.shorten(ip) if (short && @version == 6)
|
1080
|
+
else
|
1081
|
+
ip = IPAdmin::CIDR.new(:PackedIP => my_ip, :Version => @version)
|
1082
|
+
end
|
1083
|
+
|
1084
|
+
list.push(ip)
|
1085
|
+
my_ip += bitstep
|
1086
|
+
end
|
1087
|
+
|
1088
|
+
return(list)
|
1089
|
+
end
|
1090
|
+
|
1091
|
+
#======================================#
|
1092
|
+
#
|
1093
|
+
#======================================#
|
1094
|
+
|
1095
|
+
|
1096
|
+
#==============================================================================#
|
1097
|
+
# remainder()
|
1098
|
+
#==============================================================================#
|
1099
|
+
|
1100
|
+
# Given a portion of the current CIDR, provide the remainder of
|
1101
|
+
# the CIDR. For example if the original CIDR is 192.168.0.0/24 and you
|
1102
|
+
# provide 192.168.0.64/26 as the portion to exclude, then 192.168.0.0/26,
|
1103
|
+
# 192.168.0.128/26, and 192.168.0.192/26 will be returned as the remainder.
|
1104
|
+
#
|
1105
|
+
# - Arguments:
|
1106
|
+
# * Optional hash with the following fields:
|
1107
|
+
# - :Exclude -- CIDR object to use in calculating the remainder.
|
1108
|
+
# - :Objectify -- if true, return IPAdmin::CIDR objects (optional)
|
1109
|
+
# - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
|
1110
|
+
#
|
1111
|
+
# - Returns:
|
1112
|
+
# * Array of Strings or CIDR objects
|
1113
|
+
#
|
1114
|
+
#
|
1115
|
+
# Example:
|
1116
|
+
# cidr4_2 = IPAdmin::CIDR.new(:CIDR => '192.168.1.64/26')
|
1117
|
+
# cidr4.remainder(:Exclude => cidr4_2).each {|x| puts}
|
1118
|
+
#
|
1119
|
+
def remainder(options)
|
1120
|
+
short = nil
|
1121
|
+
objectify = nil
|
1122
|
+
|
1123
|
+
if (!options.kind_of? Hash)
|
1124
|
+
raise ArgumentError, "Expected Hash, but #{options.class} provided."
|
1125
|
+
end
|
1126
|
+
|
1127
|
+
if ( !options.has_key?(:Exclude) )
|
1128
|
+
raise ArgumentError, "Missing argument: Exclude."
|
1129
|
+
end
|
1130
|
+
to_exclude = options[:Exclude]
|
1131
|
+
|
1132
|
+
if ( !to_exclude.kind_of?(IPAdmin::CIDR) )
|
1133
|
+
raise ArgumentError, "Expeced IPAdmin::CIDR, but #{exclude.class} " +
|
1134
|
+
"for option: Exclude."
|
1135
|
+
end
|
1136
|
+
|
1137
|
+
if( options.has_key?(:Short) )
|
1138
|
+
short = true
|
1139
|
+
end
|
1140
|
+
|
1141
|
+
if( options.has_key?(:Objectify) )
|
1142
|
+
objectify = true
|
1143
|
+
end
|
1144
|
+
|
1145
|
+
# make sure 'to_exclude' is the same ip version
|
1146
|
+
if ( to_exclude.version != @version )
|
1147
|
+
raise "#{to_exclude.desc(:Short => true)} is of a different " +
|
1148
|
+
"IP version than #{self.desc(:Short => true)}."
|
1149
|
+
end
|
1150
|
+
|
1151
|
+
# make sure we contain 'to_exclude'
|
1152
|
+
if ( self.contains?(to_exclude) != true )
|
1153
|
+
raise "#{to_exclude.desc(:Short => true)} does not fit " +
|
1154
|
+
"within the bounds of #{self.desc(:Short => true)}."
|
1155
|
+
end
|
1156
|
+
|
1157
|
+
# split this cidr in half & see which half 'to_exclude'
|
1158
|
+
# belongs in. take that half & repeat the process. every time
|
1159
|
+
# we repeat, store off the non-matching half
|
1160
|
+
new_mask = self.bits + 1
|
1161
|
+
lower_network = self.packed_network
|
1162
|
+
upper_network = self.packed_network + 2**(@max_bits - new_mask)
|
1163
|
+
|
1164
|
+
new_subnets = []
|
1165
|
+
until(new_mask > to_exclude.bits)
|
1166
|
+
if (to_exclude.packed_network < upper_network)
|
1167
|
+
match = lower_network
|
1168
|
+
non_match = upper_network
|
1169
|
+
else
|
1170
|
+
match = upper_network
|
1171
|
+
non_match = lower_network
|
1172
|
+
end
|
1173
|
+
|
1174
|
+
|
1175
|
+
if (!objectify)
|
1176
|
+
non_match = IPAdmin.unpack_ip_addr(:Integer => non_match, :Version => @version)
|
1177
|
+
non_match = IPAdmin.shorten(non_match) if (short && @version == 6)
|
1178
|
+
new_subnets.unshift("#{non_match}/#{new_mask}")
|
1179
|
+
else
|
1180
|
+
new_subnets.unshift(IPAdmin::CIDR.new(:PackedIP => non_match,
|
1181
|
+
:Netmask => new_mask,
|
1182
|
+
:Version => @version))
|
1183
|
+
end
|
1184
|
+
|
1185
|
+
new_mask = new_mask + 1
|
1186
|
+
lower_network = match
|
1187
|
+
upper_network = match + 2**(@max_bits - new_mask)
|
1188
|
+
end
|
1189
|
+
|
1190
|
+
return(new_subnets)
|
1191
|
+
end
|
1192
|
+
|
1193
|
+
#======================================#
|
1194
|
+
#
|
1195
|
+
#======================================#
|
1196
|
+
|
1197
|
+
|
1198
|
+
#==============================================================================#
|
1199
|
+
# resize()
|
1200
|
+
#==============================================================================#
|
1201
|
+
|
1202
|
+
# Resize the CIDR by changing the size of the Netmask.
|
1203
|
+
# Return the resulting CIDR as a new object.
|
1204
|
+
#
|
1205
|
+
# - Arguments:
|
1206
|
+
# * Hash with the following fields:
|
1207
|
+
# - :Subnet -- Number of bits of new Netmask - Integer
|
1208
|
+
#
|
1209
|
+
# - Returns:
|
1210
|
+
# * CIDR object
|
1211
|
+
#
|
1212
|
+
# Example:
|
1213
|
+
# new_cidr = cidr4.resize(:Subnet => 23)
|
1214
|
+
# puts new_cidr.desc --> 192.168.1.0/23
|
1215
|
+
#
|
1216
|
+
def resize(options)
|
1217
|
+
if (!options.kind_of?(Hash))
|
1218
|
+
raise Argumenterror, "Expected Hash, but " +
|
1219
|
+
"#{options.class} provided."
|
1220
|
+
end
|
1221
|
+
|
1222
|
+
if ( !options.has_key?(:Subnet) )
|
1223
|
+
raise Argumenterror, "Missing argument: Subnet."
|
1224
|
+
end
|
1225
|
+
bits = options[:Subnet]
|
1226
|
+
|
1227
|
+
IPAdmin.validate_ip_netmask(:Netmask => bits, :Version => @version)
|
1228
|
+
netmask = IPAdmin.pack_ip_netmask(:Netmask => bits, :Version => @version)
|
1229
|
+
network = @network & netmask
|
1230
|
+
|
1231
|
+
cidr = IPAdmin::CIDR.new(:PackedIP => network, :PackedNetmask => netmask, :Version => @version)
|
1232
|
+
return(cidr)
|
1233
|
+
end
|
1234
|
+
|
1235
|
+
#======================================#
|
1236
|
+
#
|
1237
|
+
#======================================#
|
1238
|
+
|
1239
|
+
|
1240
|
+
#==============================================================================#
|
1241
|
+
# resize!()
|
1242
|
+
#==============================================================================#
|
1243
|
+
|
1244
|
+
# Resize this object by changing the size of the Netmask.
|
1245
|
+
#
|
1246
|
+
# - Arguments:
|
1247
|
+
# * Hash with the following fields:
|
1248
|
+
# - :Subnet -- Number of bits of new Netmask - Integer
|
1249
|
+
#
|
1250
|
+
# - Returns:
|
1251
|
+
# * nil
|
1252
|
+
#
|
1253
|
+
# - Notes:
|
1254
|
+
# * If CIDR is resized such that the original IP is no longer contained within,
|
1255
|
+
# then that IP will be reset to the base network address.
|
1256
|
+
#
|
1257
|
+
# Example:
|
1258
|
+
# cidr4.resize!(:Subnet => 23)
|
1259
|
+
# puts cidr4.desc --> 192.168.1.0/23
|
1260
|
+
#
|
1261
|
+
def resize!(options)
|
1262
|
+
if (!options.kind_of?(Hash))
|
1263
|
+
raise Argumenterror, "Expected Hash, but " +
|
1264
|
+
"#{options.class} provided."
|
1265
|
+
end
|
1266
|
+
|
1267
|
+
if ( !options.has_key?(:Subnet) )
|
1268
|
+
raise Argumenterror, "Missing argument: Subnet."
|
1269
|
+
end
|
1270
|
+
bits = options[:Subnet]
|
1271
|
+
|
1272
|
+
IPAdmin.validate_ip_netmask(:Netmask => bits, :Version => @version)
|
1273
|
+
netmask = IPAdmin.pack_ip_netmask(:Netmask => bits, :Version => @version)
|
1274
|
+
|
1275
|
+
@netmask = netmask
|
1276
|
+
@network = @network & netmask
|
1277
|
+
@hostmask = @netmask ^ @all_f
|
1278
|
+
|
1279
|
+
# check @ip
|
1280
|
+
if ((@ip & @netmask) != (@network))
|
1281
|
+
@ip = @network
|
1282
|
+
end
|
1283
|
+
|
1284
|
+
return(nil)
|
1285
|
+
end
|
1286
|
+
|
1287
|
+
#======================================#
|
1288
|
+
#
|
1289
|
+
#======================================#
|
1290
|
+
|
1291
|
+
|
1292
|
+
#==============================================================================#
|
1293
|
+
# size()
|
1294
|
+
#==============================================================================#
|
1295
|
+
|
1296
|
+
# Provide number of IP addresses within this object.
|
1297
|
+
#
|
1298
|
+
# - Arguments:
|
1299
|
+
# * none
|
1300
|
+
#
|
1301
|
+
# - Returns:
|
1302
|
+
# * Integer
|
1303
|
+
#
|
1304
|
+
# Example:
|
1305
|
+
# puts cidr4.size() --> 256
|
1306
|
+
#
|
1307
|
+
def size()
|
1308
|
+
return(@hostmask + 1)
|
1309
|
+
end
|
1310
|
+
|
1311
|
+
#======================================#
|
1312
|
+
#
|
1313
|
+
#======================================#
|
1314
|
+
|
1315
|
+
|
1316
|
+
#==============================================================================#
|
1317
|
+
# subnet()
|
1318
|
+
#==============================================================================#
|
1319
|
+
|
1320
|
+
# Subnet this object. Object will be fully subnetted into X number of subnets
|
1321
|
+
# of specified size. If :MinCount is provided, then method will return at least
|
1322
|
+
# that number of subnets (of size X) and the remainder of the new subnets
|
1323
|
+
# will be merged together as tightly as possible. If a size is not provided,
|
1324
|
+
# then the current object will be split in half.
|
1325
|
+
#
|
1326
|
+
# - Arguments:
|
1327
|
+
# * Optional hash with the following fields:
|
1328
|
+
# - :IPCount -- Minimum number of IP's that new subnets should contain - Integer (optional)
|
1329
|
+
# - :MinCount -- Minimum number of X sized subnets to return - Integer (optional)
|
1330
|
+
# - :Objectify -- if true, return IPAdmin::CIDR objects (optional)
|
1331
|
+
# - :Short -- if true, return IPv6 addresses in short-hand notation (optional)
|
1332
|
+
# - :Subnet -- Netmask (in bits) of new subnets - Integer (optional)
|
1333
|
+
#
|
1334
|
+
# - Returns:
|
1335
|
+
# * Array of CIDR addresses or IPAdmin::CIDR objects
|
1336
|
+
#
|
1337
|
+
# - Notes:
|
1338
|
+
# * :Subnet always takes precedence over :IPCount.
|
1339
|
+
#
|
1340
|
+
# Example:
|
1341
|
+
# cidr_list = cidr4.subnet(:Subnet => 28, :MinCount => 3)
|
1342
|
+
# cidr_list = cidr4.subnet(:IPCount => 19)
|
1343
|
+
# puts cidr_list[0] --> 192.168.1.0/27
|
1344
|
+
#
|
1345
|
+
def subnet(options=nil)
|
1346
|
+
my_network = self.packed_network
|
1347
|
+
my_mask = self.bits
|
1348
|
+
subnet_bits = my_mask + 1
|
1349
|
+
min_count = nil
|
1350
|
+
objectify = false
|
1351
|
+
short = false
|
1352
|
+
|
1353
|
+
if (options)
|
1354
|
+
if (!options.kind_of? Hash)
|
1355
|
+
raise ArgumentError, "Expected Hash, but #{options.class} provided."
|
1356
|
+
end
|
1357
|
+
|
1358
|
+
if ( options.has_key?(:IPCount) )
|
1359
|
+
subnet_bits = IPAdmin.minimum_size(:IPCount => options[:IPCount],
|
1360
|
+
:Version => @version)
|
1361
|
+
end
|
1362
|
+
|
1363
|
+
if ( options.has_key?(:Subnet) )
|
1364
|
+
subnet_bits = options[:Subnet]
|
1365
|
+
end
|
1366
|
+
|
1367
|
+
if ( options.has_key?(:MinCount) )
|
1368
|
+
min_count = options[:MinCount]
|
1369
|
+
end
|
1370
|
+
|
1371
|
+
if( options.has_key?(:Short) )
|
1372
|
+
short = true
|
1373
|
+
end
|
1374
|
+
|
1375
|
+
if( options.has_key?(:Objectify) )
|
1376
|
+
objectify = true
|
1377
|
+
end
|
1378
|
+
|
1379
|
+
end
|
1380
|
+
|
1381
|
+
# get number of subnets possible with the requested subnet_bits
|
1382
|
+
num_avail = 2**(subnet_bits - my_mask)
|
1383
|
+
|
1384
|
+
# get the number of bits in the next supernet and
|
1385
|
+
# make sure min_count is a power of 2
|
1386
|
+
bits_needed = 1
|
1387
|
+
min_count = num_avail if (!min_count)
|
1388
|
+
until (2**bits_needed >= min_count)
|
1389
|
+
bits_needed += 1
|
1390
|
+
end
|
1391
|
+
min_count = 2**bits_needed
|
1392
|
+
next_supernet_bits = subnet_bits - bits_needed
|
1393
|
+
|
1394
|
+
|
1395
|
+
# make sure subnet isnt bigger than available bits
|
1396
|
+
if (subnet_bits > @max_bits)
|
1397
|
+
raise "Requested subnet (#{subnet_bits}) does not fit " +
|
1398
|
+
"within the bounds of IPv#{@version}."
|
1399
|
+
end
|
1400
|
+
|
1401
|
+
# make sure subnet is larger than mymask
|
1402
|
+
if (subnet_bits < my_mask)
|
1403
|
+
raise "Requested subnet (#{subnet_bits}) is too large for " +
|
1404
|
+
"current CIDR space."
|
1405
|
+
end
|
1406
|
+
|
1407
|
+
# make sure MinCount is smaller than available subnets
|
1408
|
+
if (min_count > num_avail)
|
1409
|
+
raise "Requested subnet count (#{min_count}) exceeds subnets " +
|
1410
|
+
"available for allocation (#{num_avail})."
|
1411
|
+
end
|
1412
|
+
|
1413
|
+
# list all 'subnet_bits' sized subnets of this cidr block
|
1414
|
+
# with a limit of min_count
|
1415
|
+
bitstep = 2**(@max_bits - subnet_bits)
|
1416
|
+
subnets = self.enumerate(:Bitstep => bitstep, :Limit => min_count)
|
1417
|
+
|
1418
|
+
# save our subnets
|
1419
|
+
new_subnets = []
|
1420
|
+
subnets.each do |subnet|
|
1421
|
+
if (!objectify)
|
1422
|
+
subnet = IPAdmin.shorten(subnet) if (short && @version == 6)
|
1423
|
+
new_subnets.push("#{subnet}/#{subnet_bits}")
|
1424
|
+
else
|
1425
|
+
new_subnets.push(IPAdmin::CIDR.new(:CIDR => "#{subnet}/#{subnet_bits}", :Version => @version))
|
1426
|
+
end
|
1427
|
+
end
|
1428
|
+
|
1429
|
+
# now go through the rest of the cidr space and make the rest
|
1430
|
+
# of the subnets. we want these to be as tightly merged as possible
|
1431
|
+
next_supernet_bitstep = (bitstep * min_count)
|
1432
|
+
next_supernet_ip = my_network + next_supernet_bitstep
|
1433
|
+
until (next_supernet_bits == my_mask)
|
1434
|
+
if (!objectify)
|
1435
|
+
next_network = IPAdmin.unpack_ip_addr(:Integer => next_supernet_ip, :Version => @version)
|
1436
|
+
next_network = IPAdmin.shorten(next_network) if (short && @version == 6)
|
1437
|
+
new_subnets.push("#{next_network}/#{next_supernet_bits}")
|
1438
|
+
else
|
1439
|
+
new_subnets.push(IPAdmin::CIDR.new(:PackedIP => next_supernet_ip,
|
1440
|
+
:Netmask =>next_supernet_bits,
|
1441
|
+
:Version => @version))
|
1442
|
+
end
|
1443
|
+
|
1444
|
+
next_supernet_bits -= 1
|
1445
|
+
next_supernet_ip = next_supernet_ip + next_supernet_bitstep
|
1446
|
+
next_supernet_bitstep = next_supernet_bitstep << 1
|
1447
|
+
end
|
1448
|
+
|
1449
|
+
return(new_subnets)
|
1450
|
+
end
|
1451
|
+
|
1452
|
+
#======================================#
|
1453
|
+
#
|
1454
|
+
#======================================#
|
1455
|
+
|
1456
|
+
end
|
1457
|
+
|
1458
|
+
end # module IPAdmin
|
1459
|
+
__END__
|