ipaddress 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +73 -0
- data/README.rdoc +120 -86
- data/Rakefile +7 -15
- data/VERSION +1 -1
- data/lib/ipaddress.rb +131 -15
- data/lib/ipaddress/extensions/extensions.rb +8 -2
- data/lib/ipaddress/ipv4.rb +132 -49
- data/lib/ipaddress/ipv6.rb +64 -18
- data/lib/ipaddress/prefix.rb +38 -1
- data/test/ipaddress/ipv4_test.rb +92 -38
- data/test/ipaddress/ipv6_test.rb +34 -10
- data/test/ipaddress/prefix_test.rb +15 -0
- data/test/ipaddress_test.rb +17 -0
- data/test/test_helper.rb +0 -5
- metadata +11 -25
- data/lib/ipaddress/ipbase.rb +0 -83
- data/test/ipaddress/ipbase_test.rb +0 -28
data/Rakefile
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'rake'
|
3
3
|
require 'rake/clean'
|
4
|
-
require 'rcov/rcovtask'
|
5
|
-
|
6
4
|
|
7
5
|
begin
|
8
6
|
require 'jeweler'
|
@@ -12,9 +10,13 @@ begin
|
|
12
10
|
gem.email = "ceresa@gmail.com"
|
13
11
|
gem.homepage = "http://github.com/bluemonk/ipaddress"
|
14
12
|
gem.authors = ["Marco Ceresa"]
|
15
|
-
|
13
|
+
gem.description = <<-EOD
|
14
|
+
IPAddress is a Ruby library designed to make manipulation
|
15
|
+
of IPv4 and IPv6 addresses both powerful and simple. It mantains
|
16
|
+
a layer of compatibility with Ruby's own IPAddr, while
|
17
|
+
addressing many of its issues.
|
18
|
+
EOD
|
16
19
|
end
|
17
|
-
|
18
20
|
rescue LoadError
|
19
21
|
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
20
22
|
end
|
@@ -59,7 +61,7 @@ end
|
|
59
61
|
|
60
62
|
desc "Open an irb session preloaded with this library"
|
61
63
|
task :console do
|
62
|
-
sh "
|
64
|
+
sh "irb1.9 -rubygems -I lib -r ipaddress.rb"
|
63
65
|
end
|
64
66
|
|
65
67
|
desc "Look for TODO and FIXME tags in the code"
|
@@ -79,13 +81,3 @@ task :todo do
|
|
79
81
|
end
|
80
82
|
egrep /(FIXME|TODO|TBD)/
|
81
83
|
end
|
82
|
-
|
83
|
-
begin
|
84
|
-
require 'jeweler'
|
85
|
-
Jeweler::Tasks.new do |gemspec|
|
86
|
-
# omitted for brevity
|
87
|
-
end
|
88
|
-
Jeweler::GemcutterTasks.new
|
89
|
-
rescue LoadError
|
90
|
-
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
91
|
-
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.6.0
|
data/lib/ipaddress.rb
CHANGED
@@ -1,9 +1,136 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
#
|
2
|
+
# = IPAddress
|
3
|
+
#
|
4
|
+
# A ruby library to manipulate IPv4 and IPv6 addresses
|
5
|
+
#
|
6
|
+
#
|
7
|
+
# Package:: IPAddress
|
8
|
+
# Author:: Marco Ceresa <ceresa@ieee.org>
|
9
|
+
# License:: Ruby License
|
10
|
+
#
|
11
|
+
#--
|
12
|
+
#
|
13
|
+
#++
|
3
14
|
|
4
|
-
require 'ipaddress/ipbase'
|
5
15
|
require 'ipaddress/ipv4'
|
6
16
|
require 'ipaddress/ipv6'
|
17
|
+
require 'ipaddress/extensions/extensions'
|
18
|
+
|
19
|
+
|
20
|
+
module IPAddress
|
21
|
+
|
22
|
+
NAME = "IPAddress"
|
23
|
+
GEM = "ipaddress"
|
24
|
+
AUTHORS = ["Marco Ceresa <ceresa@ieee.org>"]
|
25
|
+
|
26
|
+
#
|
27
|
+
# Parse the argument string to create a new
|
28
|
+
# IPv4, IPv6 or Mapped IP object
|
29
|
+
#
|
30
|
+
# ip = IPAddress.parse "172.16.10.1/24"
|
31
|
+
# ip6 = IPAddress.parse "2001:db8::8:800:200c:417a/64"
|
32
|
+
# ip_mapped = IPAddress.parse "::ffff:172.16.10.1/128"
|
33
|
+
#
|
34
|
+
# All the object created will be instances of the
|
35
|
+
# correct class:
|
36
|
+
#
|
37
|
+
# ip.class
|
38
|
+
# #=> IPAddress::IPv4
|
39
|
+
# ip6.class
|
40
|
+
# #=> IPAddress::IPv6
|
41
|
+
# ip_mapped.class
|
42
|
+
# #=> IPAddress::IPv6::Mapped
|
43
|
+
#
|
44
|
+
def IPAddress::parse(str)
|
45
|
+
case str
|
46
|
+
when /:.+\./
|
47
|
+
IPAddress::IPv6::Mapped.new(str)
|
48
|
+
else
|
49
|
+
begin
|
50
|
+
IPAddress::IPv4.new(str)
|
51
|
+
rescue ArgumentError
|
52
|
+
IPAddress::IPv6.new(str)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
# Checks if the given string is a valid IP address,
|
59
|
+
# either IPv4 or IPv6
|
60
|
+
#
|
61
|
+
# Example:
|
62
|
+
#
|
63
|
+
# IPAddress::valid? "2002::1"
|
64
|
+
# #=> true
|
65
|
+
#
|
66
|
+
# IPAddress::valid? "10.0.0.256"
|
67
|
+
# #=> false
|
68
|
+
#
|
69
|
+
def self.valid?(addr)
|
70
|
+
valid_ipv4?(addr) || valid_ipv6?(addr)
|
71
|
+
end
|
72
|
+
|
73
|
+
#
|
74
|
+
# Checks if the given string is a valid IPv4 address
|
75
|
+
#
|
76
|
+
# Example:
|
77
|
+
#
|
78
|
+
# IPAddress::valid_ipv4? "2002::1"
|
79
|
+
# #=> false
|
80
|
+
#
|
81
|
+
# IPAddress::valid_ipv4? "172.16.10.1"
|
82
|
+
# #=> true
|
83
|
+
#
|
84
|
+
def self.valid_ipv4?(addr)
|
85
|
+
if /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\Z/ =~ addr
|
86
|
+
return $~.captures.all? {|i| i.to_i < 256}
|
87
|
+
end
|
88
|
+
false
|
89
|
+
end
|
90
|
+
|
91
|
+
#
|
92
|
+
# Checks if the argument is a valid IPv4 netmask
|
93
|
+
# expressed in dotted decimal format.
|
94
|
+
#
|
95
|
+
# IPAddress.valid_ipv4_netmask? "255.255.0.0"
|
96
|
+
# #=> true
|
97
|
+
#
|
98
|
+
def self.valid_ipv4_netmask?(addr)
|
99
|
+
arr = addr.split(".").map{|i| i.to_i}.pack("CCCC").unpack("B*").first.scan(/01/)
|
100
|
+
arr.empty? && valid_ipv4?(addr)
|
101
|
+
rescue
|
102
|
+
return false
|
103
|
+
end
|
104
|
+
|
105
|
+
#
|
106
|
+
# Checks if the given string is a valid IPv6 address
|
107
|
+
#
|
108
|
+
# Example:
|
109
|
+
#
|
110
|
+
# IPAddress::valid_ipv6? "2002::1"
|
111
|
+
# #=> true
|
112
|
+
#
|
113
|
+
# IPAddress::valid_ipv6? "2002::DEAD::BEEF"
|
114
|
+
# #=> false
|
115
|
+
#
|
116
|
+
def self.valid_ipv6?(addr)
|
117
|
+
# IPv6 (normal)
|
118
|
+
return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*\Z/ =~ addr
|
119
|
+
return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ addr
|
120
|
+
return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ addr
|
121
|
+
# IPv6 (IPv4 compat)
|
122
|
+
return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:/ =~ addr && valid_ipv4?($')
|
123
|
+
return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_ipv4?($')
|
124
|
+
return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_ipv4?($')
|
125
|
+
false
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.deprecate(message = nil) # :nodoc:
|
129
|
+
message ||= "You are using deprecated behavior which will be removed from the next major or minor release."
|
130
|
+
warn("DEPRECATION WARNING: #{message}")
|
131
|
+
end
|
132
|
+
|
133
|
+
end # module IPAddress
|
7
134
|
|
8
135
|
#
|
9
136
|
# IPAddress is a wrapper method built around
|
@@ -35,18 +162,7 @@ require 'ipaddress/ipv6'
|
|
35
162
|
# #=> IPAddress::IPv6::Mapped
|
36
163
|
#
|
37
164
|
def IPAddress(str)
|
38
|
-
|
39
|
-
when /:.+\./
|
40
|
-
IPAddress::IPv6::Mapped.new(str)
|
41
|
-
else
|
42
|
-
begin
|
43
|
-
IPAddress::IPv4.new(str)
|
44
|
-
rescue ArgumentError
|
45
|
-
IPAddress::IPv6.new(str)
|
46
|
-
end
|
47
|
-
end
|
165
|
+
IPAddress::parse str
|
48
166
|
end
|
49
167
|
|
50
168
|
|
51
|
-
|
52
|
-
|
@@ -2,13 +2,19 @@ class << Math
|
|
2
2
|
def log2(n); log(n) / log(2); end
|
3
3
|
end
|
4
4
|
|
5
|
+
if RUBY_VERSION =~ /1\.8/
|
6
|
+
class Hash
|
7
|
+
alias :key :index
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
5
11
|
class Integer
|
6
12
|
def power_of_2?
|
7
13
|
Math::log2(self).to_i == Math::log2(self)
|
8
14
|
end
|
9
15
|
|
10
|
-
def closest_power_of_2
|
11
|
-
self.upto(
|
16
|
+
def closest_power_of_2(limit=32)
|
17
|
+
self.upto(limit) do |i|
|
12
18
|
return i if i.power_of_2?
|
13
19
|
end
|
14
20
|
end
|
data/lib/ipaddress/ipv4.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'ipaddress/ipbase'
|
2
1
|
require 'ipaddress/prefix'
|
3
2
|
|
4
3
|
module IPAddress;
|
@@ -15,7 +14,7 @@ module IPAddress;
|
|
15
14
|
#
|
16
15
|
# Class IPAddress::IPv4 is used to handle IPv4 type addresses.
|
17
16
|
#
|
18
|
-
class IPv4
|
17
|
+
class IPv4
|
19
18
|
|
20
19
|
include IPAddress
|
21
20
|
include Enumerable
|
@@ -102,6 +101,7 @@ module IPAddress;
|
|
102
101
|
# as a string.
|
103
102
|
#
|
104
103
|
# ip = IPAddress("172.16.100.4/22")
|
104
|
+
#
|
105
105
|
# ip.address
|
106
106
|
# #=> "172.16.100.4"
|
107
107
|
#
|
@@ -114,8 +114,10 @@ module IPAddress;
|
|
114
114
|
# as a IPAddress::Prefix32 object
|
115
115
|
#
|
116
116
|
# ip = IPAddress("172.16.100.4/22")
|
117
|
+
#
|
117
118
|
# ip.prefix
|
118
119
|
# #=> 22
|
120
|
+
#
|
119
121
|
# ip.prefix.class
|
120
122
|
# #=> IPAddress::Prefix32
|
121
123
|
#
|
@@ -132,10 +134,12 @@ module IPAddress;
|
|
132
134
|
# mask.
|
133
135
|
#
|
134
136
|
# ip = IPAddress("172.16.100.4")
|
137
|
+
#
|
135
138
|
# puts ip
|
136
139
|
# #=> 172.16.100.4/16
|
137
140
|
#
|
138
141
|
# ip.prefix = 22
|
142
|
+
#
|
139
143
|
# puts ip
|
140
144
|
# #=> 172.16.100.4/22
|
141
145
|
#
|
@@ -147,6 +151,7 @@ module IPAddress;
|
|
147
151
|
# Returns the address as an array of decimal values
|
148
152
|
#
|
149
153
|
# ip = IPAddress("172.16.100.4")
|
154
|
+
#
|
150
155
|
# ip.octets
|
151
156
|
# #=> [172, 16, 100, 4]
|
152
157
|
#
|
@@ -154,22 +159,38 @@ module IPAddress;
|
|
154
159
|
@octets
|
155
160
|
end
|
156
161
|
|
162
|
+
#
|
163
|
+
# Returns a string with the address portion of
|
164
|
+
# the IPv4 object
|
165
|
+
#
|
166
|
+
# ip = IPAddress("172.16.100.4/22")
|
167
|
+
#
|
168
|
+
# ip.to_s
|
169
|
+
# #=> "172.16.100.4"
|
170
|
+
#
|
171
|
+
def to_s
|
172
|
+
@address
|
173
|
+
end
|
174
|
+
|
157
175
|
#
|
158
176
|
# Returns a string with the IP address in canonical
|
159
177
|
# form.
|
160
178
|
#
|
161
179
|
# ip = IPAddress("172.16.100.4/22")
|
162
|
-
#
|
180
|
+
#
|
181
|
+
# ip.to_string
|
163
182
|
# #=> "172.16.100.4/22"
|
164
183
|
#
|
165
|
-
def
|
184
|
+
def to_string
|
166
185
|
"#@address/#@prefix"
|
167
186
|
end
|
168
187
|
|
188
|
+
|
169
189
|
#
|
170
190
|
# Returns the prefix as a string in IP format
|
171
191
|
#
|
172
192
|
# ip = IPAddress("172.16.100.4/22")
|
193
|
+
#
|
173
194
|
# ip.netmask
|
174
195
|
# #=> "255.255.252.0"
|
175
196
|
#
|
@@ -183,10 +204,12 @@ module IPAddress;
|
|
183
204
|
# object.
|
184
205
|
#
|
185
206
|
# ip = IPAddress("172.16.100.4")
|
207
|
+
#
|
186
208
|
# puts ip
|
187
209
|
# #=> 172.16.100.4/16
|
188
210
|
#
|
189
211
|
# ip.netmask = "255.255.252.0"
|
212
|
+
#
|
190
213
|
# puts ip
|
191
214
|
# #=> 172.16.100.4/22
|
192
215
|
#
|
@@ -203,6 +226,7 @@ module IPAddress;
|
|
203
226
|
# structure.
|
204
227
|
#
|
205
228
|
# ip = IPAddress("10.0.0.0/8")
|
229
|
+
#
|
206
230
|
# ip.to_u32
|
207
231
|
# #=> 167772160
|
208
232
|
#
|
@@ -216,6 +240,7 @@ module IPAddress;
|
|
216
240
|
# in a network byte order format.
|
217
241
|
#
|
218
242
|
# ip = IPAddress("172.16.10.1/24")
|
243
|
+
#
|
219
244
|
# ip.data
|
220
245
|
# #=> "\254\020\n\001"
|
221
246
|
#
|
@@ -237,6 +262,7 @@ module IPAddress;
|
|
237
262
|
# Returns the octet specified by index
|
238
263
|
#
|
239
264
|
# ip = IPAddress("172.16.100.50/24")
|
265
|
+
#
|
240
266
|
# ip[0]
|
241
267
|
# #=> 172
|
242
268
|
# ip[1]
|
@@ -256,6 +282,7 @@ module IPAddress;
|
|
256
282
|
# as a string containing a sequence of 0 and 1
|
257
283
|
#
|
258
284
|
# ip = IPAddress("127.0.0.1")
|
285
|
+
#
|
259
286
|
# ip.bits
|
260
287
|
# #=> "01111111000000000000000000000001"
|
261
288
|
#
|
@@ -267,8 +294,9 @@ module IPAddress;
|
|
267
294
|
# Returns the broadcast address for the given IP.
|
268
295
|
#
|
269
296
|
# ip = IPAddress("172.16.10.64/24")
|
297
|
+
#
|
270
298
|
# ip.broadcast.to_s
|
271
|
-
# #=> "172.16.10.255
|
299
|
+
# #=> "172.16.10.255"
|
272
300
|
#
|
273
301
|
def broadcast
|
274
302
|
self.class.parse_u32(broadcast_u32, @prefix)
|
@@ -278,10 +306,12 @@ module IPAddress;
|
|
278
306
|
# Checks if the IP address is actually a network
|
279
307
|
#
|
280
308
|
# ip = IPAddress("172.16.10.64/24")
|
309
|
+
#
|
281
310
|
# ip.network?
|
282
311
|
# #=> false
|
283
312
|
#
|
284
313
|
# ip = IPAddress("172.16.10.64/26")
|
314
|
+
#
|
285
315
|
# ip.network?
|
286
316
|
# #=> true
|
287
317
|
#
|
@@ -294,8 +324,9 @@ module IPAddress;
|
|
294
324
|
# for the given IP.
|
295
325
|
#
|
296
326
|
# ip = IPAddress("172.16.10.64/24")
|
327
|
+
#
|
297
328
|
# ip.network.to_s
|
298
|
-
# #=> "172.16.10.0
|
329
|
+
# #=> "172.16.10.0"
|
299
330
|
#
|
300
331
|
def network
|
301
332
|
self.class.parse_u32(network_u32, @prefix)
|
@@ -309,15 +340,17 @@ module IPAddress;
|
|
309
340
|
# host IP address is 192.168.100.1.
|
310
341
|
#
|
311
342
|
# ip = IPAddress("192.168.100.0/24")
|
343
|
+
#
|
312
344
|
# ip.first.to_s
|
313
|
-
# #=> "192.168.100.1
|
345
|
+
# #=> "192.168.100.1"
|
314
346
|
#
|
315
347
|
# The object IP doesn't need to be a network: the method
|
316
348
|
# automatically gets the network number from it
|
317
349
|
#
|
318
350
|
# ip = IPAddress("192.168.100.50/24")
|
351
|
+
#
|
319
352
|
# ip.first.to_s
|
320
|
-
# #=> "192.168.100.1
|
353
|
+
# #=> "192.168.100.1"
|
321
354
|
#
|
322
355
|
def first
|
323
356
|
self.class.parse_u32(network_u32+1, @prefix)
|
@@ -329,18 +362,20 @@ module IPAddress;
|
|
329
362
|
# last host IP address in the range.
|
330
363
|
#
|
331
364
|
# Example: given the 192.168.100.0/24 network, the last
|
332
|
-
# host IP address is 192.168.100.
|
365
|
+
# host IP address is 192.168.100.254
|
333
366
|
#
|
334
367
|
# ip = IPAddress("192.168.100.0/24")
|
368
|
+
#
|
335
369
|
# ip.last.to_s
|
336
|
-
# #=> "192.168.100.254
|
370
|
+
# #=> "192.168.100.254"
|
337
371
|
#
|
338
372
|
# The object IP doesn't need to be a network: the method
|
339
373
|
# automatically gets the network number from it
|
340
374
|
#
|
341
375
|
# ip = IPAddress("192.168.100.50/24")
|
376
|
+
#
|
342
377
|
# ip.last.to_s
|
343
|
-
# #=> "192.168.100.254
|
378
|
+
# #=> "192.168.100.254"
|
344
379
|
#
|
345
380
|
def last
|
346
381
|
self.class.parse_u32(broadcast_u32-1, @prefix)
|
@@ -350,9 +385,10 @@ module IPAddress;
|
|
350
385
|
# Iterates over all the hosts IP addresses for the given
|
351
386
|
# network (or IP address).
|
352
387
|
#
|
353
|
-
# ip =
|
388
|
+
# ip = IPAddress("10.0.0.1/29")
|
389
|
+
#
|
354
390
|
# ip.each do |i|
|
355
|
-
# p i
|
391
|
+
# p i.to_s
|
356
392
|
# end
|
357
393
|
# #=> "10.0.0.1"
|
358
394
|
# #=> "10.0.0.2"
|
@@ -374,7 +410,8 @@ module IPAddress;
|
|
374
410
|
# The object yielded is a new IPv4 object created
|
375
411
|
# from the iteration.
|
376
412
|
#
|
377
|
-
# ip =
|
413
|
+
# ip = IPAddress("10.0.0.1/29")
|
414
|
+
#
|
378
415
|
# ip.each do |i|
|
379
416
|
# p i.address
|
380
417
|
# end
|
@@ -406,8 +443,8 @@ module IPAddress;
|
|
406
443
|
# Example:
|
407
444
|
#
|
408
445
|
# ip1 = IPAddress "10.100.100.1/8"
|
409
|
-
# ip2 = IPAddress "
|
410
|
-
# ip3 = IPAddress "
|
446
|
+
# ip2 = IPAddress "172.16.0.1/16"
|
447
|
+
# ip3 = IPAddress "10.100.100.1/16"
|
411
448
|
#
|
412
449
|
# ip1 < ip2
|
413
450
|
# #=> true
|
@@ -434,7 +471,8 @@ module IPAddress;
|
|
434
471
|
# in the network. It also counts the network
|
435
472
|
# address and the broadcast address.
|
436
473
|
#
|
437
|
-
# ip =
|
474
|
+
# ip = IPAddress("10.0.0.1/29")
|
475
|
+
#
|
438
476
|
# ip.size
|
439
477
|
# #=> 8
|
440
478
|
#
|
@@ -446,7 +484,8 @@ module IPAddress;
|
|
446
484
|
# Returns an array with the IP addresses of
|
447
485
|
# all the hosts in the network.
|
448
486
|
#
|
449
|
-
# ip =
|
487
|
+
# ip = IPAddress("10.0.0.1/29")
|
488
|
+
#
|
450
489
|
# ip.hosts.map {|i| i.address}
|
451
490
|
# #=> ["10.0.0.1",
|
452
491
|
# #=> "10.0.0.2",
|
@@ -462,7 +501,8 @@ module IPAddress;
|
|
462
501
|
#
|
463
502
|
# Returns the network number in Unsigned 32bits format
|
464
503
|
#
|
465
|
-
# ip =
|
504
|
+
# ip = IPAddress("10.0.0.1/29")
|
505
|
+
#
|
466
506
|
# ip.network_u32
|
467
507
|
# #=> 167772160
|
468
508
|
#
|
@@ -474,6 +514,7 @@ module IPAddress;
|
|
474
514
|
# Returns the broadcast address in Unsigned 32bits format
|
475
515
|
#
|
476
516
|
# ip = IPaddress("10.0.0.1/29")
|
517
|
+
#
|
477
518
|
# ip.broadcast_u32
|
478
519
|
# #=> 167772167
|
479
520
|
#
|
@@ -490,6 +531,7 @@ module IPAddress;
|
|
490
531
|
# ip = IPAddress("192.168.10.100/24")
|
491
532
|
#
|
492
533
|
# addr = IPAddress("192.168.10.102/24")
|
534
|
+
#
|
493
535
|
# ip.include? addr
|
494
536
|
# #=> true
|
495
537
|
#
|
@@ -505,12 +547,14 @@ module IPAddress;
|
|
505
547
|
# for DNS lookups
|
506
548
|
#
|
507
549
|
# ip = IPAddress("172.16.100.50/24")
|
550
|
+
#
|
508
551
|
# ip.reverse
|
509
552
|
# #=> "50.100.16.172.in-addr.arpa"
|
510
553
|
#
|
511
554
|
def reverse
|
512
555
|
@octets.reverse.join(".") + ".in-addr.arpa"
|
513
556
|
end
|
557
|
+
alias_method :arpa, :reverse
|
514
558
|
|
515
559
|
#
|
516
560
|
# Subnetting a network
|
@@ -524,7 +568,8 @@ module IPAddress;
|
|
524
568
|
# networks will be divided evenly from the supernet.
|
525
569
|
#
|
526
570
|
# network = IPAddress("172.16.10.0/24")
|
527
|
-
#
|
571
|
+
#
|
572
|
+
# network / 4 # implies map{|i| i.to_string}
|
528
573
|
# #=> ["172.16.10.0/26",
|
529
574
|
# "172.16.10.64/26",
|
530
575
|
# "172.16.10.128/26",
|
@@ -535,7 +580,8 @@ module IPAddress;
|
|
535
580
|
# other networks with the remaining addresses.
|
536
581
|
#
|
537
582
|
# network = IPAddress("172.16.10.0/24")
|
538
|
-
#
|
583
|
+
#
|
584
|
+
# network / 3 # implies map{|i| i.to_string}
|
539
585
|
# #=> ["172.16.10.0/26",
|
540
586
|
# "172.16.10.64/26",
|
541
587
|
# "172.16.10.128/25"]
|
@@ -546,7 +592,6 @@ module IPAddress;
|
|
546
592
|
unless (1..(2**(32-prefix.to_i))).include? subnets
|
547
593
|
raise ArgumentError, "Value #{subnets} out of range"
|
548
594
|
end
|
549
|
-
|
550
595
|
calculate_subnets(subnets)
|
551
596
|
end
|
552
597
|
alias_method :/, :subnet
|
@@ -564,13 +609,13 @@ module IPAddress;
|
|
564
609
|
#
|
565
610
|
# you can supernet it with a new /23 prefix
|
566
611
|
#
|
567
|
-
# ip.supernet(23).
|
612
|
+
# ip.supernet(23).to_string
|
568
613
|
# #=> "172.16.10.0/23"
|
569
614
|
#
|
570
615
|
# However if you supernet it with a /22 prefix, the
|
571
616
|
# network address will change:
|
572
617
|
#
|
573
|
-
# ip.supernet(22).
|
618
|
+
# ip.supernet(22).to_string
|
574
619
|
# #=> "172.16.8.0/22"
|
575
620
|
#
|
576
621
|
def supernet(new_prefix)
|
@@ -583,6 +628,14 @@ module IPAddress;
|
|
583
628
|
# Returns the difference between two IP addresses
|
584
629
|
# in unsigned int 32 bits format
|
585
630
|
#
|
631
|
+
# Example:
|
632
|
+
#
|
633
|
+
# ip1 = IPAddress("172.16.10.0/24")
|
634
|
+
# ip2 = IPAddress("172.16.11.0/24")
|
635
|
+
#
|
636
|
+
# puts ip1 - ip2
|
637
|
+
# #=> 256
|
638
|
+
#
|
586
639
|
def -(oth)
|
587
640
|
return (to_u32 - oth.to_u32).abs
|
588
641
|
end
|
@@ -596,14 +649,21 @@ module IPAddress;
|
|
596
649
|
#
|
597
650
|
# ip1 = IPAddress("172.16.10.1/24")
|
598
651
|
# ip2 = IPAddress("172.16.11.2/24")
|
599
|
-
#
|
600
|
-
#
|
652
|
+
#
|
653
|
+
# p (ip1 + ip2).map {|i| i.to_string}
|
654
|
+
# #=> ["172.16.10.0/23"]
|
601
655
|
#
|
602
656
|
# If the networks are not contiguous, returns
|
603
657
|
# the two network numbers from the objects
|
604
658
|
#
|
659
|
+
# ip1 = IPAddress("10.0.0.1/24")
|
660
|
+
# ip2 = IPAddress("10.0.2.1/24")
|
661
|
+
#
|
662
|
+
# p (ip1 + ip2).map {|i| i.to_string}
|
663
|
+
# #=> ["10.0.0.0/24","10.0.2.0/24"]
|
664
|
+
#
|
605
665
|
def +(oth)
|
606
|
-
|
666
|
+
aggregate(*[self,oth].sort.map{|i| i.network})
|
607
667
|
end
|
608
668
|
|
609
669
|
#
|
@@ -614,11 +674,12 @@ module IPAddress;
|
|
614
674
|
# Example:
|
615
675
|
#
|
616
676
|
# ip = IPAddress("10.0.0.1/24")
|
677
|
+
#
|
617
678
|
# ip.a?
|
618
679
|
# #=> true
|
619
680
|
#
|
620
681
|
def a?
|
621
|
-
CLASSFUL.
|
682
|
+
CLASSFUL.key(8) === bits
|
622
683
|
end
|
623
684
|
|
624
685
|
#
|
@@ -629,11 +690,12 @@ module IPAddress;
|
|
629
690
|
# Example:
|
630
691
|
#
|
631
692
|
# ip = IPAddress("172.16.10.1/24")
|
693
|
+
#
|
632
694
|
# ip.b?
|
633
695
|
# #=> true
|
634
696
|
#
|
635
697
|
def b?
|
636
|
-
CLASSFUL.
|
698
|
+
CLASSFUL.key(16) === bits
|
637
699
|
end
|
638
700
|
|
639
701
|
#
|
@@ -644,11 +706,12 @@ module IPAddress;
|
|
644
706
|
# Example:
|
645
707
|
#
|
646
708
|
# ip = IPAddress("192.168.1.1/30")
|
709
|
+
#
|
647
710
|
# ip.c?
|
648
711
|
# #=> true
|
649
712
|
#
|
650
713
|
def c?
|
651
|
-
CLASSFUL.
|
714
|
+
CLASSFUL.key(24) === bits
|
652
715
|
end
|
653
716
|
|
654
717
|
#
|
@@ -658,6 +721,7 @@ module IPAddress;
|
|
658
721
|
# Example:
|
659
722
|
#
|
660
723
|
# ip = IPAddress("172.16.10.1/24")
|
724
|
+
#
|
661
725
|
# ip.to_ipv6
|
662
726
|
# #=> "ac10:0a01"
|
663
727
|
#
|
@@ -670,14 +734,16 @@ module IPAddress;
|
|
670
734
|
# unsigned 32bits integer.
|
671
735
|
#
|
672
736
|
# ip = IPAddress::IPv4::parse_u32(167772160)
|
737
|
+
#
|
673
738
|
# ip.prefix = 8
|
674
|
-
# ip.
|
739
|
+
# ip.to_string
|
675
740
|
# #=> "10.0.0.0/8"
|
676
741
|
#
|
677
742
|
# The +prefix+ parameter is optional:
|
678
743
|
#
|
679
744
|
# ip = IPAddress::IPv4::parse_u32(167772160, 8)
|
680
|
-
#
|
745
|
+
#
|
746
|
+
# ip.to_string
|
681
747
|
# #=> "10.0.0.0/8"
|
682
748
|
#
|
683
749
|
def self.parse_u32(u32, prefix=nil)
|
@@ -699,7 +765,7 @@ module IPAddress;
|
|
699
765
|
# ip = IPAddress::IPv4::parse_data "\254\020\n\001"
|
700
766
|
# ip.prefix = 24
|
701
767
|
#
|
702
|
-
# ip.
|
768
|
+
# ip.to_string
|
703
769
|
# #=> "172.16.10.1/24"
|
704
770
|
#
|
705
771
|
def self.parse_data(str)
|
@@ -713,10 +779,10 @@ module IPAddress;
|
|
713
779
|
# Example:
|
714
780
|
#
|
715
781
|
# str = "foobar172.16.10.1barbaz"
|
716
|
-
# ip =
|
782
|
+
# ip = IPAddress::IPv4::extract str
|
717
783
|
#
|
718
784
|
# ip.to_s
|
719
|
-
# #=> "172.16.10.1
|
785
|
+
# #=> "172.16.10.1"
|
720
786
|
#
|
721
787
|
def self.extract(str)
|
722
788
|
self.new REGEXP.match(str).to_s
|
@@ -770,7 +836,8 @@ module IPAddress;
|
|
770
836
|
# ip2 = IPAddress("10.0.1.1/24")
|
771
837
|
# ip3 = IPAddress("10.0.2.1/24")
|
772
838
|
# ip4 = IPAddress("10.0.3.1/24")
|
773
|
-
#
|
839
|
+
#
|
840
|
+
# IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).to_string
|
774
841
|
# #=> "10.0.0.0/22",
|
775
842
|
#
|
776
843
|
# But the following networks can't be summarized in a single network:
|
@@ -779,27 +846,28 @@ module IPAddress;
|
|
779
846
|
# ip2 = IPAddress("10.0.2.1/24")
|
780
847
|
# ip3 = IPAddress("10.0.3.1/24")
|
781
848
|
# ip4 = IPAddress("10.0.4.1/24")
|
782
|
-
#
|
849
|
+
#
|
850
|
+
# IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string}
|
783
851
|
# #=> ["10.0.1.0/24","10.0.2.0/23","10.0.4.0/24"]
|
784
852
|
#
|
785
853
|
def self.summarize(*args)
|
786
854
|
# one network? no need to summarize
|
787
|
-
return args.
|
855
|
+
return [args.first.network] if args.size == 1
|
788
856
|
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
result << x.network unless result.any?{|i| i.include? x}
|
796
|
-
end
|
857
|
+
i = 0
|
858
|
+
result = args.dup.sort.map{|ip| ip.network}
|
859
|
+
while i < result.size-1
|
860
|
+
sum = result[i] + result[i+1]
|
861
|
+
result[i..i+1] = sum.first if sum.size == 1
|
862
|
+
i += 1
|
797
863
|
end
|
798
|
-
result << last unless result.any?{|i| i.include? last}
|
799
864
|
|
865
|
+
result.flatten!
|
800
866
|
if result.size == args.size
|
867
|
+
# nothing more to summarize
|
801
868
|
return result
|
802
869
|
else
|
870
|
+
# keep on summarizing
|
803
871
|
return self.summarize(*result)
|
804
872
|
end
|
805
873
|
end
|
@@ -815,12 +883,12 @@ module IPAddress;
|
|
815
883
|
|
816
884
|
def prefix_from_ip(ip)
|
817
885
|
bits = bits_from_address(ip)
|
818
|
-
CLASSFUL.each {|reg,prefix| return prefix if bits =~ reg}
|
886
|
+
CLASSFUL.each {|reg,prefix| return Prefix32.new(prefix) if bits =~ reg}
|
819
887
|
end
|
820
888
|
|
821
889
|
def calculate_subnets(subnets)
|
822
890
|
po2 = subnets.closest_power_of_2
|
823
|
-
new_prefix = @prefix
|
891
|
+
new_prefix = @prefix + Math::log2(po2).to_i
|
824
892
|
networks = Array.new
|
825
893
|
(0..po2-1).each do |i|
|
826
894
|
mul = i * (2**(32-new_prefix))
|
@@ -843,6 +911,21 @@ module IPAddress;
|
|
843
911
|
end
|
844
912
|
return dup.reverse
|
845
913
|
end
|
914
|
+
|
915
|
+
def aggregate(ip1,ip2)
|
916
|
+
if ip1.include? ip2
|
917
|
+
return [ip1]
|
918
|
+
else
|
919
|
+
snet = ip1.supernet(ip1.prefix-1)
|
920
|
+
arr1 = ip1.subnet(2**(ip2.prefix-ip1.prefix)).map{|i| i.to_string}
|
921
|
+
arr2 = snet.subnet(2**(ip2.prefix-snet.prefix)).map{|i| i.to_string}
|
922
|
+
if (arr2 - [ip2.to_string] - arr1).empty?
|
923
|
+
return [snet]
|
924
|
+
else
|
925
|
+
return [ip1, ip2]
|
926
|
+
end
|
927
|
+
end
|
928
|
+
end
|
846
929
|
|
847
930
|
end # class IPv4
|
848
931
|
end # module IPAddress
|