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.
@@ -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
+