1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
|
== <b>IPAddress 1.0 is currently under development and will be released soon! Stay tuned!</b>
---
= IPAddress
IPAddress is a Ruby library designed to make the use of IPv4 and IPv6
addresses simple, powerful and enjoyable. It provides a complete set of
methods to handle IP addresses for any need, from simple scripting to
full network design.
IPAddress is written with a full OO interface, and its code is easy to
read, maintain and extend. The documentation is full of examples, to
let you start being productive immediately.
This document provides a brief introduction to the library and
examples of typical usage.
== Requirements
* Ruby >= 1.8.7 (not tested with previous versions)
* Ruby 1.9.2 or later is strongly recommended
IPAddress 0.8.2 has been tested on:
* ruby-1.8.7-p334 [ i386 ]
* ree-1.8.7-2011.03 [ i386 ]
* rbx-head [ ]
* jruby-1.6.1 [ linux-i386-java ]
* ruby-1.9.1-p431 [ i386 ]
* ruby-1.9.2-p180 [ i386 ]
* ruby-2.0.0-p353 [ x86_64-darwin14.0.0 ]
* ruby-2.1.3-p242 [ x86_64-darwin14.0.0 ]
If you want to collaborate feel
free to send a small report to my email address, or
{join the discussion}[http://groups.google.com/group/ruby-ipaddress].
== Installation
Install the library using rubygems
$ gem install ipaddress
You can then use it in your programs:
require 'rubygems' # optional
require 'ipaddress'
Another way would be to clone the git repository
$ git clone git://github.com/bluemonk/ipaddress.git
And then install the library
$ cd ipaddress
ipaddress$ rake install
== Documentation
The code is fully documented with RDoc. You can generate the
documentation with Rake:
ipaddress$ rake rdoc
The latest documentation can be found online at
{this address}[http://rubydoc.info/gems/ipaddress/0.8.0/frames]
== IPv4
Class `IPAddress::IPv4` is used to handle IPv4 type addresses. IPAddress
is similar to other IP Addresses libraries, like Ruby's own
IPAddr. However it works slightly different, as we will see.
=== Create a new IPv4 address
The usual way to express an IP Address is using its dotted decimal
form, such as `172.16.10.1`, and a prefix, such as `24`, separated by a
slash.
172.16.10.1/24
To create a new IPv4 object, you can use IPv4 own class
ip = IPAddress::IPv4.new "172.16.10.1/24"
or, in a easier way, using the IPAddress parse method
ip = IPAddress.parse "172.16.10.1/24"
which accepts and parses any kind of IP (uint32, IPv4, IPV6 and
IPv4 IPv6 Mapped addresses).
If you like syntactic sugar, you can use the wrapper method
`IPAddress()`, which is built around `IPAddress::parse`:
ip = IPAddress "172.16.10.1/24"
You can specify an IPv4 address in any of two ways:
IPAddress "172.16.10.1/24"
IPAddress "172.16.10.1/255.255.255.0"
In this example, prefix `/24` and netmask `255.255.255.0` are the same and
you have the flexibility to use either one of them.
If you don't explicitly specify the prefix (or the subnet mask),
IPAddress thinks you're dealing with host addresses and not with
networks. Therefore, the default prefix will be `/32`, or
`255.255.255.255`. For example:
# let's declare an host address
host = IPAddress::IPv4.new "10.1.1.1"
puts host.to_string
#=> "10.1.1.1/32"
The new created object has prefix `/32`, which is the same
as we created the following:
host = IPAddress::IPv4.new "10.1.1.1/32"
You can also pass a `uint32` to obtain an `IPAddress::IPv4` object:
# Create host object
ip = IPAddress 167837953
puts ip.to_string
#=> "10.1.1.1/32"
=== Handling the IPv4 address
Once created, you can obtain the attributes for an IPv4 object:
ip = IPAddress("172.16.10.1/24")
ip.address
#=> "172.16.10.1"
ip.prefix
#=> 24
In case you need to retrieve the netmask in IPv4 format, you can use
the `IPv4#netmask` method:
ip.netmask
#=> "255.255.255.0"
A special attribute, `IPv4#octets`, is available to get the four
decimal octets from the IP address:
ip.octets
#=> [172,16,10,1]
Shortcut method `IPv4#[]`, provides access to a given octet whithin the
range:
ip[1]
#=> 16
If you need to print out the IPv4 address in a canonical form, you can
use `IPv4#to_string`:
ip.to_string
#=> "172.16.10.l/24"
=== Changing netmask
You can set a new prefix (netmask) after creating an IPv4
object. For example:
ip.prefix = 25
ip.to_string
#=> "172.16.10.l/25"
If you need to use a netmask in IPv4 format, you can achive so by
using the `IPv4#netmask=` method:
ip.netmask = "255.255.255.252"
ip.to_string
#=> "172.16.10.1/30"
=== Working with networks, broadcasts and addresses
Some very important topics in dealing with IP addresses are the
concepts of +network+ and +broadcast+, as well as the addresses
included in a range.
When you specify an IPv4 address such as `172.16.10.1/24`, you are
actually handling two different information:
* The IP address itself, "172.16.10.1"
* The subnet mask which indicates the network
The network number is the IP which has all zeroes in the host
portion. In our example, because the prefix is 24, we identify our
network number to have the last 8 (32-24) bits all zeroes. Thus, IP
address `172.16.10.1/24` belongs to network `172.16.10.0/24`.
This is very important because, for instance, IP `172.16.10.1/16` is
very different to the previous one, belonging to the very different
network `172.16.0.0/16`.
==== Networks
With IPAddress it's very easy to calculate the network for an IP
address:
ip = IPAddress "172.16.10.1/24"
net = ip.network
#=> #<IPAddress::IPv4:0xb7a5ab24 @octets=[172, 16, 10, 0],
@prefix=24,
@address="172.16.10.0">
net.to_string
#=> "172.16.10.0/24"
Method IPv4#network creates a new IPv4 object from the network
number, calculated after the original object. We want to outline here
that the network address is a perfect legitimate IPv4 address, which
just happen to have all zeroes in the host portion.
You can use method `IPv4#network?` to check whether an IP address is a
network or not:
ip1 = IPAddress "172.16.10.1/24"
ip2 = IPAddress "172.16.10.4/30"
ip1.network?
#=> false
ip2.network?
#=> true
==== Broadcast
The broadcast address is the contrary than the network number: where
the network number has all zeroes in the host portion, the broadcast
address has all one's. For example, ip `172.16.10.1/24` has broadcast
`172.16.10.255/24`, where ip `172.16.10.1/16` has broadcast
`172.16.255.255/16`.
Method `IPv4#broadcast` has the same behavior as is `#network`
counterpart: it creates a new IPv4 object to handle the broadcast
address:
ip = IPAddress "172.16.10.1/24"
bcast = ip.broadcast
#=> #<IPAddress::IPv4:0xb7a406fc @octets=[172, 16, 10, 255],
@prefix=24,
@address="172.16.10.255">
bcast.to_string
#=> "172.16.10.255/24"
==== Addresses, ranges and iterators
So we see that the netmask essentially specifies a range for IP
addresses that are included in a network: all the addresses between
the network number and the broadcast. IPAddress has many methods to
iterate between those addresses. Let's start with `IPv4#each`, which
iterates over all addresses in a range
ip = IPAddress "172.16.10.1/24"
ip.each do |addr|
puts addr
end
It is important to note that it doesn't matter if the original IP is a
host IP or a network number (or a broadcast address): the #each method
only considers the range that the original IP specifies.
If you only want to iterate over hosts IP, use the `IPv4#each_host`
method:
ip = IPAddress "172.16.10.1/24"
ip.each_host do |host|
puts host
end
Methods `IPv4#first` and `IPv4#last` return a new object containing
respectively the first and the last host address in the range
ip = IPAddress "172.16.10.100/24"
ip.first.to_string
#=> "172.16.10.1/24"
ip.last.to_string
#=> "172.16.10.254/24"
Checking if an address is loopback is easy with the `IPv4#loopback?`
method:
ip = IPAddress "127.0.0.1"
ip.loopback?
#=> true
Checking if an address is in the multicast range can be done using the `IPv4#multicast?`
method:
ip = IPAddress "224.0.0.1/32"
ip.multicast?
#=> true
The ability to generate a range also exists by using the `IPv4#to()` method. This allows you to create a subnet agnostic range based off a fixed amount.
ip = IPAddress "172.16.10.100/24"
ip.to('172.16.10.110')
#=> ["172.16.10.100", ..., "172.16.10.110"]
=== IP special formats
The IPAddress library provides a complete set of methods to access an
IPv4 address in special formats, such as binary, 32 bits unsigned int,
data and hexadecimal.
Let's take the following IPv4 as an example:
ip = IPAddress "172.16.10.1/24"
ip.address
#=> "172.16.10.1"
The first thing to highlight here is that all these conversion methods
only take into consideration the address portion of an IPv4 object and
not the prefix (netmask).
So, to express the address in binary format, use the `IPv4#bits` method:
ip.bits
#=> "10101100000100000000101000000001"
To calculate the 32 bits unsigned int format of the ip address, use
the `IPv4#to_u32` method
ip.to_u32
#=> 2886732289
This method is the equivalent of the Unix call `pton()`, expressing an
IP address in the so called +network byte order+ notation. However, if
you want to transmit your IP over a network socket, you might need to
transform it in data format using the `IPv4#data` method:
ip.data
#=> "\254\020\n\001"
Also, you can transform an IPv4 address into a format which is
suitable to use in IPv4-IPv6 mapped addresses:
ip.to_ipv6
#=> "ac10:0a01"
Finally, much like `IPv4#to_ipv6` you can use to `IPv4#to_h` method to return a non-semicolon delineated string (useful with pcap/byte level usage):
ip.to_h
#=> "ac100a01"
=== Classful networks
IPAddress allows you to create and manipulate objects using the old
and deprecated (but apparently still popular) classful networks concept.
Classful networks and addresses don't have a prefix: their subnet mask
is univocally identified by their address, and therefore divided in classes.
As per RFC 791, these classes are:
* Class A, from 0.0.0.0 to 127.255.255.255
* Class B, from 128.0.0.0 to 191.255.255.255
* Class C, from 192.0.0.0 to 255.255.255.255
Since classful networks here are only considered to calculate the default
prefix number, classes D and E are not considered.
To create a classful IP and prefix from an IP address, use the
IPv4::parse_classful method:
# classful ip
ip = IPAddress::IPv4::parse_classful "10.1.1.1"
ip.prefix
#=> 8
The method automatically created a new IPv4 object and assigned it
the correct prefix.
You can easily check which CLASSFUL network an IPv4 object belongs:
ip = IPAddress("10.0.0.1/24")
ip.a?
#=> true
ip = IPAddress("172.16.10.1/24")
ip.b?
#=> true
ip = IPAddress("192.168.1.1/30")
ip.c?
#=> true
Remember that these methods are only checking the address portion of an IP, and are
independent from its prefix, as classful networks have no concept of prefix.
For more information on CLASSFUL networks visit the
{Wikipedia page}[http://en.wikipedia.org/wiki/Classful_network]
=== Network design with IPAddress
IPAddress includes a lot of useful methods to manipulate IPv4 and IPv6
networks and do some basic network design.
==== Subnetting
The process of subnetting is the division of a network into smaller
(in terms of hosts capacity) networks, called subnets, so that they
all share a common root, which is the starting network.
For example, if you have network "172.16.10.0/24", we can subnet it
into 4 smaller subnets. The new prefix will be /26, because 4 is 2^2
and therefore we add 2 bits to the network prefix (24+2=26).
Subnetting is easy with IPAddress. You actually have two options:
* IPv4#subnet: specify a new prefix
* IPv4#split: tell IPAddress how many subnets you want to create.
Let's examine `IPv4#subnet` first. Say you have network "172.16.10.0/24"
and you want to subnet it into /26 networks. With IPAddress it's very
easy:
network = IPAddress "172.16.10.0/24"
subnets = network.subnet(26)
subnets.map{|i| i.to_string}
#=> ["172.16.10.0/26",
"172.16.10.64/26",
"172.16.10.128/26",
"172.16.10.192/26"]
As you can see, an Array has been created, containing 4 new IPv4 objects
representing the new subnets.
Another way to create subnets is to tell IPAddress how many subnets you'd
like to have, and letting the library calculate the new prefix for you.
Let's see how it works, using `IPv4#split` method. Say you want 4 new subnets:
network = IPAddress("172.16.10.0/24")
subnets = network.split(4)
subnets.map{|i| i.to_string}
#=> ["172.16.10.0/26",
"172.16.10.64/26",
"172.16.10.128/26",
"172.16.10.192/26"]
Hey, that's the same result as before! This actually makes sense, as the
two operations are complementary. When you use `IPv4#subnet` with the new
prefix, IPAddress will always create a number of subnets that is a power
of two. This is equivalent to use IPv4#split with a power of 2.
Where `IPv4#split` really shines is with the so called "uneven subnetting".
You are not limited to split a network into a power-of-two numbers of
subnets: IPAddress lets you create any number of subnets, and it will
try to organize the new created network in the best possible way, making
an efficient allocation of the space.
An example here is worth a thousand words. Let's use the same network
as the previous examples:
network = IPAddress("172.16.10.0/24")
How do we split this network into 3 subnets? Very easy:
subnets = network.split(3)
subnets.map{|i| i.to_string}
#=> ["172.16.10.0/26",
"172.16.10.64/26",
"172.16.10.128/25"]
As you can see, IPAddress tried to perform a good allocation by filling up
all the address space from the original network. There is no point in splitting
a network into 3 subnets like `172.16.10.0/26`, `172.16.10.64/26` and
`172.16.10.128/26`, as you would end up having `172.16.10.192/26` wasted (plus,
I suppose I wouldn't need a Ruby library to perform un-efficient IP
allocation, as I do that myself very well ;) ).
We can go even further and split into 11 subnets:
network.split(11)
#=> ["172.16.10.0/28", "172.16.10.16/28", "172.16.10.32/28",
"172.16.10.48/28", "172.16.10.64/28", "172.16.10.80/28",
"172.16.10.96/28", "172.16.10.112/28", "172.16.10.128/27",
"172.16.10.160/27", "172.16.10.192/26"]
As you can see, most of the networks are `/28`, with a few `/27` and one
`/26` to fill up the remaining space.
==== Summarization
Summarization (or aggregation) is the process when two or more
networks are taken together to check if a supernet, including
all and only these networks, exists. If it exists then this supernet
is called the summarized (or aggregated) network.
It is very important to understand that summarization can only
occur if there are no holes in the aggregated network, or, in
other words, if the given networks fill completely the address space
of the supernet. So the two rules are:
1) The aggregate network must contain +all+ the IP addresses of the
original networks;
2) The aggregate network must contain +only+ the IP addresses of the
original networks;
A few examples will help clarify the above. Let's consider for
instance the following two networks:
ip1 = IPAddress("172.16.10.0/24")
ip2 = IPAddress("172.16.11.0/24")
These two networks can be expressed using only one IP address
network if we change the prefix. Let Ruby do the work:
IPAddress::IPv4::summarize(ip1,ip2).map(&:to_string)
#=> "172.16.10.0/23"
We note how the network `172.16.10.0/23` includes all the
addresses specified in the above networks, and (more important) includes
ONLY those addresses.
If we summarized +ip1+ and +ip2+ with the following network:
"172.16.0.0/16"
we would have satisfied rule #1 above, but not rule #2. So
"172.16.0.0/16"
is not an aggregate network for +ip1+ and +ip2+.
If it's not possible to compute a single aggregated network for
all the original networks, the method returns an array with all the
aggregate networks found. For example, the following four networks can be
aggregated in a single `/22`:
ip1 = IPAddress("10.0.0.1/24")
ip2 = IPAddress("10.0.1.1/24")
ip3 = IPAddress("10.0.2.1/24")
ip4 = IPAddress("10.0.3.1/24")
IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string}
#=> ["10.0.0.0/22"]
But the following networks can't be summarized in a single
network:
ip1 = IPAddress("10.0.1.1/24")
ip2 = IPAddress("10.0.2.1/24")
ip3 = IPAddress("10.0.3.1/24")
ip4 = IPAddress("10.0.4.1/24")
IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).map{|i| i.to_string}
#=> ["10.0.1.0/24","10.0.2.0/23","10.0.4.0/24"]
In this case, the two summarizables networks have been aggregated into
a single `/23`, while the other two networks have been left untouched.
==== Supernetting
Supernetting is a different operation than aggregation, as it only
works on a single network and returns a new single IPv4 object,
representing the supernet.
Supernetting is similar to subnetting, except that you getting as a
result a network with a smaller prefix (bigger host space). For
example, given the network
ip = IPAddress("172.16.10.0/24")
you can supernet it with a new /23 prefix
ip.supernet(23).to_string
#=> "172.16.10.0/23"
However if you supernet it with a `/22` prefix, the network address will
change:
ip.supernet(22).to_string
#=> "172.16.8.0/22"
This is because `172.16.10.0/22` is not a network anymore, but an host
address.
== IPv6
IPAddress is not only fantastic for IPv4 addresses, it's also great to
handle IPv6 addresses family! Let's discover together how to use it in
our projects.
=== IPv6 addresses
IPv6 addresses are 128 bits long, in contrast with IPv4 addresses
which are only 32 bits long. An IPv6 address is generally written as
eight groups of four hexadecimal digits, each group representing 16
bits or two octet. For example, the following is a valid IPv6
address:
2001:0db8:0000:0000:0008:0800:200c:417a
Letters in an IPv6 address are usually written downcase, as per
RFC. You can create a new IPv6 object using uppercase letters, but
they will be converted.
==== Compression
Since IPv6 addresses are very long to write, there are some
simplifications and compressions that you can use to shorten them.
* Leading zeroes: all the leading zeroes within a group can be
omitted: "0008" would become "8"
* A string of consecutive zeroes can be replaced by the string
"::". This can be only applied once.
Using compression, the IPv6 address written above can be shorten into
the following, equivalent, address
2001:db8::8:800:200c:417a
This short version is often used in human representation.
==== Network Mask
As we used to do with IPv4 addresses, an IPv6 address can be written
using the prefix notation to specify the subnet mask:
2001:db8::8:800:200c:417a/64
The /64 part means that the first 64 bits of the address are
representing the network portion, and the last 64 bits are the host
portion.
=== Using IPAddress with IPv6 addresses
All the IPv6 representations we've just seen are perfectly fine when
you want to create a new IPv6 address:
ip6 = IPAddress "2001:0db8:0000:0000:0008:0800:200C:417A"
ip6 = IPAddress "2001:db8:0:0:8:800:200C:417A"
ip6 = IPAddress "2001:db8:8:800:200C:417A"
All three are giving out the same IPv6 object. The default subnet mask
for an IPv6 is 128, as IPv6 addresses don't have classes like IPv4
addresses. If you want a different mask, you can go ahead and explicit
it:
ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
Access the address portion and the prefix by using the respective
methods:
ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
ip6.address
#=> "2001:0db8:0000:0000:0008:0800:200c:417a"
ip6.prefix
#=> 64
A compressed version of the IPv6 address can be obtained with the
`IPv6#compressed` method:
ip6 = IPAddress "2001:0db8:0000:0000:0008:200c:417a:00ab/64"
ip6.compressed
#=> "2001:db8::8:800:200c:417a"
=== Handling the IPv6 address
Accessing the groups that form an IPv6 address is very easy with the
`IPv6#groups` method:
ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
ip6.groups
#=> [8193, 3512, 0, 0, 8, 2048, 8204, 16762]
As with IPv4 addresses, each individual group can be accessed using
the `IPv6#[]` shortcut method:
ip6[0]
#=> 8193
ip6[1]
#=> 3512
ip6[2]
#=> 0
ip6[3]
#=> 0
Note that each 16 bits group is expressed in its decimal form. You can
also obtain the groups into hexadecimal format using the `IPv6#hexs`
method:
ip6.hexs
#=> => ["2001", "0db8", "0000", "0000", "0008", "0800", "200c", "417a"]
A few other methods are available to transform an IPv6 address into
decimal representation, with `IPv6.to_i`
ip6.to_i
#=> 42540766411282592856906245548098208122
or to hexadecimal representation
ip6.to_hex
#=> "20010db80000000000080800200c417a"
To print out an IPv6 address in human readable form, use the `IPv6#to_s`, `IPv6#to_string`
and `IPv6#to_string_uncompressed` methods
ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
ip6.to_string
#=> "2001:db8::8:800:200c:417a/96"
ip6.to_string_uncompressed
#=> "2001:0db8:0000:0000:0008:0800:200c:417a/96"
As you can see, `IPv6.to_string` prints out the compressed form, while
`IPv6.to_string_uncompressed` uses the expanded version.
==== Compressing and uncompressing
If you have a string representing an IPv6 address, you can easily
compress it and uncompress it using the two class methods IPv6::expand
and IPv6::compress.
For example, let's say you have the following uncompressed IPv6
address:
ip6str = "2001:0DB8:0000:CD30:0000:0000:0000:0000"
Here is the compressed version:
IPAddress::IPv6.compress ip6str
#=> "2001:db8:0:cd30::"
The other way works as well:
ip6str = "2001:db8:0:cd30::"
IPAddress::IPv6.expand ip6str
#=> "2001:0DB8:0000:CD30:0000:0000:0000:0000"
These methods can be used when you don't want to create a new object
just for expanding or compressing an address (although a new object is
actually created internally).
=== New IPv6 address from other formats
You can create a new IPv6 address from different formats than just a
string representing the colon-hex groups.
For instance, if you have a data stream, you can use `IPv6::parse_data`,
like in the following example:
data = " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
ip6 = IPAddress::IPv6::parse_data data
ip6.prefix = 64
ip6.to_string
#=> "2001:db8::8:800:200c:417a/64"
A new IPv6 address can also be created from an unsigned 128 bits
integer:
u128 = 42540766411282592856906245548098208122
ip6 = IPAddress::IPv6::parse_u128 u128
ip6.prefix = 64
ip6.to_string
#=>"2001:db8::8:800:200c:417a/64"
Finally, a new IPv6 address can be created from an hex string:
hex = "20010db80000000000080800200c417a"
ip6 = IPAddress::IPv6::parse_hex hex
ip6.prefix = 64
ip6.to_string
#=> "2001:db8::8:800:200c:417a/64"
=== Special IPv6 addresses
Some IPv6 have a special meaning and are expressed in a special form,
quite different than an usual IPv6 address. IPAddress has built-in
support for unspecified, loopback and mapped IPv6 addresses.
==== Unspecified address
The address with all zero bits is called the +unspecified+ address
(corresponding to `0.0.0.0` in IPv4). It should be something like this:
0000:0000:0000:0000:0000:0000:0000:0000
but, with the use of compression, it is usually written as just two
colons:
::
or, specifying the netmask:
::/128
With IPAddress, create a new unspecified IPv6 address using its own
subclass:
ip = IPAddress::IPv6::Unspecified.new
ip.to_string
#=> "::/128"
You can easily check if an IPv6 object is an unspecified address by
using the IPv6#unspecified? method
ip.unspecified?
#=> true
An unspecified IPv6 address can also be created with the wrapper
method, like we've seen before
ip = IPAddress "::"
ip.unspecified?
#=> true
This address must never be assigned to an interface and is to be used
only in software before the application has learned its host's source
address appropriate for a pending connection. Routers must not forward
packets with the unspecified address.
==== Loopback address
The loopback address is a unicast localhost address. If an
application in a host sends packets to this address, the IPv6 stack
will loop these packets back on the same virtual interface.
Loopback addresses are expressed in the following form:
::1
or, with their appropriate prefix,
::1/128
As for the unspecified addresses, IPv6 loopbacks can be created with
IPAddress calling their own class:
ip = IPAddress::IPv6::Loopback.new
ip.to_string
#=> "::1/128"
or by using the wrapper:
ip = IPAddress "::1"
ip.to_string
#=> "::1/128"
Checking if an address is loopback is easy with the `IPv6#loopback?`
method:
ip.loopback?
#=> true
The IPv6 loopback address corresponds to `127.0.0.1` in IPv4.
==== Mapped address
It is usually identified as a IPv4 mapped IPv6 address, a particular
IPv6 address which aids the transition from IPv4 to IPv6. The
structure of the address is
::ffff:w.y.x.z
where `w.x.y.z` is a normal IPv4 address. For example, the following is
a mapped IPv6 address:
::ffff:192.168.100.1
IPAddress is very powerful in handling mapped IPv6 addresses, as the
IPv4 portion is stored internally as a normal IPv4 object. Let's have
a look at some examples. To create a new mapped address, just use the
class builder itself
ip6 = IPAddress::IPv6::Mapped.new "::ffff:172.16.10.1/128"
or just use the wrapper method
ip6 = IPAddress "::ffff:172.16.10.1/128"
Let's check it's really a mapped address:
ip6.mapped?
#=> true
ip6.to_string
#=> "::ffff:172.16.10.1/128"
Now with the `#ipv4` attribute, we can easily access the IPv4 portion
of the mapped IPv6 address:
ip6.ipv4.address
#=> "172.16.10.1"
Internally, the IPv4 address is stored as two 16 bits
groups. Therefore all the usual methods for an IPv6 address are
working perfectly fine:
ip6.to_hex
#=> "00000000000000000000ffffac100a01"
ip6.address
#=> "0000:0000:0000:0000:0000:ffff:ac10:0a01"
A mapped IPv6 can also be created just by specify the address in the
following format:
ip6 = IPAddress "::172.16.10.1"
That is, two colons and the IPv4 address. However, as by RFC, the `ffff`
group will be automatically added at the beginning
ip6.to_string
=> "::ffff:172.16.10.1/128"
making it a mapped IPv6 compatible address.
== Why not using IPAddr?
IPAddr is the IP addresses library that comes with Ruby standard
lib. We found this library, although very well written, not very
suitable for all our needs, and not very flexible.
Some quick examples of things you can't do with IPAddr:
* store both the address and the prefix information
* quickly find the broadcast address of a network
* iterate over hosts
* perform subnetting or network aggregation
Many methods and procedures are so old that they have been
declared deprecated by the IETF, and some others have bugs in their
implementation.
Moreover, IPAddress is more robust and is already around 50% faster than IPAddr,
in addition to provide an organic API with logical separation and OO structure.
We hope that IPAddress will address all these issues and meet all your
needs in network programming.
== Community
Want to join the community?
* {IPAddress google group}[http://groups.google.com/group/ruby-ipaddress]
We've created a group to discuss about
IPAddress future development, features and provide some kind of support.
Feel free to join us and tell us what you think!
== Thanks to
Thanks to Luca Russo (vargolo) and Simone Carletti
(weppos) for all the support and technical review. Thanks to Marco Beri,
Bryan T. Richardson, Nicolas Fevrier, jdpace, Daniele Alessandri, jrdioko,
Ghislain Charrier, Pawel Krzesniak, Mark Sullivan, Leif Gensert,
Erik Ahlström, Peter Vandenberk and Steve Rawlinson for their support,
feedback and bug reports.
== Copyright
Copyright (c) 2009-2015 Marco Ceresa and Mike Mackintosh. See LICENSE for details.
|