netaddr 1.5.1 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of netaddr might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/LICENSE +201 -0
- data/README.md +29 -0
- data/lib/eui48.rb +65 -0
- data/lib/eui64.rb +77 -0
- data/lib/ipv4.rb +77 -0
- data/lib/ipv4net.rb +245 -0
- data/lib/ipv6.rb +110 -0
- data/lib/ipv6net.rb +246 -0
- data/lib/mask128.rb +68 -0
- data/lib/mask32.rb +99 -0
- data/lib/netaddr.rb +105 -27
- data/lib/util.rb +310 -0
- data/test/eui48_test.rb +30 -0
- data/test/eui64_test.rb +32 -0
- data/test/examples.rb +137 -0
- data/test/ipv4_test.rb +54 -0
- data/test/ipv4net_test.rb +166 -0
- data/test/ipv6_test.rb +90 -0
- data/test/ipv6net_test.rb +146 -0
- data/test/mask128_test.rb +45 -0
- data/test/mask32_test.rb +51 -0
- data/test/netaddr_test.rb +127 -0
- data/test/run_all.rb +10 -0
- metadata +29 -24
- data/Errors +0 -7
- data/README +0 -17
- data/changelog +0 -58
- data/lib/cidr.rb +0 -2189
- data/lib/cidr_shortcuts.rb +0 -440
- data/lib/eui.rb +0 -463
- data/lib/ip_math.rb +0 -259
- data/lib/methods.rb +0 -1079
- data/lib/tree.rb +0 -921
- data/lib/validation_shortcuts.rb +0 -219
- data/license +0 -13
- data/test/cidr_test.rb +0 -544
- data/test/eui_test.rb +0 -101
- data/test/methods_test.rb +0 -331
- data/test/tree_test.rb +0 -347
data/lib/ipv4net.rb
ADDED
@@ -0,0 +1,245 @@
|
|
1
|
+
module NetAddr
|
2
|
+
|
3
|
+
#IPv4Net represents an IPv4 network.
|
4
|
+
class IPv4Net
|
5
|
+
|
6
|
+
#arguments:
|
7
|
+
#* ip - an IPv4 object
|
8
|
+
#* m32 - a Mask32 object. will default to a /32 if nil
|
9
|
+
def initialize(ip,m32)
|
10
|
+
if (!ip.kind_of?(IPv4))
|
11
|
+
raise ArgumentError, "Expected an IPv4 object for 'ip' but got a #{ip.class}."
|
12
|
+
elsif (m32 != nil && !m32.kind_of?(Mask32))
|
13
|
+
raise ArgumentError, "Expected a Mask32 object for 'm32' but got a #{m32.class}."
|
14
|
+
end
|
15
|
+
|
16
|
+
if (m32 == nil)
|
17
|
+
m32 = Mask32.new(32)
|
18
|
+
end
|
19
|
+
@m32 = m32
|
20
|
+
@base = IPv4.new(ip.addr & m32.mask)
|
21
|
+
end
|
22
|
+
|
23
|
+
# parse will create an IPv4Net from its string representation. Will default to a /32 netmask if not specified.
|
24
|
+
# Throws ValidationError on error.
|
25
|
+
def IPv4Net.parse(net)
|
26
|
+
net.strip!
|
27
|
+
m32 = nil
|
28
|
+
if (net.include?("/")) # cidr format
|
29
|
+
addr,mask = net.split("/")
|
30
|
+
m32 = Mask32.parse(mask)
|
31
|
+
elsif (net.include?(" ") ) # extended format
|
32
|
+
addr,mask = net.split(' ')
|
33
|
+
m32 = Mask32.parse(mask)
|
34
|
+
else
|
35
|
+
addr = net
|
36
|
+
end
|
37
|
+
ip = IPv4.parse(addr)
|
38
|
+
return IPv4Net.new(ip,m32)
|
39
|
+
end
|
40
|
+
|
41
|
+
# extended returns the IPv4Net in extended format (eg. x.x.x.x y.y.y.y)
|
42
|
+
def extended()
|
43
|
+
return @base.to_s + " " + Util.int_to_IPv4(@m32.mask)
|
44
|
+
end
|
45
|
+
|
46
|
+
#cmp compares equality with another IPv4Net. Return:
|
47
|
+
#* 1 if this IPv4Net is numerically greater
|
48
|
+
#* 0 if the two are equal
|
49
|
+
#* -1 if this IPv4Net is numerically less
|
50
|
+
#
|
51
|
+
#The comparison is initially performed on using the cmp() method of the network address, however, in cases where the network #addresses are identical then the netmasks will be compared with the cmp() method of the netmask.
|
52
|
+
def cmp(other)
|
53
|
+
if (!other.kind_of?(IPv4Net))
|
54
|
+
raise ArgumentError, "Expected an IPv4Net object for 'other' but got a #{other.class}."
|
55
|
+
end
|
56
|
+
cmp = self.network.cmp(other.network)
|
57
|
+
if (cmp != 0)
|
58
|
+
return cmp
|
59
|
+
end
|
60
|
+
return self.netmask.cmp(other.netmask)
|
61
|
+
end
|
62
|
+
|
63
|
+
# fill returns a copy of the given Array, stripped of any networks which are not subnets of this IPv4Net
|
64
|
+
# and with any missing gaps filled in.
|
65
|
+
def fill(list)
|
66
|
+
list = Util.filter_IPv4Net(list)
|
67
|
+
return Util.fill(self,list)
|
68
|
+
end
|
69
|
+
|
70
|
+
# netmask returns the Mask32 object representing the netmask for this network
|
71
|
+
def netmask()
|
72
|
+
@m32
|
73
|
+
end
|
74
|
+
|
75
|
+
# network returns the IPv4 object representing the network address
|
76
|
+
def network()
|
77
|
+
@base
|
78
|
+
end
|
79
|
+
|
80
|
+
#len returns the number of IP addresses in this network. It will return 0 for /0 networks.
|
81
|
+
def len()
|
82
|
+
return self.netmask.len
|
83
|
+
end
|
84
|
+
|
85
|
+
# next returns the next largest consecutive IP network or nil if the end of the address space is reached.
|
86
|
+
def next()
|
87
|
+
net = self.nth_next_sib(1)
|
88
|
+
if (!net)
|
89
|
+
return nil
|
90
|
+
end
|
91
|
+
return net.grow
|
92
|
+
end
|
93
|
+
|
94
|
+
# next_sib returns the network immediately following this one or nil if the end of the address space is reached.
|
95
|
+
def next_sib()
|
96
|
+
self.nth_next_sib(1)
|
97
|
+
end
|
98
|
+
|
99
|
+
# nth returns the IPv4 at the given index.
|
100
|
+
# The size of the network may be determined with the len() method.
|
101
|
+
# If the range is exceeded then return nil.
|
102
|
+
def nth(index)
|
103
|
+
if (!index.kind_of?(Integer))
|
104
|
+
raise ArgumentError, "Expected an Integer for 'index' but got a #{index.class}."
|
105
|
+
elsif (index >= self.len)
|
106
|
+
return nil
|
107
|
+
end
|
108
|
+
return IPv4.new(self.network.addr + index)
|
109
|
+
end
|
110
|
+
|
111
|
+
# nth_subnet returns the subnet IPv4Net at the given index.
|
112
|
+
# The number of subnets may be determined with the subnet_count() method.
|
113
|
+
# If the range is exceeded or an invalid prefix_len is provided then return nil.
|
114
|
+
def nth_subnet(prefix_len,index)
|
115
|
+
count = self.subnet_count(prefix_len)
|
116
|
+
if (count == 0 || index >= count)
|
117
|
+
return nil
|
118
|
+
end
|
119
|
+
sub0 = IPv4Net.new(self.network, Mask32.new(prefix_len))
|
120
|
+
return sub0.nth_next_sib(index)
|
121
|
+
end
|
122
|
+
|
123
|
+
# prev returns the previous largest consecutive IP network or nil if this is 0.0.0.0.
|
124
|
+
def prev()
|
125
|
+
net = self.grow
|
126
|
+
return net.prev_sib
|
127
|
+
end
|
128
|
+
|
129
|
+
# prev_sib returns the network immediately preceding this one or nil if this network is 0.0.0.0.
|
130
|
+
def prev_sib()
|
131
|
+
if (self.network.addr == 0)
|
132
|
+
return nil
|
133
|
+
end
|
134
|
+
|
135
|
+
shift = 32 - self.netmask.prefix_len
|
136
|
+
addr = ((self.network.addr>>shift) - 1) << shift
|
137
|
+
return IPv4Net.new(IPv4.new(addr), self.netmask)
|
138
|
+
end
|
139
|
+
|
140
|
+
# rel determines the relationship to another IPv4Net. Returns:
|
141
|
+
# * 1 if this IPv4Net is the supernet of other
|
142
|
+
# * 0 if the two are equal
|
143
|
+
# * -1 if this IPv4Net is a subnet of other
|
144
|
+
# * nil if the networks are unrelated
|
145
|
+
def rel(other)
|
146
|
+
if (!other.kind_of?(IPv4Net))
|
147
|
+
raise ArgumentError, "Expected an IPv4Net object for 'other' but got a #{other.class}."
|
148
|
+
end
|
149
|
+
|
150
|
+
# when networks are equal then we can look exlusively at the netmask
|
151
|
+
if (self.network.addr == other.network.addr)
|
152
|
+
return self.netmask.cmp(other.netmask)
|
153
|
+
end
|
154
|
+
|
155
|
+
# when networks are not equal we can use hostmask to test if they are
|
156
|
+
# related and which is the supernet vs the subnet
|
157
|
+
hostmask = self.netmask.mask ^ NetAddr::F32
|
158
|
+
otherHostmask = other.netmask.mask ^ NetAddr::F32
|
159
|
+
if (self.network.addr|hostmask == other.network.addr|hostmask)
|
160
|
+
return 1
|
161
|
+
elsif (self.network.addr|otherHostmask == other.network.addr|otherHostmask)
|
162
|
+
return -1
|
163
|
+
end
|
164
|
+
return nil
|
165
|
+
end
|
166
|
+
|
167
|
+
# resize returns a copy of the network with an adjusted netmask.
|
168
|
+
# Throws ValidationError on invalid prefix_len.
|
169
|
+
def resize(prefix_len)
|
170
|
+
m32 = Mask32.new(prefix_len)
|
171
|
+
return IPv4Net.new(self.network,m32)
|
172
|
+
end
|
173
|
+
|
174
|
+
# subnet_count returns the number a subnets of a given prefix length that this IPv4Net contains.
|
175
|
+
# It will return 0 for invalid requests (ie. bad prefix or prefix is shorter than that of this network).
|
176
|
+
# It will also return 0 if the result exceeds the capacity of a 32-bit integer (ie. if you want the # of /32 a /0 will hold)
|
177
|
+
def subnet_count(prefix_len)
|
178
|
+
if (prefix_len <= self.netmask.prefix_len || prefix_len > 32 || prefix_len - self.netmask.prefix_len >= 32)
|
179
|
+
return 0
|
180
|
+
end
|
181
|
+
return 1 << (prefix_len - self.netmask.prefix_len)
|
182
|
+
end
|
183
|
+
|
184
|
+
# summ creates a summary address from this IPv4Net and another.
|
185
|
+
# It returns nil if the two networks are incapable of being summarized.
|
186
|
+
def summ(other)
|
187
|
+
if (!other.kind_of?(IPv4Net))
|
188
|
+
raise ArgumentError, "Expected an IPv4Net object for 'other' but got a #{other.class}."
|
189
|
+
end
|
190
|
+
|
191
|
+
# netmasks must be identical
|
192
|
+
if (self.netmask.prefix_len != other.netmask.prefix_len)
|
193
|
+
return nil
|
194
|
+
end
|
195
|
+
|
196
|
+
# merge-able networks will be identical if you right shift them by the number of bits in the hostmask + 1
|
197
|
+
shift = 32 - self.netmask.prefix_len + 1
|
198
|
+
addr = self.network.addr >> shift
|
199
|
+
otherAddr = other.network.addr >> shift
|
200
|
+
if (addr != otherAddr)
|
201
|
+
return nil
|
202
|
+
end
|
203
|
+
return self.resize(self.netmask.prefix_len - 1)
|
204
|
+
end
|
205
|
+
|
206
|
+
# to_s returns the IPv4Net as a String
|
207
|
+
def to_s()
|
208
|
+
return @base.to_s + @m32.to_s
|
209
|
+
end
|
210
|
+
|
211
|
+
|
212
|
+
protected
|
213
|
+
|
214
|
+
# grow decreases the prefix length as much as possible without crossing a bit boundary.
|
215
|
+
def grow()
|
216
|
+
addr = self.network.addr
|
217
|
+
mask = self.netmask.mask
|
218
|
+
prefix_len = self.netmask.prefix_len
|
219
|
+
self.netmask.prefix_len.downto(0) do
|
220
|
+
mask = (mask << 1) & NetAddr::F32
|
221
|
+
if addr|mask != mask || prefix_len == 0 # // bit boundary crossed when there are '1' bits in the host portion
|
222
|
+
break
|
223
|
+
end
|
224
|
+
prefix_len -= 1
|
225
|
+
end
|
226
|
+
return IPv4Net.new(IPv4.new(addr),Mask32.new(prefix_len))
|
227
|
+
end
|
228
|
+
|
229
|
+
# nth_next_sib returns the nth next sibling network or nil if address space exceeded.
|
230
|
+
def nth_next_sib(nth)
|
231
|
+
if (nth < 0)
|
232
|
+
return nil
|
233
|
+
end
|
234
|
+
|
235
|
+
shift = 32 - self.netmask.prefix_len
|
236
|
+
addr = ((self.network.addr>>shift) + nth) << shift
|
237
|
+
if addr > NetAddr::F32
|
238
|
+
return nil
|
239
|
+
end
|
240
|
+
return IPv4Net.new(IPv4.new(addr), self.netmask)
|
241
|
+
end
|
242
|
+
|
243
|
+
end # end class IPv4Net
|
244
|
+
|
245
|
+
end # end module
|
data/lib/ipv6.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
module NetAddr
|
2
|
+
|
3
|
+
#IPv6 represents a single IPv6 address.
|
4
|
+
class IPv6
|
5
|
+
# addr is the Integer representation of this IP address
|
6
|
+
attr_reader :addr
|
7
|
+
|
8
|
+
#Create an IPv6 from an Integer. Must be between 0 and 2**128-1.
|
9
|
+
#Throws ValidationError on error.
|
10
|
+
def initialize(i)
|
11
|
+
if (!i.kind_of?(Integer))
|
12
|
+
raise ValidationError, "Expected an Integer for 'i' but got a #{i.class}."
|
13
|
+
elsif ( (i < 0) || (i > NetAddr::F128) )
|
14
|
+
raise ValidationError, "#{i} is out of bounds for IPv6."
|
15
|
+
end
|
16
|
+
@addr = i
|
17
|
+
end
|
18
|
+
|
19
|
+
# parse will create an IPv6 from its string representation (ie. "192.168.1.1").
|
20
|
+
# Throws ValidationError on error.
|
21
|
+
def IPv6.parse(ip)
|
22
|
+
ip.strip!
|
23
|
+
i = Util.parse_IPv6(ip)
|
24
|
+
return IPv6.new(i)
|
25
|
+
end
|
26
|
+
|
27
|
+
#cmp compares equality with another IPv6. Return:
|
28
|
+
#* 1 if this IPv6 is numerically greater
|
29
|
+
#* 0 if the two are equal
|
30
|
+
#* -1 if this IPv6 is numerically less
|
31
|
+
def cmp(other)
|
32
|
+
if (!other.kind_of?(IPv6))
|
33
|
+
raise ArgumentError, "Expected an IPv6 object for 'other' but got a #{other.class}."
|
34
|
+
end
|
35
|
+
if (self.addr > other.addr)
|
36
|
+
return 1
|
37
|
+
elsif (self.addr < other.addr)
|
38
|
+
return -1
|
39
|
+
end
|
40
|
+
return 0
|
41
|
+
end
|
42
|
+
|
43
|
+
# long returns the IPv6 as a string in long (uncompressed) format
|
44
|
+
def long()
|
45
|
+
words = []
|
46
|
+
7.downto(0) do |x|
|
47
|
+
word = (@addr >> 16*x) & 0xffff
|
48
|
+
words.push( word.to_s(16).rjust(4, "0") )
|
49
|
+
end
|
50
|
+
return words.join(':')
|
51
|
+
end
|
52
|
+
|
53
|
+
# next returns the next consecutive IPv6 or nil if the address space is exceeded
|
54
|
+
def next()
|
55
|
+
if (self.addr == NetAddr::F128)
|
56
|
+
return nil
|
57
|
+
end
|
58
|
+
return IPv6.new(self.addr + 1)
|
59
|
+
end
|
60
|
+
|
61
|
+
# prev returns the preceding IPv6 or nil if this is 0.0.0.0
|
62
|
+
def prev()
|
63
|
+
if (self.addr == 0)
|
64
|
+
return nil
|
65
|
+
end
|
66
|
+
return IPv6.new(self.addr - 1)
|
67
|
+
end
|
68
|
+
|
69
|
+
# to_s returns the IPv6 as a String in zero-compressed format (per rfc5952).
|
70
|
+
def to_s()
|
71
|
+
hexStr = ["","","","","","","",""]
|
72
|
+
zeroStart, consec0, finalStart, finalLen = -1,0,-1,0
|
73
|
+
8.times do |i|
|
74
|
+
# capture 2-byte word
|
75
|
+
shift = 112 - 16*i
|
76
|
+
wd = (self.addr >> shift) & 0xffff
|
77
|
+
hexStr[i] = wd.to_s(16)
|
78
|
+
|
79
|
+
# capture count of consecutive zeros
|
80
|
+
if (wd == 0)
|
81
|
+
if (zeroStart == -1)
|
82
|
+
zeroStart = i
|
83
|
+
end
|
84
|
+
consec0 += 1
|
85
|
+
end
|
86
|
+
|
87
|
+
# test for longest consecutive zeros when non-zero encountered or we're at the end
|
88
|
+
if (wd != 0 || i == 7)
|
89
|
+
if (consec0 > finalStart+finalLen-1)
|
90
|
+
finalStart = zeroStart
|
91
|
+
finalLen = consec0
|
92
|
+
end
|
93
|
+
zeroStart = -1
|
94
|
+
consec0 = 0
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# compress if we've found a series of 0 words in a row
|
99
|
+
if (finalStart != -1)
|
100
|
+
head = hexStr[0,finalStart].join(":")
|
101
|
+
tailStart = finalStart + finalLen
|
102
|
+
tail = hexStr[tailStart..7].join(":")
|
103
|
+
return head + "::" + tail
|
104
|
+
end
|
105
|
+
return hexStr.join(":")
|
106
|
+
end
|
107
|
+
|
108
|
+
end # end class IPv6
|
109
|
+
|
110
|
+
end # end module
|
data/lib/ipv6net.rb
ADDED
@@ -0,0 +1,246 @@
|
|
1
|
+
module NetAddr
|
2
|
+
|
3
|
+
#IPv6Net represents an IPv6 network.
|
4
|
+
class IPv6Net
|
5
|
+
|
6
|
+
#arguments:
|
7
|
+
#* ip - an IPv6 object
|
8
|
+
#* m128 - a Mask128 object. will default to a /64 if nil
|
9
|
+
def initialize(ip,m128)
|
10
|
+
if (!ip.kind_of?(IPv6))
|
11
|
+
raise ArgumentError, "Expected an IPv6 object for 'ip' but got a #{ip.class}."
|
12
|
+
elsif (m128 != nil && !m128.kind_of?(Mask128))
|
13
|
+
raise ArgumentError, "Expected a Mask128 object for 'm128' but got a #{m128.class}."
|
14
|
+
end
|
15
|
+
|
16
|
+
if (m128 == nil)
|
17
|
+
if (ip.addr != 0)
|
18
|
+
m128 = Mask128.new(64) # use /64 mask per rfc 4291
|
19
|
+
else
|
20
|
+
m128 = Mask128.new(128) # use /128 mask per rfc 4291
|
21
|
+
end
|
22
|
+
end
|
23
|
+
@m128 = m128
|
24
|
+
@base = IPv6.new(ip.addr & m128.mask)
|
25
|
+
end
|
26
|
+
|
27
|
+
# parse will create an IPv6Net from its string representation. Will default to a /64 netmask if not specified.
|
28
|
+
# Throws ValidationError on error.
|
29
|
+
def IPv6Net.parse(net)
|
30
|
+
m128 = nil
|
31
|
+
net.strip!
|
32
|
+
if (net.include?("/")) # cidr format
|
33
|
+
addr,mask = net.split("/")
|
34
|
+
m128 = Mask128.parse(mask)
|
35
|
+
else
|
36
|
+
addr = net
|
37
|
+
end
|
38
|
+
ip = IPv6.parse(addr)
|
39
|
+
return IPv6Net.new(ip,m128)
|
40
|
+
end
|
41
|
+
|
42
|
+
#cmp compares equality with another IPv6Net. Return:
|
43
|
+
#* 1 if this IPv6Net is numerically greater
|
44
|
+
#* 0 if the two are equal
|
45
|
+
#* -1 if this IPv6Net is numerically less
|
46
|
+
#
|
47
|
+
#The comparison is initially performed on using the cmp() method of the network address, however, in cases where the network #addresses are identical then the netmasks will be compared with the cmp() method of the netmask.
|
48
|
+
def cmp(other)
|
49
|
+
if (!other.kind_of?(IPv6Net))
|
50
|
+
raise ArgumentError, "Expected an IPv6Net object for 'other' but got a #{other.class}."
|
51
|
+
end
|
52
|
+
cmp = self.network.cmp(other.network)
|
53
|
+
if (cmp != 0)
|
54
|
+
return cmp
|
55
|
+
end
|
56
|
+
return self.netmask.cmp(other.netmask)
|
57
|
+
end
|
58
|
+
|
59
|
+
# fill returns a copy of the given Array, stripped of any networks which are not subnets of this IPv6Net
|
60
|
+
# and with any missing gaps filled in.
|
61
|
+
def fill(list)
|
62
|
+
list = Util.filter_IPv6Net(list)
|
63
|
+
return Util.fill(self,list)
|
64
|
+
end
|
65
|
+
|
66
|
+
#len returns the number of IP addresses in this network. It will return 0 for /0 networks.
|
67
|
+
def len()
|
68
|
+
return self.netmask.len
|
69
|
+
end
|
70
|
+
|
71
|
+
# netmask returns the Mask128 object representing the netmask for this network
|
72
|
+
def netmask()
|
73
|
+
@m128
|
74
|
+
end
|
75
|
+
|
76
|
+
# network returns the IPv6 object representing the network address
|
77
|
+
def network()
|
78
|
+
@base
|
79
|
+
end
|
80
|
+
|
81
|
+
# next returns the next largest consecutive IP network or nil if the end of the address space is reached.
|
82
|
+
def next()
|
83
|
+
net = self.nth_next_sib(1)
|
84
|
+
if (!net)
|
85
|
+
return nil
|
86
|
+
end
|
87
|
+
return net.grow
|
88
|
+
end
|
89
|
+
|
90
|
+
# next_sib returns the network immediately following this one or nil if the end of the address space is reached.
|
91
|
+
def next_sib()
|
92
|
+
self.nth_next_sib(1)
|
93
|
+
end
|
94
|
+
|
95
|
+
# nth returns the IPv6 at the given index.
|
96
|
+
# The size of the network may be determined with the len() method.
|
97
|
+
# If the range is exceeded then return nil.
|
98
|
+
def nth(index)
|
99
|
+
if (!index.kind_of?(Integer))
|
100
|
+
raise ArgumentError, "Expected an Integer for 'index' but got a #{index.class}."
|
101
|
+
elsif (self.netmask.prefix_len < 64 || (self.netmask.prefix_len > 64 && index >= self.len))
|
102
|
+
return nil
|
103
|
+
end
|
104
|
+
return IPv6.new(self.network.addr + index)
|
105
|
+
end
|
106
|
+
|
107
|
+
# nth_subnet returns the subnet IPv6Net at the given index.
|
108
|
+
# The number of subnets may be determined with the subnet_count() method.
|
109
|
+
# If the range is exceeded or an invalid prefix_len is provided then return nil.
|
110
|
+
def nth_subnet(prefix_len,index)
|
111
|
+
count = self.subnet_count(prefix_len)
|
112
|
+
if (count == 0 || index >= count)
|
113
|
+
return nil
|
114
|
+
end
|
115
|
+
sub0 = IPv6Net.new(self.network, Mask128.new(prefix_len))
|
116
|
+
return sub0.nth_next_sib(index)
|
117
|
+
end
|
118
|
+
|
119
|
+
# prev returns the previous largest consecutive IP network or nil if this is 0.0.0.0.
|
120
|
+
def prev()
|
121
|
+
net = self.grow
|
122
|
+
return net.prev_sib
|
123
|
+
end
|
124
|
+
|
125
|
+
# prev_sib returns the network immediately preceding this one or nil if this network is 0.0.0.0.
|
126
|
+
def prev_sib()
|
127
|
+
if (self.network.addr == 0)
|
128
|
+
return nil
|
129
|
+
end
|
130
|
+
|
131
|
+
shift = 128 - self.netmask.prefix_len
|
132
|
+
addr = ((self.network.addr>>shift) - 1) << shift
|
133
|
+
if addr < 0
|
134
|
+
return nil
|
135
|
+
end
|
136
|
+
return IPv6Net.new(IPv6.new(addr), self.netmask)
|
137
|
+
end
|
138
|
+
|
139
|
+
# rel determines the relationship to another IPv6Net. Returns:
|
140
|
+
# * 1 if this IPv6Net is the supernet of other
|
141
|
+
# * 0 if the two are equal
|
142
|
+
# * -1 if this IPv6Net is a subnet of other
|
143
|
+
# * nil if the networks are unrelated
|
144
|
+
def rel(other)
|
145
|
+
if (!other.kind_of?(IPv6Net))
|
146
|
+
raise ArgumentError, "Expected an IPv6Net object for 'other' but got a #{other.class}."
|
147
|
+
end
|
148
|
+
|
149
|
+
# when networks are equal then we can look exlusively at the netmask
|
150
|
+
if (self.network.addr == other.network.addr)
|
151
|
+
return self.netmask.cmp(other.netmask)
|
152
|
+
end
|
153
|
+
|
154
|
+
# when networks are not equal we can use hostmask to test if they are
|
155
|
+
# related and which is the supernet vs the subnet
|
156
|
+
hostmask = self.netmask.mask ^ NetAddr::F128
|
157
|
+
otherHostmask = other.netmask.mask ^ NetAddr::F128
|
158
|
+
if (self.network.addr|hostmask == other.network.addr|hostmask)
|
159
|
+
return 1
|
160
|
+
elsif (self.network.addr|otherHostmask == other.network.addr|otherHostmask)
|
161
|
+
return -1
|
162
|
+
end
|
163
|
+
return nil
|
164
|
+
end
|
165
|
+
|
166
|
+
# resize returns a copy of the network with an adjusted netmask.
|
167
|
+
# Throws ValidationError on invalid prefix_len.
|
168
|
+
def resize(prefix_len)
|
169
|
+
m128 = Mask128.new(prefix_len)
|
170
|
+
return IPv6Net.new(self.network,m128)
|
171
|
+
end
|
172
|
+
|
173
|
+
# subnet_count returns the number a subnets of a given prefix length that this IPv6Net contains.
|
174
|
+
# It will return 0 for invalid requests (ie. bad prefix or prefix is shorter than that of this network).
|
175
|
+
# It will also return 0 if the result exceeds the capacity of a 128-bit integer (ie. if you want the # of /128 a /0 will hold)
|
176
|
+
def subnet_count(prefix_len)
|
177
|
+
if (prefix_len <= self.netmask.prefix_len || prefix_len > 128 || prefix_len - self.netmask.prefix_len >= 128)
|
178
|
+
return 0
|
179
|
+
end
|
180
|
+
return 1 << (prefix_len - self.netmask.prefix_len)
|
181
|
+
end
|
182
|
+
|
183
|
+
# summ creates a summary address from this IPv6Net and another.
|
184
|
+
# It returns nil if the two networks are incapable of being summarized.
|
185
|
+
def summ(other)
|
186
|
+
if (!other.kind_of?(IPv6Net))
|
187
|
+
raise ArgumentError, "Expected an IPv6Net object for 'other' but got a #{other.class}."
|
188
|
+
end
|
189
|
+
|
190
|
+
# netmasks must be identical
|
191
|
+
if (self.netmask.prefix_len != other.netmask.prefix_len)
|
192
|
+
return nil
|
193
|
+
end
|
194
|
+
|
195
|
+
# merge-able networks will be identical if you right shift them by the number of bits in the hostmask + 1
|
196
|
+
shift = 128 - self.netmask.prefix_len + 1
|
197
|
+
addr = self.network.addr >> shift
|
198
|
+
otherAddr = other.network.addr >> shift
|
199
|
+
if (addr != otherAddr)
|
200
|
+
return nil
|
201
|
+
end
|
202
|
+
return self.resize(self.netmask.prefix_len - 1)
|
203
|
+
end
|
204
|
+
|
205
|
+
# to_s returns the IPv6Net as a String
|
206
|
+
def to_s()
|
207
|
+
return @base.to_s + @m128.to_s
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
protected
|
212
|
+
|
213
|
+
# grow decreases the prefix length as much as possible without crossing a bit boundary.
|
214
|
+
def grow()
|
215
|
+
addr = self.network.addr
|
216
|
+
mask = self.netmask.mask
|
217
|
+
prefix_len = self.netmask.prefix_len
|
218
|
+
self.netmask.prefix_len.downto(0) do
|
219
|
+
mask = (mask << 1) & NetAddr::F128
|
220
|
+
if addr|mask != mask || prefix_len == 0 # // bit boundary crossed when there are '1' bits in the host portion
|
221
|
+
break
|
222
|
+
end
|
223
|
+
prefix_len -= 1
|
224
|
+
end
|
225
|
+
|
226
|
+
newNet = IPv6Net.new(IPv6.new(addr),Mask128.new(prefix_len))
|
227
|
+
return IPv6Net.new(IPv6.new(addr),Mask128.new(prefix_len))
|
228
|
+
end
|
229
|
+
|
230
|
+
# nth_next_sib returns the nth next sibling network or nil if address space exceeded.
|
231
|
+
def nth_next_sib(nth)
|
232
|
+
if (nth < 0)
|
233
|
+
return nil
|
234
|
+
end
|
235
|
+
|
236
|
+
shift = 128 - self.netmask.prefix_len
|
237
|
+
addr = ((self.network.addr>>shift) + nth) << shift
|
238
|
+
if addr > NetAddr::F128
|
239
|
+
return nil
|
240
|
+
end
|
241
|
+
return IPv6Net.new(IPv6.new(addr), self.netmask)
|
242
|
+
end
|
243
|
+
|
244
|
+
end # end class IPv6Net
|
245
|
+
|
246
|
+
end # end module
|