ipaddress_link_local 0.8.3

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,909 @@
1
+ require 'ipaddress/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
+ ip, netmask = str.split("/")
91
+
92
+ if str =~ /:.+\./
93
+ raise ArgumentError, "Please use #{self.class}::Mapped for IPv4 mapped addresses"
94
+ end
95
+
96
+ if IPAddress.valid_ipv6?(ip)
97
+ @groups = self.class.groups(ip)
98
+ @address = IN6FORMAT % @groups
99
+ @compressed = compress_address
100
+ else
101
+ raise ArgumentError, "Invalid IP #{ip.inspect}"
102
+ end
103
+
104
+ @prefix = Prefix128.new(netmask ? netmask : 128)
105
+
106
+ end # def initialize
107
+
108
+ #
109
+ # Returns the IPv6 address in uncompressed form:
110
+ #
111
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
112
+ #
113
+ # ip6.address
114
+ # #=> "2001:0db8:0000:0000:0008:0800:200c:417a"
115
+ #
116
+ def address
117
+ @address
118
+ end
119
+
120
+ #
121
+ # Returns an array with the 16 bits groups in decimal
122
+ # format:
123
+ #
124
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
125
+ #
126
+ # ip6.groups
127
+ # #=> [8193, 3512, 0, 0, 8, 2048, 8204, 16762]
128
+ #
129
+ def groups
130
+ @groups
131
+ end
132
+
133
+ #
134
+ # Returns an instance of the prefix object
135
+ #
136
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
137
+ #
138
+ # ip6.prefix
139
+ # #=> 64
140
+ #
141
+ def prefix
142
+ @prefix
143
+ end
144
+
145
+ #
146
+ # Set a new prefix number for the object
147
+ #
148
+ # This is useful if you want to change the prefix
149
+ # to an object created with IPv6::parse_u128 or
150
+ # if the object was created using the default prefix
151
+ # of 128 bits.
152
+ #
153
+ # ip6 = IPAddress("2001:db8::8:800:200c:417a")
154
+ #
155
+ # puts ip6.to_string
156
+ # #=> "2001:db8::8:800:200c:417a/128"
157
+ #
158
+ # ip6.prefix = 64
159
+ # puts ip6.to_string
160
+ # #=> "2001:db8::8:800:200c:417a/64"
161
+ #
162
+ def prefix=(num)
163
+ @prefix = Prefix128.new(num)
164
+ end
165
+
166
+ #
167
+ # Unlike its counterpart IPv6#to_string method, IPv6#to_string_uncompressed
168
+ # returns the whole IPv6 address and prefix in an uncompressed form
169
+ #
170
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
171
+ #
172
+ # ip6.to_string_uncompressed
173
+ # #=> "2001:0db8:0000:0000:0008:0800:200c:417a/64"
174
+ #
175
+ def to_string_uncompressed
176
+ "#@address/#@prefix"
177
+ end
178
+
179
+ #
180
+ # Returns the IPv6 address in a human readable form,
181
+ # using the compressed address.
182
+ #
183
+ # ip6 = IPAddress "2001:0db8:0000:0000:0008:0800:200c:417a/64"
184
+ #
185
+ # ip6.to_string
186
+ # #=> "2001:db8::8:800:200c:417a/64"
187
+ #
188
+ def to_string
189
+ "#@compressed/#@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:db8::8:800:200c:417a/64"
197
+ #
198
+ # ip6.to_s
199
+ # #=> "2001:db8::8:800:200c:417a"
200
+ #
201
+ def to_s
202
+ @compressed
203
+ end
204
+
205
+ #
206
+ # Returns a decimal format (unsigned 128 bit) of the
207
+ # IPv6 address
208
+ #
209
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
210
+ #
211
+ # ip6.to_i
212
+ # #=> 42540766411282592856906245548098208122
213
+ #
214
+ def to_i
215
+ to_hex.hex
216
+ end
217
+ alias_method :to_u128, :to_i
218
+
219
+ #
220
+ # True if the IPv6 address is a network
221
+ #
222
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
223
+ #
224
+ # ip6.network?
225
+ # #=> false
226
+ #
227
+ # ip6 = IPAddress "2001:db8:8:800::/64"
228
+ #
229
+ # ip6.network?
230
+ # #=> true
231
+ #
232
+ def network?
233
+ to_u128 | @prefix.to_u128 == @prefix.to_u128
234
+ end
235
+
236
+ #
237
+ # Returns the 16-bits value specified by index
238
+ #
239
+ # ip = IPAddress("2001:db8::8:800:200c:417a/64")
240
+ #
241
+ # ip[0]
242
+ # #=> 8193
243
+ # ip[1]
244
+ # #=> 3512
245
+ # ip[2]
246
+ # #=> 0
247
+ # ip[3]
248
+ # #=> 0
249
+ #
250
+ def [](index)
251
+ @groups[index]
252
+ end
253
+ alias_method :group, :[]
254
+
255
+ #
256
+ # Updated the octet specified at index
257
+ #
258
+ def []=(index, value)
259
+ @groups[index] = value
260
+ initialize("#{IN6FORMAT % @groups}/#{prefix}")
261
+ end
262
+ alias_method :group=, :[]=
263
+
264
+ #
265
+ # Returns a Base16 number representing the IPv6
266
+ # address
267
+ #
268
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
269
+ #
270
+ # ip6.to_hex
271
+ # #=> "20010db80000000000080800200c417a"
272
+ #
273
+ def to_hex
274
+ hexs.join("")
275
+ end
276
+
277
+ # Returns the address portion of an IPv6 object
278
+ # in a network byte order format.
279
+ #
280
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
281
+ #
282
+ # ip6.data
283
+ # #=> " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
284
+ #
285
+ # It is usually used to include an IP address
286
+ # in a data packet to be sent over a socket
287
+ #
288
+ # a = Socket.open(params) # socket details here
289
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
290
+ # binary_data = ["Address: "].pack("a*") + ip.data
291
+ #
292
+ # # Send binary data
293
+ # a.puts binary_data
294
+ #
295
+ def data
296
+ @groups.pack("n8")
297
+ end
298
+
299
+ #
300
+ # Returns an array of the 16 bits groups in hexdecimal
301
+ # format:
302
+ #
303
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
304
+ #
305
+ # ip6.hexs
306
+ # #=> ["2001", "0db8", "0000", "0000", "0008", "0800", "200c", "417a"]
307
+ #
308
+ # Not to be confused with the similar IPv6#to_hex method.
309
+ #
310
+ def hexs
311
+ @address.split(":")
312
+ end
313
+
314
+ #
315
+ # Returns the IPv6 address in a DNS reverse lookup
316
+ # string, as per RFC3172 and RFC2874.
317
+ #
318
+ # ip6 = IPAddress "3ffe:505:2::f"
319
+ #
320
+ # ip6.reverse
321
+ # #=> "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"
322
+ #
323
+ def reverse
324
+ to_hex.reverse.gsub(/./){|c| c+"."} + "ip6.arpa"
325
+ end
326
+ alias_method :arpa, :reverse
327
+
328
+ #
329
+ # Returns the network number in Unsigned 128bits format
330
+ #
331
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
332
+ #
333
+ # ip6.network_u128
334
+ # #=> 42540766411282592856903984951653826560
335
+ #
336
+ def network_u128
337
+ to_u128 & @prefix.to_u128
338
+ end
339
+
340
+ #
341
+ # Returns the broadcast address in Unsigned 128bits format
342
+ #
343
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
344
+ #
345
+ # ip6.broadcast_u128
346
+ # #=> 42540766411282592875350729025363378175
347
+ #
348
+ # Please note that there is no Broadcast concept in IPv6
349
+ # addresses as in IPv4 addresses, and this method is just
350
+ # an helper to other functions.
351
+ #
352
+ def broadcast_u128
353
+ network_u128 + size - 1
354
+ end
355
+
356
+ #
357
+ # Returns the number of IP addresses included
358
+ # in the network. It also counts the network
359
+ # address and the broadcast address.
360
+ #
361
+ # ip6 = IPAddress("2001:db8::8:800:200c:417a/64")
362
+ #
363
+ # ip6.size
364
+ # #=> 18446744073709551616
365
+ #
366
+ def size
367
+ 2 ** @prefix.host_prefix
368
+ end
369
+
370
+ #
371
+ # Checks whether a subnet includes the given IP address.
372
+ #
373
+ # Example:
374
+ #
375
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
376
+ # addr = IPAddress "2001:db8::8:800:200c:1/128"
377
+ #
378
+ # ip6.include? addr
379
+ # #=> true
380
+ #
381
+ # ip6.include? IPAddress("2001:db8:1::8:800:200c:417a/76")
382
+ # #=> false
383
+ #
384
+ def include?(oth)
385
+ @prefix <= oth.prefix and network_u128 == self.class.new(oth.address+"/#@prefix").network_u128
386
+ end
387
+
388
+ #
389
+ # Compressed form of the IPv6 address
390
+ #
391
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
392
+ #
393
+ # ip6.compressed
394
+ # #=> "2001:db8::8:800:200c:417a"
395
+ #
396
+ def compressed
397
+ @compressed
398
+ end
399
+
400
+ #
401
+ # Returns true if the address is an unspecified address
402
+ #
403
+ # See IPAddress::IPv6::Unspecified for more information
404
+ #
405
+ def unspecified?
406
+ @prefix == 128 and @compressed == "::"
407
+ end
408
+
409
+ #
410
+ # Returns true if the address is a loopback address
411
+ #
412
+ # See IPAddress::IPv6::Loopback for more information
413
+ #
414
+ def loopback?
415
+ @prefix == 128 and @compressed == "::1"
416
+ end
417
+
418
+ #
419
+ # Checks if an IPv4 address objects belongs
420
+ # to a link-local network RFC4291
421
+ #
422
+ # Example:
423
+ #
424
+ # ip = IPAddress "fe80::1"
425
+ # ip.link_local?
426
+ # #=> true
427
+ #
428
+ def link_local?
429
+ [self.class.new("fe80::/64")].any? {|i| i.include? self}
430
+ end
431
+
432
+ #
433
+ # Returns true if the address is a mapped address
434
+ #
435
+ # See IPAddress::IPv6::Mapped for more information
436
+ #
437
+ def mapped?
438
+ to_u128 >> 32 == 0xffff
439
+ end
440
+
441
+ #
442
+ # Iterates over all the IP addresses for the given
443
+ # network (or IP address).
444
+ #
445
+ # The object yielded is a new IPv6 object created
446
+ # from the iteration.
447
+ #
448
+ # ip6 = IPAddress("2001:db8::4/125")
449
+ #
450
+ # ip6.each do |i|
451
+ # p i.compressed
452
+ # end
453
+ # #=> "2001:db8::"
454
+ # #=> "2001:db8::1"
455
+ # #=> "2001:db8::2"
456
+ # #=> "2001:db8::3"
457
+ # #=> "2001:db8::4"
458
+ # #=> "2001:db8::5"
459
+ # #=> "2001:db8::6"
460
+ # #=> "2001:db8::7"
461
+ #
462
+ # WARNING: if the host portion is very large, this method
463
+ # can be very slow and possibly hang your system!
464
+ #
465
+ def each
466
+ (network_u128..broadcast_u128).each do |i|
467
+ yield self.class.parse_u128(i, @prefix)
468
+ end
469
+ end
470
+
471
+ #
472
+ # Spaceship operator to compare IPv6 objects
473
+ #
474
+ # Comparing IPv6 addresses is useful to ordinate
475
+ # them into lists that match our intuitive
476
+ # perception of ordered IP addresses.
477
+ #
478
+ # The first comparison criteria is the u128 value.
479
+ # For example, 2001:db8:1::1 will be considered
480
+ # to be less than 2001:db8:2::1, because, in a ordered list,
481
+ # we expect 2001:db8:1::1 to come before 2001:db8:2::1.
482
+ #
483
+ # The second criteria, in case two IPv6 objects
484
+ # have identical addresses, is the prefix. An higher
485
+ # prefix will be considered greater than a lower
486
+ # prefix. This is because we expect to see
487
+ # 2001:db8:1::1/64 come before 2001:db8:1::1/65
488
+ #
489
+ # Example:
490
+ #
491
+ # ip1 = IPAddress "2001:db8:1::1/64"
492
+ # ip2 = IPAddress "2001:db8:2::1/64"
493
+ # ip3 = IPAddress "2001:db8:1::1/65"
494
+ #
495
+ # ip1 < ip2
496
+ # #=> true
497
+ # ip1 < ip3
498
+ # #=> false
499
+ #
500
+ # [ip1,ip2,ip3].sort.map{|i| i.to_string}
501
+ # #=> ["2001:db8:1::1/64","2001:db8:1::1/65","2001:db8:2::1/64"]
502
+ #
503
+ def <=>(oth)
504
+ return prefix <=> oth.prefix if to_u128 == oth.to_u128
505
+ to_u128 <=> oth.to_u128
506
+ end
507
+
508
+ #
509
+ # Returns the address portion of an IP in binary format,
510
+ # as a string containing a sequence of 0 and 1
511
+ #
512
+ # ip6 = IPAddress("2001:db8::8:800:200c:417a")
513
+ #
514
+ # ip6.bits
515
+ # #=> "0010000000000001000011011011100000 [...] "
516
+ #
517
+ def bits
518
+ data.unpack("B*").first
519
+ end
520
+
521
+ #
522
+ # Expands an IPv6 address in the canocical form
523
+ #
524
+ # IPAddress::IPv6.expand "2001:0DB8:0:CD30::"
525
+ # #=> "2001:0DB8:0000:CD30:0000:0000:0000:0000"
526
+ #
527
+ def self.expand(str)
528
+ self.new(str).address
529
+ end
530
+
531
+ #
532
+ # Compress an IPv6 address in its compressed form
533
+ #
534
+ # IPAddress::IPv6.compress "2001:0DB8:0000:CD30:0000:0000:0000:0000"
535
+ # #=> "2001:db8:0:cd30::"
536
+ #
537
+ def self.compress(str)
538
+ self.new(str).compressed
539
+ end
540
+
541
+ #
542
+ # Literal version of the IPv6 address
543
+ #
544
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
545
+ #
546
+ # ip6.literal
547
+ # #=> "2001-0db8-0000-0000-0008-0800-200c-417a.ipv6-literal.net"
548
+ #
549
+ def literal
550
+ @address.gsub(":","-") + ".ipv6-literal.net"
551
+ end
552
+
553
+ #
554
+ # Returns a new IPv6 object with the network number
555
+ # for the given IP.
556
+ #
557
+ # ip = IPAddress "2001:db8:1:1:1:1:1:1/32"
558
+ #
559
+ # ip.network.to_string
560
+ # #=> "2001:db8::/32"
561
+ #
562
+ def network
563
+ self.class.parse_u128(network_u128, @prefix)
564
+ end
565
+
566
+ #
567
+ # Extract 16 bits groups from a string
568
+ #
569
+ def self.groups(str)
570
+ l, r = if str =~ /^(.*)::(.*)$/
571
+ [$1,$2].map {|i| i.split ":"}
572
+ else
573
+ [str.split(":"),[]]
574
+ end
575
+ (l + Array.new(8-l.size-r.size, '0') + r).map {|i| i.hex}
576
+ end
577
+
578
+ #
579
+ # Creates a new IPv6 object from binary data,
580
+ # like the one you get from a network stream.
581
+ #
582
+ # For example, on a network stream the IP
583
+ #
584
+ # "2001:db8::8:800:200c:417a"
585
+ #
586
+ # is represented with the binary data
587
+ #
588
+ # " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
589
+ #
590
+ # With that data you can create a new IPv6 object:
591
+ #
592
+ # ip6 = IPAddress::IPv6::parse_data " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
593
+ # ip6.prefix = 64
594
+ #
595
+ # ip6.to_s
596
+ # #=> "2001:db8::8:800:200c:417a/64"
597
+ #
598
+ def self.parse_data(str)
599
+ self.new(IN6FORMAT % str.unpack("n8"))
600
+ end
601
+
602
+ #
603
+ # Creates a new IPv6 object from an
604
+ # unsigned 128 bits integer.
605
+ #
606
+ # ip6 = IPAddress::IPv6::parse_u128(42540766411282592856906245548098208122)
607
+ # ip6.prefix = 64
608
+ #
609
+ # ip6.to_string
610
+ # #=> "2001:db8::8:800:200c:417a/64"
611
+ #
612
+ # The +prefix+ parameter is optional:
613
+ #
614
+ # ip6 = IPAddress::IPv6::parse_u128(42540766411282592856906245548098208122, 64)
615
+ #
616
+ # ip6.to_string
617
+ # #=> "2001:db8::8:800:200c:417a/64"
618
+ #
619
+ def self.parse_u128(u128, prefix=128)
620
+ str = IN6FORMAT % (0..7).map{|i| (u128>>(112-16*i))&0xffff}
621
+ self.new(str + "/#{prefix}")
622
+ end
623
+
624
+ #
625
+ # Creates a new IPv6 object from a number expressed in
626
+ # hexdecimal format:
627
+ #
628
+ # ip6 = IPAddress::IPv6::parse_hex("20010db80000000000080800200c417a")
629
+ # ip6.prefix = 64
630
+ #
631
+ # ip6.to_string
632
+ # #=> "2001:db8::8:800:200c:417a/64"
633
+ #
634
+ # The +prefix+ parameter is optional:
635
+ #
636
+ # ip6 = IPAddress::IPv6::parse_hex("20010db80000000000080800200c417a", 64)
637
+ #
638
+ # ip6.to_string
639
+ # #=> "2001:db8::8:800:200c:417a/64"
640
+ #
641
+ def self.parse_hex(hex, prefix=128)
642
+ self.parse_u128(hex.hex, prefix)
643
+ end
644
+
645
+ private
646
+
647
+ def compress_address
648
+ str = @groups.map{|i| i.to_s 16}.join ":"
649
+ loop do
650
+ break if str.sub!(/\A0:0:0:0:0:0:0:0\Z/, '::')
651
+ break if str.sub!(/\b0:0:0:0:0:0:0\b/, ':')
652
+ break if str.sub!(/\b0:0:0:0:0:0\b/, ':')
653
+ break if str.sub!(/\b0:0:0:0:0\b/, ':')
654
+ break if str.sub!(/\b0:0:0:0\b/, ':')
655
+ break if str.sub!(/\b0:0:0\b/, ':')
656
+ break if str.sub!(/\b0:0\b/, ':')
657
+ break
658
+ end
659
+ str.sub(/:{3,}/, '::')
660
+ end
661
+
662
+ end # class IPv6
663
+
664
+ #
665
+ # The address with all zero bits is called the +unspecified+ address
666
+ # (corresponding to 0.0.0.0 in IPv4). It should be something like this:
667
+ #
668
+ # 0000:0000:0000:0000:0000:0000:0000:0000
669
+ #
670
+ # but, with the use of compression, it is usually written as just two
671
+ # colons:
672
+ #
673
+ # ::
674
+ #
675
+ # or, specifying the netmask:
676
+ #
677
+ # ::/128
678
+ #
679
+ # With IPAddress, create a new unspecified IPv6 address using its own
680
+ # subclass:
681
+ #
682
+ # ip = IPAddress::IPv6::Unspecified.new
683
+ #
684
+ # ip.to_s
685
+ # #=> => "::/128"
686
+ #
687
+ # You can easily check if an IPv6 object is an unspecified address by
688
+ # using the IPv6#unspecified? method
689
+ #
690
+ # ip.unspecified?
691
+ # #=> true
692
+ #
693
+ # An unspecified IPv6 address can also be created with the wrapper
694
+ # method, like we've seen before
695
+ #
696
+ # ip = IPAddress "::"
697
+ #
698
+ # ip.unspecified?
699
+ # #=> true
700
+ #
701
+ # This address must never be assigned to an interface and is to be used
702
+ # only in software before the application has learned its host's source
703
+ # address appropriate for a pending connection. Routers must not forward
704
+ # packets with the unspecified address.
705
+ #
706
+ class IPAddress::IPv6::Unspecified < IPAddress::IPv6
707
+ #
708
+ # Creates a new IPv6 unspecified address
709
+ #
710
+ # ip = IPAddress::IPv6::Unspecified.new
711
+ #
712
+ # ip.to_s
713
+ # #=> => "::/128"
714
+ #
715
+ def initialize
716
+ @address = ("0000:"*8).chop
717
+ @groups = Array.new(8,0)
718
+ @prefix = Prefix128.new(128)
719
+ @compressed = compress_address
720
+ end
721
+ end # class IPv6::Unspecified
722
+
723
+ #
724
+ # The loopback address is a unicast localhost address. If an
725
+ # application in a host sends packets to this address, the IPv6 stack
726
+ # will loop these packets back on the same virtual interface.
727
+ #
728
+ # Loopback addresses are expressed in the following form:
729
+ #
730
+ # ::1
731
+ #
732
+ # or, with their appropriate prefix,
733
+ #
734
+ # ::1/128
735
+ #
736
+ # As for the unspecified addresses, IPv6 loopbacks can be created with
737
+ # IPAddress calling their own class:
738
+ #
739
+ # ip = IPAddress::IPv6::Loopback.new
740
+ #
741
+ # ip.to_string
742
+ # #=> "::1/128"
743
+ #
744
+ # or by using the wrapper:
745
+ #
746
+ # ip = IPAddress "::1"
747
+ #
748
+ # ip.to_string
749
+ # #=> "::1/128"
750
+ #
751
+ # Checking if an address is loopback is easy with the IPv6#loopback?
752
+ # method:
753
+ #
754
+ # ip.loopback?
755
+ # #=> true
756
+ #
757
+ # The IPv6 loopback address corresponds to 127.0.0.1 in IPv4.
758
+ #
759
+ class IPAddress::IPv6::Loopback < IPAddress::IPv6
760
+ #
761
+ # Creates a new IPv6 unspecified address
762
+ #
763
+ # ip = IPAddress::IPv6::Loopback.new
764
+ #
765
+ # ip.to_string
766
+ # #=> "::1/128"
767
+ #
768
+ def initialize
769
+ @address = ("0000:"*7)+"0001"
770
+ @groups = Array.new(7,0).push(1)
771
+ @prefix = Prefix128.new(128)
772
+ @compressed = compress_address
773
+ end
774
+ end # class IPv6::Loopback
775
+
776
+ #
777
+ # It is usually identified as a IPv4 mapped IPv6 address, a particular
778
+ # IPv6 address which aids the transition from IPv4 to IPv6. The
779
+ # structure of the address is
780
+ #
781
+ # ::ffff:w.y.x.z
782
+ #
783
+ # where w.x.y.z is a normal IPv4 address. For example, the following is
784
+ # a mapped IPv6 address:
785
+ #
786
+ # ::ffff:192.168.100.1
787
+ #
788
+ # IPAddress is very powerful in handling mapped IPv6 addresses, as the
789
+ # IPv4 portion is stored internally as a normal IPv4 object. Let's have
790
+ # a look at some examples. To create a new mapped address, just use the
791
+ # class builder itself
792
+ #
793
+ # ip6 = IPAddress::IPv6::Mapped.new "::ffff:172.16.10.1/128"
794
+ #
795
+ # or just use the wrapper method
796
+ #
797
+ # ip6 = IPAddress "::ffff:172.16.10.1/128"
798
+ #
799
+ # Let's check it's really a mapped address:
800
+ #
801
+ # ip6.mapped?
802
+ # #=> true
803
+ #
804
+ # ip6.to_string
805
+ # #=> "::FFFF:172.16.10.1/128"
806
+ #
807
+ # Now with the +ipv4+ attribute, we can easily access the IPv4 portion
808
+ # of the mapped IPv6 address:
809
+ #
810
+ # ip6.ipv4.address
811
+ # #=> "172.16.10.1"
812
+ #
813
+ # Internally, the IPv4 address is stored as two 16 bits
814
+ # groups. Therefore all the usual methods for an IPv6 address are
815
+ # working perfectly fine:
816
+ #
817
+ # ip6.to_hex
818
+ # #=> "00000000000000000000ffffac100a01"
819
+ #
820
+ # ip6.address
821
+ # #=> "0000:0000:0000:0000:0000:ffff:ac10:0a01"
822
+ #
823
+ # A mapped IPv6 can also be created just by specify the address in the
824
+ # following format:
825
+ #
826
+ # ip6 = IPAddress "::172.16.10.1"
827
+ #
828
+ # That is, two colons and the IPv4 address. However, as by RFC, the ffff
829
+ # group will be automatically added at the beginning
830
+ #
831
+ # ip6.to_string
832
+ # => "::ffff:172.16.10.1/128"
833
+ #
834
+ # making it a mapped IPv6 compatible address.
835
+ #
836
+ class IPAddress::IPv6::Mapped < IPAddress::IPv6
837
+
838
+ # Access the internal IPv4 address
839
+ attr_reader :ipv4
840
+
841
+ #
842
+ # Creates a new IPv6 IPv4-mapped address
843
+ #
844
+ # ip6 = IPAddress::IPv6::Mapped.new "::ffff:172.16.10.1/128"
845
+ #
846
+ # ipv6.ipv4.class
847
+ # #=> IPAddress::IPv4
848
+ #
849
+ # An IPv6 IPv4-mapped address can also be created using the
850
+ # IPv6 only format of the address:
851
+ #
852
+ # ip6 = IPAddress::IPv6::Mapped.new "::0d01:4403"
853
+ #
854
+ # ip6.to_string
855
+ # #=> "::ffff:13.1.68.3"
856
+ #
857
+ def initialize(str)
858
+ string, netmask = str.split("/")
859
+ if string =~ /\./ # IPv4 in dotted decimal form
860
+ @ipv4 = IPAddress::IPv4.extract(string)
861
+ else # IPv4 in hex form
862
+ groups = IPAddress::IPv6.groups(string)
863
+ @ipv4 = IPAddress::IPv4.parse_u32((groups[-2]<< 16)+groups[-1])
864
+ end
865
+ super("::ffff:#{@ipv4.to_ipv6}/#{netmask}")
866
+ end
867
+
868
+ #
869
+ # Similar to IPv6#to_s, but prints out the IPv4 address
870
+ # in dotted decimal format
871
+ #
872
+ # ip6 = IPAddress "::ffff:172.16.10.1/128"
873
+ #
874
+ # ip6.to_s
875
+ # #=> "::ffff:172.16.10.1"
876
+ #
877
+ def to_s
878
+ "::ffff:#{@ipv4.address}"
879
+ end
880
+
881
+ #
882
+ # Similar to IPv6#to_string, but prints out the IPv4 address
883
+ # in dotted decimal format
884
+ #
885
+ #
886
+ # ip6 = IPAddress "::ffff:172.16.10.1/128"
887
+ #
888
+ # ip6.to_string
889
+ # #=> "::ffff:172.16.10.1/128"
890
+ #
891
+ def to_string
892
+ "::ffff:#{@ipv4.address}/#@prefix"
893
+ end
894
+
895
+ #
896
+ # Checks if the IPv6 address is IPv4 mapped
897
+ #
898
+ # ip6 = IPAddress "::ffff:172.16.10.1/128"
899
+ #
900
+ # ip6.mapped?
901
+ # #=> true
902
+ #
903
+ def mapped?
904
+ true
905
+ end
906
+ end # class IPv6::Mapped
907
+
908
+ end # module IPAddress
909
+