construqt-ipaddress 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1003 @@
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
+ # Returns a Base16 number representing the IPv6
257
+ # address
258
+ #
259
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
260
+ #
261
+ # ip6.to_hex
262
+ # #=> "20010db80000000000080800200c417a"
263
+ #
264
+ def to_hex
265
+ hexs.join("")
266
+ end
267
+
268
+ # Returns the address portion of an IPv6 object
269
+ # in a network byte order format.
270
+ #
271
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
272
+ #
273
+ # ip6.data
274
+ # #=> " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
275
+ #
276
+ # It is usually used to include an IP address
277
+ # in a data packet to be sent over a socket
278
+ #
279
+ # a = Socket.open(params) # socket details here
280
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
281
+ # binary_data = ["Address: "].pack("a*") + ip.data
282
+ #
283
+ # # Send binary data
284
+ # a.puts binary_data
285
+ #
286
+ def data
287
+ @groups.pack("n8")
288
+ end
289
+
290
+ #
291
+ # Returns an array of the 16 bits groups in hexdecimal
292
+ # format:
293
+ #
294
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
295
+ #
296
+ # ip6.hexs
297
+ # #=> ["2001", "0db8", "0000", "0000", "0008", "0800", "200c", "417a"]
298
+ #
299
+ # Not to be confused with the similar IPv6#to_hex method.
300
+ #
301
+ def hexs
302
+ @address.split(":")
303
+ end
304
+
305
+ #
306
+ # Returns the IPv6 address in a DNS reverse lookup
307
+ # string, as per RFC3172 and RFC2874.
308
+ #
309
+ # ip6 = IPAddress "3ffe:505:2::f"
310
+ #
311
+ # ip6.reverse
312
+ # #=> "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"
313
+ #
314
+ def reverse
315
+ to_hex.reverse.gsub(/./){|c| c+"."} + "ip6.arpa"
316
+ end
317
+ alias_method :arpa, :reverse
318
+
319
+ #
320
+ # Returns the IPv6 address in a DNS reverse lookup
321
+ # string, as per RFC3172 and RFC2874.
322
+ # for DNS Domain definition entries like SOA Records
323
+ #
324
+ # ipv6 = IPAddress.parse("fd04:fd80:5::/3")
325
+ #
326
+ # ip.reverse
327
+ # #=> ["e.ip6.arpa", "f.ip6.arpa"]
328
+ #
329
+ def rev_domains
330
+ ret = []
331
+ network = IPv6.parse_u128(network_u128, [(@prefix.to_i/4)*4 + 3, 123].min)
332
+ network.each_net do |net|
333
+ ret << net.reverse[(128-(net.prefix.to_i))/4*2..-1]
334
+ end
335
+ ret
336
+ end
337
+
338
+
339
+
340
+ # Returns the network number in Unsigned 128bits format
341
+ #
342
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
343
+ #
344
+ # ip6.network_u128
345
+ # #=> 42540766411282592856903984951653826560
346
+ #
347
+ def network_u128
348
+ to_u128 & @prefix.to_u128
349
+ end
350
+
351
+ #
352
+ # Returns the broadcast address in Unsigned 128bits format
353
+ #
354
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
355
+ #
356
+ # ip6.broadcast_u128
357
+ # #=> 42540766411282592875350729025363378175
358
+ #
359
+ # Please note that there is no Broadcast concept in IPv6
360
+ # addresses as in IPv4 addresses, and this method is just
361
+ # an helper to other functions.
362
+ #
363
+ def broadcast_u128
364
+ network_u128 + size - 1
365
+ end
366
+
367
+ #
368
+ # Returns the number of IP addresses included
369
+ # in the network. It also counts the network
370
+ # address and the broadcast address.
371
+ #
372
+ # ip6 = IPAddress("2001:db8::8:800:200c:417a/64")
373
+ #
374
+ # ip6.size
375
+ # #=> 18446744073709551616
376
+ #
377
+ def size
378
+ 2 ** @prefix.host_prefix
379
+ end
380
+
381
+ #
382
+ # Checks whether a subnet includes the given IP address.
383
+ #
384
+ # Example:
385
+ #
386
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
387
+ # addr = IPAddress "2001:db8::8:800:200c:1/128"
388
+ #
389
+ # ip6.include? addr
390
+ # #=> true
391
+ #
392
+ # ip6.include? IPAddress("2001:db8:1::8:800:200c:417a/76")
393
+ # #=> false
394
+ #
395
+ def include?(oth)
396
+ @prefix <= oth.prefix and network_u128 == self.class.new(oth.address+"/#@prefix").network_u128
397
+ end
398
+
399
+ #
400
+ # Compressed form of the IPv6 address
401
+ #
402
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
403
+ #
404
+ # ip6.compressed
405
+ # #=> "2001:db8::8:800:200c:417a"
406
+ #
407
+ def compressed
408
+ @compressed
409
+ end
410
+
411
+ #
412
+ # Returns true if the address is an unspecified address
413
+ #
414
+ # See IPAddress::IPv6::Unspecified for more information
415
+ #
416
+ def unspecified?
417
+ @prefix == 128 and @compressed == "::"
418
+ end
419
+
420
+ #
421
+ # Returns true if the address is a loopback address
422
+ #
423
+ # See IPAddress::IPv6::Loopback for more information
424
+ #
425
+ def loopback?
426
+ @prefix == 128 and @compressed == "::1"
427
+ end
428
+
429
+ #
430
+ # Returns true if the address is a mapped address
431
+ #
432
+ # See IPAddress::IPv6::Mapped for more information
433
+ #
434
+ def mapped?
435
+ to_u128 >> 32 == 0xffff
436
+ end
437
+
438
+ #
439
+ # Iterates over all the IP addresses for the given
440
+ # network (or IP address).
441
+ #
442
+ # The object yielded is a new IPv6 object created
443
+ # from the iteration.
444
+ #
445
+ # ip6 = IPAddress("2001:db8::4/125")
446
+ #
447
+ # ip6.each do |i|
448
+ # p i.compressed
449
+ # end
450
+ # #=> "2001:db8::"
451
+ # #=> "2001:db8::1"
452
+ # #=> "2001:db8::2"
453
+ # #=> "2001:db8::3"
454
+ # #=> "2001:db8::4"
455
+ # #=> "2001:db8::5"
456
+ # #=> "2001:db8::6"
457
+ # #=> "2001:db8::7"
458
+ #
459
+ # WARNING: if the host portion is very large, this method
460
+ # can be very slow and possibly hang your system!
461
+ #
462
+ def each
463
+ (network_u128..broadcast_u128).each do |i|
464
+ yield self.class.parse_u128(i, @prefix)
465
+ end
466
+ end
467
+
468
+ #
469
+ # Iterates over all the IP Network for the given
470
+ # network (or IP address).
471
+ #
472
+ # The object yielded is a new IPv6 object created
473
+ # from the iteration.
474
+ #
475
+ # ip6 = IPAddress("fd01::4/3")
476
+ #
477
+ # ip6.each_net do |i|
478
+ # p i.compressed
479
+ # end
480
+ # #=> "e000::/3"
481
+ # #=> "f000::/3"
482
+ #
483
+ # WARNING: if the host portion is very large, this method
484
+ # can be very slow and possibly hang your system!
485
+ #
486
+ def each_net
487
+ start = network_u128
488
+ while (@prefix.host_prefix.to_i-1) > 0 && start < broadcast_u128
489
+ yield self.class.parse_u128(start, @prefix)
490
+ start += 1<<(@prefix.host_prefix.to_i-1)
491
+ end
492
+ end
493
+
494
+ #
495
+ # Spaceship operator to compare IPv6 objects
496
+ #
497
+ # Comparing IPv6 addresses is useful to ordinate
498
+ # them into lists that match our intuitive
499
+ # perception of ordered IP addresses.
500
+ #
501
+ # The first comparison criteria is the u128 value.
502
+ # For example, 2001:db8:1::1 will be considered
503
+ # to be less than 2001:db8:2::1, because, in a ordered list,
504
+ # we expect 2001:db8:1::1 to come before 2001:db8:2::1.
505
+ #
506
+ # The second criteria, in case two IPv6 objects
507
+ # have identical addresses, is the prefix. An higher
508
+ # prefix will be considered greater than a lower
509
+ # prefix. This is because we expect to see
510
+ # 2001:db8:1::1/64 come before 2001:db8:1::1/65
511
+ #
512
+ # Example:
513
+ #
514
+ # ip1 = IPAddress "2001:db8:1::1/64"
515
+ # ip2 = IPAddress "2001:db8:2::1/64"
516
+ # ip3 = IPAddress "2001:db8:1::1/65"
517
+ #
518
+ # ip1 < ip2
519
+ # #=> true
520
+ # ip1 < ip3
521
+ # #=> false
522
+ #
523
+ # [ip1,ip2,ip3].sort.map{|i| i.to_string}
524
+ # #=> ["2001:db8:1::1/64","2001:db8:1::1/65","2001:db8:2::1/64"]
525
+ #
526
+ def <=>(oth)
527
+ return prefix <=> oth.prefix if to_u128 == oth.to_u128
528
+ to_u128 <=> oth.to_u128
529
+ end
530
+
531
+ #
532
+ # Returns the address portion of an IP in binary format,
533
+ # as a string containing a sequence of 0 and 1
534
+ #
535
+ # ip6 = IPAddress("2001:db8::8:800:200c:417a")
536
+ #
537
+ # ip6.bits
538
+ # #=> "0010000000000001000011011011100000 [...] "
539
+ #
540
+ def bits
541
+ data.unpack("B*").first
542
+ end
543
+
544
+ #
545
+ # Expands an IPv6 address in the canocical form
546
+ #
547
+ # IPAddress::IPv6.expand "2001:0DB8:0:CD30::"
548
+ # #=> "2001:0DB8:0000:CD30:0000:0000:0000:0000"
549
+ #
550
+ def self.expand(str)
551
+ self.new(str).address
552
+ end
553
+
554
+ #
555
+ # Compress an IPv6 address in its compressed form
556
+ #
557
+ # IPAddress::IPv6.compress "2001:0DB8:0000:CD30:0000:0000:0000:0000"
558
+ # #=> "2001:db8:0:cd30::"
559
+ #
560
+ def self.compress(str)
561
+ self.new(str).compressed
562
+ end
563
+
564
+ #
565
+ # Literal version of the IPv6 address
566
+ #
567
+ # ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
568
+ #
569
+ # ip6.literal
570
+ # #=> "2001-0db8-0000-0000-0008-0800-200c-417a.ipv6-literal.net"
571
+ #
572
+ def literal
573
+ @address.gsub(":","-") + ".ipv6-literal.net"
574
+ end
575
+
576
+ #
577
+ # Returns a new IPv6 object with the network number
578
+ # for the given IP.
579
+ #
580
+ # ip = IPAddress "2001:db8:1:1:1:1:1:1/32"
581
+ #
582
+ # ip.network.to_string
583
+ # #=> "2001:db8::/32"
584
+ #
585
+ def network
586
+ self.class.parse_u128(network_u128, @prefix)
587
+ end
588
+
589
+ #
590
+ # Extract 16 bits groups from a string
591
+ #
592
+ def self.groups(str)
593
+ l, r = if str =~ /^(.*)::(.*)$/
594
+ [$1,$2].map {|i| i.split ":"}
595
+ else
596
+ [str.split(":"),[]]
597
+ end
598
+ (l + Array.new(8-l.size-r.size, '0') + r).map {|i| i.hex}
599
+ end
600
+
601
+ #
602
+ # Creates a new IPv6 object from binary data,
603
+ # like the one you get from a network stream.
604
+ #
605
+ # For example, on a network stream the IP
606
+ #
607
+ # "2001:db8::8:800:200c:417a"
608
+ #
609
+ # is represented with the binary data
610
+ #
611
+ # " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
612
+ #
613
+ # With that data you can create a new IPv6 object:
614
+ #
615
+ # ip6 = IPAddress::IPv6::parse_data " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
616
+ # ip6.prefix = 64
617
+ #
618
+ # ip6.to_s
619
+ # #=> "2001:db8::8:800:200c:417a/64"
620
+ #
621
+ def self.parse_data(str)
622
+ self.new(IN6FORMAT % str.unpack("n8"))
623
+ end
624
+
625
+ #
626
+ # Creates a new IPv6 object from an
627
+ # unsigned 128 bits integer.
628
+ #
629
+ # ip6 = IPAddress::IPv6::parse_u128(42540766411282592856906245548098208122)
630
+ # ip6.prefix = 64
631
+ #
632
+ # ip6.to_string
633
+ # #=> "2001:db8::8:800:200c:417a/64"
634
+ #
635
+ # The +prefix+ parameter is optional:
636
+ #
637
+ # ip6 = IPAddress::IPv6::parse_u128(42540766411282592856906245548098208122, 64)
638
+ #
639
+ # ip6.to_string
640
+ # #=> "2001:db8::8:800:200c:417a/64"
641
+ #
642
+ def self.parse_u128(u128, prefix=128)
643
+ str = IN6FORMAT % (0..7).map{|i| (u128>>(112-16*i))&0xffff}
644
+ self.new(str + "/#{prefix}")
645
+ end
646
+
647
+ #
648
+ # Creates a new IPv6 object from a number expressed in
649
+ # hexdecimal format:
650
+ #
651
+ # ip6 = IPAddress::IPv6::parse_hex("20010db80000000000080800200c417a")
652
+ # ip6.prefix = 64
653
+ #
654
+ # ip6.to_string
655
+ # #=> "2001:db8::8:800:200c:417a/64"
656
+ #
657
+ # The +prefix+ parameter is optional:
658
+ #
659
+ # ip6 = IPAddress::IPv6::parse_hex("20010db80000000000080800200c417a", 64)
660
+ #
661
+ # ip6.to_string
662
+ # #=> "2001:db8::8:800:200c:417a/64"
663
+ #
664
+ def self.parse_hex(hex, prefix=128)
665
+ self.parse_u128(hex.hex, prefix)
666
+ end
667
+
668
+ private
669
+
670
+ def compress_address
671
+ str = @groups.map{|i| i.to_s 16}.join ":"
672
+ loop do
673
+ break if str.sub!(/\A0:0:0:0:0:0:0:0\Z/, '::')
674
+ break if str.sub!(/\b0:0:0:0:0:0:0\b/, ':')
675
+ break if str.sub!(/\b0:0:0:0:0:0\b/, ':')
676
+ break if str.sub!(/\b0:0:0:0:0\b/, ':')
677
+ break if str.sub!(/\b0:0:0:0\b/, ':')
678
+ break if str.sub!(/\b0:0:0\b/, ':')
679
+ break if str.sub!(/\b0:0\b/, ':')
680
+ break if str.sub!(/\b0:\b/, ':')
681
+ break
682
+ end
683
+ str.sub(/:{3,}/, '::')
684
+ end
685
+
686
+ def +(oth)
687
+ summarize([self,oth])
688
+ end
689
+
690
+ #
691
+ # Summarization (or aggregation) is the process when two or more
692
+ # networks are taken together to check if a supernet, including all
693
+ # and only these networks, exists. If it exists then this supernet
694
+ # is called the summarized (or aggregated) network.
695
+ #
696
+ # It is very important to understand that summarization can only
697
+ # occur if there are no holes in the aggregated network, or, in other
698
+ # words, if the given networks fill completely the address space
699
+ # of the supernet. So the two rules are:
700
+ #
701
+ # 1) The aggregate network must contain +all+ the IP addresses of the
702
+ # original networks;
703
+ # 2) The aggregate network must contain +only+ the IP addresses of the
704
+ # original networks;
705
+ #
706
+ # A few examples will help clarify the above. Let's consider for
707
+ # instance the following two networks:
708
+ #
709
+ # ip1 = IPAddress("2000:0::4/32")
710
+ # ip2 = IPAddress("2000:1::6/32")
711
+ #
712
+ # These two networks can be expressed using only one IP address
713
+ # network if we change the prefix. Let Ruby do the work:
714
+ #
715
+ # IPAddress::IPv6::summarize(ip1,ip2).to_s
716
+ # #=> "2000:0::/31"
717
+ #
718
+ # We note how the network "2000:0::/31" includes all the addresses
719
+ # specified in the above networks, and (more important) includes
720
+ # ONLY those addresses.
721
+ #
722
+ # If we summarized +ip1+ and +ip2+ with the following network:
723
+ #
724
+ # "2000::/16"
725
+ #
726
+ # we would have satisfied rule #1 above, but not rule #2. So "2000::/16"
727
+ # is not an aggregate network for +ip1+ and +ip2+.
728
+ #
729
+ # If it's not possible to compute a single aggregated network for all the
730
+ # original networks, the method returns an array with all the aggregate
731
+ # networks found. For example, the following four networks can be
732
+ # aggregated in a single /22:
733
+ #
734
+ # ip1 = IPAddress("2000:0::/32")
735
+ # ip2 = IPAddress("2000:1::/32")
736
+ # ip3 = IPAddress("2000:2::/32")
737
+ # ip4 = IPAddress("2000:3::/32")
738
+ #
739
+ # IPAddress::IPv6::summarize(ip1,ip2,ip3,ip4).to_string
740
+ # #=> ""2000:3::/30",
741
+ #
742
+ # But the following networks can't be summarized in a single network:
743
+ #
744
+ # ip1 = IPAddress("2000:1::/32")
745
+ # ip2 = IPAddress("2000:2::/32")
746
+ # ip3 = IPAddress("2000:3::/32")
747
+ # ip4 = IPAddress("2000:4::/32")
748
+ #
749
+ # IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string}
750
+ # #=> ["2000:1::/32","2000:2::/31","2000:4::/32"]
751
+ #
752
+ def self.summarize(*args)
753
+ IPAddress.summarize(*args)
754
+ end
755
+
756
+ end # class IPv6
757
+
758
+ #
759
+ # The address with all zero bits is called the +unspecified+ address
760
+ # (corresponding to 0.0.0.0 in IPv4). It should be something like this:
761
+ #
762
+ # 0000:0000:0000:0000:0000:0000:0000:0000
763
+ #
764
+ # but, with the use of compression, it is usually written as just two
765
+ # colons:
766
+ #
767
+ # ::
768
+ #
769
+ # or, specifying the netmask:
770
+ #
771
+ # ::/128
772
+ #
773
+ # With IPAddress, create a new unspecified IPv6 address using its own
774
+ # subclass:
775
+ #
776
+ # ip = IPAddress::IPv6::Unspecified.new
777
+ #
778
+ # ip.to_s
779
+ # #=> => "::/128"
780
+ #
781
+ # You can easily check if an IPv6 object is an unspecified address by
782
+ # using the IPv6#unspecified? method
783
+ #
784
+ # ip.unspecified?
785
+ # #=> true
786
+ #
787
+ # An unspecified IPv6 address can also be created with the wrapper
788
+ # method, like we've seen before
789
+ #
790
+ # ip = IPAddress "::"
791
+ #
792
+ # ip.unspecified?
793
+ # #=> true
794
+ #
795
+ # This address must never be assigned to an interface and is to be used
796
+ # only in software before the application has learned its host's source
797
+ # address appropriate for a pending connection. Routers must not forward
798
+ # packets with the unspecified address.
799
+ #
800
+ class IPAddress::IPv6::Unspecified < IPAddress::IPv6
801
+ #
802
+ # Creates a new IPv6 unspecified address
803
+ #
804
+ # ip = IPAddress::IPv6::Unspecified.new
805
+ #
806
+ # ip.to_s
807
+ # #=> => "::/128"
808
+ #
809
+ def initialize
810
+ @address = ("0000:"*8).chop
811
+ @groups = Array.new(8,0)
812
+ @prefix = Prefix128.new(128)
813
+ @compressed = compress_address
814
+ end
815
+ end # class IPv6::Unspecified
816
+
817
+ #
818
+ # The loopback address is a unicast localhost address. If an
819
+ # application in a host sends packets to this address, the IPv6 stack
820
+ # will loop these packets back on the same virtual interface.
821
+ #
822
+ # Loopback addresses are expressed in the following form:
823
+ #
824
+ # ::1
825
+ #
826
+ # or, with their appropriate prefix,
827
+ #
828
+ # ::1/128
829
+ #
830
+ # As for the unspecified addresses, IPv6 loopbacks can be created with
831
+ # IPAddress calling their own class:
832
+ #
833
+ # ip = IPAddress::IPv6::Loopback.new
834
+ #
835
+ # ip.to_string
836
+ # #=> "::1/128"
837
+ #
838
+ # or by using the wrapper:
839
+ #
840
+ # ip = IPAddress "::1"
841
+ #
842
+ # ip.to_string
843
+ # #=> "::1/128"
844
+ #
845
+ # Checking if an address is loopback is easy with the IPv6#loopback?
846
+ # method:
847
+ #
848
+ # ip.loopback?
849
+ # #=> true
850
+ #
851
+ # The IPv6 loopback address corresponds to 127.0.0.1 in IPv4.
852
+ #
853
+ class IPAddress::IPv6::Loopback < IPAddress::IPv6
854
+ #
855
+ # Creates a new IPv6 unspecified address
856
+ #
857
+ # ip = IPAddress::IPv6::Loopback.new
858
+ #
859
+ # ip.to_string
860
+ # #=> "::1/128"
861
+ #
862
+ def initialize
863
+ @address = ("0000:"*7)+"0001"
864
+ @groups = Array.new(7,0).push(1)
865
+ @prefix = Prefix128.new(128)
866
+ @compressed = compress_address
867
+ end
868
+ end # class IPv6::Loopback
869
+
870
+ #
871
+ # It is usually identified as a IPv4 mapped IPv6 address, a particular
872
+ # IPv6 address which aids the transition from IPv4 to IPv6. The
873
+ # structure of the address is
874
+ #
875
+ # ::ffff:w.y.x.z
876
+ #
877
+ # where w.x.y.z is a normal IPv4 address. For example, the following is
878
+ # a mapped IPv6 address:
879
+ #
880
+ # ::ffff:192.168.100.1
881
+ #
882
+ # IPAddress is very powerful in handling mapped IPv6 addresses, as the
883
+ # IPv4 portion is stored internally as a normal IPv4 object. Let's have
884
+ # a look at some examples. To create a new mapped address, just use the
885
+ # class builder itself
886
+ #
887
+ # ip6 = IPAddress::IPv6::Mapped.new "::ffff:172.16.10.1/128"
888
+ #
889
+ # or just use the wrapper method
890
+ #
891
+ # ip6 = IPAddress "::ffff:172.16.10.1/128"
892
+ #
893
+ # Let's check it's really a mapped address:
894
+ #
895
+ # ip6.mapped?
896
+ # #=> true
897
+ #
898
+ # ip6.to_string
899
+ # #=> "::FFFF:172.16.10.1/128"
900
+ #
901
+ # Now with the +ipv4+ attribute, we can easily access the IPv4 portion
902
+ # of the mapped IPv6 address:
903
+ #
904
+ # ip6.ipv4.address
905
+ # #=> "172.16.10.1"
906
+ #
907
+ # Internally, the IPv4 address is stored as two 16 bits
908
+ # groups. Therefore all the usual methods for an IPv6 address are
909
+ # working perfectly fine:
910
+ #
911
+ # ip6.to_hex
912
+ # #=> "00000000000000000000ffffac100a01"
913
+ #
914
+ # ip6.address
915
+ # #=> "0000:0000:0000:0000:0000:ffff:ac10:0a01"
916
+ #
917
+ # A mapped IPv6 can also be created just by specify the address in the
918
+ # following format:
919
+ #
920
+ # ip6 = IPAddress "::172.16.10.1"
921
+ #
922
+ # That is, two colons and the IPv4 address. However, as by RFC, the ffff
923
+ # group will be automatically added at the beginning
924
+ #
925
+ # ip6.to_string
926
+ # => "::ffff:172.16.10.1/128"
927
+ #
928
+ # making it a mapped IPv6 compatible address.
929
+ #
930
+ class IPAddress::IPv6::Mapped < IPAddress::IPv6
931
+
932
+ # Access the internal IPv4 address
933
+ attr_reader :ipv4
934
+
935
+ #
936
+ # Creates a new IPv6 IPv4-mapped address
937
+ #
938
+ # ip6 = IPAddress::IPv6::Mapped.new "::ffff:172.16.10.1/128"
939
+ #
940
+ # ipv6.ipv4.class
941
+ # #=> IPAddress::IPv4
942
+ #
943
+ # An IPv6 IPv4-mapped address can also be created using the
944
+ # IPv6 only format of the address:
945
+ #
946
+ # ip6 = IPAddress::IPv6::Mapped.new "::0d01:4403"
947
+ #
948
+ # ip6.to_string
949
+ # #=> "::ffff:13.1.68.3"
950
+ #
951
+ def initialize(str)
952
+ string, netmask = str.split("/")
953
+ if string =~ /\./ # IPv4 in dotted decimal form
954
+ @ipv4 = IPAddress::IPv4.extract(string)
955
+ else # IPv4 in hex form
956
+ groups = IPAddress::IPv6.groups(string)
957
+ @ipv4 = IPAddress::IPv4.parse_u32((groups[-2]<< 16)+groups[-1])
958
+ end
959
+ super("::ffff:#{@ipv4.to_ipv6}/#{netmask}")
960
+ end
961
+
962
+ #
963
+ # Similar to IPv6#to_s, but prints out the IPv4 address
964
+ # in dotted decimal format
965
+ #
966
+ # ip6 = IPAddress "::ffff:172.16.10.1/128"
967
+ #
968
+ # ip6.to_s
969
+ # #=> "::ffff:172.16.10.1"
970
+ #
971
+ def to_s
972
+ "::ffff:#{@ipv4.address}"
973
+ end
974
+
975
+ #
976
+ # Similar to IPv6#to_string, but prints out the IPv4 address
977
+ # in dotted decimal format
978
+ #
979
+ #
980
+ # ip6 = IPAddress "::ffff:172.16.10.1/128"
981
+ #
982
+ # ip6.to_string
983
+ # #=> "::ffff:172.16.10.1/128"
984
+ #
985
+ def to_string
986
+ "::ffff:#{@ipv4.address}/#@prefix"
987
+ end
988
+
989
+ #
990
+ # Checks if the IPv6 address is IPv4 mapped
991
+ #
992
+ # ip6 = IPAddress "::ffff:172.16.10.1/128"
993
+ #
994
+ # ip6.mapped?
995
+ # #=> true
996
+ #
997
+ def mapped?
998
+ true
999
+ end
1000
+ end # class IPv6::Mapped
1001
+
1002
+ end # module IPAddress
1003
+