ipaddress_2 0.11.0
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.
- checksums.yaml +7 -0
- data/.byebug_history +25 -0
- data/.document +5 -0
- data/.gitignore +20 -0
- data/.rock.yml +5 -0
- data/.travis.yml +13 -0
- data/CHANGELOG.md +31 -0
- data/CHANGELOG.rdoc +115 -0
- data/CONTRIBUTING.md +39 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +964 -0
- data/Rakefile +69 -0
- data/VERSION +1 -0
- data/ipaddress_2.gemspec +30 -0
- data/lib/ipaddress_2/ipv4.rb +1204 -0
- data/lib/ipaddress_2/ipv6.rb +1274 -0
- data/lib/ipaddress_2/mongoid.rb +75 -0
- data/lib/ipaddress_2/prefix.rb +285 -0
- data/lib/ipaddress_2/version.rb +3 -0
- data/lib/ipaddress_2.rb +293 -0
- data/test/ipaddress_2/ipv4_test.rb +701 -0
- data/test/ipaddress_2/ipv6_test.rb +707 -0
- data/test/ipaddress_2/mongoid_test.rb +70 -0
- data/test/ipaddress_2/prefix_test.rb +171 -0
- data/test/ipaddress_2_test.rb +109 -0
- data/test/test_helper.rb +37 -0
- metadata +195 -0
@@ -0,0 +1,1204 @@
|
|
1
|
+
require 'ipaddress_2/prefix'
|
2
|
+
|
3
|
+
module IPAddress;
|
4
|
+
#
|
5
|
+
# =Name
|
6
|
+
#
|
7
|
+
# IPAddress::IPv4 - IP version 4 address manipulation library
|
8
|
+
#
|
9
|
+
# =Synopsis
|
10
|
+
#
|
11
|
+
# require 'ipaddress'
|
12
|
+
#
|
13
|
+
# =Description
|
14
|
+
#
|
15
|
+
# Class IPAddress::IPv4 is used to handle IPv4 type addresses.
|
16
|
+
#
|
17
|
+
class IPv4
|
18
|
+
|
19
|
+
include IPAddress
|
20
|
+
include Enumerable
|
21
|
+
include Comparable
|
22
|
+
|
23
|
+
#
|
24
|
+
# This Hash contains the prefix values for Classful networks
|
25
|
+
#
|
26
|
+
# Note that classes C, D and E will all have a default
|
27
|
+
# prefix of /24 or 255.255.255.0
|
28
|
+
#
|
29
|
+
CLASSFUL = {
|
30
|
+
/^0../ => 8, # Class A, from 0.0.0.0 to 127.255.255.255
|
31
|
+
/^10./ => 16, # Class B, from 128.0.0.0 to 191.255.255.255
|
32
|
+
/^110/ => 24 # Class C, D and E, from 192.0.0.0 to 255.255.255.254
|
33
|
+
}
|
34
|
+
|
35
|
+
#
|
36
|
+
# Regular expression to match an IPv4 address
|
37
|
+
#
|
38
|
+
REGEXP = Regexp.new(/((25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)/)
|
39
|
+
|
40
|
+
#
|
41
|
+
# Creates a new IPv4 address object.
|
42
|
+
#
|
43
|
+
# An IPv4 address can be expressed in any of the following forms:
|
44
|
+
#
|
45
|
+
# * "10.1.1.1/24": ip +address+ and +prefix+. This is the common and
|
46
|
+
# suggested way to create an object .
|
47
|
+
# * "10.1.1.1/255.255.255.0": ip +address+ and +netmask+. Although
|
48
|
+
# convenient sometimes, this format is less clear than the previous
|
49
|
+
# one.
|
50
|
+
# * "10.1.1.1": if the address alone is specified, the prefix will be
|
51
|
+
# set as default 32, also known as the host prefix
|
52
|
+
#
|
53
|
+
# Examples:
|
54
|
+
#
|
55
|
+
# # These two are the same
|
56
|
+
# ip = IPAddress::IPv4.new("10.0.0.1/24")
|
57
|
+
# ip = IPAddress("10.0.0.1/24")
|
58
|
+
#
|
59
|
+
# # These two are the same
|
60
|
+
# IPAddress::IPv4.new "10.0.0.1/8"
|
61
|
+
# IPAddress::IPv4.new "10.0.0.1/255.0.0.0"
|
62
|
+
#
|
63
|
+
def initialize(str)
|
64
|
+
raise ArgumentError, "Nil IP" unless str
|
65
|
+
ip, netmask = str.split("/")
|
66
|
+
|
67
|
+
# Check the ip and remove white space
|
68
|
+
if IPAddress.valid_ipv4?(ip)
|
69
|
+
@address = ip.strip
|
70
|
+
else
|
71
|
+
raise ArgumentError, "Invalid IP #{ip.inspect}"
|
72
|
+
end
|
73
|
+
|
74
|
+
# Check the netmask
|
75
|
+
if netmask # netmask is defined
|
76
|
+
netmask.strip!
|
77
|
+
if netmask =~ /^\d{1,2}$/ # netmask in cidr format
|
78
|
+
@prefix = Prefix32.new(netmask.to_i)
|
79
|
+
elsif IPAddress.valid_ipv4_netmask?(netmask) # netmask in IP format
|
80
|
+
@prefix = Prefix32.parse_netmask(netmask)
|
81
|
+
else # invalid netmask
|
82
|
+
raise ArgumentError, "Invalid netmask #{netmask}"
|
83
|
+
end
|
84
|
+
else # netmask is nil, reverting to defaul classful mask
|
85
|
+
@prefix = Prefix32.new(32)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Array formed with the IP octets
|
89
|
+
@octets = @address.split(".").map{|i| i.to_i}
|
90
|
+
# 32 bits interger containing the address
|
91
|
+
@u32 = (@octets[0]<< 24) + (@octets[1]<< 16) + (@octets[2]<< 8) + (@octets[3])
|
92
|
+
|
93
|
+
@allocator = 0
|
94
|
+
end # def initialize
|
95
|
+
|
96
|
+
#
|
97
|
+
# Returns the address portion of the IPv4 object
|
98
|
+
# as a string.
|
99
|
+
#
|
100
|
+
# ip = IPAddress("172.16.100.4/22")
|
101
|
+
#
|
102
|
+
# ip.address
|
103
|
+
# #=> "172.16.100.4"
|
104
|
+
#
|
105
|
+
def address
|
106
|
+
@address
|
107
|
+
end
|
108
|
+
|
109
|
+
#
|
110
|
+
# When serializing to JSON format, just use the string representation
|
111
|
+
#
|
112
|
+
# ip = IPAddress("172.16.100.4/22")
|
113
|
+
#
|
114
|
+
# ip.as_json
|
115
|
+
# #=> "172.16.100.4/22"
|
116
|
+
#
|
117
|
+
def as_json
|
118
|
+
to_string
|
119
|
+
end
|
120
|
+
|
121
|
+
#
|
122
|
+
# Returns the prefix portion of the IPv4 object
|
123
|
+
# as a IPAddress::Prefix32 object
|
124
|
+
#
|
125
|
+
# ip = IPAddress("172.16.100.4/22")
|
126
|
+
#
|
127
|
+
# ip.prefix
|
128
|
+
# #=> 22
|
129
|
+
#
|
130
|
+
# ip.prefix.class
|
131
|
+
# #=> IPAddress::Prefix32
|
132
|
+
#
|
133
|
+
def prefix
|
134
|
+
@prefix
|
135
|
+
end
|
136
|
+
|
137
|
+
#
|
138
|
+
# Set a new prefix number for the object
|
139
|
+
#
|
140
|
+
# This is useful if you want to change the prefix
|
141
|
+
# to an object created with IPv4::parse_u32 or
|
142
|
+
# if the object was created using the classful
|
143
|
+
# mask.
|
144
|
+
#
|
145
|
+
# ip = IPAddress("172.16.100.4")
|
146
|
+
#
|
147
|
+
# puts ip
|
148
|
+
# #=> 172.16.100.4/16
|
149
|
+
#
|
150
|
+
# ip.prefix = 22
|
151
|
+
#
|
152
|
+
# puts ip
|
153
|
+
# #=> 172.16.100.4/22
|
154
|
+
#
|
155
|
+
def prefix=(num)
|
156
|
+
@prefix = Prefix32.new(num)
|
157
|
+
end
|
158
|
+
|
159
|
+
#
|
160
|
+
# Returns the address as an array of decimal values
|
161
|
+
#
|
162
|
+
# ip = IPAddress("172.16.100.4")
|
163
|
+
#
|
164
|
+
# ip.octets
|
165
|
+
# #=> [172, 16, 100, 4]
|
166
|
+
#
|
167
|
+
def octets
|
168
|
+
@octets
|
169
|
+
end
|
170
|
+
|
171
|
+
#
|
172
|
+
# Returns a string with the address portion of
|
173
|
+
# the IPv4 object
|
174
|
+
#
|
175
|
+
# ip = IPAddress("172.16.100.4/22")
|
176
|
+
#
|
177
|
+
# ip.to_s
|
178
|
+
# #=> "172.16.100.4"
|
179
|
+
#
|
180
|
+
def to_s
|
181
|
+
@address
|
182
|
+
end
|
183
|
+
|
184
|
+
#
|
185
|
+
# Returns a string with the IP address in canonical
|
186
|
+
# form.
|
187
|
+
#
|
188
|
+
# ip = IPAddress("172.16.100.4/22")
|
189
|
+
#
|
190
|
+
# ip.to_string
|
191
|
+
# #=> "172.16.100.4/22"
|
192
|
+
#
|
193
|
+
def to_string
|
194
|
+
"#@address/#@prefix"
|
195
|
+
end
|
196
|
+
|
197
|
+
#
|
198
|
+
# Returns the prefix as a string in IP format
|
199
|
+
#
|
200
|
+
# ip = IPAddress("172.16.100.4/22")
|
201
|
+
#
|
202
|
+
# ip.netmask
|
203
|
+
# #=> "255.255.252.0"
|
204
|
+
#
|
205
|
+
def netmask
|
206
|
+
@prefix.to_ip
|
207
|
+
end
|
208
|
+
|
209
|
+
#
|
210
|
+
# Like IPv4#prefix=, this method allow you to
|
211
|
+
# change the prefix / netmask of an IP address
|
212
|
+
# object.
|
213
|
+
#
|
214
|
+
# ip = IPAddress("172.16.100.4")
|
215
|
+
#
|
216
|
+
# puts ip
|
217
|
+
# #=> 172.16.100.4/16
|
218
|
+
#
|
219
|
+
# ip.netmask = "255.255.252.0"
|
220
|
+
#
|
221
|
+
# puts ip
|
222
|
+
# #=> 172.16.100.4/22
|
223
|
+
#
|
224
|
+
def netmask=(addr)
|
225
|
+
@prefix = Prefix32.parse_netmask(addr)
|
226
|
+
end
|
227
|
+
|
228
|
+
#
|
229
|
+
# Returns the address portion in unsigned
|
230
|
+
# 32 bits integer format.
|
231
|
+
#
|
232
|
+
# This method is identical to the C function
|
233
|
+
# inet_pton to create a 32 bits address family
|
234
|
+
# structure.
|
235
|
+
#
|
236
|
+
# ip = IPAddress("10.0.0.0/8")
|
237
|
+
#
|
238
|
+
# ip.to_i
|
239
|
+
# #=> 167772160
|
240
|
+
#
|
241
|
+
def u32
|
242
|
+
@u32
|
243
|
+
end
|
244
|
+
alias_method :to_i, :u32
|
245
|
+
alias_method :to_u32, :u32
|
246
|
+
|
247
|
+
#
|
248
|
+
# Returns the address portion in
|
249
|
+
# hex
|
250
|
+
#
|
251
|
+
# ip = IPAddress("10.0.0.0")
|
252
|
+
#
|
253
|
+
# ip.to_h
|
254
|
+
# #=> 0a000000
|
255
|
+
#
|
256
|
+
def hex(space=true)
|
257
|
+
"%.4x%.4x" % [to_u32].pack("N").unpack("nn")
|
258
|
+
end
|
259
|
+
alias_method :to_h, :hex
|
260
|
+
alias_method :to_hex, :hex
|
261
|
+
|
262
|
+
#
|
263
|
+
# Returns the address portion of an IPv4 object
|
264
|
+
# in a network byte order format.
|
265
|
+
#
|
266
|
+
# ip = IPAddress("172.16.10.1/24")
|
267
|
+
#
|
268
|
+
# ip.data
|
269
|
+
# #=> "\254\020\n\001"
|
270
|
+
#
|
271
|
+
# It is usually used to include an IP address
|
272
|
+
# in a data packet to be sent over a socket
|
273
|
+
#
|
274
|
+
# a = Socket.open(params) # socket details here
|
275
|
+
# ip = IPAddress("10.1.1.0/24")
|
276
|
+
# binary_data = ["Address: "].pack("a*") + ip.data
|
277
|
+
#
|
278
|
+
# # Send binary data
|
279
|
+
# a.puts binary_data
|
280
|
+
#
|
281
|
+
def data
|
282
|
+
[@u32].pack("N")
|
283
|
+
end
|
284
|
+
|
285
|
+
#
|
286
|
+
# Returns the octet specified by index
|
287
|
+
#
|
288
|
+
# ip = IPAddress("172.16.100.50/24")
|
289
|
+
#
|
290
|
+
# ip[0]
|
291
|
+
# #=> 172
|
292
|
+
# ip[1]
|
293
|
+
# #=> 16
|
294
|
+
# ip[2]
|
295
|
+
# #=> 100
|
296
|
+
# ip[3]
|
297
|
+
# #=> 50
|
298
|
+
#
|
299
|
+
def [](index)
|
300
|
+
@octets[index]
|
301
|
+
end
|
302
|
+
alias_method :octet, :[]
|
303
|
+
|
304
|
+
#
|
305
|
+
# Updated the octet specified at index
|
306
|
+
#
|
307
|
+
# ip = IPAddress("172.16.100.50/24")
|
308
|
+
# ip[2] = 200
|
309
|
+
#
|
310
|
+
# #=> #<IPAddress::IPv4:0x00000000000000 @address="172.16.200.1",
|
311
|
+
# #=> @prefix=32, @octets=[172, 16, 200, 1], @u32=2886780929>
|
312
|
+
#
|
313
|
+
def []=(index, value)
|
314
|
+
@octets[index] = value.to_i
|
315
|
+
initialize("#{@octets.join('.')}/#{prefix}")
|
316
|
+
end
|
317
|
+
alias_method :octet=, :[]=
|
318
|
+
|
319
|
+
#
|
320
|
+
# Returns the address portion of an IP in binary format,
|
321
|
+
# as a string containing a sequence of 0 and 1
|
322
|
+
#
|
323
|
+
# ip = IPAddress("127.0.0.1")
|
324
|
+
#
|
325
|
+
# ip.bits
|
326
|
+
# #=> "01111111000000000000000000000001"
|
327
|
+
#
|
328
|
+
def bits
|
329
|
+
data.unpack("B*").first
|
330
|
+
end
|
331
|
+
|
332
|
+
#
|
333
|
+
# Returns the broadcast address for the given IP.
|
334
|
+
#
|
335
|
+
# ip = IPAddress("172.16.10.64/24")
|
336
|
+
#
|
337
|
+
# ip.broadcast.to_s
|
338
|
+
# #=> "172.16.10.255"
|
339
|
+
#
|
340
|
+
def broadcast
|
341
|
+
case
|
342
|
+
when prefix <= 30
|
343
|
+
self.class.parse_u32(broadcast_u32, @prefix)
|
344
|
+
when prefix == 31
|
345
|
+
self.class.parse_u32(-1, @prefix)
|
346
|
+
when prefix == 32
|
347
|
+
return self
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
#
|
352
|
+
# Checks if the IP address is actually a network
|
353
|
+
#
|
354
|
+
# ip = IPAddress("172.16.10.64/24")
|
355
|
+
#
|
356
|
+
# ip.network?
|
357
|
+
# #=> false
|
358
|
+
#
|
359
|
+
# ip = IPAddress("172.16.10.64/26")
|
360
|
+
#
|
361
|
+
# ip.network?
|
362
|
+
# #=> true
|
363
|
+
#
|
364
|
+
def network?
|
365
|
+
(@prefix < 32) && (@u32 | @prefix.to_u32 == @prefix.to_u32)
|
366
|
+
end
|
367
|
+
|
368
|
+
#
|
369
|
+
# Returns a new IPv4 object with the network number
|
370
|
+
# for the given IP.
|
371
|
+
#
|
372
|
+
# ip = IPAddress("172.16.10.64/24")
|
373
|
+
#
|
374
|
+
# ip.network.to_s
|
375
|
+
# #=> "172.16.10.0"
|
376
|
+
#
|
377
|
+
def network
|
378
|
+
self.class.parse_u32(network_u32, @prefix)
|
379
|
+
end
|
380
|
+
|
381
|
+
#
|
382
|
+
# Returns a new IPv4 object with the
|
383
|
+
# first host IP address in the range.
|
384
|
+
#
|
385
|
+
# Example: given the 192.168.100.0/24 network, the first
|
386
|
+
# host IP address is 192.168.100.1.
|
387
|
+
#
|
388
|
+
# ip = IPAddress("192.168.100.0/24")
|
389
|
+
#
|
390
|
+
# ip.first.to_s
|
391
|
+
# #=> "192.168.100.1"
|
392
|
+
#
|
393
|
+
# The object IP doesn't need to be a network: the method
|
394
|
+
# automatically gets the network number from it
|
395
|
+
#
|
396
|
+
# ip = IPAddress("192.168.100.50/24")
|
397
|
+
#
|
398
|
+
# ip.first.to_s
|
399
|
+
# #=> "192.168.100.1"
|
400
|
+
#
|
401
|
+
def first
|
402
|
+
case
|
403
|
+
when prefix <= 30
|
404
|
+
self.class.parse_u32(network_u32+1, @prefix)
|
405
|
+
when prefix == 31
|
406
|
+
self.class.parse_u32(network_u32, @prefix)
|
407
|
+
when prefix == 32
|
408
|
+
return self
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
#
|
413
|
+
# Like its sibling method IPv4#first, this method
|
414
|
+
# returns a new IPv4 object with the
|
415
|
+
# last host IP address in the range.
|
416
|
+
#
|
417
|
+
# Example: given the 192.168.100.0/24 network, the last
|
418
|
+
# host IP address is 192.168.100.254
|
419
|
+
#
|
420
|
+
# ip = IPAddress("192.168.100.0/24")
|
421
|
+
#
|
422
|
+
# ip.last.to_s
|
423
|
+
# #=> "192.168.100.254"
|
424
|
+
#
|
425
|
+
# The object IP doesn't need to be a network: the method
|
426
|
+
# automatically gets the network number from it
|
427
|
+
#
|
428
|
+
# ip = IPAddress("192.168.100.50/24")
|
429
|
+
#
|
430
|
+
# ip.last.to_s
|
431
|
+
# #=> "192.168.100.254"
|
432
|
+
#
|
433
|
+
def last
|
434
|
+
case
|
435
|
+
when prefix <= 30
|
436
|
+
self.class.parse_u32(broadcast_u32-1, @prefix)
|
437
|
+
when prefix == 31
|
438
|
+
self.class.parse_u32(broadcast_u32, @prefix)
|
439
|
+
when prefix == 32
|
440
|
+
return self
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
#
|
445
|
+
# Iterates over all the hosts IP addresses for the given
|
446
|
+
# network (or IP address).
|
447
|
+
#
|
448
|
+
# ip = IPAddress("10.0.0.1/29")
|
449
|
+
#
|
450
|
+
# ip.each_host do |i|
|
451
|
+
# p i.to_s
|
452
|
+
# end
|
453
|
+
# #=> "10.0.0.1"
|
454
|
+
# #=> "10.0.0.2"
|
455
|
+
# #=> "10.0.0.3"
|
456
|
+
# #=> "10.0.0.4"
|
457
|
+
# #=> "10.0.0.5"
|
458
|
+
# #=> "10.0.0.6"
|
459
|
+
#
|
460
|
+
def each_host
|
461
|
+
(network_u32+1..broadcast_u32-1).each do |i|
|
462
|
+
yield self.class.parse_u32(i, @prefix)
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
466
|
+
#
|
467
|
+
# Iterates over all the IP addresses for the given
|
468
|
+
# network (or IP address).
|
469
|
+
#
|
470
|
+
# The object yielded is a new IPv4 object created
|
471
|
+
# from the iteration.
|
472
|
+
#
|
473
|
+
# ip = IPAddress("10.0.0.1/29")
|
474
|
+
#
|
475
|
+
# ip.each do |i|
|
476
|
+
# p i.address
|
477
|
+
# end
|
478
|
+
# #=> "10.0.0.0"
|
479
|
+
# #=> "10.0.0.1"
|
480
|
+
# #=> "10.0.0.2"
|
481
|
+
# #=> "10.0.0.3"
|
482
|
+
# #=> "10.0.0.4"
|
483
|
+
# #=> "10.0.0.5"
|
484
|
+
# #=> "10.0.0.6"
|
485
|
+
# #=> "10.0.0.7"
|
486
|
+
#
|
487
|
+
def each
|
488
|
+
(network_u32..broadcast_u32).each do |i|
|
489
|
+
yield self.class.parse_u32(i, @prefix)
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
493
|
+
#
|
494
|
+
# Returns the successor to the IP address
|
495
|
+
#
|
496
|
+
# Example:
|
497
|
+
#
|
498
|
+
# ip = IPAddress("192.168.45.23/16")
|
499
|
+
#
|
500
|
+
# ip.succ.to_string
|
501
|
+
# => "192.168.45.24/16"
|
502
|
+
#
|
503
|
+
def succ
|
504
|
+
self.class.new("#{IPAddress.ntoa(to_u32.succ % 0x100000000)}/#{prefix}")
|
505
|
+
end
|
506
|
+
alias_method :next, :succ
|
507
|
+
|
508
|
+
#
|
509
|
+
# Returns the predecessor to the IP address
|
510
|
+
#
|
511
|
+
# Example:
|
512
|
+
#
|
513
|
+
# ip = IPAddress("192.168.45.23/16")
|
514
|
+
#
|
515
|
+
# ip.pred.to_string
|
516
|
+
# => "192.168.45.22/16"
|
517
|
+
#
|
518
|
+
def pred
|
519
|
+
self.class.new("#{IPAddress.ntoa(to_u32.pred % 0x100000000)}/#{prefix}")
|
520
|
+
end
|
521
|
+
|
522
|
+
#
|
523
|
+
# Spaceship operator to compare IPv4 objects
|
524
|
+
#
|
525
|
+
# Comparing IPv4 addresses is useful to ordinate
|
526
|
+
# them into lists that match our intuitive
|
527
|
+
# perception of ordered IP addresses.
|
528
|
+
#
|
529
|
+
# The first comparison criteria is the u32 value.
|
530
|
+
# For example, 10.100.100.1 will be considered
|
531
|
+
# to be less than 172.16.0.1, because, in a ordered list,
|
532
|
+
# we expect 10.100.100.1 to come before 172.16.0.1.
|
533
|
+
#
|
534
|
+
# The second criteria, in case two IPv4 objects
|
535
|
+
# have identical addresses, is the prefix. An higher
|
536
|
+
# prefix will be considered greater than a lower
|
537
|
+
# prefix. This is because we expect to see
|
538
|
+
# 10.100.100.0/24 come before 10.100.100.0/25.
|
539
|
+
#
|
540
|
+
# Example:
|
541
|
+
#
|
542
|
+
# ip1 = IPAddress "10.100.100.1/8"
|
543
|
+
# ip2 = IPAddress "172.16.0.1/16"
|
544
|
+
# ip3 = IPAddress "10.100.100.1/16"
|
545
|
+
#
|
546
|
+
# ip1 < ip2
|
547
|
+
# #=> true
|
548
|
+
# ip1 > ip3
|
549
|
+
# #=> false
|
550
|
+
#
|
551
|
+
# [ip1,ip2,ip3].sort.map{|i| i.to_string}
|
552
|
+
# #=> ["10.100.100.1/8","10.100.100.1/16","172.16.0.1/16"]
|
553
|
+
#
|
554
|
+
def <=>(oth)
|
555
|
+
return nil unless oth.is_a?(self.class)
|
556
|
+
return prefix <=> oth.prefix if to_u32 == oth.to_u32
|
557
|
+
to_u32 <=> oth.to_u32
|
558
|
+
end
|
559
|
+
|
560
|
+
#
|
561
|
+
# Returns the number of IP addresses included
|
562
|
+
# in the network. It also counts the network
|
563
|
+
# address and the broadcast address.
|
564
|
+
#
|
565
|
+
# ip = IPAddress("10.0.0.1/29")
|
566
|
+
#
|
567
|
+
# ip.size
|
568
|
+
# #=> 8
|
569
|
+
#
|
570
|
+
def size
|
571
|
+
2 ** @prefix.host_prefix
|
572
|
+
end
|
573
|
+
|
574
|
+
#
|
575
|
+
# Returns an array with the IP addresses of
|
576
|
+
# all the hosts in the network.
|
577
|
+
#
|
578
|
+
# ip = IPAddress("10.0.0.1/29")
|
579
|
+
#
|
580
|
+
# ip.hosts.map {|i| i.address}
|
581
|
+
# #=> ["10.0.0.1",
|
582
|
+
# #=> "10.0.0.2",
|
583
|
+
# #=> "10.0.0.3",
|
584
|
+
# #=> "10.0.0.4",
|
585
|
+
# #=> "10.0.0.5",
|
586
|
+
# #=> "10.0.0.6"]
|
587
|
+
#
|
588
|
+
def hosts
|
589
|
+
to_a[1..-2]
|
590
|
+
end
|
591
|
+
|
592
|
+
#
|
593
|
+
# Returns the network number in Unsigned 32bits format
|
594
|
+
#
|
595
|
+
# ip = IPAddress("10.0.0.1/29")
|
596
|
+
#
|
597
|
+
# ip.network_u32
|
598
|
+
# #=> 167772160
|
599
|
+
#
|
600
|
+
def network_u32
|
601
|
+
@u32 & @prefix.to_u32
|
602
|
+
end
|
603
|
+
|
604
|
+
#
|
605
|
+
# Returns the broadcast address in Unsigned 32bits format
|
606
|
+
#
|
607
|
+
# ip = IPaddress("10.0.0.1/29")
|
608
|
+
#
|
609
|
+
# ip.broadcast_u32
|
610
|
+
# #=> 167772167
|
611
|
+
#
|
612
|
+
def broadcast_u32
|
613
|
+
network_u32 + size - 1
|
614
|
+
end
|
615
|
+
|
616
|
+
#
|
617
|
+
# Checks whether a subnet includes the given IP address.
|
618
|
+
#
|
619
|
+
# Accepts an IPAddress::IPv4 object.
|
620
|
+
#
|
621
|
+
# ip = IPAddress("192.168.10.100/24")
|
622
|
+
#
|
623
|
+
# addr = IPAddress("192.168.10.102/24")
|
624
|
+
#
|
625
|
+
# ip.include? addr
|
626
|
+
# #=> true
|
627
|
+
#
|
628
|
+
# ip.include? IPAddress("172.16.0.48/16")
|
629
|
+
# #=> false
|
630
|
+
#
|
631
|
+
def include?(oth)
|
632
|
+
@prefix <= oth.prefix and network_u32 == (oth.to_u32 & @prefix.to_u32)
|
633
|
+
end
|
634
|
+
|
635
|
+
#
|
636
|
+
# Checks whether a subnet includes all the
|
637
|
+
# given IPv4 objects.
|
638
|
+
#
|
639
|
+
# ip = IPAddress("192.168.10.100/24")
|
640
|
+
#
|
641
|
+
# addr1 = IPAddress("192.168.10.102/24")
|
642
|
+
# addr2 = IPAddress("192.168.10.103/24")
|
643
|
+
#
|
644
|
+
# ip.include_all?(addr1,addr2)
|
645
|
+
# #=> true
|
646
|
+
#
|
647
|
+
def include_all?(*others)
|
648
|
+
others.all? {|oth| include?(oth)}
|
649
|
+
end
|
650
|
+
|
651
|
+
#
|
652
|
+
# Checks if an IPv4 address objects belongs
|
653
|
+
# to a private network RFC1918
|
654
|
+
#
|
655
|
+
# Example:
|
656
|
+
#
|
657
|
+
# ip = IPAddress "10.1.1.1/24"
|
658
|
+
# ip.private?
|
659
|
+
# #=> true
|
660
|
+
#
|
661
|
+
def private?
|
662
|
+
[self.class.new("10.0.0.0/8"),
|
663
|
+
self.class.new("172.16.0.0/12"),
|
664
|
+
self.class.new("192.168.0.0/16")].any? {|i| i.include? self}
|
665
|
+
end
|
666
|
+
|
667
|
+
#
|
668
|
+
# Checks if an IPv4 address objects belongs
|
669
|
+
# to a multicast network RFC3171
|
670
|
+
#
|
671
|
+
# Example:
|
672
|
+
#
|
673
|
+
# ip = IPAddress "224.0.0.0/4"
|
674
|
+
# ip.multicast?
|
675
|
+
# #=> true
|
676
|
+
#
|
677
|
+
def multicast?
|
678
|
+
[self.class.new("224.0.0.0/4")].any? {|i| i.include? self}
|
679
|
+
end
|
680
|
+
|
681
|
+
#
|
682
|
+
# Checks if an IPv4 address objects belongs
|
683
|
+
# to a loopback network RFC1122
|
684
|
+
#
|
685
|
+
# Example:
|
686
|
+
#
|
687
|
+
# ip = IPAddress "127.0.0.1"
|
688
|
+
# ip.loopback?
|
689
|
+
# #=> true
|
690
|
+
#
|
691
|
+
def loopback?
|
692
|
+
[self.class.new("127.0.0.0/8")].any? {|i| i.include? self}
|
693
|
+
end
|
694
|
+
|
695
|
+
#
|
696
|
+
# Checks if an IPv4 address objects belongs
|
697
|
+
# to a link-local network RFC3927
|
698
|
+
#
|
699
|
+
# Example:
|
700
|
+
#
|
701
|
+
# ip = IPAddress "169.254.0.1"
|
702
|
+
# ip.link_local?
|
703
|
+
# #=> true
|
704
|
+
#
|
705
|
+
def link_local?
|
706
|
+
[self.class.new("169.254.0.0/16")].any? {|i| i.include? self}
|
707
|
+
end
|
708
|
+
|
709
|
+
#
|
710
|
+
# Returns the IP address in in-addr.arpa format
|
711
|
+
# for DNS lookups
|
712
|
+
#
|
713
|
+
# ip = IPAddress("172.16.100.50/24")
|
714
|
+
#
|
715
|
+
# ip.reverse
|
716
|
+
# #=> "50.100.16.172.in-addr.arpa"
|
717
|
+
#
|
718
|
+
def reverse
|
719
|
+
@octets.reverse.join(".") + ".in-addr.arpa"
|
720
|
+
end
|
721
|
+
alias_method :arpa, :reverse
|
722
|
+
|
723
|
+
#
|
724
|
+
# Return a list of IP's between @address
|
725
|
+
# and the supplied IP
|
726
|
+
#
|
727
|
+
# ip = IPAddress("172.16.100.51/32")
|
728
|
+
#
|
729
|
+
# ip.to("172.16.100.100")
|
730
|
+
# #=> ["172.16.100.51",
|
731
|
+
# #=> "172.16.100.52",
|
732
|
+
# #=> ...
|
733
|
+
# #=> "172.16.100.99",
|
734
|
+
# #=> "172.16.100.100"]
|
735
|
+
#
|
736
|
+
def to(e)
|
737
|
+
unless e.is_a? IPAddress::IPv4
|
738
|
+
e = IPv4.new(e)
|
739
|
+
end
|
740
|
+
|
741
|
+
Range.new(@u32, e.to_u32).map{|i| IPAddress.ntoa(i) }
|
742
|
+
end
|
743
|
+
#
|
744
|
+
# Splits a network into different subnets
|
745
|
+
#
|
746
|
+
# If the IP Address is a network, it can be divided into
|
747
|
+
# multiple networks. If +self+ is not a network, this
|
748
|
+
# method will calculate the network from the IP and then
|
749
|
+
# subnet it.
|
750
|
+
#
|
751
|
+
# If +subnets+ is an power of two number, the resulting
|
752
|
+
# networks will be divided evenly from the supernet.
|
753
|
+
#
|
754
|
+
# network = IPAddress("172.16.10.0/24")
|
755
|
+
#
|
756
|
+
# network / 4 # implies map{|i| i.to_string}
|
757
|
+
# #=> ["172.16.10.0/26",
|
758
|
+
# #=> "172.16.10.64/26",
|
759
|
+
# #=> "172.16.10.128/26",
|
760
|
+
# #=> "172.16.10.192/26"]
|
761
|
+
#
|
762
|
+
# If +num+ is any other number, the supernet will be
|
763
|
+
# divided into some networks with a even number of hosts and
|
764
|
+
# other networks with the remaining addresses.
|
765
|
+
#
|
766
|
+
# network = IPAddress("172.16.10.0/24")
|
767
|
+
#
|
768
|
+
# network / 3 # implies map{|i| i.to_string}
|
769
|
+
# #=> ["172.16.10.0/26",
|
770
|
+
# #=> "172.16.10.64/26",
|
771
|
+
# #=> "172.16.10.128/25"]
|
772
|
+
#
|
773
|
+
# Returns an array of IPv4 objects
|
774
|
+
#
|
775
|
+
def split(subnets=2)
|
776
|
+
unless (1..(2**@prefix.host_prefix)).include? subnets
|
777
|
+
raise ArgumentError, "Value #{subnets} out of range"
|
778
|
+
end
|
779
|
+
networks = subnet(newprefix(subnets))
|
780
|
+
until networks.size == subnets
|
781
|
+
networks = sum_first_found(networks)
|
782
|
+
end
|
783
|
+
return networks
|
784
|
+
end
|
785
|
+
alias_method :/, :split
|
786
|
+
|
787
|
+
#
|
788
|
+
# Returns a new IPv4 object from the supernetting
|
789
|
+
# of the instance network.
|
790
|
+
#
|
791
|
+
# Supernetting is similar to subnetting, except
|
792
|
+
# that you getting as a result a network with a
|
793
|
+
# smaller prefix (bigger host space). For example,
|
794
|
+
# given the network
|
795
|
+
#
|
796
|
+
# ip = IPAddress("172.16.10.0/24")
|
797
|
+
#
|
798
|
+
# you can supernet it with a new /23 prefix
|
799
|
+
#
|
800
|
+
# ip.supernet(23).to_string
|
801
|
+
# #=> "172.16.10.0/23"
|
802
|
+
#
|
803
|
+
# However if you supernet it with a /22 prefix, the
|
804
|
+
# network address will change:
|
805
|
+
#
|
806
|
+
# ip.supernet(22).to_string
|
807
|
+
# #=> "172.16.8.0/22"
|
808
|
+
#
|
809
|
+
# If +new_prefix+ is less than 1, returns 0.0.0.0/0
|
810
|
+
#
|
811
|
+
def supernet(new_prefix)
|
812
|
+
raise ArgumentError, "New prefix must be smaller than existing prefix" if new_prefix >= @prefix.to_i
|
813
|
+
return self.class.new("0.0.0.0/0") if new_prefix < 1
|
814
|
+
return self.class.new(@address+"/#{new_prefix}").network
|
815
|
+
end
|
816
|
+
|
817
|
+
#
|
818
|
+
# This method implements the subnetting function
|
819
|
+
# similar to the one described in RFC3531.
|
820
|
+
#
|
821
|
+
# By specifying a new prefix, the method calculates
|
822
|
+
# the network number for the given IPv4 object
|
823
|
+
# and calculates the subnets associated to the new
|
824
|
+
# prefix.
|
825
|
+
#
|
826
|
+
# For example, given the following network:
|
827
|
+
#
|
828
|
+
# ip = IPAddress "172.16.10.0/24"
|
829
|
+
#
|
830
|
+
# we can calculate the subnets with a /26 prefix
|
831
|
+
#
|
832
|
+
# ip.subnet(26).map{&:to_string)
|
833
|
+
# #=> ["172.16.10.0/26", "172.16.10.64/26",
|
834
|
+
# "172.16.10.128/26", "172.16.10.192/26"]
|
835
|
+
#
|
836
|
+
# The resulting number of subnets will of course always be
|
837
|
+
# a power of two.
|
838
|
+
#
|
839
|
+
def subnet(subprefix)
|
840
|
+
unless ((@prefix.to_i)..32).include? subprefix
|
841
|
+
raise ArgumentError, "New prefix must be between #@prefix and 32"
|
842
|
+
end
|
843
|
+
Array.new(2**(subprefix-@prefix.to_i)) do |i|
|
844
|
+
self.class.parse_u32(network_u32+(i*(2**(32-subprefix))), subprefix)
|
845
|
+
end
|
846
|
+
end
|
847
|
+
|
848
|
+
#
|
849
|
+
# Returns the difference between two IP addresses
|
850
|
+
# in unsigned int 32 bits format
|
851
|
+
#
|
852
|
+
# Example:
|
853
|
+
#
|
854
|
+
# ip1 = IPAddress("172.16.10.0/24")
|
855
|
+
# ip2 = IPAddress("172.16.11.0/24")
|
856
|
+
#
|
857
|
+
# puts ip1 - ip2
|
858
|
+
# #=> 256
|
859
|
+
#
|
860
|
+
def -(oth)
|
861
|
+
return (to_u32 - oth.to_u32).abs
|
862
|
+
end
|
863
|
+
|
864
|
+
#
|
865
|
+
# Returns a new IPv4 object which is the result
|
866
|
+
# of the summarization, if possible, of the two
|
867
|
+
# objects
|
868
|
+
#
|
869
|
+
# Example:
|
870
|
+
#
|
871
|
+
# ip1 = IPAddress("172.16.10.1/24")
|
872
|
+
# ip2 = IPAddress("172.16.11.2/24")
|
873
|
+
#
|
874
|
+
# p (ip1 + ip2).map {|i| i.to_string}
|
875
|
+
# #=> ["172.16.10.0/23"]
|
876
|
+
#
|
877
|
+
# If the networks are not contiguous, returns
|
878
|
+
# the two network numbers from the objects
|
879
|
+
#
|
880
|
+
# ip1 = IPAddress("10.0.0.1/24")
|
881
|
+
# ip2 = IPAddress("10.0.2.1/24")
|
882
|
+
#
|
883
|
+
# p (ip1 + ip2).map {|i| i.to_string}
|
884
|
+
# #=> ["10.0.0.0/24","10.0.2.0/24"]
|
885
|
+
#
|
886
|
+
def +(oth)
|
887
|
+
aggregate(*[self,oth].sort.map{|i| i.network})
|
888
|
+
end
|
889
|
+
|
890
|
+
#
|
891
|
+
# Checks whether the ip address belongs to a
|
892
|
+
# RFC 791 CLASS A network, no matter
|
893
|
+
# what the subnet mask is.
|
894
|
+
#
|
895
|
+
# Example:
|
896
|
+
#
|
897
|
+
# ip = IPAddress("10.0.0.1/24")
|
898
|
+
#
|
899
|
+
# ip.a?
|
900
|
+
# #=> true
|
901
|
+
#
|
902
|
+
def a?
|
903
|
+
CLASSFUL.key(8) === bits
|
904
|
+
end
|
905
|
+
|
906
|
+
#
|
907
|
+
# Checks whether the ip address belongs to a
|
908
|
+
# RFC 791 CLASS B network, no matter
|
909
|
+
# what the subnet mask is.
|
910
|
+
#
|
911
|
+
# Example:
|
912
|
+
#
|
913
|
+
# ip = IPAddress("172.16.10.1/24")
|
914
|
+
#
|
915
|
+
# ip.b?
|
916
|
+
# #=> true
|
917
|
+
#
|
918
|
+
def b?
|
919
|
+
CLASSFUL.key(16) === bits
|
920
|
+
end
|
921
|
+
|
922
|
+
#
|
923
|
+
# Checks whether the ip address belongs to a
|
924
|
+
# RFC 791 CLASS C network, no matter
|
925
|
+
# what the subnet mask is.
|
926
|
+
#
|
927
|
+
# Example:
|
928
|
+
#
|
929
|
+
# ip = IPAddress("192.168.1.1/30")
|
930
|
+
#
|
931
|
+
# ip.c?
|
932
|
+
# #=> true
|
933
|
+
#
|
934
|
+
def c?
|
935
|
+
CLASSFUL.key(24) === bits
|
936
|
+
end
|
937
|
+
|
938
|
+
#
|
939
|
+
# Return the ip address in a format compatible
|
940
|
+
# with the IPv6 Mapped IPv4 addresses
|
941
|
+
#
|
942
|
+
# Example:
|
943
|
+
#
|
944
|
+
# ip = IPAddress("172.16.10.1/24")
|
945
|
+
#
|
946
|
+
# ip.to_ipv6
|
947
|
+
# #=> "ac10:0a01"
|
948
|
+
#
|
949
|
+
def to_ipv6
|
950
|
+
"%.4x:%.4x" % [to_u32].pack("N").unpack("nn")
|
951
|
+
end
|
952
|
+
|
953
|
+
#
|
954
|
+
# Creates a new IPv4 object from an
|
955
|
+
# unsigned 32bits integer.
|
956
|
+
#
|
957
|
+
# ip = IPAddress::IPv4::parse_u32(167772160)
|
958
|
+
#
|
959
|
+
# ip.prefix = 8
|
960
|
+
# ip.to_string
|
961
|
+
# #=> "10.0.0.0/8"
|
962
|
+
#
|
963
|
+
# The +prefix+ parameter is optional:
|
964
|
+
#
|
965
|
+
# ip = IPAddress::IPv4::parse_u32(167772160, 8)
|
966
|
+
#
|
967
|
+
# ip.to_string
|
968
|
+
# #=> "10.0.0.0/8"
|
969
|
+
#
|
970
|
+
def self.parse_u32(u32, prefix=32)
|
971
|
+
self.new([u32].pack("N").unpack("C4").join(".")+"/#{prefix}")
|
972
|
+
end
|
973
|
+
|
974
|
+
#
|
975
|
+
# Creates a new IPv4 object from binary data,
|
976
|
+
# like the one you get from a network stream.
|
977
|
+
#
|
978
|
+
# For example, on a network stream the IP 172.16.0.1
|
979
|
+
# is represented with the binary "\254\020\n\001".
|
980
|
+
#
|
981
|
+
# ip = IPAddress::IPv4::parse_data "\254\020\n\001"
|
982
|
+
# ip.prefix = 24
|
983
|
+
#
|
984
|
+
# ip.to_string
|
985
|
+
# #=> "172.16.10.1/24"
|
986
|
+
#
|
987
|
+
def self.parse_data(str, prefix=32)
|
988
|
+
self.new(str.unpack("C4").join(".")+"/#{prefix}")
|
989
|
+
end
|
990
|
+
|
991
|
+
#
|
992
|
+
# Extract an IPv4 address from a string and
|
993
|
+
# returns a new object
|
994
|
+
#
|
995
|
+
# Example:
|
996
|
+
#
|
997
|
+
# str = "foobar172.16.10.1barbaz"
|
998
|
+
# ip = IPAddress::IPv4::extract str
|
999
|
+
#
|
1000
|
+
# ip.to_s
|
1001
|
+
# #=> "172.16.10.1"
|
1002
|
+
#
|
1003
|
+
def self.extract(str)
|
1004
|
+
self.new REGEXP.match(str).to_s
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
#
|
1008
|
+
# Summarization (or aggregation) is the process when two or more
|
1009
|
+
# networks are taken together to check if a supernet, including all
|
1010
|
+
# and only these networks, exists. If it exists then this supernet
|
1011
|
+
# is called the summarized (or aggregated) network.
|
1012
|
+
#
|
1013
|
+
# It is very important to understand that summarization can only
|
1014
|
+
# occur if there are no holes in the aggregated network, or, in other
|
1015
|
+
# words, if the given networks fill completely the address space
|
1016
|
+
# of the supernet. So the two rules are:
|
1017
|
+
#
|
1018
|
+
# 1) The aggregate network must contain +all+ the IP addresses of the
|
1019
|
+
# original networks;
|
1020
|
+
# 2) The aggregate network must contain +only+ the IP addresses of the
|
1021
|
+
# original networks;
|
1022
|
+
#
|
1023
|
+
# A few examples will help clarify the above. Let's consider for
|
1024
|
+
# instance the following two networks:
|
1025
|
+
#
|
1026
|
+
# ip1 = IPAddress("172.16.10.0/24")
|
1027
|
+
# ip2 = IPAddress("172.16.11.0/24")
|
1028
|
+
#
|
1029
|
+
# These two networks can be expressed using only one IP address
|
1030
|
+
# network if we change the prefix. Let Ruby do the work:
|
1031
|
+
#
|
1032
|
+
# IPAddress::IPv4::summarize(ip1,ip2).to_s
|
1033
|
+
# #=> "172.16.10.0/23"
|
1034
|
+
#
|
1035
|
+
# We note how the network "172.16.10.0/23" includes all the addresses
|
1036
|
+
# specified in the above networks, and (more important) includes
|
1037
|
+
# ONLY those addresses.
|
1038
|
+
#
|
1039
|
+
# If we summarized +ip1+ and +ip2+ with the following network:
|
1040
|
+
#
|
1041
|
+
# "172.16.0.0/16"
|
1042
|
+
#
|
1043
|
+
# we would have satisfied rule #1 above, but not rule #2. So "172.16.0.0/16"
|
1044
|
+
# is not an aggregate network for +ip1+ and +ip2+.
|
1045
|
+
#
|
1046
|
+
# If it's not possible to compute a single aggregated network for all the
|
1047
|
+
# original networks, the method returns an array with all the aggregate
|
1048
|
+
# networks found. For example, the following four networks can be
|
1049
|
+
# aggregated in a single /22:
|
1050
|
+
#
|
1051
|
+
# ip1 = IPAddress("10.0.0.1/24")
|
1052
|
+
# ip2 = IPAddress("10.0.1.1/24")
|
1053
|
+
# ip3 = IPAddress("10.0.2.1/24")
|
1054
|
+
# ip4 = IPAddress("10.0.3.1/24")
|
1055
|
+
#
|
1056
|
+
# IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).to_string
|
1057
|
+
# #=> "10.0.0.0/22",
|
1058
|
+
#
|
1059
|
+
# But the following networks can't be summarized in a single network:
|
1060
|
+
#
|
1061
|
+
# ip1 = IPAddress("10.0.1.1/24")
|
1062
|
+
# ip2 = IPAddress("10.0.2.1/24")
|
1063
|
+
# ip3 = IPAddress("10.0.3.1/24")
|
1064
|
+
# ip4 = IPAddress("10.0.4.1/24")
|
1065
|
+
#
|
1066
|
+
# IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string}
|
1067
|
+
# #=> ["10.0.1.0/24","10.0.2.0/23","10.0.4.0/24"]
|
1068
|
+
#
|
1069
|
+
def self.summarize(*args)
|
1070
|
+
# one network? no need to summarize
|
1071
|
+
return [args.first.network] if args.size == 1
|
1072
|
+
|
1073
|
+
i = 0
|
1074
|
+
result = args.dup.sort.map{|ip| ip.network}
|
1075
|
+
while i < result.size-1
|
1076
|
+
sum = result[i] + result[i+1]
|
1077
|
+
result[i..i+1] = sum.first if sum.size == 1
|
1078
|
+
i += 1
|
1079
|
+
end
|
1080
|
+
|
1081
|
+
result.flatten!
|
1082
|
+
if result.size == args.size
|
1083
|
+
# nothing more to summarize
|
1084
|
+
return result
|
1085
|
+
else
|
1086
|
+
# keep on summarizing
|
1087
|
+
return self.summarize(*result)
|
1088
|
+
end
|
1089
|
+
end
|
1090
|
+
|
1091
|
+
#
|
1092
|
+
# Creates a new IPv4 address object by parsing the
|
1093
|
+
# address in a classful way.
|
1094
|
+
#
|
1095
|
+
# Classful addresses have a fixed netmask based on the
|
1096
|
+
# class they belong to:
|
1097
|
+
#
|
1098
|
+
# * Class A, from 0.0.0.0 to 127.255.255.255
|
1099
|
+
# * Class B, from 128.0.0.0 to 191.255.255.255
|
1100
|
+
# * Class C, D and E, from 192.0.0.0 to 255.255.255.254
|
1101
|
+
#
|
1102
|
+
# Example:
|
1103
|
+
#
|
1104
|
+
# ip = IPAddress::IPv4.parse_classful "10.0.0.1"
|
1105
|
+
#
|
1106
|
+
# ip.netmask
|
1107
|
+
# #=> "255.0.0.0"
|
1108
|
+
# ip.a?
|
1109
|
+
# #=> true
|
1110
|
+
#
|
1111
|
+
# Note that classes C, D and E will all have a default
|
1112
|
+
# prefix of /24 or 255.255.255.0
|
1113
|
+
#
|
1114
|
+
def self.parse_classful(ip)
|
1115
|
+
if IPAddress.valid_ipv4?(ip)
|
1116
|
+
address = ip.strip
|
1117
|
+
else
|
1118
|
+
raise ArgumentError, "Invalid IP #{ip.inspect}"
|
1119
|
+
end
|
1120
|
+
prefix = CLASSFUL.find{|h,k| h === ("%.8b" % address.to_i)}.last
|
1121
|
+
self.new "#{address}/#{prefix}"
|
1122
|
+
end
|
1123
|
+
|
1124
|
+
#
|
1125
|
+
# Allocates a new ip from the current subnet. Optional skip parameter
|
1126
|
+
# can be used to skip addresses.
|
1127
|
+
#
|
1128
|
+
# Will raise StopIteration exception when all addresses have been allocated
|
1129
|
+
#
|
1130
|
+
# Example:
|
1131
|
+
#
|
1132
|
+
# ip = IPAddress("10.0.0.0/24")
|
1133
|
+
# ip.allocate
|
1134
|
+
# #=> "10.0.0.1/24"
|
1135
|
+
# ip.allocate
|
1136
|
+
# #=> "10.0.0.2/24"
|
1137
|
+
# ip.allocate(2)
|
1138
|
+
# #=> "10.0.0.5/24"
|
1139
|
+
#
|
1140
|
+
#
|
1141
|
+
# Uses an internal @allocator which tracks the state of allocated
|
1142
|
+
# addresses.
|
1143
|
+
#
|
1144
|
+
def allocate(skip=0)
|
1145
|
+
@allocator += 1 + skip
|
1146
|
+
|
1147
|
+
next_ip = network_u32+@allocator
|
1148
|
+
if next_ip > broadcast_u32+1
|
1149
|
+
raise StopIteration
|
1150
|
+
end
|
1151
|
+
self.class.parse_u32(network_u32+@allocator, @prefix)
|
1152
|
+
end
|
1153
|
+
|
1154
|
+
#
|
1155
|
+
# Finds the adjacent block to a subnet.
|
1156
|
+
#
|
1157
|
+
# Example:
|
1158
|
+
#
|
1159
|
+
# ip = IPAddress("10.0.0.0/24")
|
1160
|
+
# ip.find_adjacent_subnet
|
1161
|
+
# #=> "10.0.1.0/24"
|
1162
|
+
#
|
1163
|
+
def find_adjacent_subnet
|
1164
|
+
return false if prefix == 0
|
1165
|
+
current_subnet = to_string
|
1166
|
+
self.prefix = @prefix - 1
|
1167
|
+
(split.map{|i| i.to_string} - [current_subnet])[0]
|
1168
|
+
end
|
1169
|
+
|
1170
|
+
#
|
1171
|
+
# private methods
|
1172
|
+
#
|
1173
|
+
private
|
1174
|
+
|
1175
|
+
# Tweaked to remove the #upto(32)
|
1176
|
+
def newprefix(num)
|
1177
|
+
return @prefix + (Math::log2(num).ceil )
|
1178
|
+
end
|
1179
|
+
|
1180
|
+
def sum_first_found(arr)
|
1181
|
+
dup = arr.dup.reverse
|
1182
|
+
dup.each_with_index do |obj,i|
|
1183
|
+
a = [self.class.summarize(obj,dup[i+1])].flatten
|
1184
|
+
if a.size == 1
|
1185
|
+
dup[i..i+1] = a
|
1186
|
+
return dup.reverse
|
1187
|
+
end
|
1188
|
+
end
|
1189
|
+
return dup.reverse
|
1190
|
+
end
|
1191
|
+
|
1192
|
+
def aggregate(ip1,ip2)
|
1193
|
+
return [ip1] if ip1.include? ip2
|
1194
|
+
|
1195
|
+
snet = ip1.supernet(ip1.prefix-1)
|
1196
|
+
if snet.include_all?(ip1, ip2) && ((ip1.size + ip2.size) == snet.size)
|
1197
|
+
return [snet]
|
1198
|
+
else
|
1199
|
+
return [ip1, ip2]
|
1200
|
+
end
|
1201
|
+
end
|
1202
|
+
end # class IPv4
|
1203
|
+
end # module IPAddress
|
1204
|
+
|