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/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
- # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
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 "irb -rubygems -I lib -r ipaddress.rb"
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.5.0
1
+ 0.6.0
@@ -1,9 +1,136 @@
1
- $LOAD_PATH.unshift(File.dirname(__FILE__))
2
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
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
- case str
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(32) do |i|
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
@@ -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 < IPBase
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
- # ip.to_s
180
+ #
181
+ # ip.to_string
163
182
  # #=> "172.16.100.4/22"
164
183
  #
165
- def to_s
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/24"
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/24"
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/24"
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/24"
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.1.
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/24"
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/24"
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 = IPaddress("10.0.0.1/29")
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 = IPaddress("10.0.0.1/29")
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 ""172.16.0.1/16"
410
- # ip3 = IPAddress ""10.100.100.1/16"
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 = IPaddress("10.0.0.1/29")
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 = IPaddress("10.0.0.1/29")
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 = IPaddress("10.0.0.1/29")
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
- # network / 4 # implies map{|i| i.to_s}
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
- # network / 3 # implies map{|i| i.to_s}
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).to_s
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).to_s
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
- # puts ip1 + ip2
600
- # #=>"172.16.10.0/23"
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
- self.class.summarize(self,oth)
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.index(8) === bits
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.index(16) === bits
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.index(24) === bits
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.to_s
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
- # ip.to_s
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.to_s
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 = self.extract str
782
+ # ip = IPAddress::IPv4::extract str
717
783
  #
718
784
  # ip.to_s
719
- # #=> "172.16.10.1/16"
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
- # IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).to_s
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
- # IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).map{|i| i.to_s}
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.flatten.first if args.size == 1
855
+ return [args.first.network] if args.size == 1
788
856
 
789
- result, arr, last = [], args.sort, args.sort.last.network
790
- arr.each_cons(2) do |x,y|
791
- snet = x.supernet(x.prefix.to_i-1)
792
- if snet.include? y
793
- result << snet
794
- else
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.to_i + Math::log2(po2).to_i
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