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