ipaddress 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|