netaddr 1.5.3 → 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.

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
data/lib/mask128.rb ADDED
@@ -0,0 +1,68 @@
1
+ module NetAddr
2
+
3
+ #Mask128 represents a 128-bit netmask.
4
+ class Mask128
5
+ # mask is the Integer representation of this netmask
6
+ attr_reader :mask
7
+
8
+ # prefix_len is the Integer prefix length of this netmask
9
+ attr_reader :prefix_len
10
+
11
+ # Create a Mask128 from an Integer prefix length. Valid values are 0-128.
12
+ # Throws ValidationError on error.
13
+ def initialize(prefix_len)
14
+ if (!prefix_len.kind_of?(Integer))
15
+ raise ValidationError, "Expected an Integer for 'prefix_len' but got a #{prefix_len.class}."
16
+ elsif ( (prefix_len < 0) || (prefix_len > 128) )
17
+ raise ValidationError, "#{prefix_len} must be in the range of 0-128."
18
+ end
19
+ @prefix_len = prefix_len
20
+ @mask = NetAddr::F128 ^ (NetAddr::F128 >> @prefix_len)
21
+ end
22
+
23
+ # parse will create an Mask128 from its string representation.
24
+ # arguments:
25
+ # * mask - String representing an netmask (ie. "/64").
26
+ #
27
+ # Throws ValidationError on error.
28
+ def Mask128.parse(mask)
29
+ mask.strip!
30
+ if (mask.start_with?("/")) # cidr format
31
+ mask = mask[1..-1] # remove "/"
32
+ end
33
+ return Mask128.new(mask.to_i)
34
+ end
35
+
36
+ #cmp compares equality with another Mask128. Return:
37
+ #* 1 if this Mask128 is larger in capacity
38
+ #* 0 if the two are equal
39
+ #* -1 if this Mask128 is smaller in capacity
40
+ def cmp(other)
41
+ if (!other.kind_of?(Mask128))
42
+ raise ArgumentError, "Expected an Mask128 object for 'other' but got a #{other.class}."
43
+ end
44
+ if (self.prefix_len < other.prefix_len)
45
+ return 1
46
+ elsif (self.prefix_len > other.prefix_len)
47
+ return -1
48
+ end
49
+ return 0
50
+ end
51
+
52
+ #len returns the number of IP addresses in this network. This is only useful if you have a subnet
53
+ # smaller than a /64 as it will always return 0 for prefixes <= 64.
54
+ def len()
55
+ if (self.prefix_len <= 64)
56
+ return 0
57
+ end
58
+ return (self.mask ^ NetAddr::F128) + 1 # bit flip the netmask and add 1
59
+ end
60
+
61
+ # to_s returns the Mask128 as a String
62
+ def to_s()
63
+ return "/#{@prefix_len}"
64
+ end
65
+
66
+ end # end class Mask128
67
+
68
+ end # end module