ipadmin 0.1.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/README +228 -0
- data/lib/ip_admin.rb +2317 -0
- data/tests/cidr_table_test.rb +177 -0
- data/tests/cidr_test.rb +96 -0
- data/tests/functions_test.rb +154 -0
- data/tests/ipaddr_test.rb +53 -0
- metadata +45 -0
data/README
ADDED
@@ -0,0 +1,228 @@
|
|
1
|
+
|
2
|
+
Copyright (c) 2006 Dustin Spinhirne <dspinhir@yahoo.com>
|
3
|
+
Licensed under the same terms as Ruby, No Warranty is provided.
|
4
|
+
|
5
|
+
|
6
|
+
Comments are welcome. Please include 'Ruby - IPAdmin' in the title of
|
7
|
+
any emails.
|
8
|
+
|
9
|
+
Dustin Spinhirne
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
=Examples:
|
16
|
+
|
17
|
+
|
18
|
+
require 'rubygems'
|
19
|
+
require_gem 'ipadmin'
|
20
|
+
|
21
|
+
|
22
|
+
#============================================================================#
|
23
|
+
# IPAdmin::IPAddr
|
24
|
+
#============================================================================#
|
25
|
+
puts "IPAdmin::IPAddr"
|
26
|
+
|
27
|
+
begin
|
28
|
+
ip4 = IPAdmin::IPAddr.new(:IPAddr => '192.168.1.1/24')
|
29
|
+
ip4 = IPAdmin::IPAddr.new(:IPAddr => '192.168.1.1',
|
30
|
+
:Netmask => '255.255.255.0',
|
31
|
+
:Tag => {'test' => 'ip4 tag'})
|
32
|
+
ip6 = IPAdmin::IPAddr.new(:IPAddr => 'fec0::1/64')
|
33
|
+
rescue Exception
|
34
|
+
puts 'oops'
|
35
|
+
exit
|
36
|
+
end
|
37
|
+
|
38
|
+
puts ip4.tag['test']
|
39
|
+
ip4.tag['test'] = 'modified ip4 tag'
|
40
|
+
puts ip4.tag['test']
|
41
|
+
|
42
|
+
puts "base addr #{ip4.base()}"
|
43
|
+
puts "base addr #{ip6.base()}"
|
44
|
+
|
45
|
+
puts "bcast addr #{ip4.broadcast()}"
|
46
|
+
|
47
|
+
puts "description #{ip4.desc()}"
|
48
|
+
puts "description #{ip6.desc()}"
|
49
|
+
|
50
|
+
puts "extended hostmask #{ip4.hostmask_ext()}"
|
51
|
+
puts "extended netmask #{ip4.netmask_ext()}"
|
52
|
+
|
53
|
+
puts "ip addr #{ip4.ip()}"
|
54
|
+
puts "ip addr #{ip6.ip()}"
|
55
|
+
|
56
|
+
puts "netmask in bits #{ip4.netmask()}"
|
57
|
+
puts "netmask in bits #{ip6.netmask()}"
|
58
|
+
|
59
|
+
print "\n\n\n"
|
60
|
+
#=====================================#
|
61
|
+
#
|
62
|
+
#=====================================#
|
63
|
+
|
64
|
+
|
65
|
+
#============================================================================#
|
66
|
+
# IPAdmin::CIDR
|
67
|
+
#============================================================================#
|
68
|
+
puts "IPAdmin::CIDR"
|
69
|
+
|
70
|
+
begin
|
71
|
+
cidr4 = IPAdmin::CIDR.new(:CIDR => '192.168.1.0/24')
|
72
|
+
cidr4 = IPAdmin::CIDR.new(:CIDR => '192.168.1.0',
|
73
|
+
:Netmask => '255.255.255.0',
|
74
|
+
:Tag => {'test' => 'cidr4 tag'})
|
75
|
+
cidr6 = IPAdmin::CIDR.new(:CIDR => 'fec0::0/64')
|
76
|
+
rescue Exception
|
77
|
+
puts 'oops'
|
78
|
+
exit
|
79
|
+
end
|
80
|
+
|
81
|
+
puts cidr4.tag['test']
|
82
|
+
cidr4.tag['test'] = 'modified cidr4 tag'
|
83
|
+
puts cidr4.tag['test']
|
84
|
+
|
85
|
+
puts "cidr4 contains ip4" if ( cidr4.contains(ip4) )
|
86
|
+
puts "cidr6 contains ip6" if ( cidr6.contains(ip6) )
|
87
|
+
|
88
|
+
puts "description #{cidr4.desc()}"
|
89
|
+
puts "description #{cidr6.desc()}"
|
90
|
+
|
91
|
+
puts "extended hostmask #{cidr4.hostmask_ext()}"
|
92
|
+
puts "extended netmask #{cidr4.netmask_ext()}"
|
93
|
+
|
94
|
+
puts "netmask in bits #{cidr4.netmask()}"
|
95
|
+
puts "netmask in bits #{cidr6.netmask()}"
|
96
|
+
|
97
|
+
puts "network address #{cidr4.network()}"
|
98
|
+
puts "network address #{cidr6.network()}"
|
99
|
+
|
100
|
+
puts "1st ip is #{cidr4.nth(:Index => 1)}"
|
101
|
+
puts "1st ip is #{(cidr6.nth(:Index => 1, :Objectify => 1)).desc}"
|
102
|
+
|
103
|
+
puts "cidr4 size is #{cidr4.size()}"
|
104
|
+
puts "cidr6 size is #{cidr6.size()}"
|
105
|
+
|
106
|
+
cidr4.enumerate(:Limit => 4, :BitStep => 32) {|addr| puts addr}
|
107
|
+
cidr6.enumerate(:Limit => 4, :BitStep => 32, :Objectify => 1) {|addr| puts addr.desc()}
|
108
|
+
|
109
|
+
cidr4.subnet(:Subnet => 28, :MinCount => 3) {|cidr| puts cidr.desc()}
|
110
|
+
cidr6.subnet(:Subnet => 66, :MinCount => 4) {|cidr| puts cidr.desc()}
|
111
|
+
|
112
|
+
print "\n\n\n"
|
113
|
+
#=====================================#
|
114
|
+
#
|
115
|
+
#=====================================#
|
116
|
+
|
117
|
+
|
118
|
+
#============================================================================#
|
119
|
+
# IPAdmin::CIDRTable
|
120
|
+
#============================================================================#
|
121
|
+
puts "IPAdmin::CIDRTable"
|
122
|
+
|
123
|
+
cidr4_1 = IPAdmin::CIDR.new(:CIDR => '192.168.1.0/24')
|
124
|
+
cidr4_2 = IPAdmin::CIDR.new(:CIDR => '10.1.0.0/24')
|
125
|
+
cidr4_3 = IPAdmin::CIDR.new(:CIDR => '192.168.1.0/26')
|
126
|
+
cidr4_4 =IPAdmin::CIDR.new(:CIDR => '192.168.1.0/30')
|
127
|
+
cidr4_5 = IPAdmin::CIDR.new(:CIDR => '192.168.1.64/26')
|
128
|
+
cidr4_6 = IPAdmin::CIDR.new(:CIDR => '192.168.1.128/26')
|
129
|
+
cidr4_7 = IPAdmin::CIDR.new(:CIDR => '192.168.1.192/26')
|
130
|
+
|
131
|
+
cidr6_1 = IPAdmin::CIDR.new(:CIDR => 'fec0::/10')
|
132
|
+
cidr6_2 = IPAdmin::CIDR.new(:CIDR => 'fe80::/10')
|
133
|
+
cidr6_3 = IPAdmin::CIDR.new(:CIDR => 'fec0::/64')
|
134
|
+
cidr6_4 =IPAdmin::CIDR.new(:CIDR => 'fec0::/126')
|
135
|
+
|
136
|
+
begin
|
137
|
+
table4 = IPAdmin::CIDRTable.new(4)
|
138
|
+
table6 = IPAdmin::CIDRTable.new(6)
|
139
|
+
rescue Exception
|
140
|
+
puts 'oops'
|
141
|
+
exit
|
142
|
+
end
|
143
|
+
|
144
|
+
table4.add_cidr(cidr4_1)
|
145
|
+
table4.add_cidr(cidr4_2)
|
146
|
+
table4.add_cidr(cidr4_3)
|
147
|
+
table4.add_cidr(cidr4_4)
|
148
|
+
table4.add_cidr(cidr4_5)
|
149
|
+
table4.add_cidr(cidr4_6)
|
150
|
+
table4.add_cidr(cidr4_7)
|
151
|
+
|
152
|
+
table6.add_cidr(cidr6_1)
|
153
|
+
table6.add_cidr(cidr6_2)
|
154
|
+
table6.add_cidr(cidr6_3)
|
155
|
+
table6.add_cidr(cidr6_4)
|
156
|
+
|
157
|
+
puts "ip4 belongs in #{table4.find_ip(ip4).desc}"
|
158
|
+
puts "ip6 belongs in #{table6.find_ip(ip6).desc}"
|
159
|
+
|
160
|
+
puts "all blocks that can hold a /27"
|
161
|
+
list4 = table4.find_space(:Size => 27)
|
162
|
+
list4.each do |cidr|
|
163
|
+
puts " #{cidr.desc()}"
|
164
|
+
end
|
165
|
+
|
166
|
+
puts "first block that can hold a /64"
|
167
|
+
list6 = table6.find_space(:Size => 64, :Limit => 1)
|
168
|
+
list6.each do |cidr|
|
169
|
+
puts " #{cidr.desc()}"
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
puts 'dump table4...'
|
174
|
+
dump4 = table4.dump
|
175
|
+
dump4.each do |cidr|
|
176
|
+
puts " #{cidr.desc}"
|
177
|
+
end
|
178
|
+
puts 'dump table6...'
|
179
|
+
dump6 = table6.dump
|
180
|
+
dump6.each do |cidr|
|
181
|
+
puts " #{cidr.desc}"
|
182
|
+
end
|
183
|
+
|
184
|
+
print "\n\n\n"
|
185
|
+
#=====================================#
|
186
|
+
#
|
187
|
+
#=====================================#
|
188
|
+
|
189
|
+
|
190
|
+
#============================================================================#
|
191
|
+
# IPAdmin Misc Methods
|
192
|
+
#============================================================================#
|
193
|
+
puts "Misc Methods"
|
194
|
+
|
195
|
+
puts "192.168.1.0 is valid" if ( IPAdmin.validate_ipv4_addr('192.168.1.0') )
|
196
|
+
puts "192.168.1.0 is valid" if ( IPAdmin.validate_ipv6_addr('fec0::0') )
|
197
|
+
|
198
|
+
puts "255.255.255.0 is valid" if (IPAdmin.validate_ipv4_netmask('255.255.255.0') )
|
199
|
+
puts "/24 is valid" if ( IPAdmin.validate_ipv4_netmask(24) )
|
200
|
+
puts "/64 is valid" if ( IPAdmin.validate_ipv6_netmask(64) )
|
201
|
+
|
202
|
+
cidr4_1 = IPAdmin::CIDR.new(:CIDR => '192.168.1.0/24')
|
203
|
+
cidr4_2 = IPAdmin::CIDR.new(:CIDR => '192.168.1.0/25')
|
204
|
+
cidr6_1 = IPAdmin::CIDR.new(:CIDR => 'fec0::0/10')
|
205
|
+
cidr6_2 = IPAdmin::CIDR.new(:CIDR => 'fec0::0/64')
|
206
|
+
|
207
|
+
comp1 = IPAdmin.compare_cidr(cidr4_1,cidr4_2)
|
208
|
+
comp2 = IPAdmin.compare_cidr(cidr6_1,cidr6_2)
|
209
|
+
puts "#{(comp1[0]).desc} is the supernet of #{(comp1[1]).desc}"
|
210
|
+
puts "#{(comp2[0]).desc} is the supernet of #{(comp2[1]).desc}"
|
211
|
+
|
212
|
+
cidr4_1 = IPAdmin::CIDR.new(:CIDR => '192.168.1.0/24')
|
213
|
+
cidr4_2 = IPAdmin::CIDR.new(:CIDR => '192.168.0.0/24')
|
214
|
+
cidr6_1 = IPAdmin::CIDR.new(:CIDR => 'fec0::0/128')
|
215
|
+
cidr6_2 = IPAdmin::CIDR.new(:CIDR => 'fec0::1/128')
|
216
|
+
|
217
|
+
puts "192.168.1.0/24 and 192.168.0.0/24 merge into #{IPAdmin.merge_cidr([cidr4_1,cidr4_2]).desc}"
|
218
|
+
puts "fec0::0/128 and fec0::1/128 merge into #{IPAdmin.merge_cidr([cidr6_1,cidr6_2]).desc}"
|
219
|
+
|
220
|
+
print "\n\n\n"
|
221
|
+
#=====================================#
|
222
|
+
#
|
223
|
+
#=====================================#
|
224
|
+
|
225
|
+
|
226
|
+
|
227
|
+
|
228
|
+
|
data/lib/ip_admin.rb
ADDED
@@ -0,0 +1,2317 @@
|
|
1
|
+
module IPAdmin
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
#============================================================================#
|
6
|
+
# compare_cidr()
|
7
|
+
#============================================================================#
|
8
|
+
|
9
|
+
# Compare cidr addresses of two IPAdmin::CIDR objects.
|
10
|
+
# - Arguments:
|
11
|
+
# * Two IPAdmin::CIDR objects
|
12
|
+
#
|
13
|
+
# - Returns:
|
14
|
+
# * if one object is a subnet of another, then return an array in order of
|
15
|
+
# [supernet,subnet]
|
16
|
+
# * if both are equal, return 1
|
17
|
+
# * if neither is a subnet of the other, return nil
|
18
|
+
#
|
19
|
+
def compare_cidr(cidr1,cidr2)
|
20
|
+
|
21
|
+
# we only accept CIDR objects
|
22
|
+
unless ( (cidr1.kind_of? IPAdmin::CIDR)&&(cidr2.kind_of? IPAdmin::CIDR) )
|
23
|
+
raise "Expected IPAdmin::CIDR, but #{options.class} provided."
|
24
|
+
end
|
25
|
+
|
26
|
+
# make sure both are same version
|
27
|
+
unless (cidr1.version == cidr2.version )
|
28
|
+
raise "Provider CIDR objects are incompatible: " +
|
29
|
+
"#{cidr1.desc}/ and #{cidr2.desc}."
|
30
|
+
end
|
31
|
+
|
32
|
+
network1 = cidr1.packed_network
|
33
|
+
network2 = cidr2.packed_network
|
34
|
+
netmask1 = cidr1.packed_netmask
|
35
|
+
netmask2 = cidr2.packed_netmask
|
36
|
+
|
37
|
+
|
38
|
+
# make sure cidr's arent equal. return 1's if they are
|
39
|
+
if ( (network1 == network2) && (netmask1 == netmask2) )
|
40
|
+
return(1)
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
# whichever netmask is smaller will be the supernet
|
45
|
+
# if we '&' both networks by the supernet, and they are
|
46
|
+
# equal, then the supernet is the parent of the other network
|
47
|
+
if (netmask1 > netmask2)
|
48
|
+
if ( (netmask2 & network1) == (netmask2 & network2) )
|
49
|
+
supernet = cidr2
|
50
|
+
subnet = cidr1
|
51
|
+
end
|
52
|
+
|
53
|
+
else
|
54
|
+
if ( (netmask1 & network1) == (netmask1 & network2) )
|
55
|
+
supernet = cidr1
|
56
|
+
subnet = cidr2
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
if (supernet)
|
63
|
+
return([supernet,subnet])
|
64
|
+
else
|
65
|
+
return(nil)
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
module_function :compare_cidr
|
70
|
+
|
71
|
+
#=====================================#
|
72
|
+
#
|
73
|
+
#=====================================#
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
#============================================================================#
|
78
|
+
# merge_cidr()
|
79
|
+
#============================================================================#
|
80
|
+
|
81
|
+
# Merge (supernet) contiguous IPAdmin::CIDR blocks into a single block.
|
82
|
+
# Blocks must be contiguous, and must be able to form a single supernet.
|
83
|
+
#
|
84
|
+
# - Arguments:
|
85
|
+
# * array of IPAdmin::CIDR objects to merge
|
86
|
+
#
|
87
|
+
# - Returns:
|
88
|
+
# * IPAdmin::CIDR object
|
89
|
+
#
|
90
|
+
def merge_cidr(cidr_list)
|
91
|
+
|
92
|
+
# make sure we have an array with at least 2 objects
|
93
|
+
unless ( (cidr_list.kind_of? Array) && (cidr_list.length > 1) )
|
94
|
+
raise "Array of at least two IPAdmin::CIDR objects required."
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
# make sure all are CIDR objects of the same version
|
99
|
+
version = {}
|
100
|
+
cidr_list.each do |cidr|
|
101
|
+
unless (cidr.kind_of? IPAdmin::CIDR)
|
102
|
+
raise "Expected IPAdmin::CIDR, but #{options.class} provided."
|
103
|
+
end
|
104
|
+
version[cidr.version] = 1
|
105
|
+
end
|
106
|
+
if (version.length > 1)
|
107
|
+
raise "Provided CIDR objects must all be of the same version."
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
# cidr_list.length should be a power of 2
|
112
|
+
multiplier = 1
|
113
|
+
while (1)
|
114
|
+
if ( (2**multiplier) < cidr_list.length)
|
115
|
+
multiplier = multiplier + 1
|
116
|
+
elsif ( (2**multiplier) == cidr_list.length)
|
117
|
+
break
|
118
|
+
elsif ( (2**multiplier) > cidr_list.length)
|
119
|
+
raise "Provided CIDR objects will not merge into " +
|
120
|
+
"a single supernet"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
# take each netmask of the cidr_list items, supernet them by
|
126
|
+
# 'multiplier' bits, and store them.
|
127
|
+
netmasks = {}
|
128
|
+
cidr_list.each do |cidr|
|
129
|
+
netmask = cidr.packed_netmask
|
130
|
+
netmask = netmask << multiplier
|
131
|
+
netmasks[netmask] = 1
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
# if we have multiple, different netmasks then the provided
|
136
|
+
# cidr objects are not part of the same supernet
|
137
|
+
if (netmasks.length > 1)
|
138
|
+
raise "Provided CIDR objects will not merge into " +
|
139
|
+
"a single supernet"
|
140
|
+
end
|
141
|
+
new_netmask = (netmasks.keys)[0]
|
142
|
+
|
143
|
+
|
144
|
+
# make new supernet by combining network with new netmask
|
145
|
+
# if we have multiple, different supernets, then the provided
|
146
|
+
# cidr objects are not part of the same supernet
|
147
|
+
supernets = {}
|
148
|
+
cidr_list.each do |cidr|
|
149
|
+
network = cidr.packed_network
|
150
|
+
supernets[network & new_netmask] = 1
|
151
|
+
end
|
152
|
+
|
153
|
+
if (supernets.length > 1)
|
154
|
+
raise "Provided CIDR objects will not merge into " +
|
155
|
+
"a single supernet"
|
156
|
+
|
157
|
+
else
|
158
|
+
supernet = (supernets.keys)[0]
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
# unpack our supernet addr
|
163
|
+
if (version.has_key?(4) )
|
164
|
+
supernet = unpack_ipv4_addr(supernet)
|
165
|
+
new_netmask = unpack_ipv4_netmask(new_netmask)
|
166
|
+
else
|
167
|
+
supernet = unpack_ipv6_addr(supernet)
|
168
|
+
new_netmask = unpack_ipv6_netmask(new_netmask)
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
new_cidr = IPAdmin::CIDR.new(:CIDR => "#{supernet}/#{new_netmask}")
|
173
|
+
|
174
|
+
return(new_cidr)
|
175
|
+
|
176
|
+
end
|
177
|
+
module_function :merge_cidr
|
178
|
+
|
179
|
+
#=====================================#
|
180
|
+
#
|
181
|
+
#=====================================#
|
182
|
+
|
183
|
+
|
184
|
+
|
185
|
+
#============================================================================#
|
186
|
+
# pack_ipv4_addr()
|
187
|
+
#============================================================================#
|
188
|
+
|
189
|
+
# Pack IPv4 addresses into a single byte field. No attempt at validation
|
190
|
+
# is performed.
|
191
|
+
# - Arguments:
|
192
|
+
# * IPv4 address
|
193
|
+
#
|
194
|
+
# - Returns:
|
195
|
+
# * packed IPv4 address or exception on error.
|
196
|
+
#
|
197
|
+
def pack_ipv4_addr(ip)
|
198
|
+
|
199
|
+
# is this a string?
|
200
|
+
unless (ip.kind_of? String)
|
201
|
+
raise "Expected String, but #{ip.class} provided."
|
202
|
+
end
|
203
|
+
|
204
|
+
# pack our ip
|
205
|
+
octets = ip.split( /\./ ).reverse
|
206
|
+
packed_ip = 0
|
207
|
+
|
208
|
+
(0..3).each do |x|
|
209
|
+
octets[x] = (octets[x]).to_i
|
210
|
+
octets[x] = octets[x] << 8*x
|
211
|
+
packed_ip = packed_ip | octets[x]
|
212
|
+
end
|
213
|
+
|
214
|
+
|
215
|
+
return(packed_ip)
|
216
|
+
end
|
217
|
+
module_function :pack_ipv4_addr
|
218
|
+
|
219
|
+
#=====================================#
|
220
|
+
#
|
221
|
+
#=====================================#
|
222
|
+
|
223
|
+
|
224
|
+
|
225
|
+
|
226
|
+
#============================================================================#
|
227
|
+
# pack_ipv4_netmask()
|
228
|
+
#============================================================================#
|
229
|
+
|
230
|
+
# Pack IPv4 netmask into a single byte field. No attempt at validation
|
231
|
+
# is performed.
|
232
|
+
# - Arguments:
|
233
|
+
# * IPv4 netmask in cidr or extended notation
|
234
|
+
#
|
235
|
+
# - Returns:
|
236
|
+
# * packed IPv4 netmask or exception on error.
|
237
|
+
#
|
238
|
+
def pack_ipv4_netmask(netmask)
|
239
|
+
|
240
|
+
all_f = 2**32-1
|
241
|
+
|
242
|
+
|
243
|
+
# is this a CIDR or Extended mask?
|
244
|
+
if(netmask =~ /\./)
|
245
|
+
# pack extended mask
|
246
|
+
begin
|
247
|
+
packed_netmask = pack_ipv4_addr(netmask)
|
248
|
+
packed_hostmask = packed_netmask ^ all_f
|
249
|
+
rescue Exception
|
250
|
+
raise "#{netmask} is invalid."
|
251
|
+
end
|
252
|
+
|
253
|
+
elsif (netmask !~ /\D/)
|
254
|
+
if (netmask.kind_of? String)
|
255
|
+
netmask = netmask.to_i
|
256
|
+
end
|
257
|
+
|
258
|
+
packed_hostmask = all_f >> netmask
|
259
|
+
packed_netmask = all_f ^ packed_hostmask
|
260
|
+
|
261
|
+
else
|
262
|
+
raise "#{netmask} is unrecognized."
|
263
|
+
|
264
|
+
end
|
265
|
+
|
266
|
+
ret_vals = [packed_netmask,packed_hostmask]
|
267
|
+
|
268
|
+
return(ret_vals)
|
269
|
+
end
|
270
|
+
module_function :pack_ipv4_netmask
|
271
|
+
|
272
|
+
#=====================================#
|
273
|
+
#
|
274
|
+
#=====================================#
|
275
|
+
|
276
|
+
|
277
|
+
|
278
|
+
#============================================================================#
|
279
|
+
# pack_ipv6_addr()
|
280
|
+
#============================================================================#
|
281
|
+
|
282
|
+
# Pack IPv6 addresses into a single byte field. No attempt at validation
|
283
|
+
# is performed.
|
284
|
+
# - Arguments:
|
285
|
+
# * IPv6 address
|
286
|
+
#
|
287
|
+
# - Returns:
|
288
|
+
# * packed IPv6 address or exception on error.
|
289
|
+
#
|
290
|
+
def pack_ipv6_addr(ip)
|
291
|
+
|
292
|
+
# is this a string?
|
293
|
+
unless (ip.kind_of? String)
|
294
|
+
raise "Expected String, but #{ip.class} provided."
|
295
|
+
end
|
296
|
+
|
297
|
+
|
298
|
+
|
299
|
+
# look for a '::' to see if this address is in shorthand
|
300
|
+
# if found, split on it
|
301
|
+
hex_fields = []
|
302
|
+
if (ip =~ /::/)
|
303
|
+
shrthnd = ip.split( /::/ )
|
304
|
+
if (ip =~ /^::/)
|
305
|
+
sec_half = shrthnd[1].split( /:/ )
|
306
|
+
zero_pads = 8 - sec_half.length
|
307
|
+
(1..zero_pads).each {hex_fields.push('0')}
|
308
|
+
sec_half.each {|field| hex_fields.push(field)}
|
309
|
+
elsif (ip =~ /::$/)
|
310
|
+
hex_fields = shrthnd[0].split( /:/ )
|
311
|
+
zero_pads = 8 - hex_fields.length
|
312
|
+
(1..zero_pads).each {hex_fields.push('0')}
|
313
|
+
else
|
314
|
+
first_half = shrthnd[0].split( /:/ )
|
315
|
+
sec_half = shrthnd[1].split( /:/ )
|
316
|
+
zero_pads = 8 - (first_half.length + sec_half.length)
|
317
|
+
first_half.each {|field| hex_fields.push(field)}
|
318
|
+
(1..zero_pads).each {hex_fields.push('0')}
|
319
|
+
sec_half.each {|field| hex_fields.push(field)}
|
320
|
+
end
|
321
|
+
|
322
|
+
else
|
323
|
+
hex_fields = ip.split( /:/ )
|
324
|
+
end
|
325
|
+
|
326
|
+
|
327
|
+
|
328
|
+
# pack
|
329
|
+
hex_fields.reverse!
|
330
|
+
packed_ip = 0
|
331
|
+
(0..7).each do |x|
|
332
|
+
hex = hex_fields[x]
|
333
|
+
base16 = hex.to_i(16)
|
334
|
+
|
335
|
+
base16 = base16 << 16*x
|
336
|
+
packed_ip = packed_ip | base16
|
337
|
+
end
|
338
|
+
|
339
|
+
|
340
|
+
return(packed_ip)
|
341
|
+
end
|
342
|
+
module_function :pack_ipv6_addr
|
343
|
+
|
344
|
+
#=====================================#
|
345
|
+
#
|
346
|
+
#=====================================#
|
347
|
+
|
348
|
+
|
349
|
+
|
350
|
+
#============================================================================#
|
351
|
+
# pack_ipv6_netmask()
|
352
|
+
#============================================================================#
|
353
|
+
|
354
|
+
# Pack IPv6 netmask into a single byte field. No attempt at validation
|
355
|
+
# is performed.
|
356
|
+
# - Arguments:
|
357
|
+
# * IPv6 netmask in cidr notation
|
358
|
+
#
|
359
|
+
# - Returns:
|
360
|
+
# * packed IPv6 netmask or exception on error.
|
361
|
+
#
|
362
|
+
def pack_ipv6_netmask(netmask)
|
363
|
+
|
364
|
+
all_f = 2**128-1
|
365
|
+
|
366
|
+
|
367
|
+
if (netmask !~ /\D/)
|
368
|
+
# pack
|
369
|
+
if (netmask.kind_of? String)
|
370
|
+
netmask = netmask.to_i
|
371
|
+
end
|
372
|
+
|
373
|
+
packed_hostmask = all_f >> netmask
|
374
|
+
packed_netmask = all_f ^ packed_hostmask
|
375
|
+
|
376
|
+
else
|
377
|
+
raise "#{netmask} is unrecognized."
|
378
|
+
|
379
|
+
end
|
380
|
+
|
381
|
+
|
382
|
+
ret_vals = [packed_netmask,packed_hostmask]
|
383
|
+
|
384
|
+
return(ret_vals)
|
385
|
+
|
386
|
+
end
|
387
|
+
module_function :pack_ipv6_netmask
|
388
|
+
|
389
|
+
#=====================================#
|
390
|
+
#
|
391
|
+
#=====================================#
|
392
|
+
|
393
|
+
|
394
|
+
|
395
|
+
#============================================================================#
|
396
|
+
# unpack_ipv4_addr()
|
397
|
+
#============================================================================#
|
398
|
+
|
399
|
+
# Unack IPv4 address back into a printable string. No attempt at validation
|
400
|
+
# is performed.
|
401
|
+
# - Arguments:
|
402
|
+
# * Byte-packed IPv4 address
|
403
|
+
#
|
404
|
+
# - Returns:
|
405
|
+
# * IPv4 address.
|
406
|
+
#
|
407
|
+
def unpack_ipv4_addr(packed_ip)
|
408
|
+
|
409
|
+
octets = []
|
410
|
+
(0..3).each do |x|
|
411
|
+
octets[x] = packed_ip & 0xFF
|
412
|
+
octets[x] = (octets[x]).to_s
|
413
|
+
packed_ip = packed_ip >> 8
|
414
|
+
end
|
415
|
+
|
416
|
+
octets.reverse!
|
417
|
+
ip = octets.join('.')
|
418
|
+
|
419
|
+
return(ip)
|
420
|
+
end
|
421
|
+
module_function :unpack_ipv4_addr
|
422
|
+
|
423
|
+
#=====================================#
|
424
|
+
#
|
425
|
+
#=====================================#
|
426
|
+
|
427
|
+
|
428
|
+
|
429
|
+
#============================================================================#
|
430
|
+
# unpack_ipv4_netmask()
|
431
|
+
#============================================================================#
|
432
|
+
|
433
|
+
# Unack IPv4 netmask into a integer representing the number of
|
434
|
+
# bits in the CIDR mask. No attempt at validation is performed.
|
435
|
+
# - Arguments:
|
436
|
+
# * Byte-packed IPv4 netmask
|
437
|
+
#
|
438
|
+
# - Returns:
|
439
|
+
# * IPv4 netmask as number of bits (cidr format).
|
440
|
+
#
|
441
|
+
def unpack_ipv4_netmask(packed_mask)
|
442
|
+
|
443
|
+
mask = 32
|
444
|
+
32.times do
|
445
|
+
if ( (packed_mask & 1) != 0)
|
446
|
+
break
|
447
|
+
end
|
448
|
+
packed_mask = packed_mask >> 1
|
449
|
+
mask = mask - 1
|
450
|
+
end
|
451
|
+
|
452
|
+
return(mask)
|
453
|
+
end
|
454
|
+
module_function :unpack_ipv4_netmask
|
455
|
+
|
456
|
+
#=====================================#
|
457
|
+
#
|
458
|
+
#=====================================#
|
459
|
+
|
460
|
+
|
461
|
+
|
462
|
+
#============================================================================#
|
463
|
+
# unpack_ipv6_addr()
|
464
|
+
#============================================================================#
|
465
|
+
|
466
|
+
# Unack IPv6 address back into a printable string. No attempt at validation
|
467
|
+
# is performed.
|
468
|
+
# - Arguments:
|
469
|
+
# * Byte-packed IPv6 address
|
470
|
+
#
|
471
|
+
# - Returns:
|
472
|
+
# * IPv6 address.
|
473
|
+
#
|
474
|
+
def unpack_ipv6_addr(packed_ip)
|
475
|
+
hex_fields = []
|
476
|
+
(0..7).each do |x|
|
477
|
+
hex_fields[x] = packed_ip & 0xFFFF
|
478
|
+
hex_fields[x] = (hex_fields[x]).to_s(16)
|
479
|
+
packed_ip = packed_ip >> 16
|
480
|
+
|
481
|
+
# if hex_fields[x] < 4 characters, then pad with 0's
|
482
|
+
(4 - hex_fields[x].length).times do
|
483
|
+
hex_fields[x] = '0' << hex_fields[x]
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
hex_fields.reverse!
|
488
|
+
ip = hex_fields.join(':')
|
489
|
+
|
490
|
+
return(ip)
|
491
|
+
end
|
492
|
+
module_function :unpack_ipv6_addr
|
493
|
+
|
494
|
+
#=====================================#
|
495
|
+
#
|
496
|
+
#=====================================#
|
497
|
+
|
498
|
+
|
499
|
+
|
500
|
+
#============================================================================#
|
501
|
+
# unpack_ipv6_netmask()
|
502
|
+
#============================================================================#
|
503
|
+
|
504
|
+
# Unack IPv6 netmask into a integer representing the number of
|
505
|
+
# bits in the CIDR mask. No attempt at validation is performed.
|
506
|
+
# - Arguments:
|
507
|
+
# * Byte-packed IPv6 netmask
|
508
|
+
#
|
509
|
+
# - Returns:
|
510
|
+
# * IPv6 netmask as number of bits (cidr format).
|
511
|
+
#
|
512
|
+
def unpack_ipv6_netmask(packed_mask)
|
513
|
+
|
514
|
+
mask = 128
|
515
|
+
128.times do
|
516
|
+
if ( (packed_mask & 1) == 1)
|
517
|
+
break
|
518
|
+
end
|
519
|
+
mask = mask - 1
|
520
|
+
packed_mask = packed_mask >> 1
|
521
|
+
end
|
522
|
+
|
523
|
+
return(mask)
|
524
|
+
end
|
525
|
+
module_function :unpack_ipv6_netmask
|
526
|
+
|
527
|
+
#=====================================#
|
528
|
+
#
|
529
|
+
#=====================================#
|
530
|
+
|
531
|
+
|
532
|
+
|
533
|
+
#============================================================================#
|
534
|
+
# validate_ipv4_addr()
|
535
|
+
#============================================================================#
|
536
|
+
|
537
|
+
# Validate IPv4 addresses.
|
538
|
+
# - Arguments:
|
539
|
+
# * IPv4 address
|
540
|
+
#
|
541
|
+
# - Returns:
|
542
|
+
# * 1 on valid IP or exception on error.
|
543
|
+
#
|
544
|
+
def validate_ipv4_addr(ip)
|
545
|
+
|
546
|
+
# is this a string?
|
547
|
+
unless (ip.kind_of? String)
|
548
|
+
raise "Expected String, but #{ip.class} provided."
|
549
|
+
end
|
550
|
+
octets = ip.split( /\./ )
|
551
|
+
|
552
|
+
|
553
|
+
|
554
|
+
# check validity of characters in the addr
|
555
|
+
if ( (ip =~ /\.{2,}?/ ) || (ip =~ /[^0-9\.]/) )
|
556
|
+
raise "#{ip} is invalid."
|
557
|
+
end
|
558
|
+
|
559
|
+
|
560
|
+
|
561
|
+
# do we have 4 octets?
|
562
|
+
if (octets.length != 4)
|
563
|
+
raise "#{ip} is invalid."
|
564
|
+
end
|
565
|
+
|
566
|
+
|
567
|
+
|
568
|
+
# are octets in range 0..255?
|
569
|
+
(0..3).each do |x|
|
570
|
+
octets[x] = octets[x].to_i
|
571
|
+
unless ( (octets[x] >= 0) && (octets[x] < 256 ) )
|
572
|
+
raise "#{ip} is invalid."
|
573
|
+
end
|
574
|
+
end
|
575
|
+
|
576
|
+
|
577
|
+
# dont allow first octet to be 0
|
578
|
+
if (octets[0] == 0)
|
579
|
+
raise "#{ip} is invalid."
|
580
|
+
end
|
581
|
+
|
582
|
+
return(1)
|
583
|
+
|
584
|
+
end
|
585
|
+
module_function :validate_ipv4_addr
|
586
|
+
|
587
|
+
#=====================================#
|
588
|
+
#
|
589
|
+
#=====================================#
|
590
|
+
|
591
|
+
|
592
|
+
|
593
|
+
#============================================================================#
|
594
|
+
# validate_ipv4_netmask()
|
595
|
+
#============================================================================#
|
596
|
+
|
597
|
+
# Validate IPv4 Netmask.
|
598
|
+
# - Arguments:
|
599
|
+
# * IPv4 netmask in cidr or extended notation
|
600
|
+
#
|
601
|
+
# - Returns:
|
602
|
+
# * 1 on valid IP or exception on error.
|
603
|
+
#
|
604
|
+
def validate_ipv4_netmask(netmask)
|
605
|
+
|
606
|
+
all_f = (2**32)-1
|
607
|
+
|
608
|
+
|
609
|
+
# is this a CIDR or Extended mask?
|
610
|
+
if(netmask =~ /\./)
|
611
|
+
# validate & pack extended mask
|
612
|
+
begin
|
613
|
+
validate_ipv4_addr(netmask)
|
614
|
+
packed_mask = pack_ipv4_addr(netmask)
|
615
|
+
|
616
|
+
rescue Exception
|
617
|
+
raise "#{netmask} is invalid."
|
618
|
+
end
|
619
|
+
|
620
|
+
# cycle through the bits of hostmask and compare
|
621
|
+
# with packed_mask. when we hit the firt '1' within
|
622
|
+
# packed_mask (our netmask boundary), xor hostmask and
|
623
|
+
# packed_mask. the result should be all 1's. this whole
|
624
|
+
# process is in place to make sure that we dont have
|
625
|
+
# and crazy masks such as 255.254.255.0
|
626
|
+
hostmask = 1
|
627
|
+
32.times do
|
628
|
+
check = packed_mask & hostmask
|
629
|
+
if ( check != 0)
|
630
|
+
hostmask = hostmask >> 1
|
631
|
+
unless ( (packed_mask ^ hostmask) == all_f)
|
632
|
+
raise "#{netmask} is invalid."
|
633
|
+
end
|
634
|
+
break
|
635
|
+
else
|
636
|
+
hostmask = hostmask << 1
|
637
|
+
hostmask = hostmask | 1
|
638
|
+
end
|
639
|
+
end
|
640
|
+
|
641
|
+
else
|
642
|
+
|
643
|
+
# check if we have any non numeric characters
|
644
|
+
if (netmask =~ /\D/)
|
645
|
+
raise "#{netmask} is invalid."
|
646
|
+
end
|
647
|
+
|
648
|
+
|
649
|
+
# are we between 1 and 32 inclusive
|
650
|
+
if (netmask.kind_of? String)
|
651
|
+
netmask = netmask.to_i
|
652
|
+
end
|
653
|
+
|
654
|
+
if ( (netmask > 32) || (netmask == 0) )
|
655
|
+
raise "#{netmask} is invalid."
|
656
|
+
end
|
657
|
+
|
658
|
+
end
|
659
|
+
|
660
|
+
return(1)
|
661
|
+
|
662
|
+
end
|
663
|
+
module_function :validate_ipv4_netmask
|
664
|
+
|
665
|
+
#=====================================#
|
666
|
+
#
|
667
|
+
#=====================================#
|
668
|
+
|
669
|
+
|
670
|
+
|
671
|
+
#============================================================================#
|
672
|
+
# validate_ipv6_addr()
|
673
|
+
#============================================================================#
|
674
|
+
|
675
|
+
# Validate IPv6 addresses.
|
676
|
+
# - Arguments:
|
677
|
+
# * IPv6 address
|
678
|
+
#
|
679
|
+
# - Returns:
|
680
|
+
# * 1 on valid IP or exception on error.
|
681
|
+
#
|
682
|
+
def validate_ipv6_addr(ip)
|
683
|
+
# is this a string?
|
684
|
+
unless (ip.kind_of? String)
|
685
|
+
raise "Expected String, but #{ip.class} provided."
|
686
|
+
end
|
687
|
+
|
688
|
+
|
689
|
+
|
690
|
+
# check validity of characters in the addr
|
691
|
+
if ( (ip =~ /:{3,}?/ ) || (ip =~ /[^0-9a-fA-F:]/) )
|
692
|
+
raise "#{ip} is invalid."
|
693
|
+
end
|
694
|
+
|
695
|
+
|
696
|
+
|
697
|
+
# look for a '::' to see if this address is in shorthand
|
698
|
+
# if found, split on it & make sure that we have at most
|
699
|
+
# two elements
|
700
|
+
if (ip =~ /::/)
|
701
|
+
shrthnd = ip.split( /::/ )
|
702
|
+
unless ( (shrthnd.length > 0) && (shrthnd.length < 3) )
|
703
|
+
raise "#{ip} is invalid."
|
704
|
+
end
|
705
|
+
end
|
706
|
+
|
707
|
+
|
708
|
+
|
709
|
+
if (shrthnd)
|
710
|
+
# if shorthand, we should have between 1 and 7
|
711
|
+
# hex fields
|
712
|
+
hex_fields = []
|
713
|
+
shrthnd.each do |x|
|
714
|
+
elements = x.split( /:/ )
|
715
|
+
elements.each {|x| hex_fields.push(x)}
|
716
|
+
end
|
717
|
+
if ( (hex_fields.length < 1) || (hex_fields.length > 7) )
|
718
|
+
raise "#{ip} is invalid."
|
719
|
+
end
|
720
|
+
|
721
|
+
else
|
722
|
+
# since no shorthand notation was detected we should
|
723
|
+
# have exactly 8 hex fields
|
724
|
+
hex_fields = ip.split( /:/ )
|
725
|
+
if (hex_fields.length != 8)
|
726
|
+
raise "#{ip} is invalid."
|
727
|
+
end
|
728
|
+
|
729
|
+
end
|
730
|
+
|
731
|
+
|
732
|
+
|
733
|
+
# check that we have no more than 4 characters in each
|
734
|
+
# hex field
|
735
|
+
hex_fields.each do |x|
|
736
|
+
if (x.length > 4)
|
737
|
+
raise "#{ip} is invalid."
|
738
|
+
end
|
739
|
+
end
|
740
|
+
|
741
|
+
|
742
|
+
return(1)
|
743
|
+
|
744
|
+
end
|
745
|
+
module_function :validate_ipv6_addr
|
746
|
+
|
747
|
+
#=====================================#
|
748
|
+
#
|
749
|
+
#=====================================#
|
750
|
+
|
751
|
+
|
752
|
+
|
753
|
+
#============================================================================#
|
754
|
+
# validate_ipv6_netmask()
|
755
|
+
#============================================================================#
|
756
|
+
|
757
|
+
# Validate IPv6 netmask.
|
758
|
+
# - Arguments:
|
759
|
+
# * IPv6 netmask in cidr notation
|
760
|
+
#
|
761
|
+
# - Returns:
|
762
|
+
# * 1 on valid IP or exception on error.
|
763
|
+
#
|
764
|
+
def validate_ipv6_netmask(netmask)
|
765
|
+
|
766
|
+
if (netmask =~ /\D/)
|
767
|
+
raise "#{netmask} is invalid."
|
768
|
+
|
769
|
+
else
|
770
|
+
# are we between 1 and 128 inclusive
|
771
|
+
if (netmask.kind_of? String)
|
772
|
+
netmask = netmask.to_i
|
773
|
+
end
|
774
|
+
|
775
|
+
if ( (netmask > 128) || (netmask == 0) )
|
776
|
+
raise "#{netmask} is invalid."
|
777
|
+
end
|
778
|
+
|
779
|
+
end
|
780
|
+
|
781
|
+
return(1)
|
782
|
+
|
783
|
+
end
|
784
|
+
module_function :validate_ipv6_netmask
|
785
|
+
|
786
|
+
#=====================================#
|
787
|
+
#
|
788
|
+
#=====================================#
|
789
|
+
|
790
|
+
|
791
|
+
|
792
|
+
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
793
|
+
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
794
|
+
#
|
795
|
+
# BEGIN class CIDR
|
796
|
+
#
|
797
|
+
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
798
|
+
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
799
|
+
|
800
|
+
=begin rdoc
|
801
|
+
A class & series of methods for creating and manipulating CIDR network
|
802
|
+
addresses. Both IPv4 and IPv6 are supported.
|
803
|
+
=end
|
804
|
+
|
805
|
+
class CIDR
|
806
|
+
|
807
|
+
|
808
|
+
|
809
|
+
#============================================================================#
|
810
|
+
# attr_reader/attr_writer
|
811
|
+
#============================================================================#
|
812
|
+
|
813
|
+
# @network - packed cidr network
|
814
|
+
# @netmask - packet cidr netmask
|
815
|
+
# @hostmask - inverse packed netmask
|
816
|
+
|
817
|
+
# ip version 4 or 6
|
818
|
+
attr_reader :version
|
819
|
+
|
820
|
+
# hash of custom tags. should be in the format tag => value
|
821
|
+
attr_reader :tag
|
822
|
+
attr_writer :tag
|
823
|
+
|
824
|
+
#=====================================#
|
825
|
+
#
|
826
|
+
#=====================================#
|
827
|
+
|
828
|
+
|
829
|
+
|
830
|
+
#============================================================================#
|
831
|
+
# initialize()
|
832
|
+
#============================================================================#
|
833
|
+
|
834
|
+
# - Arguments:
|
835
|
+
# * Hash with the following fields:
|
836
|
+
# :CIDR -- IPv4 or IPv6 cidr block
|
837
|
+
# :Netmask -- IPv4 netmask in extended format (if not provided in :CIDR)
|
838
|
+
# :Tag -- Custom descriptor tag for object. Should be Hash (tag => value)
|
839
|
+
#
|
840
|
+
def initialize(options)
|
841
|
+
|
842
|
+
unless (options.kind_of? Hash)
|
843
|
+
raise "Expected Hash, but #{options.class} provided."
|
844
|
+
end
|
845
|
+
|
846
|
+
|
847
|
+
if ( options.has_key?(:CIDR) )
|
848
|
+
network = options[:CIDR]
|
849
|
+
|
850
|
+
|
851
|
+
if (network =~/\./) # assume IPv4
|
852
|
+
@version = 4
|
853
|
+
|
854
|
+
if (network =~ /\//)
|
855
|
+
ip,netmask = network.split(/\//)
|
856
|
+
|
857
|
+
# validate
|
858
|
+
IPAdmin.validate_ipv4_addr(ip)
|
859
|
+
IPAdmin.validate_ipv4_netmask(netmask)
|
860
|
+
|
861
|
+
# pack
|
862
|
+
@network = IPAdmin.pack_ipv4_addr(ip)
|
863
|
+
@netmask,@hostmask = IPAdmin.pack_ipv4_netmask(netmask)
|
864
|
+
|
865
|
+
elsif ( options.has_key?(:Netmask) )
|
866
|
+
ip = network
|
867
|
+
netmask = options[:Netmask]
|
868
|
+
|
869
|
+
# validate
|
870
|
+
IPAdmin.validate_ipv4_addr(ip)
|
871
|
+
IPAdmin.validate_ipv4_netmask(netmask)
|
872
|
+
|
873
|
+
# pack
|
874
|
+
@network = IPAdmin.pack_ipv4_addr(ip)
|
875
|
+
@netmask,@hostmask = IPAdmin.pack_ipv4_netmask(netmask)
|
876
|
+
|
877
|
+
else
|
878
|
+
raise "Missing Netmask or invalid CIDR format."
|
879
|
+
|
880
|
+
end
|
881
|
+
|
882
|
+
|
883
|
+
elsif (network =~/:/) # assume IPv6
|
884
|
+
@version = 6
|
885
|
+
|
886
|
+
if (network =~ /\//)
|
887
|
+
ip,netmask = network.split(/\//)
|
888
|
+
|
889
|
+
# validate
|
890
|
+
IPAdmin.validate_ipv6_addr(ip)
|
891
|
+
IPAdmin.validate_ipv6_netmask(netmask)
|
892
|
+
|
893
|
+
# pack
|
894
|
+
@network = IPAdmin.pack_ipv6_addr(ip)
|
895
|
+
@netmask,@hostmask = IPAdmin.pack_ipv6_netmask(netmask)
|
896
|
+
|
897
|
+
else
|
898
|
+
raise "Missing Netmask or invalid CIDR format."
|
899
|
+
|
900
|
+
end
|
901
|
+
|
902
|
+
else
|
903
|
+
raise "Invalid CIDR address format: #{network}."
|
904
|
+
end
|
905
|
+
|
906
|
+
else
|
907
|
+
raise ArgumentError, "Missing arguments: CIDR."
|
908
|
+
|
909
|
+
end
|
910
|
+
|
911
|
+
|
912
|
+
# make sure that this is a valid address/mask pair
|
913
|
+
masked_val = (@network & @netmask)
|
914
|
+
if (masked_val != @network )
|
915
|
+
raise "Invalid address/netmask pair. Did you provide "+
|
916
|
+
"an IP address instead of a CIDR block?"
|
917
|
+
end
|
918
|
+
|
919
|
+
|
920
|
+
# set tag if present
|
921
|
+
if ( options.has_key?(:Tag) )
|
922
|
+
@tag = options[:Tag]
|
923
|
+
end
|
924
|
+
|
925
|
+
|
926
|
+
end
|
927
|
+
|
928
|
+
#=====================================#
|
929
|
+
#
|
930
|
+
#=====================================#
|
931
|
+
|
932
|
+
|
933
|
+
|
934
|
+
#============================================================================#
|
935
|
+
# contains()
|
936
|
+
#============================================================================#
|
937
|
+
|
938
|
+
# Determines if cidr block of IPAdmin::CIDR contains IP address
|
939
|
+
# of provided IPAdmin:IPAddr object.
|
940
|
+
# - Arguments:
|
941
|
+
# * IPAdmin:IPAddr object
|
942
|
+
#
|
943
|
+
# - Returns:
|
944
|
+
# * 1 on true, nil on false
|
945
|
+
#
|
946
|
+
def contains(ip_obj)
|
947
|
+
|
948
|
+
if ( (ip_obj.version == 4) && (@version == 4) )
|
949
|
+
v4_all_f = (2**32)-1
|
950
|
+
hostmask = @netmask ^ v4_all_f
|
951
|
+
if ( (ip_obj.packed_ip | hostmask) == (@network | hostmask) )
|
952
|
+
return(1)
|
953
|
+
end
|
954
|
+
|
955
|
+
elsif ( (ip_obj.version == 6) && (@version == 6) )
|
956
|
+
v6_all_f = (2**128)-1
|
957
|
+
hostmask = @netmask ^ v6_all_f
|
958
|
+
if ( (ip_obj.packed_ip | hostmask) == (@network | hostmask) )
|
959
|
+
return(1)
|
960
|
+
end
|
961
|
+
|
962
|
+
else
|
963
|
+
raise "Attempted to compare a version #{ip_obj.version} IP " +
|
964
|
+
"with a version #{@version} network."
|
965
|
+
end
|
966
|
+
|
967
|
+
return(nil)
|
968
|
+
end
|
969
|
+
|
970
|
+
#=====================================#
|
971
|
+
#
|
972
|
+
#=====================================#
|
973
|
+
|
974
|
+
|
975
|
+
|
976
|
+
#============================================================================#
|
977
|
+
# desc()
|
978
|
+
#============================================================================#
|
979
|
+
|
980
|
+
# Displays network/netmask of an IPAdmin::CIDR object
|
981
|
+
# of provided IPAdmin:IPAddr object.
|
982
|
+
# - Arguments:
|
983
|
+
# * none
|
984
|
+
#
|
985
|
+
# - Returns:
|
986
|
+
# * Description in network/netmask format
|
987
|
+
#
|
988
|
+
def desc()
|
989
|
+
|
990
|
+
if (@version == 4)
|
991
|
+
ip = IPAdmin.unpack_ipv4_addr(@network)
|
992
|
+
mask = IPAdmin.unpack_ipv4_netmask(@netmask)
|
993
|
+
else
|
994
|
+
ip = IPAdmin.unpack_ipv6_addr(@network)
|
995
|
+
mask = IPAdmin.unpack_ipv6_netmask(@netmask)
|
996
|
+
end
|
997
|
+
|
998
|
+
return("#{ip}/#{mask}")
|
999
|
+
|
1000
|
+
end
|
1001
|
+
|
1002
|
+
#=====================================#
|
1003
|
+
#
|
1004
|
+
#=====================================#
|
1005
|
+
|
1006
|
+
|
1007
|
+
|
1008
|
+
#============================================================================#
|
1009
|
+
# enumerate()
|
1010
|
+
#============================================================================#
|
1011
|
+
|
1012
|
+
# Provide all IP addresses within a CIDR block
|
1013
|
+
# (warning: this can be quite large for big blocks)
|
1014
|
+
#
|
1015
|
+
# - Arguments:
|
1016
|
+
# * Hash with the following fields
|
1017
|
+
# - :BitStep -- enumerate in X sized steps
|
1018
|
+
# - :Objectify -- return IPAdmin::IPAddr objects
|
1019
|
+
# - :Limit -- limit returned list to X number of items
|
1020
|
+
#
|
1021
|
+
# - Returns:
|
1022
|
+
# * array of IP addresses or IPAdmin::IPAddr objects
|
1023
|
+
#
|
1024
|
+
def enumerate(options=nil)
|
1025
|
+
bitstep = 1
|
1026
|
+
objectify = nil
|
1027
|
+
limit = nil
|
1028
|
+
v4_all_f = (2**32)-1
|
1029
|
+
v6_all_f = (2**128)-1
|
1030
|
+
|
1031
|
+
if (options)
|
1032
|
+
if( options.has_key?(:BitStep) )
|
1033
|
+
bitstep = options[:BitStep]
|
1034
|
+
end
|
1035
|
+
|
1036
|
+
if( options.has_key?(:Objectify) )
|
1037
|
+
objectify = 1
|
1038
|
+
end
|
1039
|
+
|
1040
|
+
if( options.has_key?(:Limit) )
|
1041
|
+
limit = options[:Limit]
|
1042
|
+
end
|
1043
|
+
end
|
1044
|
+
|
1045
|
+
|
1046
|
+
list = []
|
1047
|
+
if ( (@version == 4) && !objectify)
|
1048
|
+
my_ip = @network
|
1049
|
+
hostmask = @netmask ^ v4_all_f
|
1050
|
+
change_mask = hostmask | my_ip
|
1051
|
+
|
1052
|
+
until ( change_mask != (hostmask | @network) )
|
1053
|
+
list.push( IPAdmin.unpack_ipv4_addr(my_ip) )
|
1054
|
+
my_ip = my_ip + bitstep
|
1055
|
+
change_mask = hostmask | my_ip
|
1056
|
+
if (limit)
|
1057
|
+
limit = limit -1
|
1058
|
+
break if (limit == 0)
|
1059
|
+
end
|
1060
|
+
end
|
1061
|
+
|
1062
|
+
elsif ( (@version == 4) && objectify)
|
1063
|
+
my_ip = @network
|
1064
|
+
hostmask = @netmask ^ v4_all_f
|
1065
|
+
change_mask = hostmask | my_ip
|
1066
|
+
|
1067
|
+
until ( change_mask != (hostmask | @network) )
|
1068
|
+
my_ip_s = IPAdmin.unpack_ipv4_addr(my_ip)
|
1069
|
+
list.push( IPAdmin::IPAddr.new(:IPAddr => my_ip_s) )
|
1070
|
+
my_ip = my_ip + bitstep
|
1071
|
+
change_mask = hostmask | my_ip
|
1072
|
+
if (limit)
|
1073
|
+
limit = limit -1
|
1074
|
+
break if (limit == 0)
|
1075
|
+
end
|
1076
|
+
end
|
1077
|
+
|
1078
|
+
elsif ( (@version == 6) && !objectify)
|
1079
|
+
my_ip = @network
|
1080
|
+
hostmask = @netmask ^ v6_all_f
|
1081
|
+
change_mask = hostmask | my_ip
|
1082
|
+
|
1083
|
+
until ( change_mask != (hostmask | @network) )
|
1084
|
+
list.push( IPAdmin.unpack_ipv6_addr(my_ip) )
|
1085
|
+
my_ip = my_ip + bitstep
|
1086
|
+
change_mask = hostmask | my_ip
|
1087
|
+
if (limit)
|
1088
|
+
limit = limit -1
|
1089
|
+
break if (limit == 0)
|
1090
|
+
end
|
1091
|
+
end
|
1092
|
+
|
1093
|
+
elsif ( (@version == 6) && objectify)
|
1094
|
+
my_ip = @network
|
1095
|
+
hostmask = @netmask ^ v6_all_f
|
1096
|
+
change_mask = hostmask | my_ip
|
1097
|
+
|
1098
|
+
until ( change_mask != (hostmask | @network) )
|
1099
|
+
my_ip_s = IPAdmin.unpack_ipv6_addr(my_ip)
|
1100
|
+
list.push( IPAdmin::IPAddr.new(:IPAddr => my_ip_s) )
|
1101
|
+
my_ip = my_ip + bitstep
|
1102
|
+
change_mask = hostmask | my_ip
|
1103
|
+
if (limit)
|
1104
|
+
limit = limit -1
|
1105
|
+
break if (limit == 0)
|
1106
|
+
end
|
1107
|
+
end
|
1108
|
+
|
1109
|
+
else
|
1110
|
+
list = nil
|
1111
|
+
|
1112
|
+
end
|
1113
|
+
|
1114
|
+
|
1115
|
+
return(list)
|
1116
|
+
end
|
1117
|
+
|
1118
|
+
#=====================================#
|
1119
|
+
#
|
1120
|
+
#=====================================#
|
1121
|
+
|
1122
|
+
|
1123
|
+
|
1124
|
+
#============================================================================#
|
1125
|
+
# hostmask_ext()
|
1126
|
+
#============================================================================#
|
1127
|
+
|
1128
|
+
# Provide IPv4 Hostmask in extended format.
|
1129
|
+
#
|
1130
|
+
# - Arguments:
|
1131
|
+
# * none
|
1132
|
+
#
|
1133
|
+
# - Returns:
|
1134
|
+
# * Hostmask in extended (y.y.y.y) format.
|
1135
|
+
#
|
1136
|
+
def hostmask_ext()
|
1137
|
+
if (@version == 4)
|
1138
|
+
hostmask = IPAdmin.unpack_ipv4_addr(@hostmask)
|
1139
|
+
else
|
1140
|
+
raise "IPv6 does not support extended hostmask notation."
|
1141
|
+
end
|
1142
|
+
|
1143
|
+
return(hostmask)
|
1144
|
+
end
|
1145
|
+
|
1146
|
+
#=====================================#
|
1147
|
+
#
|
1148
|
+
#=====================================#
|
1149
|
+
|
1150
|
+
|
1151
|
+
|
1152
|
+
#============================================================================#
|
1153
|
+
# netmask()
|
1154
|
+
#============================================================================#
|
1155
|
+
|
1156
|
+
# Provide Netmask in cidr format.
|
1157
|
+
#
|
1158
|
+
# - Arguments:
|
1159
|
+
# * none
|
1160
|
+
#
|
1161
|
+
# - Returns:
|
1162
|
+
# * Number of bits in the netmask.
|
1163
|
+
#
|
1164
|
+
def netmask()
|
1165
|
+
if (@version == 4)
|
1166
|
+
bits = IPAdmin.unpack_ipv4_netmask(@netmask)
|
1167
|
+
else
|
1168
|
+
bits = IPAdmin.unpack_ipv6_netmask(@netmask)
|
1169
|
+
end
|
1170
|
+
|
1171
|
+
return(bits)
|
1172
|
+
end
|
1173
|
+
|
1174
|
+
#=====================================#
|
1175
|
+
#
|
1176
|
+
#=====================================#
|
1177
|
+
|
1178
|
+
|
1179
|
+
|
1180
|
+
#============================================================================#
|
1181
|
+
# netmask_ext()
|
1182
|
+
#============================================================================#
|
1183
|
+
|
1184
|
+
# Provide IPv4 Netmask in extended format.
|
1185
|
+
#
|
1186
|
+
# - Arguments:
|
1187
|
+
# * none
|
1188
|
+
#
|
1189
|
+
# - Returns:
|
1190
|
+
# * Netmask in extended (y.y.y.y) format.
|
1191
|
+
#
|
1192
|
+
def netmask_ext()
|
1193
|
+
if (@version == 4)
|
1194
|
+
netmask = IPAdmin.unpack_ipv4_addr(@netmask)
|
1195
|
+
else
|
1196
|
+
raise "IPv6 does not support extended netmask notation. " +
|
1197
|
+
"Use bits() method instead."
|
1198
|
+
end
|
1199
|
+
|
1200
|
+
return(netmask)
|
1201
|
+
end
|
1202
|
+
|
1203
|
+
#=====================================#
|
1204
|
+
#
|
1205
|
+
#=====================================#
|
1206
|
+
|
1207
|
+
|
1208
|
+
|
1209
|
+
#============================================================================#
|
1210
|
+
# network()
|
1211
|
+
#============================================================================#
|
1212
|
+
|
1213
|
+
# Provide base Network address.
|
1214
|
+
#
|
1215
|
+
# - Arguments:
|
1216
|
+
# * none
|
1217
|
+
#
|
1218
|
+
# - Returns:
|
1219
|
+
# * Base network address.
|
1220
|
+
#
|
1221
|
+
def network()
|
1222
|
+
if (@version == 4)
|
1223
|
+
formatted = IPAdmin.unpack_ipv4_addr(@network)
|
1224
|
+
else
|
1225
|
+
formatted = IPAdmin.unpack_ipv6_addr(@network)
|
1226
|
+
end
|
1227
|
+
|
1228
|
+
return(formatted)
|
1229
|
+
end
|
1230
|
+
|
1231
|
+
#=====================================#
|
1232
|
+
#
|
1233
|
+
#=====================================#
|
1234
|
+
|
1235
|
+
|
1236
|
+
|
1237
|
+
#============================================================================#
|
1238
|
+
# nth()
|
1239
|
+
#============================================================================#
|
1240
|
+
|
1241
|
+
# Provide the nth IP within the cidr block of an IPAdmin::CIDR object.
|
1242
|
+
#
|
1243
|
+
# - Arguments:
|
1244
|
+
# * Hash with the following fields
|
1245
|
+
# - :Index -- index of the address to return
|
1246
|
+
# - :Objectify -- return IPAdmin::IPAddr objects
|
1247
|
+
#
|
1248
|
+
# - Returns:
|
1249
|
+
# * IP address or IPAdmin::IPAddr object.
|
1250
|
+
#
|
1251
|
+
def nth(options)
|
1252
|
+
objectify = nil
|
1253
|
+
v4_all_f = (2**32)-1
|
1254
|
+
v6_all_f = (2**128)-1
|
1255
|
+
|
1256
|
+
unless( options.has_key?(:Index) )
|
1257
|
+
raise ArgumentError, "Missing arguments: Index."
|
1258
|
+
end
|
1259
|
+
index = options[:Index]
|
1260
|
+
|
1261
|
+
|
1262
|
+
if( options.has_key?(:Objectify) )
|
1263
|
+
objectify = 1
|
1264
|
+
end
|
1265
|
+
|
1266
|
+
|
1267
|
+
|
1268
|
+
if ( (@version == 4) && !objectify)
|
1269
|
+
my_ip = @network + index
|
1270
|
+
hostmask = @netmask ^ v4_all_f
|
1271
|
+
|
1272
|
+
if ( (hostmask | my_ip) == (hostmask | @network) )
|
1273
|
+
my_ip = IPAdmin.unpack_ipv4_addr(my_ip)
|
1274
|
+
return(my_ip)
|
1275
|
+
end
|
1276
|
+
|
1277
|
+
elsif ( (@version == 4) && objectify)
|
1278
|
+
my_ip = @network + index
|
1279
|
+
hostmask = @netmask ^ v4_all_f
|
1280
|
+
|
1281
|
+
if ( (hostmask | my_ip) == (hostmask | @network) )
|
1282
|
+
my_ip_s = IPAdmin.unpack_ipv4_addr(my_ip)
|
1283
|
+
return( IPAdmin::IPAddr.new(:IPAddr => my_ip_s) )
|
1284
|
+
end
|
1285
|
+
|
1286
|
+
elsif ( (@version == 6) && !objectify)
|
1287
|
+
my_ip = @network + index
|
1288
|
+
hostmask = @netmask ^ v6_all_f
|
1289
|
+
|
1290
|
+
if ( (hostmask | my_ip) == (hostmask | @network) )
|
1291
|
+
my_ip = IPAdmin.unpack_ipv6_addr(my_ip)
|
1292
|
+
return(my_ip)
|
1293
|
+
end
|
1294
|
+
|
1295
|
+
elsif ( (@version == 6) && objectify)
|
1296
|
+
my_ip = @network + index
|
1297
|
+
hostmask = @netmask ^ v6_all_f
|
1298
|
+
|
1299
|
+
if ( (hostmask | my_ip) == (hostmask | @network) )
|
1300
|
+
my_ip_s = IPAdmin.unpack_ipv6_addr(my_ip)
|
1301
|
+
return( IPAdmin::IPAddr.new(:IPAddr => my_ip_s) )
|
1302
|
+
end
|
1303
|
+
|
1304
|
+
end
|
1305
|
+
|
1306
|
+
raise "Index of #{index} returns IP that is out of " +
|
1307
|
+
"bounds of CIDR network."
|
1308
|
+
|
1309
|
+
end
|
1310
|
+
|
1311
|
+
#=====================================#
|
1312
|
+
#
|
1313
|
+
#=====================================#
|
1314
|
+
|
1315
|
+
|
1316
|
+
|
1317
|
+
#============================================================================#
|
1318
|
+
# packed_hostmask()
|
1319
|
+
#============================================================================#
|
1320
|
+
|
1321
|
+
# Provide byte-packed Hostmask of cidr block of an IPAdmin::CIDR object.
|
1322
|
+
#
|
1323
|
+
# - Arguments:
|
1324
|
+
# * none
|
1325
|
+
#
|
1326
|
+
# - Returns:
|
1327
|
+
# * Byte-packed Hostmask.
|
1328
|
+
#
|
1329
|
+
def packed_hostmask()
|
1330
|
+
return(@hostmask)
|
1331
|
+
end
|
1332
|
+
|
1333
|
+
#=====================================#
|
1334
|
+
#
|
1335
|
+
#=====================================#
|
1336
|
+
|
1337
|
+
|
1338
|
+
|
1339
|
+
#============================================================================#
|
1340
|
+
# packed_netmask()
|
1341
|
+
#============================================================================#
|
1342
|
+
|
1343
|
+
# Provide byte-packed Netmask of cidr block of an IPAdmin::CIDR object.
|
1344
|
+
#
|
1345
|
+
# - Arguments:
|
1346
|
+
# * none
|
1347
|
+
#
|
1348
|
+
# - Returns:
|
1349
|
+
# * Byte-packed Netmask.
|
1350
|
+
#
|
1351
|
+
def packed_netmask()
|
1352
|
+
return(@netmask)
|
1353
|
+
end
|
1354
|
+
|
1355
|
+
#=====================================#
|
1356
|
+
#
|
1357
|
+
#=====================================#
|
1358
|
+
|
1359
|
+
|
1360
|
+
|
1361
|
+
#============================================================================#
|
1362
|
+
# packed_network()
|
1363
|
+
#============================================================================#
|
1364
|
+
|
1365
|
+
# Provide byte-packed Network address of cidr block of an IPAdmin::CIDR object.
|
1366
|
+
#
|
1367
|
+
# - Arguments:
|
1368
|
+
# * none
|
1369
|
+
#
|
1370
|
+
# - Returns:
|
1371
|
+
# * Byte-packed Network Address.
|
1372
|
+
#
|
1373
|
+
def packed_network()
|
1374
|
+
return(@network)
|
1375
|
+
end
|
1376
|
+
|
1377
|
+
#=====================================#
|
1378
|
+
#
|
1379
|
+
#=====================================#
|
1380
|
+
|
1381
|
+
|
1382
|
+
|
1383
|
+
#============================================================================#
|
1384
|
+
# size()
|
1385
|
+
#============================================================================#
|
1386
|
+
|
1387
|
+
# Provide number of addresses within cidr block of an IPAdmin::CIDR object.
|
1388
|
+
#
|
1389
|
+
# - Arguments:
|
1390
|
+
# * none
|
1391
|
+
#
|
1392
|
+
# - Returns:
|
1393
|
+
# * Number of IP addresses in this CIDR block.
|
1394
|
+
#
|
1395
|
+
def size()
|
1396
|
+
return(@hostmask + 1)
|
1397
|
+
end
|
1398
|
+
|
1399
|
+
#=====================================#
|
1400
|
+
#
|
1401
|
+
#=====================================#
|
1402
|
+
|
1403
|
+
|
1404
|
+
|
1405
|
+
#============================================================================#
|
1406
|
+
# subnet()
|
1407
|
+
#============================================================================#
|
1408
|
+
|
1409
|
+
# Subnet cidr block within an IPAdmin::CIDR object according to user
|
1410
|
+
# specifications.
|
1411
|
+
#
|
1412
|
+
# - Arguments:
|
1413
|
+
# * Hash with the following fields:
|
1414
|
+
# :Subnet -- number of bits of new subnet to create (24,26, etc...)
|
1415
|
+
# :MinCount -- minimum number of subnets of size :Subnet to return
|
1416
|
+
#
|
1417
|
+
# - Returns:
|
1418
|
+
# * array of IPAdmin::CIDR objects
|
1419
|
+
#
|
1420
|
+
def subnet(options)
|
1421
|
+
subnet = options[:Subnet]
|
1422
|
+
min_count = options[:MinCount]
|
1423
|
+
mymask = self.netmask
|
1424
|
+
num_avail = 2**(subnet - mymask)
|
1425
|
+
new_subnets = []
|
1426
|
+
|
1427
|
+
|
1428
|
+
|
1429
|
+
# make sure subnet isnt bigger than available bits
|
1430
|
+
if (@version == 4)
|
1431
|
+
max_bits = 32
|
1432
|
+
else
|
1433
|
+
max_bits = 128
|
1434
|
+
end
|
1435
|
+
|
1436
|
+
if (subnet > max_bits)
|
1437
|
+
raise "Requested subnet #{subnet} does not fit " +
|
1438
|
+
"within available CIDR space."
|
1439
|
+
end
|
1440
|
+
|
1441
|
+
|
1442
|
+
# make sure subnet is larger than mymask
|
1443
|
+
if (subnet < mymask)
|
1444
|
+
raise "Requested subnet #{subnet} does not fit " +
|
1445
|
+
"within available CIDR space."
|
1446
|
+
end
|
1447
|
+
|
1448
|
+
|
1449
|
+
# make sure MinCount is smaller than available subnets
|
1450
|
+
if (min_count > num_avail)
|
1451
|
+
raise "Requested subnet count exceeds subnets available for " +
|
1452
|
+
"allocation."
|
1453
|
+
end
|
1454
|
+
|
1455
|
+
|
1456
|
+
# list all 'subnet' sized subnets of this cidr block
|
1457
|
+
bitstep = 2**(max_bits - subnet)
|
1458
|
+
subs = self.enumerate(:BitStep => bitstep)
|
1459
|
+
subs.each do |sub|
|
1460
|
+
cidr = "#{sub}/#{subnet}"
|
1461
|
+
new_subnets.push(IPAdmin::CIDR.new(:CIDR => cidr))
|
1462
|
+
end
|
1463
|
+
|
1464
|
+
|
1465
|
+
# combine any unneeded subnets into as few
|
1466
|
+
# supernets as possible
|
1467
|
+
supernets = []
|
1468
|
+
supernet_groups = []
|
1469
|
+
if (min_count < num_avail )
|
1470
|
+
cidr_list = []
|
1471
|
+
num_left = num_avail - min_count
|
1472
|
+
multiplier = 1
|
1473
|
+
until (num_left < 2)
|
1474
|
+
if ( (2**multiplier) < num_left)
|
1475
|
+
multiplier = multiplier + 1
|
1476
|
+
elsif ( (2**multiplier) == num_left)
|
1477
|
+
supernet_groups.push( (2**multiplier) )
|
1478
|
+
num_left = num_left - (2**multiplier)
|
1479
|
+
break
|
1480
|
+
elsif ( (2**multiplier) > num_left)
|
1481
|
+
multiplier = multiplier -1
|
1482
|
+
supernet_groups.push( (2**multiplier) )
|
1483
|
+
num_left = num_left - (2**multiplier)
|
1484
|
+
multiplier = 1
|
1485
|
+
end
|
1486
|
+
end
|
1487
|
+
end
|
1488
|
+
|
1489
|
+
|
1490
|
+
supernet_groups.each do |group|
|
1491
|
+
cidr_list = []
|
1492
|
+
(1..group).each do
|
1493
|
+
cidr_list.push(new_subnets.pop)
|
1494
|
+
end
|
1495
|
+
supernets.push( IPAdmin.merge_cidr(cidr_list) )
|
1496
|
+
end
|
1497
|
+
|
1498
|
+
|
1499
|
+
# add supernets to new_subnets
|
1500
|
+
(1..supernets.length).each do
|
1501
|
+
new_subnets.push(supernets.pop)
|
1502
|
+
end
|
1503
|
+
|
1504
|
+
return(new_subnets)
|
1505
|
+
end
|
1506
|
+
|
1507
|
+
#=====================================#
|
1508
|
+
#
|
1509
|
+
#=====================================#
|
1510
|
+
|
1511
|
+
|
1512
|
+
|
1513
|
+
end
|
1514
|
+
|
1515
|
+
|
1516
|
+
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
1517
|
+
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
1518
|
+
#
|
1519
|
+
# END class CIDR
|
1520
|
+
#
|
1521
|
+
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
1522
|
+
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
1523
|
+
|
1524
|
+
|
1525
|
+
|
1526
|
+
|
1527
|
+
|
1528
|
+
|
1529
|
+
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
1530
|
+
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
1531
|
+
#
|
1532
|
+
# BEGIN class CIDRTable
|
1533
|
+
#
|
1534
|
+
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
1535
|
+
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
1536
|
+
|
1537
|
+
=begin rdoc
|
1538
|
+
A class & series of methods for creating and manipulating CIDR-based
|
1539
|
+
heirarchical tables. Both IPv4 and IPv6 are supported.
|
1540
|
+
=end
|
1541
|
+
|
1542
|
+
class CIDRTable
|
1543
|
+
|
1544
|
+
|
1545
|
+
|
1546
|
+
#============================================================================#
|
1547
|
+
# attr_reader / attr_writer
|
1548
|
+
#============================================================================#
|
1549
|
+
|
1550
|
+
|
1551
|
+
# ip table version (4 or 6)
|
1552
|
+
attr_reader :version
|
1553
|
+
|
1554
|
+
# contains IPAdmin::CIDR entries. hash (IPAdmin::CIDR => IPAdmin::CIDRTable)
|
1555
|
+
attr_reader :cidr_table
|
1556
|
+
|
1557
|
+
#=====================================#
|
1558
|
+
#
|
1559
|
+
#=====================================#
|
1560
|
+
|
1561
|
+
|
1562
|
+
|
1563
|
+
#============================================================================#
|
1564
|
+
# initialize()
|
1565
|
+
#============================================================================#
|
1566
|
+
|
1567
|
+
# - Arguments:
|
1568
|
+
# * Table version (4 or 6)
|
1569
|
+
#
|
1570
|
+
def initialize(version)
|
1571
|
+
|
1572
|
+
unless ( version.kind_of? Fixnum )
|
1573
|
+
raise "Expected Fixnum, but #{version.class} provided."
|
1574
|
+
end
|
1575
|
+
|
1576
|
+
unless ( version == 4 || version == 6 )
|
1577
|
+
raise "Version should be either 4 or 6."
|
1578
|
+
end
|
1579
|
+
|
1580
|
+
@version = version
|
1581
|
+
@cidr_table = {}
|
1582
|
+
end
|
1583
|
+
|
1584
|
+
#=====================================#
|
1585
|
+
#
|
1586
|
+
#=====================================#
|
1587
|
+
|
1588
|
+
|
1589
|
+
|
1590
|
+
#============================================================================#
|
1591
|
+
# add_cidr()
|
1592
|
+
#============================================================================#
|
1593
|
+
|
1594
|
+
# Add an IPAdmin::CIDR object to this table
|
1595
|
+
#
|
1596
|
+
# - Arguments:
|
1597
|
+
# * IPAdmin::CIDR object
|
1598
|
+
#
|
1599
|
+
# - Returns:
|
1600
|
+
# * nil
|
1601
|
+
#
|
1602
|
+
def add_cidr(new_cidr)
|
1603
|
+
|
1604
|
+
unless (new_cidr.version == @version )
|
1605
|
+
raise "CIDR version #{new_cidr.version} is incompatible with " +
|
1606
|
+
"CIDRTable version #{@version}."
|
1607
|
+
end
|
1608
|
+
|
1609
|
+
|
1610
|
+
|
1611
|
+
# compare new cidr with existing cidr's
|
1612
|
+
# determine if this is a parent or child of an existing cidr.
|
1613
|
+
# if neither, add @networks. if child of existing, add to that
|
1614
|
+
# child's @networks. if parent of existing, make existing child
|
1615
|
+
# of new cidr
|
1616
|
+
duplicate = nil
|
1617
|
+
parent_of = []
|
1618
|
+
child_of = nil
|
1619
|
+
@cidr_table.each_key do |old_cidr|
|
1620
|
+
parent,child = IPAdmin.compare_cidr(new_cidr,old_cidr)
|
1621
|
+
|
1622
|
+
if (parent)
|
1623
|
+
if (new_cidr == parent)
|
1624
|
+
parent_of.push(child)
|
1625
|
+
@cidr_table.delete(child)
|
1626
|
+
|
1627
|
+
elsif (new_cidr == child)
|
1628
|
+
child_of = parent
|
1629
|
+
break
|
1630
|
+
|
1631
|
+
elsif (parent == 1)
|
1632
|
+
duplicate = 1
|
1633
|
+
break
|
1634
|
+
end
|
1635
|
+
end
|
1636
|
+
end
|
1637
|
+
|
1638
|
+
|
1639
|
+
|
1640
|
+
if (parent_of.length != 0)
|
1641
|
+
new_entry = IPAdmin::CIDRTable.new(@version)
|
1642
|
+
parent_of.each do |old_cidr|
|
1643
|
+
new_entry.add_cidr(old_cidr)
|
1644
|
+
@cidr_table.delete(old_cidr)
|
1645
|
+
end
|
1646
|
+
@cidr_table[new_cidr] = new_entry
|
1647
|
+
|
1648
|
+
elsif (child_of)
|
1649
|
+
if (@cidr_table[child_of] == 0)
|
1650
|
+
new_entry = IPAdmin::CIDRTable.new(@version)
|
1651
|
+
@cidr_table[child_of] = new_entry
|
1652
|
+
(@cidr_table[child_of]).add_cidr(new_cidr)
|
1653
|
+
else
|
1654
|
+
(@cidr_table[child_of]).add_cidr(new_cidr)
|
1655
|
+
end
|
1656
|
+
|
1657
|
+
elsif (!duplicate)
|
1658
|
+
@cidr_table[new_cidr] = 0
|
1659
|
+
end
|
1660
|
+
end
|
1661
|
+
|
1662
|
+
#=====================================#
|
1663
|
+
#
|
1664
|
+
#=====================================#
|
1665
|
+
|
1666
|
+
|
1667
|
+
|
1668
|
+
#============================================================================#
|
1669
|
+
# dump()
|
1670
|
+
#============================================================================#
|
1671
|
+
|
1672
|
+
# Dump contents of this table
|
1673
|
+
#
|
1674
|
+
# - Arguments:
|
1675
|
+
# * nil
|
1676
|
+
#
|
1677
|
+
# - Returns:
|
1678
|
+
# * ordered array of IPAdmin::CIDR objects within this table
|
1679
|
+
#
|
1680
|
+
def dump()
|
1681
|
+
|
1682
|
+
list = []
|
1683
|
+
sorted_parents = []
|
1684
|
+
parent_hash = {}
|
1685
|
+
@cidr_table.each_key do |parent|
|
1686
|
+
parent_hash[parent.packed_network] = parent
|
1687
|
+
end
|
1688
|
+
sorted_parents = (parent_hash.keys).sort
|
1689
|
+
|
1690
|
+
|
1691
|
+
sorted_parents.each do |parent|
|
1692
|
+
parent_obj = parent_hash[parent]
|
1693
|
+
list.push(parent_obj)
|
1694
|
+
if (@cidr_table[parent_obj] != 0)
|
1695
|
+
children = (@cidr_table[parent_obj]).dump
|
1696
|
+
children.each do |child|
|
1697
|
+
list.push(child)
|
1698
|
+
end
|
1699
|
+
end
|
1700
|
+
|
1701
|
+
end
|
1702
|
+
|
1703
|
+
return(list)
|
1704
|
+
|
1705
|
+
end
|
1706
|
+
|
1707
|
+
#=====================================#
|
1708
|
+
#
|
1709
|
+
#=====================================#
|
1710
|
+
|
1711
|
+
|
1712
|
+
|
1713
|
+
#============================================================================#
|
1714
|
+
# find_ip()
|
1715
|
+
#============================================================================#
|
1716
|
+
|
1717
|
+
# Find longest matching IPAdmin::CIDR object whose cidr block contains the IP
|
1718
|
+
# address within the provided IPAdmin::IPAddr object
|
1719
|
+
#
|
1720
|
+
# - Arguments:
|
1721
|
+
# * IPAdmin::IPAddr object
|
1722
|
+
#
|
1723
|
+
# - Returns:
|
1724
|
+
# * IPAdmin::CIDR object, or nil on no match
|
1725
|
+
#
|
1726
|
+
def find_ip(ip_obj)
|
1727
|
+
|
1728
|
+
unless (ip_obj.kind_of? IPAdmin::IPAddr)
|
1729
|
+
raise "Expected IPAdmin::IPAddr, but #{options.class} provided."
|
1730
|
+
end
|
1731
|
+
|
1732
|
+
unless (ip_obj.version == @version )
|
1733
|
+
raise "IPAddr version #{ip_addr.version} is incompatible with " +
|
1734
|
+
"CIDRTable version #{@version}."
|
1735
|
+
end
|
1736
|
+
|
1737
|
+
belongs_to = nil
|
1738
|
+
@cidr_table.each_key do |parent|
|
1739
|
+
if ( parent.contains(ip_obj) )
|
1740
|
+
if (@cidr_table[parent] != 0)
|
1741
|
+
belongs_to = (@cidr_table[parent]).find_ip(ip_obj)
|
1742
|
+
end
|
1743
|
+
|
1744
|
+
if (!belongs_to)
|
1745
|
+
belongs_to = parent
|
1746
|
+
end
|
1747
|
+
end
|
1748
|
+
end
|
1749
|
+
|
1750
|
+
return(belongs_to)
|
1751
|
+
|
1752
|
+
end
|
1753
|
+
|
1754
|
+
#=====================================#
|
1755
|
+
#
|
1756
|
+
#=====================================#
|
1757
|
+
|
1758
|
+
|
1759
|
+
|
1760
|
+
#============================================================================#
|
1761
|
+
# find_space()
|
1762
|
+
#============================================================================#
|
1763
|
+
|
1764
|
+
# Find CIDR blocks of at least X size. Returns only the smallest matching
|
1765
|
+
# subnets of each supernet.
|
1766
|
+
#
|
1767
|
+
# - Arguments:
|
1768
|
+
# * Hash with the following fields:
|
1769
|
+
# :Size -- subnet size (number of bits)
|
1770
|
+
# :Limit -- max entries to return
|
1771
|
+
#
|
1772
|
+
# - Returns:
|
1773
|
+
# * ordered array of IPAdmin::CIDR objects, or nil on no match
|
1774
|
+
#
|
1775
|
+
def find_space(options)
|
1776
|
+
limit = nil
|
1777
|
+
list = []
|
1778
|
+
sorted_parents = []
|
1779
|
+
parent_hash = {}
|
1780
|
+
|
1781
|
+
|
1782
|
+
|
1783
|
+
# validate options
|
1784
|
+
unless (options.kind_of? Hash)
|
1785
|
+
raise "Expected Hash, but #{options.class} provided."
|
1786
|
+
end
|
1787
|
+
|
1788
|
+
unless ( options.has_key?(:Size) )
|
1789
|
+
raise "Missing argument :Size."
|
1790
|
+
end
|
1791
|
+
subnet_size = options[:Size]
|
1792
|
+
|
1793
|
+
if ( options.has_key?(:Limit) )
|
1794
|
+
limit = options[:Limit]
|
1795
|
+
end
|
1796
|
+
|
1797
|
+
|
1798
|
+
|
1799
|
+
# check that subnet_size is a valid size
|
1800
|
+
if (@version == 4)
|
1801
|
+
unless ( (subnet_size > 0) && (subnet_size < 33) )
|
1802
|
+
raise "#{subnet_size} is out of range for this CIDRTable."
|
1803
|
+
end
|
1804
|
+
|
1805
|
+
else
|
1806
|
+
unless ( (subnet_size > 0) && (subnet_size < 129) )
|
1807
|
+
raise "#{subnet_size} is out of range for this CIDRTable."
|
1808
|
+
end
|
1809
|
+
end
|
1810
|
+
|
1811
|
+
|
1812
|
+
# order cidr objects
|
1813
|
+
@cidr_table.each_key do |parent|
|
1814
|
+
parent_hash[parent.packed_network] = parent
|
1815
|
+
end
|
1816
|
+
sorted_parents = (parent_hash.keys).sort
|
1817
|
+
|
1818
|
+
|
1819
|
+
|
1820
|
+
# find space
|
1821
|
+
sorted_parents.each do |key|
|
1822
|
+
parent = parent_hash[key]
|
1823
|
+
if ( parent.netmask() < subnet_size )
|
1824
|
+
if (@cidr_table[parent] != 0)
|
1825
|
+
child_list = []
|
1826
|
+
child_list = (@cidr_table[parent]).find_space(:Size => subnet_size)
|
1827
|
+
child_list.each do |child|
|
1828
|
+
list.push(child)
|
1829
|
+
if ( (limit) && (list.length == limit) )
|
1830
|
+
break
|
1831
|
+
end
|
1832
|
+
|
1833
|
+
end
|
1834
|
+
|
1835
|
+
else
|
1836
|
+
list.push(parent)
|
1837
|
+
|
1838
|
+
end
|
1839
|
+
|
1840
|
+
elsif ( (parent_hash[key].netmask() == subnet_size) &&
|
1841
|
+
(@cidr_table[parent] == 0) )
|
1842
|
+
list.push(parent)
|
1843
|
+
|
1844
|
+
end
|
1845
|
+
|
1846
|
+
|
1847
|
+
if ( (limit) && (list.length == limit) )
|
1848
|
+
break
|
1849
|
+
end
|
1850
|
+
|
1851
|
+
end
|
1852
|
+
|
1853
|
+
return(list)
|
1854
|
+
|
1855
|
+
end
|
1856
|
+
|
1857
|
+
#=====================================#
|
1858
|
+
#
|
1859
|
+
#=====================================#
|
1860
|
+
|
1861
|
+
|
1862
|
+
|
1863
|
+
|
1864
|
+
end
|
1865
|
+
|
1866
|
+
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
1867
|
+
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
1868
|
+
#
|
1869
|
+
# END class CIDRTable
|
1870
|
+
#
|
1871
|
+
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
1872
|
+
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
1873
|
+
|
1874
|
+
|
1875
|
+
|
1876
|
+
|
1877
|
+
|
1878
|
+
|
1879
|
+
|
1880
|
+
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
1881
|
+
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
1882
|
+
#
|
1883
|
+
# BEGIN class IPAddr
|
1884
|
+
#
|
1885
|
+
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
1886
|
+
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
1887
|
+
|
1888
|
+
=begin rdoc
|
1889
|
+
A class & series of methods for creating and manipulating IP
|
1890
|
+
addresses. Both IPv4 and IPv6 are supported.
|
1891
|
+
=end
|
1892
|
+
|
1893
|
+
class IPAddr
|
1894
|
+
|
1895
|
+
|
1896
|
+
|
1897
|
+
#============================================================================#
|
1898
|
+
# attr_reader/attr_writer
|
1899
|
+
#============================================================================#
|
1900
|
+
|
1901
|
+
# @ip - packed ip address
|
1902
|
+
# @netmask - packed netmask
|
1903
|
+
# @hostmask - inverse packed netmask
|
1904
|
+
|
1905
|
+
# ip version 4 or 6
|
1906
|
+
attr_reader :version
|
1907
|
+
|
1908
|
+
# hash of custom tags. should be in the format tag => value
|
1909
|
+
attr_reader :tag
|
1910
|
+
attr_writer :tag
|
1911
|
+
|
1912
|
+
|
1913
|
+
#=====================================#
|
1914
|
+
#
|
1915
|
+
#=====================================#
|
1916
|
+
|
1917
|
+
|
1918
|
+
|
1919
|
+
#============================================================================#
|
1920
|
+
# initialize()
|
1921
|
+
#============================================================================#
|
1922
|
+
|
1923
|
+
# - Arguments:
|
1924
|
+
# * Hash with the following fields
|
1925
|
+
# :IPAddr -- IPv4 or IPv6 address (assume host address by default)
|
1926
|
+
# :Netmask -- IPv4 netmask in extended format ( if not part of IPAddr)
|
1927
|
+
# :Tag -- Custom descriptor tag for object. Should be Hash (tag => value)
|
1928
|
+
#
|
1929
|
+
def initialize(options)
|
1930
|
+
|
1931
|
+
unless (options.kind_of? Hash)
|
1932
|
+
raise "Expected Hash, but #{options.class} provided."
|
1933
|
+
end
|
1934
|
+
|
1935
|
+
if ( options.has_key?(:IPAddr) )
|
1936
|
+
ip_addr = options[:IPAddr]
|
1937
|
+
|
1938
|
+
|
1939
|
+
if (ip_addr =~/\./) # assume IPv4
|
1940
|
+
@version = 4
|
1941
|
+
|
1942
|
+
if (ip_addr =~ /\//)
|
1943
|
+
ip,netmask = ip_addr.split(/\//)
|
1944
|
+
|
1945
|
+
# validate
|
1946
|
+
IPAdmin.validate_ipv4_addr(ip)
|
1947
|
+
IPAdmin.validate_ipv4_netmask(netmask)
|
1948
|
+
|
1949
|
+
# pack
|
1950
|
+
@ip = IPAdmin.pack_ipv4_addr(ip)
|
1951
|
+
@netmask,@hostmask = IPAdmin.pack_ipv4_netmask(netmask)
|
1952
|
+
|
1953
|
+
else
|
1954
|
+
# validate
|
1955
|
+
IPAdmin.validate_ipv4_addr(ip_addr)
|
1956
|
+
|
1957
|
+
# pack
|
1958
|
+
@ip = IPAdmin.pack_ipv4_addr(ip_addr)
|
1959
|
+
|
1960
|
+
# check if netmask provided separately
|
1961
|
+
if ( options.has_key?(:Netmask) )
|
1962
|
+
netmask = options[:Netmask]
|
1963
|
+
|
1964
|
+
# validate
|
1965
|
+
IPAdmin.validate_ipv4_netmask(netmask)
|
1966
|
+
|
1967
|
+
# pack
|
1968
|
+
@netmask,@hostmask =
|
1969
|
+
IPAdmin.pack_ipv4_netmask(netmask)
|
1970
|
+
|
1971
|
+
else
|
1972
|
+
@netmask,@hostmask = IPAdmin.pack_ipv4_netmask('32')
|
1973
|
+
|
1974
|
+
end
|
1975
|
+
|
1976
|
+
end
|
1977
|
+
|
1978
|
+
|
1979
|
+
elsif (ip_addr =~/:/) # assume IPv6
|
1980
|
+
@version = 6
|
1981
|
+
|
1982
|
+
if (ip_addr =~ /\//)
|
1983
|
+
ip,netmask = ip_addr.split(/\//)
|
1984
|
+
|
1985
|
+
# validate
|
1986
|
+
IPAdmin.validate_ipv6_addr(ip)
|
1987
|
+
IPAdmin.validate_ipv6_netmask(netmask)
|
1988
|
+
|
1989
|
+
# pack
|
1990
|
+
@ip = IPAdmin.pack_ipv6_addr(ip)
|
1991
|
+
@netmask,@hostmask = IPAdmin.pack_ipv6_netmask(netmask)
|
1992
|
+
|
1993
|
+
else
|
1994
|
+
# validate
|
1995
|
+
IPAdmin.validate_ipv6_addr(ip_addr)
|
1996
|
+
|
1997
|
+
# pack
|
1998
|
+
@ip = IPAdmin.pack_ipv6_addr(ip_addr)
|
1999
|
+
@netmask,@hostmask = IPAdmin.pack_ipv6_netmask('128')
|
2000
|
+
|
2001
|
+
end
|
2002
|
+
|
2003
|
+
else
|
2004
|
+
raise "Invalid IPAddr address format: #{ip_addr}."
|
2005
|
+
end
|
2006
|
+
|
2007
|
+
else
|
2008
|
+
raise ArgumentError, "Missing arguments: IPAddr."
|
2009
|
+
|
2010
|
+
end
|
2011
|
+
|
2012
|
+
|
2013
|
+
# set tag if present
|
2014
|
+
if ( options.has_key?(:Tag) )
|
2015
|
+
@tag = options[:Tag]
|
2016
|
+
end
|
2017
|
+
|
2018
|
+
|
2019
|
+
end
|
2020
|
+
|
2021
|
+
|
2022
|
+
|
2023
|
+
#=====================================#
|
2024
|
+
#
|
2025
|
+
#=====================================#
|
2026
|
+
|
2027
|
+
|
2028
|
+
|
2029
|
+
#============================================================================#
|
2030
|
+
# base()
|
2031
|
+
#============================================================================#
|
2032
|
+
|
2033
|
+
# Provide base network address for address within IPAdmin::IPAddr object.
|
2034
|
+
#
|
2035
|
+
# - Arguments:
|
2036
|
+
# * none
|
2037
|
+
#
|
2038
|
+
# - Returns:
|
2039
|
+
# * base network address.
|
2040
|
+
#
|
2041
|
+
def base()
|
2042
|
+
if (@version == 4)
|
2043
|
+
packed_base = @ip & @netmask
|
2044
|
+
base = IPAdmin.unpack_ipv4_addr(packed_base)
|
2045
|
+
else
|
2046
|
+
packed_base = @ip & @netmask
|
2047
|
+
base = IPAdmin.unpack_ipv6_addr(packed_base)
|
2048
|
+
end
|
2049
|
+
|
2050
|
+
return(base)
|
2051
|
+
end
|
2052
|
+
|
2053
|
+
#=====================================#
|
2054
|
+
#
|
2055
|
+
#=====================================#
|
2056
|
+
|
2057
|
+
|
2058
|
+
|
2059
|
+
#============================================================================#
|
2060
|
+
# broadcast()
|
2061
|
+
#============================================================================#
|
2062
|
+
|
2063
|
+
# Provide IPv4 broadcast address for address within IPAdmin::IPAddr object.
|
2064
|
+
#
|
2065
|
+
# - Arguments:
|
2066
|
+
# * none
|
2067
|
+
#
|
2068
|
+
# - Returns:
|
2069
|
+
# * broadcast address.
|
2070
|
+
#
|
2071
|
+
def broadcast()
|
2072
|
+
if (@version == 4)
|
2073
|
+
inverse_mask = ~ @netmask
|
2074
|
+
packed_bcast = @ip | inverse_mask
|
2075
|
+
bcast = IPAdmin.unpack_ipv4_addr(packed_bcast)
|
2076
|
+
else
|
2077
|
+
raise "IPv6 does not support IP broadcast addresses."
|
2078
|
+
end
|
2079
|
+
|
2080
|
+
return(bcast)
|
2081
|
+
end
|
2082
|
+
|
2083
|
+
#=====================================#
|
2084
|
+
#
|
2085
|
+
#=====================================#
|
2086
|
+
|
2087
|
+
|
2088
|
+
|
2089
|
+
#============================================================================#
|
2090
|
+
# desc()
|
2091
|
+
#============================================================================#
|
2092
|
+
|
2093
|
+
# Provide description for IP address within IPAdmin::IPAddr object.
|
2094
|
+
#
|
2095
|
+
# - Arguments:
|
2096
|
+
# * none
|
2097
|
+
#
|
2098
|
+
# - Returns:
|
2099
|
+
# * ip/netmask.
|
2100
|
+
#
|
2101
|
+
def desc()
|
2102
|
+
if (@version == 4)
|
2103
|
+
ip = IPAdmin.unpack_ipv4_addr(@ip)
|
2104
|
+
netmask = IPAdmin.unpack_ipv4_netmask(@netmask)
|
2105
|
+
formatted = "#{ip}/#{netmask}"
|
2106
|
+
else
|
2107
|
+
ip = IPAdmin.unpack_ipv6_addr(@ip)
|
2108
|
+
netmask = IPAdmin.unpack_ipv6_netmask(@netmask)
|
2109
|
+
formatted = "#{ip}/#{netmask}"
|
2110
|
+
end
|
2111
|
+
|
2112
|
+
return(formatted)
|
2113
|
+
end
|
2114
|
+
|
2115
|
+
#=====================================#
|
2116
|
+
#
|
2117
|
+
#=====================================#
|
2118
|
+
|
2119
|
+
|
2120
|
+
|
2121
|
+
#============================================================================#
|
2122
|
+
# hostmask_ext()
|
2123
|
+
#============================================================================#
|
2124
|
+
|
2125
|
+
# Provide IPv4 Hostmask for address within IPAdmin::IPAddr object.
|
2126
|
+
#
|
2127
|
+
# - Arguments:
|
2128
|
+
# * none
|
2129
|
+
#
|
2130
|
+
# - Returns:
|
2131
|
+
# * IPv4 Hostmask in extended (y.y.y.y) format.
|
2132
|
+
#
|
2133
|
+
def hostmask_ext()
|
2134
|
+
if (@version == 4)
|
2135
|
+
hostmask = IPAdmin.unpack_ipv4_addr(@hostmask)
|
2136
|
+
else
|
2137
|
+
raise "IPv6 does not support extended hostmask notation."
|
2138
|
+
end
|
2139
|
+
|
2140
|
+
return(hostmask)
|
2141
|
+
end
|
2142
|
+
|
2143
|
+
#=====================================#
|
2144
|
+
#
|
2145
|
+
#=====================================#
|
2146
|
+
|
2147
|
+
|
2148
|
+
|
2149
|
+
#============================================================================#
|
2150
|
+
# ip()
|
2151
|
+
#============================================================================#
|
2152
|
+
|
2153
|
+
# Provide IP address of IPAdmin::IPAddr object.
|
2154
|
+
#
|
2155
|
+
# - Arguments:
|
2156
|
+
# * none
|
2157
|
+
#
|
2158
|
+
# - Returns:
|
2159
|
+
# * IP address.
|
2160
|
+
#
|
2161
|
+
def ip()
|
2162
|
+
if (@version == 4)
|
2163
|
+
formatted = IPAdmin.unpack_ipv4_addr(@ip)
|
2164
|
+
else
|
2165
|
+
formatted = IPAdmin.unpack_ipv6_addr(@ip)
|
2166
|
+
end
|
2167
|
+
|
2168
|
+
return(formatted)
|
2169
|
+
end
|
2170
|
+
|
2171
|
+
#=====================================#
|
2172
|
+
#
|
2173
|
+
#=====================================#
|
2174
|
+
|
2175
|
+
|
2176
|
+
|
2177
|
+
#============================================================================#
|
2178
|
+
# netmask()
|
2179
|
+
#============================================================================#
|
2180
|
+
|
2181
|
+
# Provide Netmask for address within IPAdmin::IPAddr object.
|
2182
|
+
#
|
2183
|
+
# - Arguments:
|
2184
|
+
# * none
|
2185
|
+
#
|
2186
|
+
# - Returns:
|
2187
|
+
# * Number of bits in the netmask.
|
2188
|
+
#
|
2189
|
+
def netmask()
|
2190
|
+
if (@version == 4)
|
2191
|
+
bits = IPAdmin.unpack_ipv4_netmask(@netmask)
|
2192
|
+
else
|
2193
|
+
bits = IPAdmin.unpack_ipv6_netmask(@netmask)
|
2194
|
+
end
|
2195
|
+
|
2196
|
+
return(bits)
|
2197
|
+
end
|
2198
|
+
|
2199
|
+
#=====================================#
|
2200
|
+
#
|
2201
|
+
#=====================================#
|
2202
|
+
|
2203
|
+
|
2204
|
+
|
2205
|
+
#============================================================================#
|
2206
|
+
# netmask_ext()
|
2207
|
+
#============================================================================#
|
2208
|
+
|
2209
|
+
# Provide IPv4 Netmask for address within IPAdmin::IPAddr object.
|
2210
|
+
#
|
2211
|
+
# - Arguments:
|
2212
|
+
# * none
|
2213
|
+
#
|
2214
|
+
# - Returns:
|
2215
|
+
# * IPv4 Netmask in extended (y.y.y.y) format.
|
2216
|
+
#
|
2217
|
+
def netmask_ext()
|
2218
|
+
if (@version == 4)
|
2219
|
+
netmask = IPAdmin.unpack_ipv4_addr(@netmask)
|
2220
|
+
else
|
2221
|
+
raise "IPv6 does not support extended netmask notation. " +
|
2222
|
+
"Use bits() method instead."
|
2223
|
+
end
|
2224
|
+
|
2225
|
+
return(netmask)
|
2226
|
+
end
|
2227
|
+
|
2228
|
+
#=====================================#
|
2229
|
+
#
|
2230
|
+
#=====================================#
|
2231
|
+
|
2232
|
+
|
2233
|
+
|
2234
|
+
#============================================================================#
|
2235
|
+
# packed_hostmask()
|
2236
|
+
#============================================================================#
|
2237
|
+
|
2238
|
+
# Provide byte-packed Hostmask within IPAdmin::IPAddr object.
|
2239
|
+
#
|
2240
|
+
# - Arguments:
|
2241
|
+
# * none
|
2242
|
+
#
|
2243
|
+
# - Returns:
|
2244
|
+
# * byte-packed Hostmask.
|
2245
|
+
#
|
2246
|
+
def packed_hostmask()
|
2247
|
+
return(@hostmask)
|
2248
|
+
end
|
2249
|
+
|
2250
|
+
#=====================================#
|
2251
|
+
#
|
2252
|
+
#=====================================#
|
2253
|
+
|
2254
|
+
|
2255
|
+
|
2256
|
+
#============================================================================#
|
2257
|
+
# packed_ip()
|
2258
|
+
#============================================================================#
|
2259
|
+
|
2260
|
+
# Provide byte-packed IP address of IPAdmin::IPAddr object.
|
2261
|
+
#
|
2262
|
+
# - Arguments:
|
2263
|
+
# * none
|
2264
|
+
#
|
2265
|
+
# - Returns:
|
2266
|
+
# * byte-packed IP address.
|
2267
|
+
#
|
2268
|
+
def packed_ip()
|
2269
|
+
return(@ip)
|
2270
|
+
end
|
2271
|
+
|
2272
|
+
#=====================================#
|
2273
|
+
#
|
2274
|
+
#=====================================#
|
2275
|
+
|
2276
|
+
|
2277
|
+
|
2278
|
+
#============================================================================#
|
2279
|
+
# packed_netmask()
|
2280
|
+
#============================================================================#
|
2281
|
+
|
2282
|
+
# Provide byte-packed Netmask for IP address of IPAdmin::IPAddr object.
|
2283
|
+
#
|
2284
|
+
# - Arguments:
|
2285
|
+
# * none
|
2286
|
+
#
|
2287
|
+
# - Returns:
|
2288
|
+
# * byte-packed Netask.
|
2289
|
+
#
|
2290
|
+
def packed_netmask()
|
2291
|
+
return(@netmask)
|
2292
|
+
end
|
2293
|
+
|
2294
|
+
#=====================================#
|
2295
|
+
#
|
2296
|
+
#=====================================#
|
2297
|
+
|
2298
|
+
|
2299
|
+
|
2300
|
+
end
|
2301
|
+
|
2302
|
+
|
2303
|
+
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
2304
|
+
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
2305
|
+
#
|
2306
|
+
# END class IPAddr
|
2307
|
+
#
|
2308
|
+
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
2309
|
+
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
|
2310
|
+
|
2311
|
+
|
2312
|
+
end # module IPAdmin
|
2313
|
+
|
2314
|
+
|
2315
|
+
|
2316
|
+
__END__
|
2317
|
+
|