summaryrefslogtreecommitdiff
path: root/README.rdoc
blob: f09d2292e540104c53d69097ddebec7972846d78 (plain)
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
= IPAddress

IPAddress is a Ruby library designed to work with IPv4 and
IPv6 addresses. The library 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 at one's will. This document provides a
brief introduction to the library and examples of typical usage.

= 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/net-dns.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 the following address

http://marcoceresa.com/ipaddress

= Usage

In this section I will illustrate how to use the IPAddress library
through some examples of common tasks.

== 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 wrapper method

  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.

=== Classful networks

If you don't specify a prefix (or a subnet mask), then the library
will create an object base on the CLASSFUL network from the given
IP. Remember the CLASSFUL network are the following (RFC 791)

* 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. 

You can easily check which CLASSFUL network the IP 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

These methods are only checking the address portion of an IP, and are
indipendent from its prefix.

For more information on CLASSFUL networks visit the following
Wikipedia page: http://en.wikipedia.org/wiki/Classful_network

=== 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_s

  ip.to_s
    #=> "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_s
    #=> "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_s
    #=> "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".

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_s
    #=> "172.16.10.0/24"

The IPv4#network method 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

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 behaviour 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_s
    #=> "172.16.10.255/24"

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_s
    #=> "172.16.10.1/24"

  ip.last.to_s
    #=> "172.16.10.254/24"

=== 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 trasmit 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"

Finally, you can transform an IPv4 address into a format which is
suitable to use in IPv4-IPv6 mapped addresses:

  ip.to_ipv6
    #=> "ac10:0a01"


== 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. Let's work out the last example:

  network = IPAddress("172.16.10.0/24")

  subnets = network / 4
    #=> [#<IPAddress::IPv4:0xb7b10e10 @octets=[172,16,10,0] [...]
         #<IPAddress::IPv4:0xb7b0f1b4 @octets=[172,16,10,64] [...]
         #<IPAddress::IPv4:0xb7b0e5ac @octets=[172,16,10,128] [...]  
         #<IPAddress::IPv4:0xb7b0e0c0 @octets=[172,16,10,192] [...]]

  subnets.map{|i| i.to_s}
    #=> ["172.16.10.0/26", "172.16.10.64/26", "172.16.10.128/26", 
         "172.16.10.192/26"]

You can also use method IPv4#subnets, which is an alias for
IPv4#/. Please note that you don't have to specify a network to
calculate a subnet: if the IPv4 object is a host IPv4, the method will
calculate the network number for that network and then subnet it. For
example:

  ip = IPAddress("172.16.10.58/24")
 
  ip.subnet(4).map{|i| i.to_s}
    #=> ["172.16.10.0/26", "172.16.10.64/26", "172.16.10.128/26",
         "172.16.10.192/26"]

Usually, subnetting implies dividing a network to a number of subnets
which is a power of two: in this way, you can be sure that the network
will be divived evenly, and all the subnets will have the same number
of hosts. 

=== Uneven subnetting

IPAddress also handles un-even subnetting: if you specify any number
(up to the prefix limit), the network will be divided so that the
first power-of-two networks will be even, and all the rest will just
fill out the space. 

As an example, let's divide network 172.16.10.0/24 into 3 different subnets:

  network = IPAddress("172.16.10.0/24")
  network.subnet(3).map{|i| i.to_s}
    #=> ["172.16.10.0/26",
         "172.16.10.64/26",
         "172.16.10.128/25"]

We can go even further and divide into 11 subnets:

  network = IPAddress("172.16.10.0/24")
  network.subnet(11).map{|i| i.to_s}
    #=> ["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 remaning 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).to_s
    #=> "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).to_s
    #=> "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_s}
    #=> ["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_s
    #=> "172.16.10.0/23"

However if you supernet it with a /22 prefix, the network address will
change: 

  ip.supernet(22).to_s
    #=> "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 octect. For example, the following is a valid IPv6
address: 

  1080:0000: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
semplifications 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

  1080::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:

  1080::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 "1080:0000:0000:0000:0008:0800:200C:417A"
  
  ip6 = IPAddress "1080:0:0:0:8:800:200C:417A"
 
  ip6 = IPAddress "1080::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"





   

== Special IPv6 addresses

=== Unspecified address

=== Loopback address

== Copyright

Copyright (c) 2009-2010 Marco Ceresa. See LICENSE for details.