ipaddress 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +7 -0
- data/LICENSE +20 -0
- data/README.rdoc +866 -0
- data/Rakefile +91 -0
- data/VERSION +1 -0
- data/lib/ipaddress.rb +52 -0
- data/lib/ipaddress/extensions/extensions.rb +16 -0
- data/lib/ipaddress/ipbase.rb +83 -0
- data/lib/ipaddress/ipv4.rb +849 -0
- data/lib/ipaddress/ipv6.rb +683 -0
- data/lib/ipaddress/prefix.rb +200 -0
- data/test/ipaddress/extensions/extensions_test.rb +18 -0
- data/test/ipaddress/ipbase_test.rb +28 -0
- data/test/ipaddress/ipv4_test.rb +392 -0
- data/test/ipaddress/ipv6_test.rb +290 -0
- data/test/ipaddress/prefix_test.rb +139 -0
- data/test/ipaddress_test.rb +38 -0
- data/test/test_helper.rb +33 -0
- metadata +92 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Marco Ceresa
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,866 @@
|
|
1
|
+
= IPAddress
|
2
|
+
|
3
|
+
IPAddress is a Ruby library designed to make the use of IPv4 and IPv6
|
4
|
+
addresses easy, powerful and enjoyable. It provides a complete set of
|
5
|
+
methods to handle IP addresses for any need, from simple scripting to
|
6
|
+
full network design.
|
7
|
+
|
8
|
+
IPAddress is written with a full OO interface, and its code is easy to
|
9
|
+
read, maintain and extend. The documentation is full of examples, to
|
10
|
+
let you start being productive immediately.
|
11
|
+
|
12
|
+
This document provides a brief introduction to the library and
|
13
|
+
examples of typical usage.
|
14
|
+
|
15
|
+
=== Why not using IPAddr?
|
16
|
+
|
17
|
+
IPAddr is the IP addresses library that comes with Ruby standard
|
18
|
+
lib. We found this library, although very well written, not very
|
19
|
+
suitable for all our needs, and not very flexible.
|
20
|
+
|
21
|
+
Some quick examples of things you can't do with IPAddr:
|
22
|
+
|
23
|
+
* store both the address and the prefix information
|
24
|
+
* quickly find the broadcast address of a network
|
25
|
+
* iterate over hosts
|
26
|
+
* perform subnetting or network aggregation
|
27
|
+
|
28
|
+
We hope that IPAddress will address all these issues and meet all your
|
29
|
+
needs in network programming.
|
30
|
+
|
31
|
+
= Installation
|
32
|
+
|
33
|
+
Install the library using rubygems
|
34
|
+
|
35
|
+
$ gem install ipaddress
|
36
|
+
|
37
|
+
You can then use it in your programs:
|
38
|
+
|
39
|
+
require 'rubygems' # optional
|
40
|
+
require 'ipaddress'
|
41
|
+
|
42
|
+
Another way would be to clone the git repository
|
43
|
+
|
44
|
+
$ git clone git://github.com/bluemonk/net-dns.git
|
45
|
+
|
46
|
+
And then install the library
|
47
|
+
|
48
|
+
$ cd ipaddress
|
49
|
+
ipaddress$ rake install
|
50
|
+
|
51
|
+
= Documentation
|
52
|
+
|
53
|
+
The code is fully documented with RDoc. You can generate the
|
54
|
+
documentation with Rake:
|
55
|
+
|
56
|
+
ipaddress$ rake rdoc
|
57
|
+
|
58
|
+
The latest documentation can be found online at the following address
|
59
|
+
|
60
|
+
http://marcoceresa.com/ipaddress
|
61
|
+
|
62
|
+
= Usage
|
63
|
+
|
64
|
+
In this section I will illustrate how to use the IPAddress library
|
65
|
+
through some examples of common tasks.
|
66
|
+
|
67
|
+
== IPv4
|
68
|
+
|
69
|
+
Class IPAddress::IPv4 is used to handle IPv4 type addresses. IPAddress
|
70
|
+
is similar to other IP Addresses libraries, like Ruby's own
|
71
|
+
IPAddr. However it works slightly different, as we will see.
|
72
|
+
|
73
|
+
=== Create a new IPv4 address
|
74
|
+
|
75
|
+
The usual way to express an IP Address is using its dotted decimal
|
76
|
+
form, such as 172.16.10.1, and a prefix, such as 24, separated by a
|
77
|
+
slash.
|
78
|
+
|
79
|
+
172.16.10.1/24
|
80
|
+
|
81
|
+
To create a new IPv4 object, you can use IPv4 own class
|
82
|
+
|
83
|
+
ip = IPAddress::IPv4.new "172.16.10.1/24"
|
84
|
+
|
85
|
+
or, in a easier way, using the IPAddress wrapper method
|
86
|
+
|
87
|
+
ip = IPAddress "172.16.10.1/24"
|
88
|
+
|
89
|
+
You can specify an IPv4 address in any of two ways:
|
90
|
+
|
91
|
+
IPAddress "172.16.10.1/24"
|
92
|
+
IPAddress "172.16.10.1/255.255.255.0"
|
93
|
+
|
94
|
+
In this example, prefix /24 and netmask 255.255.255.0 are the same and
|
95
|
+
you have the flexibility to use either one of them.
|
96
|
+
|
97
|
+
=== Classful networks
|
98
|
+
|
99
|
+
If you don't specify a prefix (or a subnet mask), then the library
|
100
|
+
will create an object base on the CLASSFUL network from the given
|
101
|
+
IP. Remember the CLASSFUL network are the following (RFC 791)
|
102
|
+
|
103
|
+
* Class A, from 0.0.0.0 to 127.255.255.255
|
104
|
+
* Class B, from 128.0.0.0 to 191.255.255.255
|
105
|
+
* Class C, from 192.0.0.0 to 255.255.255.255
|
106
|
+
|
107
|
+
Since classful networks here are only considered to calculate the default
|
108
|
+
prefix number, classes D and E are not considered.
|
109
|
+
|
110
|
+
You can easily check which CLASSFUL network the IP belongs:
|
111
|
+
|
112
|
+
ip = IPAddress("10.0.0.1/24")
|
113
|
+
ip.a?
|
114
|
+
#=> true
|
115
|
+
|
116
|
+
ip = IPAddress("172.16.10.1/24")
|
117
|
+
ip.b?
|
118
|
+
#=> true
|
119
|
+
|
120
|
+
ip = IPAddress("192.168.1.1/30")
|
121
|
+
ip.c?
|
122
|
+
#=> true
|
123
|
+
|
124
|
+
These methods are only checking the address portion of an IP, and are
|
125
|
+
indipendent from its prefix.
|
126
|
+
|
127
|
+
For more information on CLASSFUL networks visit the following
|
128
|
+
Wikipedia page: http://en.wikipedia.org/wiki/Classful_network
|
129
|
+
|
130
|
+
=== Handling the IPv4 address
|
131
|
+
|
132
|
+
Once created, you can obtain the attributes for an IPv4 object:
|
133
|
+
|
134
|
+
ip = IPAddress("172.16.10.1/24")
|
135
|
+
|
136
|
+
ip.address
|
137
|
+
#=> "172.16.10.1"
|
138
|
+
ip.prefix
|
139
|
+
#=> 24
|
140
|
+
|
141
|
+
In case you need to retrieve the netmask in IPv4 format, you can use
|
142
|
+
the IPv4#netmask method:
|
143
|
+
|
144
|
+
ip.netmask
|
145
|
+
#=> "255.255.255.0"
|
146
|
+
|
147
|
+
A special attribute, IPv4#octets, is available to get the four
|
148
|
+
decimal octets from the IP address:
|
149
|
+
|
150
|
+
ip.octets
|
151
|
+
#=> [172,16,10,1]
|
152
|
+
|
153
|
+
Shortcut method IPv4#[], provides access to a given octet whithin the
|
154
|
+
range:
|
155
|
+
|
156
|
+
ip[1]
|
157
|
+
#=> 16
|
158
|
+
|
159
|
+
If you need to print out the IPv4 address in a canonical form, you can
|
160
|
+
use IPv4#to_s
|
161
|
+
|
162
|
+
ip.to_s
|
163
|
+
#=> "172.16.10.l/24"
|
164
|
+
|
165
|
+
=== Changing netmask
|
166
|
+
|
167
|
+
You can set a new prefix (netmask) after creating an IPv4
|
168
|
+
object. For example:
|
169
|
+
|
170
|
+
ip.prefix = 25
|
171
|
+
ip.to_s
|
172
|
+
#=> "172.16.10.l/25"
|
173
|
+
|
174
|
+
If you need to use a netmask in IPv4 format, you can achive so by
|
175
|
+
using the IPv4#netmask= method
|
176
|
+
|
177
|
+
ip.netmask = "255.255.255.252"
|
178
|
+
ip.to_s
|
179
|
+
#=> "172.16.10.1/30"
|
180
|
+
|
181
|
+
=== Working with networks, broadcasts and addresses
|
182
|
+
|
183
|
+
Some very important topics in dealing with IP addresses are the
|
184
|
+
concepts of +network+ and +broadcast+, as well as the addresses
|
185
|
+
included in a range.
|
186
|
+
|
187
|
+
When you specify an IPv4 address such as "172.16.10.1/24", you are
|
188
|
+
actually handling two different information:
|
189
|
+
|
190
|
+
* The IP address itself, "172.16.10.1"
|
191
|
+
* The subnet mask which indicates the network
|
192
|
+
|
193
|
+
The network number is the IP which has all zeroes in the host
|
194
|
+
portion. In our example, because the prefix is 24, we identify our
|
195
|
+
network number to have the last 8 (32-24) bits all zeroes. Thus, IP
|
196
|
+
address "172.16.10.1/24" belongs to network "172.16.10.0/24".
|
197
|
+
|
198
|
+
This is very important because, for instance, IP "172.16.10.1/16" is
|
199
|
+
very different to the previous one, belonging to the very different
|
200
|
+
network "172.16.0.0/16".
|
201
|
+
|
202
|
+
With IPAddress it's very easy to calculate the network for an IP
|
203
|
+
address:
|
204
|
+
|
205
|
+
ip = IPAddress "172.16.10.1/24"
|
206
|
+
net = ip.network
|
207
|
+
#=> #<IPAddress::IPv4:0xb7a5ab24 @octets=[172, 16, 10, 0],
|
208
|
+
@prefix=24,
|
209
|
+
@address="172.16.10.0">
|
210
|
+
net.to_s
|
211
|
+
#=> "172.16.10.0/24"
|
212
|
+
|
213
|
+
The IPv4#network method creates a new IPv4 object from the network
|
214
|
+
number, calculated after the original object. We want to outline here
|
215
|
+
that the network address is a perfect legitimate IPv4 address, which
|
216
|
+
just happen to have all zeroes in the host portion.
|
217
|
+
|
218
|
+
You can use method IPv4#network? to check whether an IP address is a
|
219
|
+
network or not:
|
220
|
+
|
221
|
+
ip1 = IPAddress "172.16.10.1/24"
|
222
|
+
ip2 = IPAddress "172.16.10.4/30"
|
223
|
+
|
224
|
+
ip1.network?
|
225
|
+
#=> false
|
226
|
+
ip2.network?
|
227
|
+
#=> true
|
228
|
+
|
229
|
+
The broadcast address is the contrary than the network number: where
|
230
|
+
the network number has all zeroes in the host portion, the broadcast
|
231
|
+
address has all one's. For example, ip "172.16.10.1/24" has broadcast
|
232
|
+
"172.16.10.255/24", where ip "172.16.10.1/16" has broadcast
|
233
|
+
"172.16.255.255/16".
|
234
|
+
|
235
|
+
Method IPv4#broadcast has the same behaviour as is #network
|
236
|
+
counterpart: it creates a new IPv4 object to handle the broadcast
|
237
|
+
address:
|
238
|
+
|
239
|
+
ip = IPAddress "172.16.10.1/24"
|
240
|
+
bcast = ip.broadcast
|
241
|
+
#=> #<IPAddress::IPv4:0xb7a406fc @octets=[172, 16, 10, 255],
|
242
|
+
@prefix=24,
|
243
|
+
@address="172.16.10.255">
|
244
|
+
bcast.to_s
|
245
|
+
#=> "172.16.10.255/24"
|
246
|
+
|
247
|
+
So we see that the netmask essentially specifies a range for IP
|
248
|
+
addresses that are included in a network: all the addresses between
|
249
|
+
the network number and the broadcast. IPAddress has many methods to
|
250
|
+
iterate between those addresses. Let's start with IPv4#each, which
|
251
|
+
iterates over all addresses in a range
|
252
|
+
|
253
|
+
ip = IPAddress "172.16.10.1/24"
|
254
|
+
|
255
|
+
ip.each do |addr|
|
256
|
+
puts addr
|
257
|
+
end
|
258
|
+
|
259
|
+
It is important to note that it doesn't matter if the original IP is a
|
260
|
+
host IP or a network number (or a broadcast address): the #each method
|
261
|
+
only considers the range that the original IP specifies.
|
262
|
+
|
263
|
+
If you only want to iterate over hosts IP, use the Ipv4#each_host
|
264
|
+
method:
|
265
|
+
|
266
|
+
ip = IPAddress "172.16.10.1/24"
|
267
|
+
|
268
|
+
ip.each_host do |host|
|
269
|
+
puts host
|
270
|
+
end
|
271
|
+
|
272
|
+
Methods IPv4#first and IPv4#last return a new object containing
|
273
|
+
respectively the first and the last host address in the range
|
274
|
+
|
275
|
+
ip = IPAddress "172.16.10.100/24"
|
276
|
+
|
277
|
+
ip.first.to_s
|
278
|
+
#=> "172.16.10.1/24"
|
279
|
+
|
280
|
+
ip.last.to_s
|
281
|
+
#=> "172.16.10.254/24"
|
282
|
+
|
283
|
+
=== IP special formats
|
284
|
+
|
285
|
+
The IPAddress library provides a complete set of methods to access an
|
286
|
+
IPv4 address in special formats, such as binary, 32 bits unsigned int,
|
287
|
+
data and hexadecimal.
|
288
|
+
|
289
|
+
Let's take the following IPv4 as an example:
|
290
|
+
|
291
|
+
ip = IPAddress "172.16.10.1/24"
|
292
|
+
ip.address
|
293
|
+
#=> "172.16.10.1"
|
294
|
+
|
295
|
+
The first thing to highlight here is that all these conversion methods
|
296
|
+
only take into consideration the address portion of an IPv4 object and
|
297
|
+
not the prefix (netmask).
|
298
|
+
|
299
|
+
So, to express the address in binary format, use the IPv4#bits method:
|
300
|
+
|
301
|
+
ip.bits
|
302
|
+
#=> "10101100000100000000101000000001"
|
303
|
+
|
304
|
+
To calculate the 32 bits unsigned int format of the ip address, use
|
305
|
+
the IPv4#to_u32 method
|
306
|
+
|
307
|
+
ip.to_u32
|
308
|
+
#=> 2886732289
|
309
|
+
|
310
|
+
This method is the equivalent of the Unix call pton(), expressing an
|
311
|
+
IP address in the so called +network byte order+ notation. However, if
|
312
|
+
you want to trasmit your IP over a network socket, you might need to
|
313
|
+
transform it in data format using the IPv4#data method:
|
314
|
+
|
315
|
+
ip.data
|
316
|
+
#=> "\254\020\n\001"
|
317
|
+
|
318
|
+
Finally, you can transform an IPv4 address into a format which is
|
319
|
+
suitable to use in IPv4-IPv6 mapped addresses:
|
320
|
+
|
321
|
+
ip.to_ipv6
|
322
|
+
#=> "ac10:0a01"
|
323
|
+
|
324
|
+
|
325
|
+
== Network design with IPAddress
|
326
|
+
|
327
|
+
IPAddress includes a lot of useful methods to manipulate IPv4 and IPv6
|
328
|
+
networks and do some basic network design.
|
329
|
+
|
330
|
+
=== Subnetting
|
331
|
+
|
332
|
+
The process of subnetting is the division of a network into smaller
|
333
|
+
(in terms of hosts capacity) networks, called subnets, so that they
|
334
|
+
all share a common root, which is the starting network.
|
335
|
+
|
336
|
+
For example, if you have network "172.16.10.0/24", we can subnet it
|
337
|
+
into 4 smaller subnets. The new prefix will be /26, because 4 is 2^2
|
338
|
+
and therefore we add 2 bits to the network prefix (24+2=26).
|
339
|
+
|
340
|
+
Subnetting is easy with IPAddress. Let's work out the last example:
|
341
|
+
|
342
|
+
network = IPAddress("172.16.10.0/24")
|
343
|
+
|
344
|
+
subnets = network / 4
|
345
|
+
#=> [#<IPAddress::IPv4:0xb7b10e10 @octets=[172,16,10,0] [...]
|
346
|
+
#<IPAddress::IPv4:0xb7b0f1b4 @octets=[172,16,10,64] [...]
|
347
|
+
#<IPAddress::IPv4:0xb7b0e5ac @octets=[172,16,10,128] [...]
|
348
|
+
#<IPAddress::IPv4:0xb7b0e0c0 @octets=[172,16,10,192] [...]]
|
349
|
+
|
350
|
+
subnets.map{|i| i.to_s}
|
351
|
+
#=> ["172.16.10.0/26", "172.16.10.64/26", "172.16.10.128/26",
|
352
|
+
"172.16.10.192/26"]
|
353
|
+
|
354
|
+
You can also use method IPv4#subnets, which is an alias for
|
355
|
+
IPv4#/. Please note that you don't have to specify a network to
|
356
|
+
calculate a subnet: if the IPv4 object is a host IPv4, the method will
|
357
|
+
calculate the network number for that network and then subnet it. For
|
358
|
+
example:
|
359
|
+
|
360
|
+
ip = IPAddress("172.16.10.58/24")
|
361
|
+
|
362
|
+
ip.subnet(4).map{|i| i.to_s}
|
363
|
+
#=> ["172.16.10.0/26", "172.16.10.64/26", "172.16.10.128/26",
|
364
|
+
"172.16.10.192/26"]
|
365
|
+
|
366
|
+
Usually, subnetting implies dividing a network to a number of subnets
|
367
|
+
which is a power of two: in this way, you can be sure that the network
|
368
|
+
will be divived evenly, and all the subnets will have the same number
|
369
|
+
of hosts.
|
370
|
+
|
371
|
+
=== Uneven subnetting
|
372
|
+
|
373
|
+
IPAddress also handles un-even subnetting: if you specify any number
|
374
|
+
(up to the prefix limit), the network will be divided so that the
|
375
|
+
first power-of-two networks will be even, and all the rest will just
|
376
|
+
fill out the space.
|
377
|
+
|
378
|
+
As an example, let's divide network 172.16.10.0/24 into 3 different subnets:
|
379
|
+
|
380
|
+
network = IPAddress("172.16.10.0/24")
|
381
|
+
network.subnet(3).map{|i| i.to_s}
|
382
|
+
#=> ["172.16.10.0/26",
|
383
|
+
"172.16.10.64/26",
|
384
|
+
"172.16.10.128/25"]
|
385
|
+
|
386
|
+
We can go even further and divide into 11 subnets:
|
387
|
+
|
388
|
+
network = IPAddress("172.16.10.0/24")
|
389
|
+
network.subnet(11).map{|i| i.to_s}
|
390
|
+
#=> ["172.16.10.0/28", "172.16.10.16/28", "172.16.10.32/28",
|
391
|
+
"172.16.10.48/28", "172.16.10.64/28", "172.16.10.80/28",
|
392
|
+
"172.16.10.96/28", "172.16.10.112/28", "172.16.10.128/27",
|
393
|
+
"172.16.10.160/27", "172.16.10.192/26"]
|
394
|
+
|
395
|
+
As you can see, most of the networks are /28, with a few /27 and one
|
396
|
+
/26 to fill up the remaning space.
|
397
|
+
|
398
|
+
=== Summarization
|
399
|
+
|
400
|
+
Summarization (or aggregation) is the process when two or more
|
401
|
+
networks are taken together to check if a supernet, including
|
402
|
+
all and only these networks, exists. If it exists then this supernet
|
403
|
+
is called the summarized (or aggregated) network.
|
404
|
+
It is very important to understand that summarization can only
|
405
|
+
occur if there are no holes in the aggregated network, or, in
|
406
|
+
other words, if the given networks fill completely the address space
|
407
|
+
of the supernet. So the two rules are:
|
408
|
+
|
409
|
+
1) The aggregate network must contain +all+ the IP addresses of the
|
410
|
+
original networks;
|
411
|
+
2) The aggregate network must contain +only+ the IP addresses of the
|
412
|
+
original networks;
|
413
|
+
|
414
|
+
A few examples will help clarify the above. Let's consider for
|
415
|
+
instance the following two networks:
|
416
|
+
|
417
|
+
ip1 = IPAddress("172.16.10.0/24")
|
418
|
+
ip2 = IPAddress("172.16.11.0/24")
|
419
|
+
|
420
|
+
These two networks can be expressed using only one IP address
|
421
|
+
network if we change the prefix. Let Ruby do the work:
|
422
|
+
|
423
|
+
IPAddress::IPv4::summarize(ip1,ip2).to_s
|
424
|
+
#=> "172.16.10.0/23"
|
425
|
+
|
426
|
+
We note how the network "172.16.10.0/23" includes all the
|
427
|
+
addresses specified in the above networks, and (more important) includes
|
428
|
+
ONLY those addresses.
|
429
|
+
|
430
|
+
If we summarized +ip1+ and +ip2+ with the following network:
|
431
|
+
|
432
|
+
"172.16.0.0/16"
|
433
|
+
|
434
|
+
we would have satisfied rule #1 above, but not rule #2. So
|
435
|
+
|
436
|
+
"172.16.0.0/16"
|
437
|
+
|
438
|
+
is not an aggregate network for +ip1+ and +ip2+.
|
439
|
+
|
440
|
+
If it's not possible to compute a single aggregated network for
|
441
|
+
all the original networks, the method returns an array with all the
|
442
|
+
aggregate networks found. For example, the following four networks can be
|
443
|
+
aggregated in a single /22:
|
444
|
+
|
445
|
+
ip1 = IPAddress("10.0.0.1/24")
|
446
|
+
ip2 = IPAddress("10.0.1.1/24")
|
447
|
+
ip3 = IPAddress("10.0.2.1/24")
|
448
|
+
ip4 = IPAddress("10.0.3.1/24")
|
449
|
+
|
450
|
+
IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).to_s
|
451
|
+
#=> "10.0.0.0/22",
|
452
|
+
|
453
|
+
But the following networks can't be summarized in a single
|
454
|
+
network:
|
455
|
+
|
456
|
+
ip1 = IPAddress("10.0.1.1/24")
|
457
|
+
ip2 = IPAddress("10.0.2.1/24")
|
458
|
+
ip3 = IPAddress("10.0.3.1/24")
|
459
|
+
ip4 = IPAddress("10.0.4.1/24")
|
460
|
+
IPAddress::IPv4::summarize(ip1,ip2,ip3,ip4).map{|i| i.to_s}
|
461
|
+
#=> ["10.0.1.0/24","10.0.2.0/23","10.0.4.0/24"]
|
462
|
+
|
463
|
+
In this case, the two summarizables networks have been aggregated into
|
464
|
+
a single /23, while the other two networks have been left untouched.
|
465
|
+
|
466
|
+
=== Supernetting
|
467
|
+
|
468
|
+
Supernetting is a different operation than aggregation, as it only
|
469
|
+
works on a single network and returns a new single IPv4 object,
|
470
|
+
representing the supernet.
|
471
|
+
|
472
|
+
Supernetting is similar to subnetting, except that you getting as a
|
473
|
+
result a network with a smaller prefix (bigger host space). For
|
474
|
+
example, given the network
|
475
|
+
|
476
|
+
ip = IPAddress("172.16.10.0/24")
|
477
|
+
|
478
|
+
you can supernet it with a new /23 prefix
|
479
|
+
|
480
|
+
ip.supernet(23).to_s
|
481
|
+
#=> "172.16.10.0/23"
|
482
|
+
|
483
|
+
However if you supernet it with a /22 prefix, the network address will
|
484
|
+
change:
|
485
|
+
|
486
|
+
ip.supernet(22).to_s
|
487
|
+
#=> "172.16.8.0/22"
|
488
|
+
|
489
|
+
This is because "172.16.10.0/22" is not a network anymore, but an host
|
490
|
+
address.
|
491
|
+
|
492
|
+
|
493
|
+
=IPv6
|
494
|
+
|
495
|
+
IPAddress is not only fantastic for IPv4 addresses, it's also great to
|
496
|
+
handle IPv6 addresses family! Let's discover together how to use it in
|
497
|
+
our projects.
|
498
|
+
|
499
|
+
== IPv6 addresses
|
500
|
+
|
501
|
+
IPv6 addresses are 128 bits long, in contrast with IPv4 addresses
|
502
|
+
which are only 32 bits long. An IPv6 address is generally written as
|
503
|
+
eight groups of four hexadecimal digits, each group representing 16
|
504
|
+
bits or two octect. For example, the following is a valid IPv6
|
505
|
+
address:
|
506
|
+
|
507
|
+
1080:0000:0000:0000:0008:0800:200c:417a
|
508
|
+
|
509
|
+
Letters in an IPv6 address are usually written downcase, as per
|
510
|
+
RFC. You can create a new IPv6 object using uppercase letters, but
|
511
|
+
they will be converted.
|
512
|
+
|
513
|
+
=== Compression
|
514
|
+
|
515
|
+
Since IPv6 addresses are very long to write, there are some
|
516
|
+
semplifications and compressions that you can use to shorten them.
|
517
|
+
|
518
|
+
* Leading zeroes: all the leading zeroes within a group can be
|
519
|
+
omitted: "0008" would become "8"
|
520
|
+
|
521
|
+
* A string of consecutive zeroes can be replaced by the string
|
522
|
+
"::". This can be only applied once.
|
523
|
+
|
524
|
+
Using compression, the IPv6 address written above can be shorten into
|
525
|
+
the following, equivalent, address
|
526
|
+
|
527
|
+
1080::8:800:200c:417a
|
528
|
+
|
529
|
+
This short version is often used in human representation.
|
530
|
+
|
531
|
+
=== Network Mask
|
532
|
+
|
533
|
+
As we used to do with IPv4 addresses, an IPv6 address can be written
|
534
|
+
using the prefix notation to specify the subnet mask:
|
535
|
+
|
536
|
+
1080::8:800:200c:417a/64
|
537
|
+
|
538
|
+
The /64 part means that the first 64 bits of the address are
|
539
|
+
representing the network portion, and the last 64 bits are the host
|
540
|
+
portion.
|
541
|
+
|
542
|
+
== Using IPAddress with IPv6 addresses
|
543
|
+
|
544
|
+
All the IPv6 representations we've just seen are perfectly fine when
|
545
|
+
you want to create a new IPv6 address:
|
546
|
+
|
547
|
+
ip6 = IPAddress "1080:0000:0000:0000:0008:0800:200C:417A"
|
548
|
+
|
549
|
+
ip6 = IPAddress "1080:0:0:0:8:800:200C:417A"
|
550
|
+
|
551
|
+
ip6 = IPAddress "1080::8:800:200C:417A"
|
552
|
+
|
553
|
+
All three are giving out the same IPv6 object. The default subnet mask
|
554
|
+
for an IPv6 is 128, as IPv6 addresses don't have classes like IPv4
|
555
|
+
addresses. If you want a different mask, you can go ahead and explicit
|
556
|
+
it:
|
557
|
+
|
558
|
+
ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
|
559
|
+
|
560
|
+
Access the address portion and the prefix by using the respective
|
561
|
+
methods:
|
562
|
+
|
563
|
+
ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
|
564
|
+
|
565
|
+
ip6.address
|
566
|
+
#=> "2001:0db8:0000:0000:0008:0800:200c:417a"
|
567
|
+
|
568
|
+
ip6.prefix
|
569
|
+
#=> 64
|
570
|
+
|
571
|
+
A compressed version of the IPv6 address can be obtained with the
|
572
|
+
IPv6#compressed method:
|
573
|
+
|
574
|
+
ip6 = IPAddress "2001:0db8:0000:0000:0008:200c:417a:00ab/64"
|
575
|
+
|
576
|
+
ip6.compressed
|
577
|
+
#=> "2001:db8::8:800:200c:417a"
|
578
|
+
|
579
|
+
== Handling the IPv6 address
|
580
|
+
|
581
|
+
Accessing the groups that form an IPv6 address is very easy with the
|
582
|
+
IPv6#groups method:
|
583
|
+
|
584
|
+
ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
|
585
|
+
|
586
|
+
ip6.groups
|
587
|
+
#=> [8193, 3512, 0, 0, 8, 2048, 8204, 16762]
|
588
|
+
|
589
|
+
As with IPv4 addresses, each individual group can be accessed using
|
590
|
+
the IPv6#[] shortcut method:
|
591
|
+
|
592
|
+
ip6[0]
|
593
|
+
#=> 8193
|
594
|
+
ip6[1]
|
595
|
+
#=> 3512
|
596
|
+
ip6[2]
|
597
|
+
#=> 0
|
598
|
+
ip6[3]
|
599
|
+
#=> 0
|
600
|
+
|
601
|
+
Note that each 16 bits group is expressed in its decimal form. You can
|
602
|
+
also obtain the groups into hexadecimal format using the IPv6#hexs
|
603
|
+
method:
|
604
|
+
|
605
|
+
ip6.hexs
|
606
|
+
#=> => ["2001", "0db8", "0000", "0000", "0008", "0800", "200c", "417a"]
|
607
|
+
|
608
|
+
A few other methods are available to transform an IPv6 address into
|
609
|
+
decimal representation, with IPv6.to_i
|
610
|
+
|
611
|
+
ip6.to_i
|
612
|
+
#=> 42540766411282592856906245548098208122
|
613
|
+
|
614
|
+
or to hexadecimal representation
|
615
|
+
|
616
|
+
ip6.to_hex
|
617
|
+
#=> "20010db80000000000080800200c417a"
|
618
|
+
|
619
|
+
To print out an IPv6 address in human readable form, use the IPv6#to_s
|
620
|
+
and IPv6#to_string methods
|
621
|
+
|
622
|
+
ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
|
623
|
+
|
624
|
+
ip6.to_s
|
625
|
+
#=> "2001:db8::8:800:200c:417a/96"
|
626
|
+
|
627
|
+
ip6.to_string
|
628
|
+
#=> "2001:0db8:0000:0000:0008:0800:200c:417a/96"
|
629
|
+
|
630
|
+
As you can see, IPv6.to_s prints out the compressed form, while
|
631
|
+
IPv6.to_string uses the expanded version.
|
632
|
+
|
633
|
+
=== Compressing and uncompressing
|
634
|
+
|
635
|
+
If you have a string representing an IPv6 address, you can easily
|
636
|
+
compress it and uncompress it using the two class methods IPv6::expand
|
637
|
+
and IPv6::compress.
|
638
|
+
|
639
|
+
For example, let's say you have the following uncompressed IPv6
|
640
|
+
address:
|
641
|
+
|
642
|
+
ip6str = "2001:0DB8:0000:CD30:0000:0000:0000:0000"
|
643
|
+
|
644
|
+
Here is the compressed version:
|
645
|
+
|
646
|
+
IPAddress::IPv6.compress ip6str
|
647
|
+
#=> "2001:db8:0:cd30::"
|
648
|
+
|
649
|
+
The other way works as well:
|
650
|
+
|
651
|
+
ip6str = "2001:db8:0:cd30::"
|
652
|
+
|
653
|
+
IPAddress::IPv6.expand ip6str
|
654
|
+
#=> "2001:0DB8:0000:CD30:0000:0000:0000:0000"
|
655
|
+
|
656
|
+
These methods can be used when you don't want to create a new object
|
657
|
+
just for expanding or compressing an address (although a new object is
|
658
|
+
actually created internally).
|
659
|
+
|
660
|
+
== New IPv6 address from other formats
|
661
|
+
|
662
|
+
You can create a new IPv6 address from different formats than just a
|
663
|
+
string representing the colon-hex groups.
|
664
|
+
|
665
|
+
For instance, if you have a data stream, you can use IPv6::parse_data,
|
666
|
+
like in the following example:
|
667
|
+
|
668
|
+
data = " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
|
669
|
+
|
670
|
+
ip6 = IPAddress::IPv6::parse_data data
|
671
|
+
ip6.prefix = 64
|
672
|
+
|
673
|
+
ip6.to_s
|
674
|
+
#=> "2001:db8::8:800:200c:417a/64"
|
675
|
+
|
676
|
+
A new IPv6 address can also be created from an unsigned 128 bits
|
677
|
+
integer:
|
678
|
+
|
679
|
+
u128 = 21932261930451111902915077091070067066
|
680
|
+
|
681
|
+
ip6 = IPAddress::IPv6::parse_u128 u128
|
682
|
+
ip6.prefix = 64
|
683
|
+
|
684
|
+
ip6.to_s
|
685
|
+
#=> "1080::8:800:200c:417a/64"
|
686
|
+
|
687
|
+
Finally, a new IPv6 address can be created from an hex string:
|
688
|
+
|
689
|
+
hex = "20010db80000000000080800200c417a"
|
690
|
+
|
691
|
+
ip6 = IPAddress::IPv6::parse_hex hex
|
692
|
+
ip6.prefix = 64
|
693
|
+
|
694
|
+
ip6.to_s
|
695
|
+
#=> "2001:db8::8:800:200c:417a/64"
|
696
|
+
|
697
|
+
== Special IPv6 addresses
|
698
|
+
|
699
|
+
Some IPv6 have a special meaning and are expressed in a special form,
|
700
|
+
quite different than an usual IPv6 address. IPAddress has builtin
|
701
|
+
support for unspecified, loopback and mapped IPv6 addresses.
|
702
|
+
|
703
|
+
=== Unspecified address
|
704
|
+
|
705
|
+
The address with all zero bits is called the +unspecified+ address
|
706
|
+
(corresponding to 0.0.0.0 in IPv4). It should be something like this:
|
707
|
+
|
708
|
+
0000:0000:0000:0000:0000:0000:0000:0000
|
709
|
+
|
710
|
+
but, with the use of compression, it is usually written as just two
|
711
|
+
colons:
|
712
|
+
|
713
|
+
::
|
714
|
+
|
715
|
+
or, specifying the netmask:
|
716
|
+
|
717
|
+
::/128
|
718
|
+
|
719
|
+
With IPAddress, create a new unspecified IPv6 address using its own
|
720
|
+
subclass:
|
721
|
+
|
722
|
+
ip = IPAddress::IPv6::Unspecified.new
|
723
|
+
|
724
|
+
ip.to_s
|
725
|
+
#=> => "::/128"
|
726
|
+
|
727
|
+
You can easily check if an IPv6 object is an unspecified address by
|
728
|
+
using the IPv6#unspecified? method
|
729
|
+
|
730
|
+
ip.unspecified?
|
731
|
+
#=> true
|
732
|
+
|
733
|
+
An unspecified IPv6 address can also be created with the wrapper
|
734
|
+
method, like we've seen before
|
735
|
+
|
736
|
+
ip = IPAddress "::"
|
737
|
+
|
738
|
+
ip.unspecified?
|
739
|
+
#=> true
|
740
|
+
|
741
|
+
This address must never be assigned to an interface and is to be used
|
742
|
+
only in software before the application has learned its host's source
|
743
|
+
address appropriate for a pending connection. Routers must not forward
|
744
|
+
packets with the unspecified address.
|
745
|
+
|
746
|
+
=== Loopback address
|
747
|
+
|
748
|
+
The loopback address is a unicast localhost address. If an
|
749
|
+
application in a host sends packets to this address, the IPv6 stack
|
750
|
+
will loop these packets back on the same virtual interface.
|
751
|
+
|
752
|
+
Loopback addresses are expressed in the following form:
|
753
|
+
|
754
|
+
::1
|
755
|
+
|
756
|
+
or, with their appropriate prefix,
|
757
|
+
|
758
|
+
::1/128
|
759
|
+
|
760
|
+
As for the unspecified addresses, IPv6 loopbacks can be created with
|
761
|
+
IPAddress calling their own class:
|
762
|
+
|
763
|
+
ip = IPAddress::IPv6::Loopback.new
|
764
|
+
|
765
|
+
ip.to_s
|
766
|
+
#=> "::1/128"
|
767
|
+
|
768
|
+
or by using the wrapper:
|
769
|
+
|
770
|
+
ip = IPAddress "::1"
|
771
|
+
|
772
|
+
ip.to_s
|
773
|
+
#=> "::1/128"
|
774
|
+
|
775
|
+
Checking if an address is loopback is easy with the IPv6#loopback?
|
776
|
+
method:
|
777
|
+
|
778
|
+
ip.loopback?
|
779
|
+
#=> true
|
780
|
+
|
781
|
+
The IPv6 loopback address corresponds to 127.0.0.1 in IPv4.
|
782
|
+
|
783
|
+
=== Mapped address
|
784
|
+
|
785
|
+
It is usually identified as a IPv4 mapped IPv6 address, a particular
|
786
|
+
IPv6 address which aids the transition from IPv4 to IPv6. The
|
787
|
+
structure of the address is
|
788
|
+
|
789
|
+
::ffff:w.y.x.z
|
790
|
+
|
791
|
+
where w.x.y.z is a normal IPv4 address. For example, the following is
|
792
|
+
a mapped IPv6 address:
|
793
|
+
|
794
|
+
::ffff:192.168.100.1
|
795
|
+
|
796
|
+
IPAddress is very powerful in handling mapped IPv6 addresses, as the
|
797
|
+
IPv4 portion is stored internally as a normal IPv4 object. Let's have
|
798
|
+
a look at some examples. To create a new mapped address, just use the
|
799
|
+
class builder itself
|
800
|
+
|
801
|
+
ip6 = IPAddress::IPv6::Mapped.new "::ffff:172.16.10.1/128"
|
802
|
+
|
803
|
+
or just use the wrapper method
|
804
|
+
|
805
|
+
ip6 = IPAddress "::ffff:172.16.10.1/128"
|
806
|
+
|
807
|
+
Let's check it's really a mapped address:
|
808
|
+
|
809
|
+
ip6.mapped?
|
810
|
+
#=> true
|
811
|
+
|
812
|
+
ip6.to_s
|
813
|
+
#=> "::FFFF:172.16.10.1/128"
|
814
|
+
|
815
|
+
Now with the +ipv4+ attribute, we can easily access the IPv4 portion
|
816
|
+
of the mapped IPv6 address:
|
817
|
+
|
818
|
+
ip6.ipv4.address
|
819
|
+
#=> "172.16.10.1"
|
820
|
+
|
821
|
+
Internally, the IPv4 address is stored as two 16 bits
|
822
|
+
groups. Therefore all the usual methods for an IPv6 address are
|
823
|
+
working perfectly fine:
|
824
|
+
|
825
|
+
ip6.to_hex
|
826
|
+
#=> "00000000000000000000ffffac100a01"
|
827
|
+
|
828
|
+
ip6.address
|
829
|
+
#=> "0000:0000:0000:0000:0000:ffff:ac10:0a01"
|
830
|
+
|
831
|
+
A mapped IPv6 can also be created just by specify the address in the
|
832
|
+
following format:
|
833
|
+
|
834
|
+
ip6 = IPAddress "::172.16.10.1"
|
835
|
+
|
836
|
+
That is, two colons and the IPv4 address. However, as by RFC, the ffff
|
837
|
+
group will be automatically added at the beginning
|
838
|
+
|
839
|
+
ip6.to_s
|
840
|
+
=> "::ffff:172.16.10.1/128"
|
841
|
+
|
842
|
+
making it a mapped IPv6 compatible address.
|
843
|
+
|
844
|
+
= Future versions:
|
845
|
+
|
846
|
+
Some new features we'd like to add in the future versions:
|
847
|
+
|
848
|
+
* support for wildmasks
|
849
|
+
* network design methods for IPv6
|
850
|
+
* parameter to IPv4#subnet to select where to fill the space
|
851
|
+
(beginning or ending)
|
852
|
+
* method to check if a network is private
|
853
|
+
|
854
|
+
Stay tuned!
|
855
|
+
|
856
|
+
= Thanks to
|
857
|
+
|
858
|
+
Thanks to Luca Russo (vargolo) for all the support and technical
|
859
|
+
review.
|
860
|
+
|
861
|
+
= Copyright
|
862
|
+
|
863
|
+
Copyright (c) 2009-2010 Marco Ceresa. See LICENSE for details.
|
864
|
+
|
865
|
+
|
866
|
+
|