ipaddress 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +7 -0
- data/README.rdoc +83 -49
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/lib/ipaddress.rb +1 -5
- data/lib/ipaddress/extensions/extensions.rb +3 -3
- data/lib/ipaddress/ipv4.rb +83 -53
- data/lib/ipaddress/ipv6.rb +57 -10
- data/lib/ipaddress/prefix.rb +20 -5
- data/test/ipaddress/ipv4_test.rb +33 -3
- data/test/ipaddress/ipv6_test.rb +48 -10
- data/test/ipaddress/prefix_test.rb +1 -0
- metadata +17 -5
data/CHANGELOG.rdoc
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
== master
|
2
|
+
|
3
|
+
NEW:: IPAddress::IPv6#include?
|
4
|
+
NEW:: IPAddress::IPv6#network_u128
|
5
|
+
NEW:: Modified IPAddress::IPv6::Mapped to accept IPv4 mapped addresses in IPv6 format
|
6
|
+
NEW:: IPAddress::IPv4#private?
|
7
|
+
NEW:: IPAddress::IPv4::parse_classful
|
1
8
|
|
2
9
|
== ipaddress 0.6.0
|
3
10
|
|
data/README.rdoc
CHANGED
@@ -43,8 +43,12 @@ Some quick examples of things you can't do with IPAddr:
|
|
43
43
|
* iterate over hosts
|
44
44
|
* perform subnetting or network aggregation
|
45
45
|
|
46
|
-
|
47
|
-
declared deprecated by the IETF
|
46
|
+
Many methods and procedures are so old that they have been
|
47
|
+
declared deprecated by the IETF, and some others have bugs in their
|
48
|
+
implementation.
|
49
|
+
|
50
|
+
Moreover, IPAddress is more robust and is already around 50% faster than IPAddr,
|
51
|
+
in addition to provide an organic API with logical separation and OO structure.
|
48
52
|
|
49
53
|
We hope that IPAddress will address all these issues and meet all your
|
50
54
|
needs in network programming.
|
@@ -77,20 +81,15 @@ documentation with Rake:
|
|
77
81
|
ipaddress$ rake rdoc
|
78
82
|
|
79
83
|
The latest documentation can be found online at
|
80
|
-
{this address}[http://
|
81
|
-
|
82
|
-
==
|
83
|
-
|
84
|
-
In this section I will illustrate how to use the IPAddress library
|
85
|
-
through some examples of common tasks.
|
86
|
-
|
87
|
-
=== IPv4
|
84
|
+
{this address}[http://rubydoc.info/github/bluemonk/ipaddress/master/frames]
|
85
|
+
|
86
|
+
== IPv4
|
88
87
|
|
89
88
|
Class IPAddress::IPv4 is used to handle IPv4 type addresses. IPAddress
|
90
89
|
is similar to other IP Addresses libraries, like Ruby's own
|
91
90
|
IPAddr. However it works slightly different, as we will see.
|
92
91
|
|
93
|
-
|
92
|
+
=== Create a new IPv4 address
|
94
93
|
|
95
94
|
The usual way to express an IP Address is using its dotted decimal
|
96
95
|
form, such as 172.16.10.1, and a prefix, such as 24, separated by a
|
@@ -102,7 +101,15 @@ To create a new IPv4 object, you can use IPv4 own class
|
|
102
101
|
|
103
102
|
ip = IPAddress::IPv4.new "172.16.10.1/24"
|
104
103
|
|
105
|
-
or, in a easier way, using the IPAddress
|
104
|
+
or, in a easier way, using the IPAddress parse method
|
105
|
+
|
106
|
+
ip = IPAddress.parse "172.16.10.1/24"
|
107
|
+
|
108
|
+
which accepts and parses any kind of IP (IPv4, IPV6 and
|
109
|
+
IPv4 IPv6 Mapped addresses).
|
110
|
+
|
111
|
+
If you like sintactic sugar, you can use the wrapper method
|
112
|
+
IPAddress(), which is built around IPAddress::parse:
|
106
113
|
|
107
114
|
ip = IPAddress "172.16.10.1/24"
|
108
115
|
|
@@ -114,40 +121,20 @@ You can specify an IPv4 address in any of two ways:
|
|
114
121
|
In this example, prefix /24 and netmask 255.255.255.0 are the same and
|
115
122
|
you have the flexibility to use either one of them.
|
116
123
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
IP. Remember the CLASSFUL network are the following (RFC 791)
|
122
|
-
|
123
|
-
* Class A, from 0.0.0.0 to 127.255.255.255
|
124
|
-
* Class B, from 128.0.0.0 to 191.255.255.255
|
125
|
-
* Class C, from 192.0.0.0 to 255.255.255.255
|
126
|
-
|
127
|
-
Since classful networks here are only considered to calculate the default
|
128
|
-
prefix number, classes D and E are not considered.
|
129
|
-
|
130
|
-
You can easily check which CLASSFUL network the IP belongs:
|
124
|
+
If you don't explicitly specify the prefix (or the subnet mask),
|
125
|
+
IPAddress thinks you're dealing with host addresses and not with
|
126
|
+
networks. Therefore, the default prefix will be /32, or
|
127
|
+
255.255.255.255. For example:
|
131
128
|
|
132
|
-
|
133
|
-
|
134
|
-
#=> true
|
135
|
-
|
136
|
-
ip = IPAddress("172.16.10.1/24")
|
137
|
-
ip.b?
|
138
|
-
#=> true
|
129
|
+
# let's declare an host address
|
130
|
+
host = IPAddress::IPv4.new "10.1.1.1."
|
139
131
|
|
140
|
-
|
141
|
-
|
142
|
-
#=> true
|
143
|
-
|
144
|
-
These methods are only checking the address portion of an IP, and are
|
145
|
-
indipendent from its prefix.
|
132
|
+
The new created object will have prefix /32, which is the same
|
133
|
+
as we created the following:
|
146
134
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
==== Handling the IPv4 address
|
135
|
+
host = IPAddress::IPv4.new "10.1.1.1/32"
|
136
|
+
|
137
|
+
=== Handling the IPv4 address
|
151
138
|
|
152
139
|
Once created, you can obtain the attributes for an IPv4 object:
|
153
140
|
|
@@ -182,7 +169,7 @@ use IPv4#to_string
|
|
182
169
|
ip.to_string
|
183
170
|
#=> "172.16.10.l/24"
|
184
171
|
|
185
|
-
|
172
|
+
=== Changing netmask
|
186
173
|
|
187
174
|
You can set a new prefix (netmask) after creating an IPv4
|
188
175
|
object. For example:
|
@@ -200,7 +187,7 @@ using the IPv4#netmask= method
|
|
200
187
|
ip.to_string
|
201
188
|
#=> "172.16.10.1/30"
|
202
189
|
|
203
|
-
|
190
|
+
=== Working with networks, broadcasts and addresses
|
204
191
|
|
205
192
|
Some very important topics in dealing with IP addresses are the
|
206
193
|
concepts of +network+ and +broadcast+, as well as the addresses
|
@@ -221,7 +208,7 @@ This is very important because, for instance, IP "172.16.10.1/16" is
|
|
221
208
|
very different to the previous one, belonging to the very different
|
222
209
|
network "172.16.0.0/16".
|
223
210
|
|
224
|
-
|
211
|
+
==== Networks
|
225
212
|
|
226
213
|
With IPAddress it's very easy to calculate the network for an IP
|
227
214
|
address:
|
@@ -251,7 +238,7 @@ network or not:
|
|
251
238
|
ip2.network?
|
252
239
|
#=> true
|
253
240
|
|
254
|
-
|
241
|
+
==== Broadcast
|
255
242
|
|
256
243
|
The broadcast address is the contrary than the network number: where
|
257
244
|
the network number has all zeroes in the host portion, the broadcast
|
@@ -272,7 +259,7 @@ address:
|
|
272
259
|
bcast.to_string
|
273
260
|
#=> "172.16.10.255/24"
|
274
261
|
|
275
|
-
|
262
|
+
==== Addresses, ranges and iterators
|
276
263
|
|
277
264
|
So we see that the netmask essentially specifies a range for IP
|
278
265
|
addresses that are included in a network: all the addresses between
|
@@ -310,7 +297,7 @@ respectively the first and the last host address in the range
|
|
310
297
|
ip.last.to_string
|
311
298
|
#=> "172.16.10.254/24"
|
312
299
|
|
313
|
-
|
300
|
+
=== IP special formats
|
314
301
|
|
315
302
|
The IPAddress library provides a complete set of methods to access an
|
316
303
|
IPv4 address in special formats, such as binary, 32 bits unsigned int,
|
@@ -352,6 +339,53 @@ suitable to use in IPv4-IPv6 mapped addresses:
|
|
352
339
|
ip.to_ipv6
|
353
340
|
#=> "ac10:0a01"
|
354
341
|
|
342
|
+
=== Classful networks
|
343
|
+
|
344
|
+
IPAddress allows you to create and manipulate objects using the old
|
345
|
+
and deprecated (but apparently still popular) classful networks concept.
|
346
|
+
|
347
|
+
Classful networks and addresses don't have a prefix: their subnet mask
|
348
|
+
is univocally identified by their address, and therefore diveded in classes.
|
349
|
+
As per RFC 791, these classes are:
|
350
|
+
|
351
|
+
* Class A, from 0.0.0.0 to 127.255.255.255
|
352
|
+
* Class B, from 128.0.0.0 to 191.255.255.255
|
353
|
+
* Class C, from 192.0.0.0 to 255.255.255.255
|
354
|
+
|
355
|
+
Since classful networks here are only considered to calculate the default
|
356
|
+
prefix number, classes D and E are not considered.
|
357
|
+
|
358
|
+
To create a classful IP and prefix from an IP address, use the
|
359
|
+
IPv4::parse_classful method:
|
360
|
+
|
361
|
+
# classful ip
|
362
|
+
ip = IPAddress::IPv4::parse_classful "10.1.1.1"
|
363
|
+
|
364
|
+
ip.prefix
|
365
|
+
#=> 8
|
366
|
+
|
367
|
+
The method automatically created a new IPv4 object and assigned it
|
368
|
+
the correct prefix.
|
369
|
+
|
370
|
+
You can easily check which CLASSFUL network an IPv4 object belongs:
|
371
|
+
|
372
|
+
ip = IPAddress("10.0.0.1/24")
|
373
|
+
ip.a?
|
374
|
+
#=> true
|
375
|
+
|
376
|
+
ip = IPAddress("172.16.10.1/24")
|
377
|
+
ip.b?
|
378
|
+
#=> true
|
379
|
+
|
380
|
+
ip = IPAddress("192.168.1.1/30")
|
381
|
+
ip.c?
|
382
|
+
#=> true
|
383
|
+
|
384
|
+
Remember that these methods are only checking the address portion of an IP, and are
|
385
|
+
indipendent from its prefix, as classful networks have no concept of prefix.
|
386
|
+
|
387
|
+
For more information on CLASSFUL networks visit the
|
388
|
+
{Wikipedia page}[http://en.wikipedia.org/wiki/Classful_network]
|
355
389
|
|
356
390
|
=== Network design with IPAddress
|
357
391
|
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.7.0
|
data/lib/ipaddress.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
|
-
class << Math
|
1
|
+
class << Math # :nodoc:
|
2
2
|
def log2(n); log(n) / log(2); end
|
3
3
|
end
|
4
4
|
|
5
5
|
if RUBY_VERSION =~ /1\.8/
|
6
|
-
class Hash
|
6
|
+
class Hash # :nodoc:
|
7
7
|
alias :key :index
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
class Integer
|
11
|
+
class Integer # :nodoc:
|
12
12
|
def power_of_2?
|
13
13
|
Math::log2(self).to_i == Math::log2(self)
|
14
14
|
end
|
data/lib/ipaddress/ipv4.rb
CHANGED
@@ -42,30 +42,23 @@ module IPAddress;
|
|
42
42
|
#
|
43
43
|
# An IPv4 address can be expressed in any of the following forms:
|
44
44
|
#
|
45
|
-
# * "10.1.1.1/24": ip address and prefix
|
46
|
-
#
|
47
|
-
# * "10.1.1.1/255.255.255.0": ip address and netmask
|
48
|
-
#
|
49
|
-
#
|
50
|
-
# * "10.1.1.1": if the address alone is specified, the prefix will be
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
# It is advisable to use the syntactic shortcut provided with the
|
55
|
-
# IPAddress() method, as in all the examples below.
|
56
|
-
#
|
45
|
+
# * "10.1.1.1/24": ip +address+ and +prefix+. This is the common and
|
46
|
+
# suggested way to create an object .
|
47
|
+
# * "10.1.1.1/255.255.255.0": ip +address+ and +netmask+. Although
|
48
|
+
# convenient sometimes, this format is less clear than the previous
|
49
|
+
# one.
|
50
|
+
# * "10.1.1.1": if the address alone is specified, the prefix will be
|
51
|
+
# set as default 32, also known as the host prefix
|
52
|
+
#
|
57
53
|
# Examples:
|
58
54
|
#
|
59
|
-
# # These two
|
55
|
+
# # These two are the same
|
60
56
|
# ip = IPAddress::IPv4.new("10.0.0.1/24")
|
61
57
|
# ip = IPAddress("10.0.0.1/24")
|
62
58
|
#
|
63
|
-
# # These
|
64
|
-
# IPAddress
|
65
|
-
# IPAddress
|
66
|
-
# IPAddress("10.0.0.1")
|
67
|
-
# #=> #<IPAddress::IPv4:0xb7b1a438
|
68
|
-
# @octets=[10, 0, 0, 1], @address="10.0.0.1", @prefix=8>
|
59
|
+
# # These two are the same
|
60
|
+
# IPAddress::IPv4.new "10.0.0.1/8"
|
61
|
+
# IPAddress::IPv4.new "10.0.0.1/255.0.0.0"
|
69
62
|
#
|
70
63
|
def initialize(str)
|
71
64
|
ip, netmask = str.split("/")
|
@@ -88,12 +81,14 @@ module IPAddress;
|
|
88
81
|
raise ArgumentError, "Invalid netmask #{netmask}"
|
89
82
|
end
|
90
83
|
else # netmask is nil, reverting to defaul classful mask
|
91
|
-
@prefix =
|
84
|
+
@prefix = Prefix32.new(32)
|
92
85
|
end
|
93
86
|
|
94
87
|
# Array formed with the IP octets
|
95
88
|
@octets = @address.split(".").map{|i| i.to_i}
|
96
|
-
|
89
|
+
# 32 bits interger containing the address
|
90
|
+
@u32 = (@octets[0]<< 24) + (@octets[1]<< 16) + (@octets[2]<< 8) + (@octets[3])
|
91
|
+
|
97
92
|
end # def initialize
|
98
93
|
|
99
94
|
#
|
@@ -185,7 +180,6 @@ module IPAddress;
|
|
185
180
|
"#@address/#@prefix"
|
186
181
|
end
|
187
182
|
|
188
|
-
|
189
183
|
#
|
190
184
|
# Returns the prefix as a string in IP format
|
191
185
|
#
|
@@ -227,14 +221,15 @@ module IPAddress;
|
|
227
221
|
#
|
228
222
|
# ip = IPAddress("10.0.0.0/8")
|
229
223
|
#
|
230
|
-
# ip.
|
224
|
+
# ip.to_i
|
231
225
|
# #=> 167772160
|
232
226
|
#
|
233
|
-
def
|
234
|
-
|
227
|
+
def u32
|
228
|
+
@u32
|
235
229
|
end
|
236
|
-
alias_method :to_i, :
|
237
|
-
|
230
|
+
alias_method :to_i, :u32
|
231
|
+
alias_method :to_u32, :u32
|
232
|
+
|
238
233
|
#
|
239
234
|
# Returns the address portion of an IPv4 object
|
240
235
|
# in a network byte order format.
|
@@ -255,7 +250,7 @@ module IPAddress;
|
|
255
250
|
# a.puts binary_data
|
256
251
|
#
|
257
252
|
def data
|
258
|
-
@
|
253
|
+
[@u32].pack("N")
|
259
254
|
end
|
260
255
|
|
261
256
|
#
|
@@ -316,7 +311,7 @@ module IPAddress;
|
|
316
311
|
# #=> true
|
317
312
|
#
|
318
313
|
def network?
|
319
|
-
|
314
|
+
@u32 | @prefix.to_u32 == @prefix.to_u32
|
320
315
|
end
|
321
316
|
|
322
317
|
#
|
@@ -477,7 +472,7 @@ module IPAddress;
|
|
477
472
|
# #=> 8
|
478
473
|
#
|
479
474
|
def size
|
480
|
-
|
475
|
+
2 ** @prefix.host_prefix
|
481
476
|
end
|
482
477
|
|
483
478
|
#
|
@@ -507,7 +502,7 @@ module IPAddress;
|
|
507
502
|
# #=> 167772160
|
508
503
|
#
|
509
504
|
def network_u32
|
510
|
-
|
505
|
+
@u32 & @prefix.to_u32
|
511
506
|
end
|
512
507
|
|
513
508
|
#
|
@@ -519,7 +514,7 @@ module IPAddress;
|
|
519
514
|
# #=> 167772167
|
520
515
|
#
|
521
516
|
def broadcast_u32
|
522
|
-
|
517
|
+
network_u32 + size - 1
|
523
518
|
end
|
524
519
|
|
525
520
|
#
|
@@ -539,7 +534,23 @@ module IPAddress;
|
|
539
534
|
# #=> false
|
540
535
|
#
|
541
536
|
def include?(oth)
|
542
|
-
@prefix <= oth.prefix and network_u32 ==
|
537
|
+
@prefix <= oth.prefix and network_u32 == (oth.to_u32 & @prefix.to_u32)
|
538
|
+
end
|
539
|
+
|
540
|
+
#
|
541
|
+
# Checks if an IPv4 address objects belongs
|
542
|
+
# to a private network RFC1918
|
543
|
+
#
|
544
|
+
# Example:
|
545
|
+
#
|
546
|
+
# ip = IPAddress "10.1.1.1/24"
|
547
|
+
# ip.private?
|
548
|
+
# #=> true
|
549
|
+
#
|
550
|
+
def private?
|
551
|
+
[self.class.new("10.0.0.0/8"),
|
552
|
+
self.class.new("172.16.0.0/12"),
|
553
|
+
self.class.new("192.168.0.0/16")].any? {|i| i.include? self}
|
543
554
|
end
|
544
555
|
|
545
556
|
#
|
@@ -589,7 +600,7 @@ module IPAddress;
|
|
589
600
|
# Returns an array of IPAddress objects
|
590
601
|
#
|
591
602
|
def subnet(subnets=2)
|
592
|
-
unless (1..(2
|
603
|
+
unless (1..(2**@prefix.host_prefix)).include? subnets
|
593
604
|
raise ArgumentError, "Value #{subnets} out of range"
|
594
605
|
end
|
595
606
|
calculate_subnets(subnets)
|
@@ -746,13 +757,8 @@ module IPAddress;
|
|
746
757
|
# ip.to_string
|
747
758
|
# #=> "10.0.0.0/8"
|
748
759
|
#
|
749
|
-
def self.parse_u32(u32, prefix=
|
750
|
-
|
751
|
-
if prefix
|
752
|
-
self.new(ip+"/#{prefix}")
|
753
|
-
else
|
754
|
-
self.new(ip)
|
755
|
-
end
|
760
|
+
def self.parse_u32(u32, prefix=32)
|
761
|
+
self.new([u32].pack("N").unpack("C4").join(".")+"/#{prefix}")
|
756
762
|
end
|
757
763
|
|
758
764
|
#
|
@@ -768,12 +774,12 @@ module IPAddress;
|
|
768
774
|
# ip.to_string
|
769
775
|
# #=> "172.16.10.1/24"
|
770
776
|
#
|
771
|
-
def self.parse_data(str)
|
772
|
-
self.new
|
777
|
+
def self.parse_data(str, prefix=32)
|
778
|
+
self.new(str.unpack("C4").join(".")+"/#{prefix}")
|
773
779
|
end
|
774
780
|
|
775
781
|
#
|
776
|
-
#
|
782
|
+
# Extract an IPv4 address from a string and
|
777
783
|
# returns a new object
|
778
784
|
#
|
779
785
|
# Example:
|
@@ -872,20 +878,44 @@ module IPAddress;
|
|
872
878
|
end
|
873
879
|
end
|
874
880
|
|
881
|
+
#
|
882
|
+
# Creates a new IPv4 address object by parsing the
|
883
|
+
# address in a classful way.
|
884
|
+
#
|
885
|
+
# Classful addresses have a fixed netmask based on the
|
886
|
+
# class they belong to:
|
887
|
+
#
|
888
|
+
# * Class A, from 0.0.0.0 to 127.255.255.255
|
889
|
+
# * Class B, from 128.0.0.0 to 191.255.255.255
|
890
|
+
# * Class C, D and E, from 192.0.0.0 to 255.255.255.254
|
891
|
+
#
|
892
|
+
# Note that classes C, D and E will all have a default
|
893
|
+
# prefix of /24 or 255.255.255.0
|
894
|
+
#
|
895
|
+
# Example:
|
896
|
+
#
|
897
|
+
# ip = IPAddress::IPv4.parse_classful "10.0.0.1"
|
898
|
+
#
|
899
|
+
# ip.netmask
|
900
|
+
# #=> "255.0.0.0"
|
901
|
+
# ip.a?
|
902
|
+
# #=> true
|
903
|
+
#
|
904
|
+
def self.parse_classful(ip)
|
905
|
+
if IPAddress.valid_ipv4?(ip)
|
906
|
+
address = ip.strip
|
907
|
+
else
|
908
|
+
raise ArgumentError, "Invalid IP #{ip.inspect}"
|
909
|
+
end
|
910
|
+
prefix = CLASSFUL.find{|h,k| h === ("%.8b" % address.to_i)}.last
|
911
|
+
self.new "#{address}/#{prefix}"
|
912
|
+
end
|
913
|
+
|
875
914
|
#
|
876
915
|
# private methods
|
877
916
|
#
|
878
917
|
private
|
879
918
|
|
880
|
-
def bits_from_address(ip)
|
881
|
-
ip.split(".").map{|i| i.to_i}.pack("C4").unpack("B*").first
|
882
|
-
end
|
883
|
-
|
884
|
-
def prefix_from_ip(ip)
|
885
|
-
bits = bits_from_address(ip)
|
886
|
-
CLASSFUL.each {|reg,prefix| return Prefix32.new(prefix) if bits =~ reg}
|
887
|
-
end
|
888
|
-
|
889
919
|
def calculate_subnets(subnets)
|
890
920
|
po2 = subnets.closest_power_of_2
|
891
921
|
new_prefix = @prefix + Math::log2(po2).to_i
|
data/lib/ipaddress/ipv6.rb
CHANGED
@@ -88,6 +88,10 @@ module IPAddress;
|
|
88
88
|
#
|
89
89
|
def initialize(str)
|
90
90
|
ip, netmask = str.split("/")
|
91
|
+
|
92
|
+
if str =~ /:.+\./
|
93
|
+
raise ArgumentError, "Please use #{self.class}::Mapped for IPv4 mapped addresses"
|
94
|
+
end
|
91
95
|
|
92
96
|
if IPAddress.valid_ipv6?(ip)
|
93
97
|
@groups = self.class.groups(ip)
|
@@ -101,7 +105,6 @@ module IPAddress;
|
|
101
105
|
|
102
106
|
end # def initialize
|
103
107
|
|
104
|
-
|
105
108
|
#
|
106
109
|
# Returns the IPv6 address in uncompressed form:
|
107
110
|
#
|
@@ -303,7 +306,7 @@ module IPAddress;
|
|
303
306
|
# Returns the IPv6 address in a DNS reverse lookup
|
304
307
|
# string, as per RFC3172 and RFC2874.
|
305
308
|
#
|
306
|
-
# ip6 = IPAddress "3ffe:505:2::f"
|
309
|
+
# ip6 = IPAddress "3ffe:505:2::f"
|
307
310
|
#
|
308
311
|
# ip6.reverse
|
309
312
|
# #=> "f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.arpa"
|
@@ -312,7 +315,36 @@ module IPAddress;
|
|
312
315
|
to_hex.reverse.gsub(/./){|c| c+"."} + "ip6.arpa"
|
313
316
|
end
|
314
317
|
alias_method :arpa, :reverse
|
315
|
-
|
318
|
+
|
319
|
+
#
|
320
|
+
# Returns the network number in Unsigned 128bits format
|
321
|
+
#
|
322
|
+
# ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
|
323
|
+
#
|
324
|
+
# ip6.network_u128
|
325
|
+
# #=> 42540766411282592856903984951653826560
|
326
|
+
#
|
327
|
+
def network_u128
|
328
|
+
to_u128 & @prefix.to_u128
|
329
|
+
end
|
330
|
+
|
331
|
+
#
|
332
|
+
# Checks whether a subnet includes the given IP address.
|
333
|
+
#
|
334
|
+
# Example:
|
335
|
+
#
|
336
|
+
# ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
|
337
|
+
# addr = IPAddress "2001:db8::8:800:200c:1/128"
|
338
|
+
#
|
339
|
+
# ip6.include? addr
|
340
|
+
# #=> true
|
341
|
+
#
|
342
|
+
# ip6.include? IPAddress("2001:db8:1::8:800:200c:417a/76")
|
343
|
+
# #=> false
|
344
|
+
#
|
345
|
+
def include?(oth)
|
346
|
+
@prefix <= oth.prefix and network_u128 == self.class.new(oth.address+"/#@prefix").network_u128
|
347
|
+
end
|
316
348
|
|
317
349
|
#
|
318
350
|
# Compressed form of the IPv6 address
|
@@ -350,7 +382,7 @@ module IPAddress;
|
|
350
382
|
# See IPAddress::IPv6::Mapped for more information
|
351
383
|
#
|
352
384
|
def mapped?
|
353
|
-
|
385
|
+
to_u128 >> 32 == 0xffff
|
354
386
|
end
|
355
387
|
|
356
388
|
#
|
@@ -674,13 +706,29 @@ module IPAddress;
|
|
674
706
|
attr_reader :ipv4
|
675
707
|
|
676
708
|
#
|
677
|
-
# Creates a new IPv6
|
709
|
+
# Creates a new IPv6 IPv4-mapped address
|
678
710
|
#
|
679
711
|
# ip6 = IPAddress::IPv6::Mapped.new "::ffff:172.16.10.1/128"
|
680
712
|
#
|
713
|
+
# ipv6.ipv4.class
|
714
|
+
# #=> IPAddress::IPv4
|
715
|
+
#
|
716
|
+
# An IPv6 IPv4-mapped address can also be created using the
|
717
|
+
# IPv6 only format of the address:
|
718
|
+
#
|
719
|
+
# ip6 = IPAddress::IPv6::Mapped.new "::0d01:4403"
|
720
|
+
#
|
721
|
+
# ip6.to_s
|
722
|
+
# #=> "::ffff:13.1.68.3"
|
723
|
+
#
|
681
724
|
def initialize(str)
|
682
725
|
string, netmask = str.split("/")
|
683
|
-
|
726
|
+
if string =~ /\./ # IPv4 in dotted decimal form
|
727
|
+
@ipv4 = IPAddress::IPv4.extract(string)
|
728
|
+
else # IPv4 in hex form
|
729
|
+
groups = IPAddress::IPv6.groups(string)
|
730
|
+
@ipv4 = IPAddress::IPv4.parse_u32((groups[-2]<< 16)+groups[-1])
|
731
|
+
end
|
684
732
|
super("::ffff:#{@ipv4.to_ipv6}/#{netmask}")
|
685
733
|
end
|
686
734
|
|
@@ -688,11 +736,10 @@ module IPAddress;
|
|
688
736
|
# Similar to IPv6#to_s, but prints out the IPv4 address
|
689
737
|
# in dotted decimal format
|
690
738
|
#
|
691
|
-
#
|
692
739
|
# ip6 = IPAddress "::ffff:172.16.10.1/128"
|
693
740
|
#
|
694
741
|
# ip6.to_s
|
695
|
-
# #=> "::
|
742
|
+
# #=> "::ffff:172.16.10.1"
|
696
743
|
#
|
697
744
|
def to_s
|
698
745
|
"::ffff:#{@ipv4.address}"
|
@@ -705,8 +752,8 @@ module IPAddress;
|
|
705
752
|
#
|
706
753
|
# ip6 = IPAddress "::ffff:172.16.10.1/128"
|
707
754
|
#
|
708
|
-
# ip6.
|
709
|
-
# #=> "::
|
755
|
+
# ip6.to_string
|
756
|
+
# #=> "::ffff:172.16.10.1/128"
|
710
757
|
#
|
711
758
|
def to_string
|
712
759
|
"::ffff:#{@ipv4.address}/#@prefix"
|
data/lib/ipaddress/prefix.rb
CHANGED
@@ -83,6 +83,8 @@ module IPAddress
|
|
83
83
|
|
84
84
|
class Prefix32 < Prefix
|
85
85
|
|
86
|
+
IN4MASK = 0xffffffff
|
87
|
+
|
86
88
|
#
|
87
89
|
# Creates a new prefix object for 32 bits IPv4 addresses
|
88
90
|
#
|
@@ -91,11 +93,24 @@ module IPAddress
|
|
91
93
|
#
|
92
94
|
def initialize(num)
|
93
95
|
unless (1..32).include? num
|
94
|
-
raise ArgumentError, "Prefix must be in range 1..
|
96
|
+
raise ArgumentError, "Prefix must be in range 1..32, got: #{num}"
|
95
97
|
end
|
96
98
|
super(num)
|
97
99
|
end
|
98
100
|
|
101
|
+
#
|
102
|
+
# Returns the length of the host portion
|
103
|
+
# of a netmask.
|
104
|
+
#
|
105
|
+
# prefix = Prefix32.new 24
|
106
|
+
#
|
107
|
+
# prefix.host_prefix
|
108
|
+
# #=> 8
|
109
|
+
#
|
110
|
+
def host_prefix
|
111
|
+
32 - @prefix
|
112
|
+
end
|
113
|
+
|
99
114
|
#
|
100
115
|
# Transforms the prefix into a string of bits
|
101
116
|
# representing the netmask
|
@@ -106,7 +121,7 @@ module IPAddress
|
|
106
121
|
# #=> "11111111111111111111111100000000"
|
107
122
|
#
|
108
123
|
def bits
|
109
|
-
|
124
|
+
to_u32.to_s(2)
|
110
125
|
end
|
111
126
|
|
112
127
|
#
|
@@ -145,7 +160,7 @@ module IPAddress
|
|
145
160
|
# #=> 4294967040
|
146
161
|
#
|
147
162
|
def to_u32
|
148
|
-
|
163
|
+
(IN4MASK >> host_prefix) << host_prefix
|
149
164
|
end
|
150
165
|
|
151
166
|
#
|
@@ -185,7 +200,7 @@ module IPAddress
|
|
185
200
|
def self.parse_netmask(netmask)
|
186
201
|
octets = netmask.split(".").map{|i| i.to_i}
|
187
202
|
num = octets.pack("C"*octets.size).unpack("B*").first.count "1"
|
188
|
-
return
|
203
|
+
return self.new(num)
|
189
204
|
end
|
190
205
|
|
191
206
|
end # class Prefix32 < Prefix
|
@@ -229,7 +244,7 @@ module IPAddress
|
|
229
244
|
# #=> 340282366920938463444927863358058659840
|
230
245
|
#
|
231
246
|
def to_u128
|
232
|
-
|
247
|
+
bits.to_i(2)
|
233
248
|
end
|
234
249
|
|
235
250
|
end # class Prefix123 < Prefix
|
data/test/ipaddress/ipv4_test.rb
CHANGED
@@ -6,8 +6,8 @@ class IPv4Test < Test::Unit::TestCase
|
|
6
6
|
@klass = IPAddress::IPv4
|
7
7
|
|
8
8
|
@valid_ipv4 = {
|
9
|
-
"10.0.0.0" => ["10.0.0.0",
|
10
|
-
"10.0.0.1" => ["10.0.0.1",
|
9
|
+
"10.0.0.0" => ["10.0.0.0", 32],
|
10
|
+
"10.0.0.1" => ["10.0.0.1", 32],
|
11
11
|
"10.0.0.1/24" => ["10.0.0.1", 24],
|
12
12
|
"10.0.0.1/255.255.255.0" => ["10.0.0.1", 24]}
|
13
13
|
|
@@ -50,6 +50,11 @@ class IPv4Test < Test::Unit::TestCase
|
|
50
50
|
@class_a = @klass.new("10.0.0.1/8")
|
51
51
|
@class_b = @klass.new("172.16.0.1/16")
|
52
52
|
@class_c = @klass.new("192.168.0.1/24")
|
53
|
+
|
54
|
+
@classful = {
|
55
|
+
"10.1.1.1" => 8,
|
56
|
+
"150.1.1.1" => 16,
|
57
|
+
"200.1.1.1" => 24 }
|
53
58
|
|
54
59
|
end
|
55
60
|
|
@@ -80,6 +85,7 @@ class IPv4Test < Test::Unit::TestCase
|
|
80
85
|
end
|
81
86
|
ip = @klass.new("10.10.0.0")
|
82
87
|
assert_instance_of IPAddress::Prefix32, ip.prefix
|
88
|
+
assert_equal 32, ip.prefix.to_i
|
83
89
|
end
|
84
90
|
|
85
91
|
def test_attributes
|
@@ -231,6 +237,21 @@ class IPv4Test < Test::Unit::TestCase
|
|
231
237
|
assert_equal false, ip.include?(@klass.new("13.16.0.0/32"))
|
232
238
|
end
|
233
239
|
|
240
|
+
def test_method_private?
|
241
|
+
assert_equal true, @klass.new("192.168.10.50/24").private?
|
242
|
+
assert_equal true, @klass.new("192.168.10.50/16").private?
|
243
|
+
assert_equal true, @klass.new("172.16.77.40/24").private?
|
244
|
+
assert_equal true, @klass.new("172.16.10.50/14").private?
|
245
|
+
assert_equal true, @klass.new("10.10.10.10/10").private?
|
246
|
+
assert_equal true, @klass.new("10.0.0.0/8").private?
|
247
|
+
assert_equal false, @klass.new("192.168.10.50/12").private?
|
248
|
+
assert_equal false, @klass.new("3.3.3.3").private?
|
249
|
+
assert_equal false, @klass.new("10.0.0.0/7").private?
|
250
|
+
assert_equal false, @klass.new("172.32.0.0/12").private?
|
251
|
+
assert_equal false, @klass.new("172.16.0.0/11").private?
|
252
|
+
assert_equal false, @klass.new("192.0.0.2/24").private?
|
253
|
+
end
|
254
|
+
|
234
255
|
def test_method_octet
|
235
256
|
assert_equal 172, @ip[0]
|
236
257
|
assert_equal 16, @ip[1]
|
@@ -438,7 +459,16 @@ class IPv4Test < Test::Unit::TestCase
|
|
438
459
|
ip = @klass.parse_data "\254\020\n\001"
|
439
460
|
assert_instance_of @klass, ip
|
440
461
|
assert_equal "172.16.10.1", ip.address
|
441
|
-
assert_equal "172.16.10.1/
|
462
|
+
assert_equal "172.16.10.1/32", ip.to_string
|
463
|
+
end
|
464
|
+
|
465
|
+
def test_classmethod_parse_classful
|
466
|
+
@classful.each do |ip,prefix|
|
467
|
+
res = @klass.parse_classful(ip)
|
468
|
+
assert_equal prefix, res.prefix
|
469
|
+
assert_equal "#{ip}/#{prefix}", res.to_string
|
470
|
+
end
|
471
|
+
assert_raise(ArgumentError){ @klass.parse_classful("192.168.256.257") }
|
442
472
|
end
|
443
473
|
|
444
474
|
end # class IPv4Test
|
data/test/ipaddress/ipv6_test.rb
CHANGED
@@ -39,7 +39,6 @@ class IPv6Test < Test::Unit::TestCase
|
|
39
39
|
@hex = "20010db80000000000080800200c417a"
|
40
40
|
end
|
41
41
|
|
42
|
-
|
43
42
|
def test_attribute_address
|
44
43
|
addr = "2001:0db8:0000:0000:0008:0800:200c:417a"
|
45
44
|
assert_equal addr, @ip.address
|
@@ -54,6 +53,10 @@ class IPv6Test < Test::Unit::TestCase
|
|
54
53
|
assert_raise(ArgumentError) {@klass.new ip}
|
55
54
|
end
|
56
55
|
assert_equal 64, @ip.prefix
|
56
|
+
|
57
|
+
assert_raise(ArgumentError) {
|
58
|
+
@klass.new "::10.1.1.1"
|
59
|
+
}
|
57
60
|
end
|
58
61
|
|
59
62
|
def test_attribute_groups
|
@@ -88,6 +91,8 @@ class IPv6Test < Test::Unit::TestCase
|
|
88
91
|
|
89
92
|
def test_method_mapped?
|
90
93
|
assert_equal false, @ip.mapped?
|
94
|
+
ip6 = @klass.new "::ffff:1234:5678"
|
95
|
+
assert_equal true, ip6.mapped?
|
91
96
|
end
|
92
97
|
|
93
98
|
def test_method_literal
|
@@ -106,6 +111,29 @@ class IPv6Test < Test::Unit::TestCase
|
|
106
111
|
assert_equal false, @ip.network?
|
107
112
|
end
|
108
113
|
|
114
|
+
def test_method_network_u128
|
115
|
+
assert_equal 42540766411282592856903984951653826560, @ip.network_u128
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_method_include?
|
119
|
+
assert_equal true, @ip.include?(@ip)
|
120
|
+
# test prefix on same address
|
121
|
+
included = @klass.new "2001:db8::8:800:200c:417a/128"
|
122
|
+
not_included = @klass.new "2001:db8::8:800:200c:417a/46"
|
123
|
+
assert_equal true, @ip.include?(included)
|
124
|
+
assert_equal false, @ip.include?(not_included)
|
125
|
+
# test address on same prefix
|
126
|
+
included = @klass.new "2001:db8::8:800:200c:0/64"
|
127
|
+
not_included = @klass.new "2001:db8:1::8:800:200c:417a/64"
|
128
|
+
assert_equal true, @ip.include?(included)
|
129
|
+
assert_equal false, @ip.include?(not_included)
|
130
|
+
# general test
|
131
|
+
included = @klass.new "2001:db8::8:800:200c:1/128"
|
132
|
+
not_included = @klass.new "2001:db8:1::8:800:200c:417a/76"
|
133
|
+
assert_equal true, @ip.include?(included)
|
134
|
+
assert_equal false, @ip.include?(not_included)
|
135
|
+
end
|
136
|
+
|
109
137
|
def test_method_to_hex
|
110
138
|
assert_equal @hex, @ip.to_hex
|
111
139
|
end
|
@@ -128,20 +156,11 @@ class IPv6Test < Test::Unit::TestCase
|
|
128
156
|
assert_equal str, @ip.data
|
129
157
|
end
|
130
158
|
|
131
|
-
|
132
159
|
def test_method_reverse
|
133
160
|
str = "f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.arpa"
|
134
161
|
assert_equal str, @klass.new("3ffe:505:2::f").reverse
|
135
162
|
end
|
136
163
|
|
137
|
-
# def test_ip6_int
|
138
|
-
# assert_equal("f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.int", IPAddress("3ffe:505:2::f").ip6_int)
|
139
|
-
# assert_raises(ArgumentError) {
|
140
|
-
# IPAddress("192.168.2.1").ip6_int
|
141
|
-
# }
|
142
|
-
# end
|
143
|
-
|
144
|
-
|
145
164
|
def test_method_compressed
|
146
165
|
assert_equal "1:1:1::1", @klass.new("1:1:1:0:0:0:0:1").compressed
|
147
166
|
assert_equal "1:0:1::1", @klass.new("1:0:1:0:0:0:0:1").compressed
|
@@ -287,6 +306,15 @@ class IPv6MappedTest < Test::Unit::TestCase
|
|
287
306
|
@valid_mapped = {'::13.1.68.3' => 281470899930115,
|
288
307
|
'0:0:0:0:0:ffff:129.144.52.38' => 281472855454758,
|
289
308
|
'::ffff:129.144.52.38' => 281472855454758}
|
309
|
+
|
310
|
+
@valid_mapped_ipv6 = {'::0d01:4403' => 281470899930115,
|
311
|
+
'0:0:0:0:0:ffff:8190:3426' => 281472855454758,
|
312
|
+
'::ffff:8190:3426' => 281472855454758}
|
313
|
+
|
314
|
+
@valid_mapped_ipv6_conversion = {'::0d01:4403' => "13.1.68.3",
|
315
|
+
'0:0:0:0:0:ffff:8190:3426' => "129.144.52.38",
|
316
|
+
'::ffff:8190:3426' => "129.144.52.38"}
|
317
|
+
|
290
318
|
end
|
291
319
|
|
292
320
|
def test_initialize
|
@@ -296,6 +324,16 @@ class IPv6MappedTest < Test::Unit::TestCase
|
|
296
324
|
assert_nothing_raised {@klass.new ip}
|
297
325
|
assert_equal u128, @klass.new(ip).to_u128
|
298
326
|
end
|
327
|
+
@valid_mapped_ipv6.each do |ip, u128|
|
328
|
+
assert_nothing_raised {@klass.new ip}
|
329
|
+
assert_equal u128, @klass.new(ip).to_u128
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
def test_mapped_from_ipv6_conversion
|
334
|
+
@valid_mapped_ipv6_conversion.each do |ip6,ip4|
|
335
|
+
assert_equal ip4, @klass.new(ip6).ipv4.to_s
|
336
|
+
end
|
299
337
|
end
|
300
338
|
|
301
339
|
def test_attributes
|
metadata
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ipaddress
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 3
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 7
|
9
|
+
- 0
|
10
|
+
version: 0.7.0
|
5
11
|
platform: ruby
|
6
12
|
authors:
|
7
13
|
- Marco Ceresa
|
@@ -9,7 +15,7 @@ autorequire:
|
|
9
15
|
bindir: bin
|
10
16
|
cert_chain: []
|
11
17
|
|
12
|
-
date: 2010-
|
18
|
+
date: 2010-09-08 00:00:00 +02:00
|
13
19
|
default_executable:
|
14
20
|
dependencies: []
|
15
21
|
|
@@ -51,21 +57,27 @@ rdoc_options:
|
|
51
57
|
require_paths:
|
52
58
|
- lib
|
53
59
|
required_ruby_version: !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
54
61
|
requirements:
|
55
62
|
- - ">="
|
56
63
|
- !ruby/object:Gem::Version
|
64
|
+
hash: 3
|
65
|
+
segments:
|
66
|
+
- 0
|
57
67
|
version: "0"
|
58
|
-
version:
|
59
68
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
60
70
|
requirements:
|
61
71
|
- - ">="
|
62
72
|
- !ruby/object:Gem::Version
|
73
|
+
hash: 3
|
74
|
+
segments:
|
75
|
+
- 0
|
63
76
|
version: "0"
|
64
|
-
version:
|
65
77
|
requirements: []
|
66
78
|
|
67
79
|
rubyforge_project:
|
68
|
-
rubygems_version: 1.3.
|
80
|
+
rubygems_version: 1.3.7
|
69
81
|
signing_key:
|
70
82
|
specification_version: 3
|
71
83
|
summary: IPv4/IPv6 addresses manipulation library
|