netaddr 1.5.3 → 2.0.6

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/lib/ipv4net.rb ADDED
@@ -0,0 +1,262 @@
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 = 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
52
+ # addresses are identical then the netmasks will be compared with the cmp() method of the netmask.
53
+ def cmp(other)
54
+ if (!other.kind_of?(IPv4Net))
55
+ raise ArgumentError, "Expected an IPv4Net object for 'other' but got a #{other.class}."
56
+ end
57
+ cmp = self.network.cmp(other.network)
58
+ if (cmp != 0)
59
+ return cmp
60
+ end
61
+ return self.netmask.cmp(other.netmask)
62
+ end
63
+
64
+ #contains returns true if the IPv4Net contains the IPv4
65
+ def contains(ip)
66
+ if (!ip.kind_of?(IPv4))
67
+ raise ArgumentError, "Expected an IPv4 object for 'ip' but got a #{ip.class}."
68
+ end
69
+ if (@base.addr == ip.addr & @m32.mask)
70
+ return true
71
+ end
72
+ return false
73
+ end
74
+
75
+ # fill returns a copy of the given Array, stripped of any networks which are not subnets of this IPv4Net
76
+ # and with any missing gaps filled in.
77
+ def fill(list)
78
+ list = Util.filter_IPv4Net(list)
79
+ return Util.fill(self,list)
80
+ end
81
+
82
+ # netmask returns the Mask32 object representing the netmask for this network
83
+ def netmask()
84
+ @m32
85
+ end
86
+
87
+ # network returns the IPv4 object representing the network address
88
+ def network()
89
+ @base
90
+ end
91
+
92
+ #len returns the number of IP addresses in this network. It will return 0 for /0 networks.
93
+ def len()
94
+ return self.netmask.len
95
+ end
96
+
97
+ # next returns the next largest consecutive IP network or nil if the end of the address space is reached.
98
+ def next()
99
+ net = self.nth_next_sib(1)
100
+ if (!net)
101
+ return nil
102
+ end
103
+ return net.grow
104
+ end
105
+
106
+ # next_sib returns the network immediately following this one or nil if the end of the address space is reached.
107
+ def next_sib()
108
+ self.nth_next_sib(1)
109
+ end
110
+
111
+ # nth returns the IPv4 at the given index.
112
+ # The size of the network may be determined with the len() method.
113
+ # If the range is exceeded then return nil.
114
+ def nth(index)
115
+ if (!index.kind_of?(Integer))
116
+ raise ArgumentError, "Expected an Integer for 'index' but got a #{index.class}."
117
+ elsif (index >= self.len)
118
+ return nil
119
+ end
120
+ return IPv4.new(self.network.addr + index)
121
+ end
122
+
123
+ # nth_subnet returns the subnet IPv4Net at the given index.
124
+ # The number of subnets may be determined with the subnet_count() method.
125
+ # If the range is exceeded or an invalid prefix_len is provided then return nil.
126
+ def nth_subnet(prefix_len,index)
127
+ count = self.subnet_count(prefix_len)
128
+ if (count == 0 || index >= count)
129
+ return nil
130
+ end
131
+ sub0 = IPv4Net.new(self.network, Mask32.new(prefix_len))
132
+ return sub0.nth_next_sib(index)
133
+ end
134
+
135
+ # prev returns the previous largest consecutive IP network or nil if this is 0.0.0.0.
136
+ def prev()
137
+ net = self.grow
138
+ return net.prev_sib
139
+ end
140
+
141
+ # prev_sib returns the network immediately preceding this one or nil if this network is 0.0.0.0.
142
+ def prev_sib()
143
+ if (self.network.addr == 0)
144
+ return nil
145
+ end
146
+
147
+ shift = 32 - self.netmask.prefix_len
148
+ addr = ((self.network.addr>>shift) - 1) << shift
149
+ return IPv4Net.new(IPv4.new(addr), self.netmask)
150
+ end
151
+
152
+ # rel determines the relationship to another IPv4Net. Returns:
153
+ # * 1 if this IPv4Net is the supernet of other
154
+ # * 0 if the two are equal
155
+ # * -1 if this IPv4Net is a subnet of other
156
+ # * nil if the networks are unrelated
157
+ def rel(other)
158
+ if (!other.kind_of?(IPv4Net))
159
+ raise ArgumentError, "Expected an IPv4Net object for 'other' but got a #{other.class}."
160
+ end
161
+
162
+ # when networks are equal then we can look exlusively at the netmask
163
+ if (self.network.addr == other.network.addr)
164
+ return self.netmask.cmp(other.netmask)
165
+ end
166
+
167
+ # when networks are not equal we can use hostmask to test if they are
168
+ # related and which is the supernet vs the subnet
169
+ hostmask = self.netmask.mask ^ NetAddr::F32
170
+ otherHostmask = other.netmask.mask ^ NetAddr::F32
171
+ if (self.network.addr|hostmask == other.network.addr|hostmask)
172
+ return 1
173
+ elsif (self.network.addr|otherHostmask == other.network.addr|otherHostmask)
174
+ return -1
175
+ end
176
+ return nil
177
+ end
178
+
179
+ # resize returns a copy of the network with an adjusted netmask.
180
+ # Throws ValidationError on invalid prefix_len.
181
+ def resize(prefix_len)
182
+ m32 = Mask32.new(prefix_len)
183
+ return IPv4Net.new(self.network,m32)
184
+ end
185
+
186
+ # subnet_count returns the number a subnets of a given prefix length that this IPv4Net contains.
187
+ # It will return 0 for invalid requests (ie. bad prefix or prefix is shorter than that of this network).
188
+ # 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)
189
+ def subnet_count(prefix_len)
190
+ if (prefix_len <= self.netmask.prefix_len || prefix_len > 32 || prefix_len - self.netmask.prefix_len >= 32)
191
+ return 0
192
+ end
193
+ return 1 << (prefix_len - self.netmask.prefix_len)
194
+ end
195
+
196
+ # summ creates a summary address from this IPv4Net and another.
197
+ # It returns nil if the two networks are incapable of being summarized.
198
+ def summ(other)
199
+ if (!other.kind_of?(IPv4Net))
200
+ raise ArgumentError, "Expected an IPv4Net object for 'other' but got a #{other.class}."
201
+ end
202
+
203
+ # netmasks must be identical
204
+ if (self.netmask.prefix_len != other.netmask.prefix_len)
205
+ return nil
206
+ end
207
+
208
+ # merge-able networks will be identical if you right shift them by the number of bits in the hostmask + 1
209
+ shift = 32 - self.netmask.prefix_len + 1
210
+ addr = self.network.addr >> shift
211
+ otherAddr = other.network.addr >> shift
212
+ if (addr != otherAddr)
213
+ return nil
214
+ end
215
+ return self.resize(self.netmask.prefix_len - 1)
216
+ end
217
+
218
+ # to_s returns the IPv4Net as a String
219
+ def to_s()
220
+ return @base.to_s + @m32.to_s
221
+ end
222
+
223
+ # version returns "4" for IPv4
224
+ def version()
225
+ return 4
226
+ end
227
+
228
+
229
+ protected
230
+
231
+ # grow decreases the prefix length as much as possible without crossing a bit boundary.
232
+ def grow()
233
+ addr = self.network.addr
234
+ mask = self.netmask.mask
235
+ prefix_len = self.netmask.prefix_len
236
+ self.netmask.prefix_len.downto(0) do
237
+ mask = (mask << 1) & NetAddr::F32
238
+ if addr|mask != mask || prefix_len == 0 # // bit boundary crossed when there are '1' bits in the host portion
239
+ break
240
+ end
241
+ prefix_len -= 1
242
+ end
243
+ return IPv4Net.new(IPv4.new(addr),Mask32.new(prefix_len))
244
+ end
245
+
246
+ # nth_next_sib returns the nth next sibling network or nil if address space exceeded.
247
+ def nth_next_sib(nth)
248
+ if (nth < 0)
249
+ return nil
250
+ end
251
+
252
+ shift = 32 - self.netmask.prefix_len
253
+ addr = ((self.network.addr>>shift) + nth) << shift
254
+ if addr > NetAddr::F32
255
+ return nil
256
+ end
257
+ return IPv4Net.new(IPv4.new(addr), self.netmask)
258
+ end
259
+
260
+ end # end class IPv4Net
261
+
262
+ end # end module
data/lib/ipv6.rb ADDED
@@ -0,0 +1,146 @@
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. "1::").
20
+ # Throws ValidationError on error.
21
+ def IPv6.parse(ip)
22
+ ip = 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
+ # ipv4 generates an IPv4 from an IPv6 address. The IPv4 address is generated based on the mechanism described by RFC 6052.
44
+ # The argument pl (prefix length) should be one of: 32, 40, 48, 56, 64, or 96. Default is 96 unless one of the supported values is provided.
45
+ def ipv4(pl=96)
46
+ if (pl == 32)
47
+ i = (@addr >> 64) # get bits 32-63 into position
48
+ return IPv4.new(i & NetAddr::F32)
49
+ elsif (pl == 40)
50
+ i = (@addr >> 48) & 0xff # get the last 8 bits into position
51
+ i2 = (@addr & 0xffffff0000000000000000) >> 56 # get first 24 bits into position
52
+ return IPv4.new(i | i2)
53
+ elsif (pl == 48)
54
+ i = (@addr >> 40) & 0xffff # get the last 16 bits into position
55
+ i2 = (@addr & 0xffff0000000000000000) >> 48 # get first 16 bits into position
56
+ return IPv4.new(i | i2)
57
+ elsif (pl == 56)
58
+ i = (@addr >> 32) & 0xffffff # get the last 24 bits into position
59
+ i2 = (@addr & 0xff0000000000000000) >> 40 # get first 8 bits into position
60
+ return IPv4.new(i | i2)
61
+ elsif (pl == 64)
62
+ i = (@addr >> 24) # get the 32 bits into position
63
+ return IPv4.new(i & NetAddr::F32)
64
+ end
65
+ return IPv4.new(@addr & NetAddr::F32)
66
+ end
67
+
68
+ # long returns the IPv6 as a string in long (uncompressed) format
69
+ def long()
70
+ words = []
71
+ 7.downto(0) do |x|
72
+ word = (@addr >> 16*x) & 0xffff
73
+ words.push( word.to_s(16).rjust(4, "0") )
74
+ end
75
+ return words.join(':')
76
+ end
77
+
78
+ # next returns the next consecutive IPv6 or nil if the address space is exceeded
79
+ def next()
80
+ if (self.addr == NetAddr::F128)
81
+ return nil
82
+ end
83
+ return IPv6.new(self.addr + 1)
84
+ end
85
+
86
+ # prev returns the preceding IPv6 or nil if this is 0.0.0.0
87
+ def prev()
88
+ if (self.addr == 0)
89
+ return nil
90
+ end
91
+ return IPv6.new(self.addr - 1)
92
+ end
93
+
94
+ # to_net returns the IPv6 as a IPv6Net
95
+ def to_net()
96
+ NetAddr::IPv6Net.new(self,nil)
97
+ end
98
+
99
+ # to_s returns the IPv6 as a String in zero-compressed format (per rfc5952).
100
+ def to_s()
101
+ hexStr = ["","","","","","","",""]
102
+ zeroStart, consec0, finalStart, finalLen = -1,0,-1,0
103
+ 8.times do |i|
104
+ # capture 2-byte word
105
+ shift = 112 - 16*i
106
+ wd = (self.addr >> shift) & 0xffff
107
+ hexStr[i] = wd.to_s(16)
108
+
109
+ # capture count of consecutive zeros
110
+ if (wd == 0)
111
+ if (zeroStart == -1)
112
+ zeroStart = i
113
+ end
114
+ consec0 += 1
115
+ end
116
+
117
+ # test for longest consecutive zeros when non-zero encountered or we're at the end
118
+ if (wd != 0 || i == 7)
119
+ if (consec0 > finalStart+finalLen-1)
120
+ finalStart = zeroStart
121
+ finalLen = consec0
122
+ end
123
+ zeroStart = -1
124
+ consec0 = 0
125
+ end
126
+ end
127
+
128
+ # compress if we've found a series of zero fields in a row.
129
+ # per https://tools.ietf.org/html/rfc5952#section-4.2.2 we must not compress just a single 16-bit zero field.
130
+ if (finalLen > 1)
131
+ head = hexStr[0,finalStart].join(":")
132
+ tailStart = finalStart + finalLen
133
+ tail = hexStr[tailStart..7].join(":")
134
+ return head + "::" + tail
135
+ end
136
+ return hexStr.join(":")
137
+ end
138
+
139
+ # version returns "6" for IPv6
140
+ def version()
141
+ return 6
142
+ end
143
+
144
+ end # end class IPv6
145
+
146
+ end # end module
data/lib/ipv6net.rb ADDED
@@ -0,0 +1,267 @@
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. A default netmask will be used if not specified.
28
+ # Throws ValidationError on error.
29
+ def IPv6Net.parse(net)
30
+ m128 = nil
31
+ net = 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
48
+ # addresses are identical then the netmasks will be compared with the cmp() method of the netmask.
49
+ def cmp(other)
50
+ if (!other.kind_of?(IPv6Net))
51
+ raise ArgumentError, "Expected an IPv6Net object for 'other' but got a #{other.class}."
52
+ end
53
+ cmp = self.network.cmp(other.network)
54
+ if (cmp != 0)
55
+ return cmp
56
+ end
57
+ return self.netmask.cmp(other.netmask)
58
+ end
59
+
60
+ #contains returns true if the IPv6Net contains the IPv6
61
+ def contains(ip)
62
+ if (!ip.kind_of?(IPv6))
63
+ raise ArgumentError, "Expected an IPv6 object for 'ip' but got a #{ip.class}."
64
+ end
65
+ if (@base.addr == ip.addr & @m128.mask)
66
+ return true
67
+ end
68
+ return false
69
+ end
70
+
71
+ # fill returns a copy of the given Array, stripped of any networks which are not subnets of this IPv6Net
72
+ # and with any missing gaps filled in.
73
+ def fill(list)
74
+ list = Util.filter_IPv6Net(list)
75
+ return Util.fill(self,list)
76
+ end
77
+
78
+ #len returns the number of IP addresses in this network. It will return 0 for /0 networks.
79
+ def len()
80
+ return self.netmask.len
81
+ end
82
+
83
+ # long returns the IPv6Net as a string in long (uncompressed) format
84
+ def long()
85
+ return @base.long() + @m128.to_s
86
+ end
87
+
88
+ # netmask returns the Mask128 object representing the netmask for this network
89
+ def netmask()
90
+ @m128
91
+ end
92
+
93
+ # network returns the IPv6 object representing the network address
94
+ def network()
95
+ @base
96
+ end
97
+
98
+ # next returns the next largest consecutive IP network or nil if the end of the address space is reached.
99
+ def next()
100
+ net = self.nth_next_sib(1)
101
+ if (!net)
102
+ return nil
103
+ end
104
+ return net.grow
105
+ end
106
+
107
+ # next_sib returns the network immediately following this one or nil if the end of the address space is reached.
108
+ def next_sib()
109
+ self.nth_next_sib(1)
110
+ end
111
+
112
+ # nth returns the IPv6 at the given index.
113
+ # The size of the network may be determined with the len() method.
114
+ # If the range is exceeded then return nil.
115
+ def nth(index)
116
+ if (!index.kind_of?(Integer))
117
+ raise ArgumentError, "Expected an Integer for 'index' but got a #{index.class}."
118
+ elsif (self.netmask.prefix_len < 64 || (self.netmask.prefix_len > 64 && index >= self.len))
119
+ return nil
120
+ end
121
+ return IPv6.new(self.network.addr + index)
122
+ end
123
+
124
+ # nth_subnet returns the subnet IPv6Net at the given index.
125
+ # The number of subnets may be determined with the subnet_count() method.
126
+ # If the range is exceeded or an invalid prefix_len is provided then return nil.
127
+ def nth_subnet(prefix_len,index)
128
+ count = self.subnet_count(prefix_len)
129
+ if (count == 0 || index >= count)
130
+ return nil
131
+ end
132
+ sub0 = IPv6Net.new(self.network, Mask128.new(prefix_len))
133
+ return sub0.nth_next_sib(index)
134
+ end
135
+
136
+ # prev returns the previous largest consecutive IP network or nil if this is ::.
137
+ def prev()
138
+ net = self.grow
139
+ return net.prev_sib
140
+ end
141
+
142
+ # prev_sib returns the network immediately preceding this one or nil if this network is ::.
143
+ def prev_sib()
144
+ if (self.network.addr == 0)
145
+ return nil
146
+ end
147
+
148
+ shift = 128 - self.netmask.prefix_len
149
+ addr = ((self.network.addr>>shift) - 1) << shift
150
+ if addr < 0
151
+ return nil
152
+ end
153
+ return IPv6Net.new(IPv6.new(addr), self.netmask)
154
+ end
155
+
156
+ # rel determines the relationship to another IPv6Net. Returns:
157
+ # * 1 if this IPv6Net is the supernet of other
158
+ # * 0 if the two are equal
159
+ # * -1 if this IPv6Net is a subnet of other
160
+ # * nil if the networks are unrelated
161
+ def rel(other)
162
+ if (!other.kind_of?(IPv6Net))
163
+ raise ArgumentError, "Expected an IPv6Net object for 'other' but got a #{other.class}."
164
+ end
165
+
166
+ # when networks are equal then we can look exlusively at the netmask
167
+ if (self.network.addr == other.network.addr)
168
+ return self.netmask.cmp(other.netmask)
169
+ end
170
+
171
+ # when networks are not equal we can use hostmask to test if they are
172
+ # related and which is the supernet vs the subnet
173
+ hostmask = self.netmask.mask ^ NetAddr::F128
174
+ otherHostmask = other.netmask.mask ^ NetAddr::F128
175
+ if (self.network.addr|hostmask == other.network.addr|hostmask)
176
+ return 1
177
+ elsif (self.network.addr|otherHostmask == other.network.addr|otherHostmask)
178
+ return -1
179
+ end
180
+ return nil
181
+ end
182
+
183
+ # resize returns a copy of the network with an adjusted netmask.
184
+ # Throws ValidationError on invalid prefix_len.
185
+ def resize(prefix_len)
186
+ m128 = Mask128.new(prefix_len)
187
+ return IPv6Net.new(self.network,m128)
188
+ end
189
+
190
+ # subnet_count returns the number a subnets of a given prefix length that this IPv6Net contains.
191
+ # It will return 0 for invalid requests (ie. bad prefix or prefix is shorter than that of this network).
192
+ # 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)
193
+ def subnet_count(prefix_len)
194
+ if (prefix_len <= self.netmask.prefix_len || prefix_len > 128 || prefix_len - self.netmask.prefix_len >= 128)
195
+ return 0
196
+ end
197
+ return 1 << (prefix_len - self.netmask.prefix_len)
198
+ end
199
+
200
+ # summ creates a summary address from this IPv6Net and another.
201
+ # It returns nil if the two networks are incapable of being summarized.
202
+ def summ(other)
203
+ if (!other.kind_of?(IPv6Net))
204
+ raise ArgumentError, "Expected an IPv6Net object for 'other' but got a #{other.class}."
205
+ end
206
+
207
+ # netmasks must be identical
208
+ if (self.netmask.prefix_len != other.netmask.prefix_len)
209
+ return nil
210
+ end
211
+
212
+ # merge-able networks will be identical if you right shift them by the number of bits in the hostmask + 1
213
+ shift = 128 - self.netmask.prefix_len + 1
214
+ addr = self.network.addr >> shift
215
+ otherAddr = other.network.addr >> shift
216
+ if (addr != otherAddr)
217
+ return nil
218
+ end
219
+ return self.resize(self.netmask.prefix_len - 1)
220
+ end
221
+
222
+ # to_s returns the IPv6Net as a String
223
+ def to_s()
224
+ return @base.to_s + @m128.to_s
225
+ end
226
+
227
+ # version returns "6" for IPv6
228
+ def version()
229
+ return 6
230
+ end
231
+
232
+
233
+ protected
234
+
235
+ # grow decreases the prefix length as much as possible without crossing a bit boundary.
236
+ def grow()
237
+ addr = self.network.addr
238
+ mask = self.netmask.mask
239
+ prefix_len = self.netmask.prefix_len
240
+ self.netmask.prefix_len.downto(0) do
241
+ mask = (mask << 1) & NetAddr::F128
242
+ if addr|mask != mask || prefix_len == 0 # // bit boundary crossed when there are '1' bits in the host portion
243
+ break
244
+ end
245
+ prefix_len -= 1
246
+ end
247
+
248
+ return IPv6Net.new(IPv6.new(addr),Mask128.new(prefix_len))
249
+ end
250
+
251
+ # nth_next_sib returns the nth next sibling network or nil if address space exceeded.
252
+ def nth_next_sib(nth)
253
+ if (nth < 0)
254
+ return nil
255
+ end
256
+
257
+ shift = 128 - self.netmask.prefix_len
258
+ addr = ((self.network.addr>>shift) + nth) << shift
259
+ if addr > NetAddr::F128
260
+ return nil
261
+ end
262
+ return IPv6Net.new(IPv6.new(addr), self.netmask)
263
+ end
264
+
265
+ end # end class IPv6Net
266
+
267
+ end # end module