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,1274 @@
1
+ require 'ipaddress_2/prefix'
2
+
3
+ module IPAddress;
4
+ #
5
+ # =Name
6
+ #
7
+ # IPAddress::IPv6 - IP version 6 address manipulation library
8
+ #
9
+ # =Synopsis
10
+ #
11
+ # require 'ipaddress'
12
+ #
13
+ # =Description
14
+ #
15
+ # Class IPAddress::IPv6 is used to handle IPv6 type addresses.
16
+ #
17
+ # == IPv6 addresses
18
+ #
19
+ # IPv6 addresses are 128 bits long, in contrast with IPv4 addresses
20
+ # which are only 32 bits long. An IPv6 address is generally written as
21
+ # eight groups of four hexadecimal digits, each group representing 16
22
+ # bits or two octect. For example, the following is a valid IPv6
23
+ # address:
24
+ #
25
+ # 2001:0db8:0000:0000:0008:0800:200c:417a
26
+ #
27
+ # Letters in an IPv6 address are usually written downcase, as per
28
+ # RFC. You can create a new IPv6 object using uppercase letters, but
29
+ # they will be converted.
30
+ #
31
+ # === Compression
32
+ #
33
+ # Since IPv6 addresses are very long to write, there are some
34
+ # semplifications and compressions that you can use to shorten them.
35
+ #
36
+ # * Leading zeroes: all the leading zeroes within a group can be
37
+ # omitted: "0008" would become "8"
38
+ #
39
+ # * A string of consecutive zeroes can be replaced by the string
40
+ # "::". This can be only applied once.
41
+ #
42
+ # Using compression, the IPv6 address written above can be shorten into
43
+ # the following, equivalent, address
44
+ #
45
+ # 2001:db8::8:800:200c:417a
46
+ #
47
+ # This short version is often used in human representation.
48
+ #
49
+ # === Network Mask
50
+ #
51
+ # As we used to do with IPv4 addresses, an IPv6 address can be written
52
+ # using the prefix notation to specify the subnet mask:
53
+ #
54
+ # 2001:db8::8:800:200c:417a/64
55
+ #
56
+ # The /64 part means that the first 64 bits of the address are
57
+ # representing the network portion, and the last 64 bits are the host
58
+ # portion.
59
+ #
60
+ #
61
+ class IPv6
62
+
63
+ include IPAddress
64
+ include Enumerable
65
+ include Comparable
66
+
67
+
68
+ #
69
+ # Format string to pretty print IPv6 addresses
70
+ #
71
+ IN6FORMAT = ("%.4x:"*8).chop
72
+
73
+ #
74
+ # Creates a new IPv6 address object.
75
+ #
76
+ # An IPv6 address can be expressed in any of the following forms:
77
+ #
78
+ # * "2001:0db8:0000:0000:0008:0800:200C:417A": IPv6 address with no compression
79
+ # * "2001:db8:0:0:8:800:200C:417A": IPv6 address with leading zeros compression
80
+ # * "2001:db8::8:800:200C:417A": IPv6 address with full compression
81
+ #
82
+ # In all these 3 cases, a new IPv6 address object will be created, using the default
83
+ # subnet mask /128
84
+ #
85
+ # You can also specify the subnet mask as with IPv4 addresses:
86
+ #
87
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
88
+ #
89
+ def initialize(str)
90
+ raise ArgumentError, "Nil IP" unless str
91
+ ip, netmask = str.split("/")
92
+
93
+ if str =~ /:.+\./
94
+ raise ArgumentError, "Please use #{self.class}::Mapped for IPv4 mapped addresses"
95
+ end
96
+
97
+ if IPAddress.valid_ipv6?(ip)
98
+ @groups = self.class.groups(ip)
99
+ @address = IN6FORMAT % @groups
100
+ @compressed = compress_address
101
+ else
102
+ raise ArgumentError, "Invalid IP #{ip.inspect}"
103
+ end
104
+
105
+ @prefix = Prefix128.new(netmask ? netmask : 128)
106
+ @allocator = 0
107
+
108
+ end # def initialize
109
+
110
+ #
111
+ # Returns the IPv6 address in uncompressed form:
112
+ #
113
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
114
+ #
115
+ # ip6.address
116
+ # #=> "2001:0db8:0000:0000:0008:0800:200c:417a"
117
+ #
118
+ def address
119
+ @address
120
+ end
121
+
122
+ # When serializing to JSON format, just use the string representation
123
+ #
124
+ # ip = IPAddress "2001:db8::8:800:200c:417a/64"
125
+ #
126
+ # ip.as_json
127
+ # #=> "2001:db8::8:800:200c:417a/64"
128
+ #
129
+ def as_json
130
+ to_string
131
+ end
132
+
133
+ #
134
+ # Returns an array with the 16 bits groups in decimal
135
+ # format:
136
+ #
137
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
138
+ #
139
+ # ip6.groups
140
+ # #=> [8193, 3512, 0, 0, 8, 2048, 8204, 16762]
141
+ #
142
+ def groups
143
+ @groups
144
+ end
145
+
146
+ #
147
+ # Returns an instance of the prefix object
148
+ #
149
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
150
+ #
151
+ # ip6.prefix
152
+ # #=> 64
153
+ #
154
+ def prefix
155
+ @prefix
156
+ end
157
+
158
+ #
159
+ # Set a new prefix number for the object
160
+ #
161
+ # This is useful if you want to change the prefix
162
+ # to an object created with IPv6::parse_u128 or
163
+ # if the object was created using the default prefix
164
+ # of 128 bits.
165
+ #
166
+ # ip6 = IPAddress("2001:db8::8:800:200c:417a")
167
+ #
168
+ # puts ip6.to_string
169
+ # #=> "2001:db8::8:800:200c:417a/128"
170
+ #
171
+ # ip6.prefix = 64
172
+ # puts ip6.to_string
173
+ # #=> "2001:db8::8:800:200c:417a/64"
174
+ #
175
+ def prefix=(num)
176
+ @prefix = Prefix128.new(num)
177
+ end
178
+
179
+ #
180
+ # Unlike its counterpart IPv6#to_string method, IPv6#to_string_uncompressed
181
+ # returns the whole IPv6 address and prefix in an uncompressed form
182
+ #
183
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
184
+ #
185
+ # ip6.to_string_uncompressed
186
+ # #=> "2001:0db8:0000:0000:0008:0800:200c:417a/64"
187
+ #
188
+ def to_string_uncompressed
189
+ "#@address/#@prefix"
190
+ end
191
+
192
+ #
193
+ # Returns the IPv6 address in a human readable form,
194
+ # using the compressed address.
195
+ #
196
+ # ip6 = IPAddress "2001:0db8:0000:0000:0008:0800:200c:417a/64"
197
+ #
198
+ # ip6.to_string
199
+ # #=> "2001:db8::8:800:200c:417a/64"
200
+ #
201
+ def to_string
202
+ "#@compressed/#@prefix"
203
+ end
204
+
205
+ #
206
+ # Returns the IPv6 address in a human readable form,
207
+ # using the compressed address.
208
+ #
209
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
210
+ #
211
+ # ip6.to_s
212
+ # #=> "2001:db8::8:800:200c:417a"
213
+ #
214
+ def to_s
215
+ @compressed
216
+ end
217
+
218
+ #
219
+ # Returns a decimal format (unsigned 128 bit) of the
220
+ # IPv6 address
221
+ #
222
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
223
+ #
224
+ # ip6.to_i
225
+ # #=> 42540766411282592856906245548098208122
226
+ #
227
+ def to_i
228
+ to_hex.hex
229
+ end
230
+ alias_method :to_u128, :to_i
231
+
232
+ #
233
+ # True if the IPv6 address is a network
234
+ #
235
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
236
+ #
237
+ # ip6.network?
238
+ # #=> false
239
+ #
240
+ # ip6 = IPAddress "2001:db8:8:800::/64"
241
+ #
242
+ # ip6.network?
243
+ # #=> true
244
+ #
245
+ def network?
246
+ to_u128 | @prefix.to_u128 == @prefix.to_u128
247
+ end
248
+
249
+ #
250
+ # Returns the 16-bits value specified by index
251
+ #
252
+ # ip = IPAddress("2001:db8::8:800:200c:417a/64")
253
+ #
254
+ # ip[0]
255
+ # #=> 8193
256
+ # ip[1]
257
+ # #=> 3512
258
+ # ip[2]
259
+ # #=> 0
260
+ # ip[3]
261
+ # #=> 0
262
+ #
263
+ def [](index)
264
+ @groups[index]
265
+ end
266
+ alias_method :group, :[]
267
+
268
+ #
269
+ # Updated the octet specified at index
270
+ #
271
+ def []=(index, value)
272
+ @groups[index] = value
273
+ initialize("#{IN6FORMAT % @groups}/#{prefix}")
274
+ end
275
+ alias_method :group=, :[]=
276
+
277
+ #
278
+ # Returns a Base16 number representing the IPv6
279
+ # address
280
+ #
281
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
282
+ #
283
+ # ip6.to_hex
284
+ # #=> "20010db80000000000080800200c417a"
285
+ #
286
+ def to_hex
287
+ hexs.join("")
288
+ end
289
+
290
+ # Returns the address portion of an IPv6 object
291
+ # in a network byte order format.
292
+ #
293
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
294
+ #
295
+ # ip6.data
296
+ # #=> " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
297
+ #
298
+ # It is usually used to include an IP address
299
+ # in a data packet to be sent over a socket
300
+ #
301
+ # a = Socket.open(params) # socket details here
302
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
303
+ # binary_data = ["Address: "].pack("a*") + ip.data
304
+ #
305
+ # # Send binary data
306
+ # a.puts binary_data
307
+ #
308
+ def data
309
+ @groups.pack("n8")
310
+ end
311
+
312
+ #
313
+ # Returns an array of the 16 bits groups in hexdecimal
314
+ # format:
315
+ #
316
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
317
+ #
318
+ # ip6.hexs
319
+ # #=> ["2001", "0db8", "0000", "0000", "0008", "0800", "200c", "417a"]
320
+ #
321
+ # Not to be confused with the similar IPv6#to_hex method.
322
+ #
323
+ def hexs
324
+ @address.split(":")
325
+ end
326
+
327
+ #
328
+ # Returns the IPv6 address in a DNS reverse lookup
329
+ # string, as per RFC3172 and RFC2874.
330
+ #
331
+ # ip6 = IPAddress "3ffe:505:2::f"
332
+ #
333
+ # ip6.reverse
334
+ # #=> "f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.arpa"
335
+ #
336
+ def reverse
337
+ to_hex.reverse.gsub(/./){|c| c+"."} + "ip6.arpa"
338
+ end
339
+ alias_method :arpa, :reverse
340
+
341
+ #
342
+ # Splits a network into different subnets
343
+ #
344
+ # NOTE: Will allow you to split past /64 against RFC 5375
345
+ #
346
+ # If the IP Address is a network, it can be divided into
347
+ # multiple networks. If +self+ is not a network, this
348
+ # method will calculate the network from the IP and then
349
+ # subnet it.
350
+ #
351
+ # If +subnets+ is an power of two number, the resulting
352
+ # networks will be divided evenly from the supernet.
353
+ #
354
+ # network = IPAddress("2001:db8:8::/48")
355
+ #
356
+ # network / 4 # implies map{|i| i.to_string}
357
+ # #=> ["2001:db8:8::/50",
358
+ # #=> "2001:db8:8:4000::/50",
359
+ # #=> "2001:db8:8:8000::/50",
360
+ # #=> "2001:db8:8:c000::/50"]
361
+ #
362
+ # If +num+ is any other number, the supernet will be
363
+ # divided into some networks with a even number of hosts and
364
+ # other networks with the remaining addresses.
365
+ #
366
+ # network = IPAddress("2001:db8:8::/48")
367
+ #
368
+ # network / 3 # implies map{|i| i.to_string}
369
+ #
370
+ # #=> ["2001:db8:8::/50",
371
+ # #=> "2001:db8:8:4000::/50",
372
+ # #=> "2001:db8:8:8000::/49"]
373
+ #
374
+ # Returns an array of IPv6 objects
375
+ #
376
+ def split(subnets=2)
377
+ unless (1..(2**@prefix.host_prefix)).include? subnets
378
+ raise ArgumentError, "Value #{subnets} out of range"
379
+ end
380
+ networks = subnet(newprefix(subnets))
381
+ until networks.size == subnets
382
+ networks = sum_first_found(networks)
383
+ end
384
+ return networks
385
+ end
386
+ alias_method :/, :split
387
+
388
+ #
389
+ # Returns the network number in Unsigned 128bits format
390
+ #
391
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
392
+ #
393
+ # ip6.network_u128
394
+ # #=> 42540766411282592856903984951653826560
395
+ #
396
+ def network_u128
397
+ to_u128 & @prefix.to_u128
398
+ end
399
+
400
+ #
401
+ # Returns the broadcast address in Unsigned 128bits format
402
+ #
403
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
404
+ #
405
+ # ip6.broadcast_u128
406
+ # #=> 42540766411282592875350729025363378175
407
+ #
408
+ # Please note that there is no Broadcast concept in IPv6
409
+ # addresses as in IPv4 addresses, and this method is just
410
+ # an helper to other functions.
411
+ #
412
+ def broadcast_u128
413
+ network_u128 + size - 1
414
+ end
415
+
416
+ #
417
+ # Returns the number of IP addresses included
418
+ # in the network. It also counts the network
419
+ # address and the broadcast address.
420
+ #
421
+ # ip6 = IPAddress("2001:db8::8:800:200c:417a/64")
422
+ #
423
+ # ip6.size
424
+ # #=> 18446744073709551616
425
+ #
426
+ def size
427
+ 2 ** @prefix.host_prefix
428
+ end
429
+
430
+ #
431
+ # Checks whether a subnet includes the given IP address.
432
+ #
433
+ # Example:
434
+ #
435
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
436
+ # addr = IPAddress "2001:db8::8:800:200c:1/128"
437
+ #
438
+ # ip6.include? addr
439
+ # #=> true
440
+ #
441
+ # ip6.include? IPAddress("2001:db8:1::8:800:200c:417a/76")
442
+ # #=> false
443
+ #
444
+ def include?(oth)
445
+ @prefix <= oth.prefix and network_u128 == self.class.new(oth.address+"/#@prefix").network_u128
446
+ end
447
+
448
+ #
449
+ # Checks whether a subnet includes all the
450
+ # given IPv4 objects.
451
+ #
452
+ # ip = IPAddress("2001:db8:8:800::1/64")
453
+ #
454
+ # addr1 = IPAddress("2001:db8:8:800::2/64")
455
+ # addr2 = IPAddress("2001:db8:8:800::8/64")
456
+ #
457
+ # ip.include_all?(addr1,addr2)
458
+ # #=> true
459
+ #
460
+ def include_all?(*others)
461
+ others.all? {|oth| include?(oth)}
462
+ end
463
+
464
+ #
465
+ # Compressed form of the IPv6 address
466
+ #
467
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
468
+ #
469
+ # ip6.compressed
470
+ # #=> "2001:db8::8:800:200c:417a"
471
+ #
472
+ def compressed
473
+ @compressed
474
+ end
475
+
476
+ #
477
+ # Returns true if the address is an unspecified address
478
+ #
479
+ # See IPAddress::IPv6::Unspecified for more information
480
+ #
481
+ def unspecified?
482
+ @prefix == 128 and @compressed == "::"
483
+ end
484
+
485
+ #
486
+ # Returns true if the address is a loopback address
487
+ #
488
+ # See IPAddress::IPv6::Loopback for more information
489
+ #
490
+ def loopback?
491
+ @prefix == 128 and @compressed == "::1"
492
+ end
493
+
494
+ #
495
+ # Checks if an IPv6 address objects belongs
496
+ # to a link-local network RFC4291
497
+ #
498
+ # Example:
499
+ #
500
+ # ip = IPAddress "fe80::1"
501
+ # ip.link_local?
502
+ # #=> true
503
+ #
504
+ def link_local?
505
+ [self.class.new("fe80::/10")].any? {|i| i.include? self}
506
+ end
507
+
508
+ #
509
+ # Checks if an IPv6 address objects belongs
510
+ # to a unique-local network RFC4193
511
+ #
512
+ # Example:
513
+ #
514
+ # ip = IPAddress "fc00::1"
515
+ # ip.unique_local?
516
+ # #=> true
517
+ #
518
+ def unique_local?
519
+ [self.class.new("fc00::/7")].any? {|i| i.include? self}
520
+ end
521
+
522
+ #
523
+ # Returns true if the address is a mapped address
524
+ #
525
+ # See IPAddress::IPv6::Mapped for more information
526
+ #
527
+ def mapped?
528
+ to_u128 >> 32 == 0xffff
529
+ end
530
+
531
+ #
532
+ # Returns a new IPv6 object which is the result
533
+ # of the summarization, if possible, of the two
534
+ # objects
535
+ #
536
+ # Example:
537
+ #
538
+ # ip1 = IPAddress("172.16.10.1/24")
539
+ # ip2 = IPAddress("172.16.11.2/24")
540
+ #
541
+ # p (ip1 + ip2).map {|i| i.to_string}
542
+ # #=> ["172.16.10.0/23"]
543
+ #
544
+ # If the networks are not contiguous, returns
545
+ # the two network numbers from the objects
546
+ #
547
+ # ip1 = IPAddress("10.0.0.1/24")
548
+ # ip2 = IPAddress("10.0.2.1/24")
549
+ #
550
+ # p (ip1 + ip2).map {|i| i.to_string}
551
+ # #=> ["10.0.0.0/24","10.0.2.0/24"]
552
+ #
553
+ def +(oth)
554
+ aggregate(*[self,oth].sort.map{|i| i.network})
555
+ end
556
+
557
+ #
558
+ # Returns a new IPv4 object from the supernetting
559
+ # of the instance network.
560
+ #
561
+ # Supernetting is similar to subnetting, except
562
+ # that you getting as a result a network with a
563
+ # smaller prefix (bigger host space). For example,
564
+ # given the network
565
+ #
566
+ # ip = IPAddress("2001:db8:8:800::1/64")
567
+ #
568
+ # you can supernet it with a new /32 prefix
569
+ #
570
+ # ip.supernet(32).to_string
571
+ # #=> "2001:db8::/32"
572
+ #
573
+ # However if you supernet it with a /22 prefix, the
574
+ # network address will change:
575
+ #
576
+ # ip.supernet(22).to_string
577
+ # #=> "2001:c00::/22"
578
+ #
579
+ # If +new_prefix+ is less than 1, returns 0000:0000:0000:0000:0000:0000:0000:0000/0
580
+ #
581
+ def supernet(new_prefix)
582
+ raise ArgumentError, "New prefix must be smaller than existing prefix" if new_prefix >= @prefix.to_i
583
+ return self.class.new("0000:0000:0000:0000:0000:0000:0000:0000/0") if new_prefix < 1
584
+ return self.class.new(@address+"/#{new_prefix}").network
585
+ end
586
+
587
+ #
588
+ # This method implements the subnetting function
589
+ # similar to the one described in RFC3531.
590
+ #
591
+ # By specifying a new prefix, the method calculates
592
+ # the network number for the given IPv4 object
593
+ # and calculates the subnets associated to the new
594
+ # prefix.
595
+ #
596
+ # For example, given the following network:
597
+ #
598
+ # ip = IPAddress "172.16.10.0/24"
599
+ #
600
+ # we can calculate the subnets with a /26 prefix
601
+ #
602
+ # ip.subnet(26).map{&:to_string)
603
+ # #=> ["172.16.10.0/26", "172.16.10.64/26",
604
+ # "172.16.10.128/26", "172.16.10.192/26"]
605
+ #
606
+ # The resulting number of subnets will of course always be
607
+ # a power of two.
608
+ #
609
+ def subnet(subprefix)
610
+ unless ((@prefix.to_i)..128).include? subprefix
611
+ raise ArgumentError, "New prefix must be between #@prefix and 128"
612
+ end
613
+ Array.new(2**(subprefix-@prefix.to_i)) do |i|
614
+ self.class.parse_u128(network_u128+(i*(2**(128-subprefix))), subprefix)
615
+ end
616
+ end
617
+
618
+
619
+ #
620
+ # Iterates over all the IP addresses for the given
621
+ # network (or IP address).
622
+ #
623
+ # The object yielded is a new IPv6 object created
624
+ # from the iteration.
625
+ #
626
+ # ip6 = IPAddress("2001:db8::4/125")
627
+ #
628
+ # ip6.each do |i|
629
+ # p i.compressed
630
+ # end
631
+ # #=> "2001:db8::"
632
+ # #=> "2001:db8::1"
633
+ # #=> "2001:db8::2"
634
+ # #=> "2001:db8::3"
635
+ # #=> "2001:db8::4"
636
+ # #=> "2001:db8::5"
637
+ # #=> "2001:db8::6"
638
+ # #=> "2001:db8::7"
639
+ #
640
+ # WARNING: if the host portion is very large, this method
641
+ # can be very slow and possibly hang your system!
642
+ #
643
+ def each
644
+ (network_u128..broadcast_u128).each do |i|
645
+ yield self.class.parse_u128(i, @prefix)
646
+ end
647
+ end
648
+
649
+ #
650
+ # Returns the successor to the IP address
651
+ #
652
+ # Example:
653
+ #
654
+ # ip6 = IPAddress("2001:db8::8:800:200c:417a/64")
655
+ #
656
+ # ip6.succ.to_string
657
+ # => "2001:db8::8:800:200c:417b/64"
658
+ #
659
+ def succ
660
+ IPAddress::IPv6.parse_u128(to_u128.succ, prefix)
661
+ end
662
+ alias_method :next, :succ
663
+
664
+ #
665
+ # Returns the predecessor to the IP address
666
+ #
667
+ # Example:
668
+ #
669
+ # ip6 = IPAddress("2001:db8::8:800:200c:417a/64")
670
+ #
671
+ # ip6.pred.to_string
672
+ # => "2001:db8::8:800:200c:4179/64"
673
+ #
674
+ def pred
675
+ IPAddress::IPv6.parse_u128(to_u128.pred, prefix)
676
+ end
677
+
678
+ #
679
+ # Spaceship operator to compare IPv6 objects
680
+ #
681
+ # Comparing IPv6 addresses is useful to ordinate
682
+ # them into lists that match our intuitive
683
+ # perception of ordered IP addresses.
684
+ #
685
+ # The first comparison criteria is the u128 value.
686
+ # For example, 2001:db8:1::1 will be considered
687
+ # to be less than 2001:db8:2::1, because, in a ordered list,
688
+ # we expect 2001:db8:1::1 to come before 2001:db8:2::1.
689
+ #
690
+ # The second criteria, in case two IPv6 objects
691
+ # have identical addresses, is the prefix. An higher
692
+ # prefix will be considered greater than a lower
693
+ # prefix. This is because we expect to see
694
+ # 2001:db8:1::1/64 come before 2001:db8:1::1/65
695
+ #
696
+ # Example:
697
+ #
698
+ # ip1 = IPAddress "2001:db8:1::1/64"
699
+ # ip2 = IPAddress "2001:db8:2::1/64"
700
+ # ip3 = IPAddress "2001:db8:1::1/65"
701
+ #
702
+ # ip1 < ip2
703
+ # #=> true
704
+ # ip1 < ip3
705
+ # #=> false
706
+ #
707
+ # [ip1,ip2,ip3].sort.map{|i| i.to_string}
708
+ # #=> ["2001:db8:1::1/64","2001:db8:1::1/65","2001:db8:2::1/64"]
709
+ #
710
+ def <=>(oth)
711
+ return nil unless oth.is_a?(self.class)
712
+ return prefix <=> oth.prefix if to_u128 == oth.to_u128
713
+ to_u128 <=> oth.to_u128
714
+ end
715
+
716
+ #
717
+ # Returns the address portion of an IP in binary format,
718
+ # as a string containing a sequence of 0 and 1
719
+ #
720
+ # ip6 = IPAddress("2001:db8::8:800:200c:417a")
721
+ #
722
+ # ip6.bits
723
+ # #=> "0010000000000001000011011011100000 [...] "
724
+ #
725
+ def bits
726
+ data.unpack("B*").first
727
+ end
728
+
729
+ #
730
+ # Expands an IPv6 address in the canocical form
731
+ #
732
+ # IPAddress::IPv6.expand "2001:0DB8:0:CD30::"
733
+ # #=> "2001:0DB8:0000:CD30:0000:0000:0000:0000"
734
+ #
735
+ def self.expand(str)
736
+ self.new(str).address
737
+ end
738
+
739
+ #
740
+ # Compress an IPv6 address in its compressed form
741
+ #
742
+ # IPAddress::IPv6.compress "2001:0DB8:0000:CD30:0000:0000:0000:0000"
743
+ # #=> "2001:db8:0:cd30::"
744
+ #
745
+ def self.compress(str)
746
+ self.new(str).compressed
747
+ end
748
+
749
+ #
750
+ # Literal version of the IPv6 address
751
+ #
752
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
753
+ #
754
+ # ip6.literal
755
+ # #=> "2001-0db8-0000-0000-0008-0800-200c-417a.ipv6-literal.net"
756
+ #
757
+ def literal
758
+ @address.gsub(":","-") + ".ipv6-literal.net"
759
+ end
760
+
761
+ #
762
+ # Returns a new IPv6 object with the network number
763
+ # for the given IP.
764
+ #
765
+ # ip = IPAddress "2001:db8:1:1:1:1:1:1/32"
766
+ #
767
+ # ip.network.to_string
768
+ # #=> "2001:db8::/32"
769
+ #
770
+ def network
771
+ self.class.parse_u128(network_u128, @prefix)
772
+ end
773
+
774
+ #
775
+ # Extract 16 bits groups from a string
776
+ #
777
+ def self.groups(str)
778
+ l, r = if str =~ /^(.*)::(.*)$/
779
+ [$1,$2].map {|i| i.split ":"}
780
+ else
781
+ [str.split(":"),[]]
782
+ end
783
+ (l + Array.new(8-l.size-r.size, '0') + r).map {|i| i.hex}
784
+ end
785
+
786
+ #
787
+ # Creates a new IPv6 object from binary data,
788
+ # like the one you get from a network stream.
789
+ #
790
+ # For example, on a network stream the IP
791
+ #
792
+ # "2001:db8::8:800:200c:417a"
793
+ #
794
+ # is represented with the binary data
795
+ #
796
+ # " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
797
+ #
798
+ # With that data you can create a new IPv6 object:
799
+ #
800
+ # ip6 = IPAddress::IPv6::parse_data " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
801
+ # ip6.prefix = 64
802
+ #
803
+ # ip6.to_s
804
+ # #=> "2001:db8::8:800:200c:417a/64"
805
+ #
806
+ def self.parse_data(str)
807
+ self.new(IN6FORMAT % str.unpack("n8"))
808
+ end
809
+
810
+ #
811
+ # Creates a new IPv6 object from an
812
+ # unsigned 128 bits integer.
813
+ #
814
+ # ip6 = IPAddress::IPv6::parse_u128(42540766411282592856906245548098208122)
815
+ # ip6.prefix = 64
816
+ #
817
+ # ip6.to_string
818
+ # #=> "2001:db8::8:800:200c:417a/64"
819
+ #
820
+ # The +prefix+ parameter is optional:
821
+ #
822
+ # ip6 = IPAddress::IPv6::parse_u128(42540766411282592856906245548098208122, 64)
823
+ #
824
+ # ip6.to_string
825
+ # #=> "2001:db8::8:800:200c:417a/64"
826
+ #
827
+ def self.parse_u128(u128, prefix=128)
828
+ str = IN6FORMAT % (0..7).map{|i| (u128>>(112-16*i))&0xffff}
829
+ self.new(str + "/#{prefix}")
830
+ end
831
+
832
+ #
833
+ # Creates a new IPv6 object from a number expressed in
834
+ # hexdecimal format:
835
+ #
836
+ # ip6 = IPAddress::IPv6::parse_hex("20010db80000000000080800200c417a")
837
+ # ip6.prefix = 64
838
+ #
839
+ # ip6.to_string
840
+ # #=> "2001:db8::8:800:200c:417a/64"
841
+ #
842
+ # The +prefix+ parameter is optional:
843
+ #
844
+ # ip6 = IPAddress::IPv6::parse_hex("20010db80000000000080800200c417a", 64)
845
+ #
846
+ # ip6.to_string
847
+ # #=> "2001:db8::8:800:200c:417a/64"
848
+ #
849
+ def self.parse_hex(hex, prefix=128)
850
+ self.parse_u128(hex.hex, prefix)
851
+ end
852
+
853
+ #
854
+ # Summarization (or aggregation) is the process when two or more
855
+ # networks are taken together to check if a supernet, including all
856
+ # and only these networks, exists. If it exists then this supernet
857
+ # is called the summarized (or aggregated) network.
858
+ #
859
+ # It is very important to understand that summarization can only
860
+ # occur if there are no holes in the aggregated network, or, in other
861
+ # words, if the given networks fill completely the address space
862
+ # of the supernet. So the two rules are:
863
+ #
864
+ # 1) The aggregate network must contain +all+ the IP addresses of the
865
+ # original networks;
866
+ # 2) The aggregate network must contain +only+ the IP addresses of the
867
+ # original networks;
868
+ #
869
+ # A few examples will help clarify the above. Let's consider for
870
+ # instance the following two networks:
871
+ #
872
+ # ip1 = IPAddress("2001:db8:8:800::1/64")
873
+ # ip2 = IPAddress("2001:0db8:8:801::2/64")
874
+ #
875
+ # These two networks can be expressed using only one IP address
876
+ # network if we change the prefix. Let Ruby do the work:
877
+ #
878
+ # IPAddress::IPv6::summarize(ip1,ip2).to_s
879
+ # #=> "2001:db8:8:800::/63"
880
+ #
881
+ # We note how the network "2001:db8:8:800::/63" includes all the addresses
882
+ # specified in the above networks, and (more important) includes
883
+ # ONLY those addresses.
884
+ #
885
+ # If we summarized +ip1+ and +ip2+ with the following network:
886
+ #
887
+ # "2001:db8::/32"
888
+ #
889
+ # we would have satisfied rule #1 above, but not rule #2. So "2001:db8::/32"
890
+ # is not an aggregate network for +ip1+ and +ip2+.
891
+ #
892
+ # If it's not possible to compute a single aggregated network for all the
893
+ # original networks, the method returns an array with all the aggregate
894
+ # networks found. For example, the following four networks can be
895
+ # aggregated in a single /22:
896
+ #
897
+ # ip1 = IPAddress("2001:db8:8:800::1/64")
898
+ # ip2 = IPAddress("2001:db8:8:801::1/64")
899
+ # ip3 = IPAddress("2001:db8:8:802::1/64")
900
+ # ip4 = IPAddress("2001:db8:8:803::1/64")
901
+ #
902
+ # IPAddress::IPv6::summarize(ip1,ip2,ip3,ip4).to_string
903
+ # #=> "2001:db8:8:800::/62",
904
+ #
905
+ # But the following networks can't be summarized in a single network:
906
+ #
907
+ # ip1 = IPAddress("2001:db8:8:801::1/64")
908
+ # ip2 = IPAddress("2001:db8:8:802::1/64")
909
+ # ip3 = IPAddress("2001:db8:8:803::1/64")
910
+ # ip4 = IPAddress("2001:db8:8:804::1/64")
911
+ #
912
+ # IPAddress::IPv6::summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string}
913
+ # #=> ["2001:db8:8:801::/64","2001:db8:8:802::/63","2001:db8:8:804::/64"]
914
+ #
915
+ def self.summarize(*args)
916
+ # one network? no need to summarize
917
+ return [args.first.network] if args.size == 1
918
+
919
+ i = 0
920
+ result = args.dup.sort.map{|ip| ip.network}
921
+ while i < result.size-1
922
+ sum = result[i] + result[i+1]
923
+ result[i..i+1] = sum.first if sum.size == 1
924
+ i += 1
925
+ end
926
+
927
+ result.flatten!
928
+ if result.size == args.size
929
+ # nothing more to summarize
930
+ return result
931
+ else
932
+ # keep on summarizing
933
+ return self.summarize(*result)
934
+ end
935
+ end
936
+
937
+ #
938
+ # Allocates a new ip from the current subnet. Optional skip parameter
939
+ # can be used to skip addresses.
940
+ #
941
+ # Will raise StopIteration exception when all addresses have been allocated
942
+ #
943
+ # Example:
944
+ #
945
+ # ip = IPAddress("10.0.0.0/24")
946
+ # ip.allocate
947
+ # #=> "10.0.0.1/24"
948
+ # ip.allocate
949
+ # #=> "10.0.0.2/24"
950
+ # ip.allocate(2)
951
+ # #=> "10.0.0.5/24"
952
+ #
953
+ #
954
+ # Uses an internal @allocator which tracks the state of allocated
955
+ # addresses.
956
+ #
957
+ def allocate(skip=0)
958
+ @allocator += 1 + skip
959
+
960
+ next_ip = network_u128+@allocator
961
+ if next_ip > broadcast_u128
962
+ raise StopIteration
963
+ end
964
+ self.class.parse_u128(next_ip, @prefix)
965
+ end
966
+
967
+ #
968
+ # Finds the adjacent block to a subnet.
969
+ #
970
+ # Example:
971
+ #
972
+ # ip = IPAddress("2001:db8::/32")
973
+ # ip.find_adjacent_subnet
974
+ # #=> "2001:db9::/32"
975
+ #
976
+ def find_adjacent_subnet
977
+ return false if prefix == 0
978
+ current_subnet = to_string
979
+ self.prefix = @prefix - 1
980
+ (split.map{|i| i.to_string} - [current_subnet])[0]
981
+ end
982
+
983
+ private
984
+
985
+ def newprefix(num)
986
+ return @prefix + (Math::log2(num).ceil )
987
+ end
988
+
989
+ def sum_first_found(arr)
990
+ dup = arr.dup.reverse
991
+ dup.each_with_index do |obj,i|
992
+ a = [self.class.summarize(obj,dup[i+1])].flatten
993
+ if a.size == 1
994
+ dup[i..i+1] = a
995
+ return dup.reverse
996
+ end
997
+ end
998
+ return dup.reverse
999
+ end
1000
+
1001
+ def compress_address
1002
+ str = @groups.map{|i| i.to_s 16}.join ":"
1003
+ loop do
1004
+ break if str.sub!(/\A0:0:0:0:0:0:0:0\Z/, '::')
1005
+ break if str.sub!(/\b0:0:0:0:0:0:0\b/, ':')
1006
+ break if str.sub!(/\b0:0:0:0:0:0\b/, ':')
1007
+ break if str.sub!(/\b0:0:0:0:0\b/, ':')
1008
+ break if str.sub!(/\b0:0:0:0\b/, ':')
1009
+ break if str.sub!(/\b0:0:0\b/, ':')
1010
+ break if str.sub!(/\b0:0\b/, ':')
1011
+ break
1012
+ end
1013
+ str.sub(/:{3,}/, '::')
1014
+ end
1015
+
1016
+ def aggregate(ip1,ip2)
1017
+ return [ip1] if ip1.include? ip2
1018
+
1019
+ snet = ip1.supernet(ip1.prefix-1)
1020
+ if snet.include_all?(ip1, ip2) && ((ip1.size + ip2.size) == snet.size)
1021
+ return [snet]
1022
+ else
1023
+ return [ip1, ip2]
1024
+ end
1025
+ end
1026
+
1027
+ end # class IPv6
1028
+
1029
+ #
1030
+ # The address with all zero bits is called the +unspecified+ address
1031
+ # (corresponding to 0.0.0.0 in IPv4). It should be something like this:
1032
+ #
1033
+ # 0000:0000:0000:0000:0000:0000:0000:0000
1034
+ #
1035
+ # but, with the use of compression, it is usually written as just two
1036
+ # colons:
1037
+ #
1038
+ # ::
1039
+ #
1040
+ # or, specifying the netmask:
1041
+ #
1042
+ # ::/128
1043
+ #
1044
+ # With IPAddress, create a new unspecified IPv6 address using its own
1045
+ # subclass:
1046
+ #
1047
+ # ip = IPAddress::IPv6::Unspecified.new
1048
+ #
1049
+ # ip.to_s
1050
+ # #=> => "::/128"
1051
+ #
1052
+ # You can easily check if an IPv6 object is an unspecified address by
1053
+ # using the IPv6#unspecified? method
1054
+ #
1055
+ # ip.unspecified?
1056
+ # #=> true
1057
+ #
1058
+ # An unspecified IPv6 address can also be created with the wrapper
1059
+ # method, like we've seen before
1060
+ #
1061
+ # ip = IPAddress "::"
1062
+ #
1063
+ # ip.unspecified?
1064
+ # #=> true
1065
+ #
1066
+ # This address must never be assigned to an interface and is to be used
1067
+ # only in software before the application has learned its host's source
1068
+ # address appropriate for a pending connection. Routers must not forward
1069
+ # packets with the unspecified address.
1070
+ #
1071
+ class IPAddress::IPv6::Unspecified < IPAddress::IPv6
1072
+ #
1073
+ # Creates a new IPv6 unspecified address
1074
+ #
1075
+ # ip = IPAddress::IPv6::Unspecified.new
1076
+ #
1077
+ # ip.to_s
1078
+ # #=> => "::/128"
1079
+ #
1080
+ def initialize
1081
+ @address = ("0000:"*8).chop
1082
+ @groups = Array.new(8,0)
1083
+ @prefix = Prefix128.new(128)
1084
+ @compressed = compress_address
1085
+ end
1086
+ end # class IPv6::Unspecified
1087
+
1088
+ #
1089
+ # The loopback address is a unicast localhost address. If an
1090
+ # application in a host sends packets to this address, the IPv6 stack
1091
+ # will loop these packets back on the same virtual interface.
1092
+ #
1093
+ # Loopback addresses are expressed in the following form:
1094
+ #
1095
+ # ::1
1096
+ #
1097
+ # or, with their appropriate prefix,
1098
+ #
1099
+ # ::1/128
1100
+ #
1101
+ # As for the unspecified addresses, IPv6 loopbacks can be created with
1102
+ # IPAddress calling their own class:
1103
+ #
1104
+ # ip = IPAddress::IPv6::Loopback.new
1105
+ #
1106
+ # ip.to_string
1107
+ # #=> "::1/128"
1108
+ #
1109
+ # or by using the wrapper:
1110
+ #
1111
+ # ip = IPAddress "::1"
1112
+ #
1113
+ # ip.to_string
1114
+ # #=> "::1/128"
1115
+ #
1116
+ # Checking if an address is loopback is easy with the IPv6#loopback?
1117
+ # method:
1118
+ #
1119
+ # ip.loopback?
1120
+ # #=> true
1121
+ #
1122
+ # The IPv6 loopback address corresponds to 127.0.0.1 in IPv4.
1123
+ #
1124
+ class IPAddress::IPv6::Loopback < IPAddress::IPv6
1125
+ #
1126
+ # Creates a new IPv6 unspecified address
1127
+ #
1128
+ # ip = IPAddress::IPv6::Loopback.new
1129
+ #
1130
+ # ip.to_string
1131
+ # #=> "::1/128"
1132
+ #
1133
+ def initialize
1134
+ @address = ("0000:"*7)+"0001"
1135
+ @groups = Array.new(7,0).push(1)
1136
+ @prefix = Prefix128.new(128)
1137
+ @compressed = compress_address
1138
+ end
1139
+ end # class IPv6::Loopback
1140
+
1141
+ #
1142
+ # It is usually identified as a IPv4 mapped IPv6 address, a particular
1143
+ # IPv6 address which aids the transition from IPv4 to IPv6. The
1144
+ # structure of the address is
1145
+ #
1146
+ # ::ffff:w.y.x.z
1147
+ #
1148
+ # where w.x.y.z is a normal IPv4 address. For example, the following is
1149
+ # a mapped IPv6 address:
1150
+ #
1151
+ # ::ffff:192.168.100.1
1152
+ #
1153
+ # IPAddress is very powerful in handling mapped IPv6 addresses, as the
1154
+ # IPv4 portion is stored internally as a normal IPv4 object. Let's have
1155
+ # a look at some examples. To create a new mapped address, just use the
1156
+ # class builder itself
1157
+ #
1158
+ # ip6 = IPAddress::IPv6::Mapped.new "::ffff:172.16.10.1/128"
1159
+ #
1160
+ # or just use the wrapper method
1161
+ #
1162
+ # ip6 = IPAddress "::ffff:172.16.10.1/128"
1163
+ #
1164
+ # Let's check it's really a mapped address:
1165
+ #
1166
+ # ip6.mapped?
1167
+ # #=> true
1168
+ #
1169
+ # ip6.to_string
1170
+ # #=> "::FFFF:172.16.10.1/128"
1171
+ #
1172
+ # Now with the +ipv4+ attribute, we can easily access the IPv4 portion
1173
+ # of the mapped IPv6 address:
1174
+ #
1175
+ # ip6.ipv4.address
1176
+ # #=> "172.16.10.1"
1177
+ #
1178
+ # Internally, the IPv4 address is stored as two 16 bits
1179
+ # groups. Therefore all the usual methods for an IPv6 address are
1180
+ # working perfectly fine:
1181
+ #
1182
+ # ip6.to_hex
1183
+ # #=> "00000000000000000000ffffac100a01"
1184
+ #
1185
+ # ip6.address
1186
+ # #=> "0000:0000:0000:0000:0000:ffff:ac10:0a01"
1187
+ #
1188
+ # A mapped IPv6 can also be created just by specify the address in the
1189
+ # following format:
1190
+ #
1191
+ # ip6 = IPAddress "::172.16.10.1"
1192
+ #
1193
+ # That is, two colons and the IPv4 address. However, as by RFC, the ffff
1194
+ # group will be automatically added at the beginning
1195
+ #
1196
+ # ip6.to_string
1197
+ # => "::ffff:172.16.10.1/128"
1198
+ #
1199
+ # making it a mapped IPv6 compatible address.
1200
+ #
1201
+ class IPAddress::IPv6::Mapped < IPAddress::IPv6
1202
+
1203
+ # Access the internal IPv4 address
1204
+ attr_reader :ipv4
1205
+
1206
+ #
1207
+ # Creates a new IPv6 IPv4-mapped address
1208
+ #
1209
+ # ip6 = IPAddress::IPv6::Mapped.new "::ffff:172.16.10.1/128"
1210
+ #
1211
+ # ipv6.ipv4.class
1212
+ # #=> IPAddress::IPv4
1213
+ #
1214
+ # An IPv6 IPv4-mapped address can also be created using the
1215
+ # IPv6 only format of the address:
1216
+ #
1217
+ # ip6 = IPAddress::IPv6::Mapped.new "::0d01:4403"
1218
+ #
1219
+ # ip6.to_string
1220
+ # #=> "::ffff:13.1.68.3"
1221
+ #
1222
+ def initialize(str)
1223
+ string, netmask = str.split("/")
1224
+ if string =~ /\./ # IPv4 in dotted decimal form
1225
+ @ipv4 = IPAddress::IPv4.extract(string)
1226
+ else # IPv4 in hex form
1227
+ groups = IPAddress::IPv6.groups(string)
1228
+ @ipv4 = IPAddress::IPv4.parse_u32((groups[-2]<< 16)+groups[-1])
1229
+ end
1230
+ super("::ffff:#{@ipv4.to_ipv6}/#{netmask}")
1231
+ end
1232
+
1233
+ #
1234
+ # Similar to IPv6#to_s, but prints out the IPv4 address
1235
+ # in dotted decimal format
1236
+ #
1237
+ # ip6 = IPAddress "::ffff:172.16.10.1/128"
1238
+ #
1239
+ # ip6.to_s
1240
+ # #=> "::ffff:172.16.10.1"
1241
+ #
1242
+ def to_s
1243
+ "::ffff:#{@ipv4.address}"
1244
+ end
1245
+
1246
+ #
1247
+ # Similar to IPv6#to_string, but prints out the IPv4 address
1248
+ # in dotted decimal format
1249
+ #
1250
+ #
1251
+ # ip6 = IPAddress "::ffff:172.16.10.1/128"
1252
+ #
1253
+ # ip6.to_string
1254
+ # #=> "::ffff:172.16.10.1/128"
1255
+ #
1256
+ def to_string
1257
+ "::ffff:#{@ipv4.address}/#@prefix"
1258
+ end
1259
+
1260
+ #
1261
+ # Checks if the IPv6 address is IPv4 mapped
1262
+ #
1263
+ # ip6 = IPAddress "::ffff:172.16.10.1/128"
1264
+ #
1265
+ # ip6.mapped?
1266
+ # #=> true
1267
+ #
1268
+ def mapped?
1269
+ true
1270
+ end
1271
+ end # class IPv6::Mapped
1272
+
1273
+ end # module IPAddress
1274
+