netaddr 1.5.1 → 2.0.3

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,257 @@
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
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
+
224
+ protected
225
+
226
+ # grow decreases the prefix length as much as possible without crossing a bit boundary.
227
+ def grow()
228
+ addr = self.network.addr
229
+ mask = self.netmask.mask
230
+ prefix_len = self.netmask.prefix_len
231
+ self.netmask.prefix_len.downto(0) do
232
+ mask = (mask << 1) & NetAddr::F32
233
+ if addr|mask != mask || prefix_len == 0 # // bit boundary crossed when there are '1' bits in the host portion
234
+ break
235
+ end
236
+ prefix_len -= 1
237
+ end
238
+ return IPv4Net.new(IPv4.new(addr),Mask32.new(prefix_len))
239
+ end
240
+
241
+ # nth_next_sib returns the nth next sibling network or nil if address space exceeded.
242
+ def nth_next_sib(nth)
243
+ if (nth < 0)
244
+ return nil
245
+ end
246
+
247
+ shift = 32 - self.netmask.prefix_len
248
+ addr = ((self.network.addr>>shift) + nth) << shift
249
+ if addr > NetAddr::F32
250
+ return nil
251
+ end
252
+ return IPv4Net.new(IPv4.new(addr), self.netmask)
253
+ end
254
+
255
+ end # end class IPv4Net
256
+
257
+ end # end module
data/lib/ipv6.rb ADDED
@@ -0,0 +1,115 @@
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.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_net returns the IPv6 as a IPv6Net
70
+ def to_net()
71
+ NetAddr::IPv6Net.new(self,nil)
72
+ end
73
+
74
+ # to_s returns the IPv6 as a String in zero-compressed format (per rfc5952).
75
+ def to_s()
76
+ hexStr = ["","","","","","","",""]
77
+ zeroStart, consec0, finalStart, finalLen = -1,0,-1,0
78
+ 8.times do |i|
79
+ # capture 2-byte word
80
+ shift = 112 - 16*i
81
+ wd = (self.addr >> shift) & 0xffff
82
+ hexStr[i] = wd.to_s(16)
83
+
84
+ # capture count of consecutive zeros
85
+ if (wd == 0)
86
+ if (zeroStart == -1)
87
+ zeroStart = i
88
+ end
89
+ consec0 += 1
90
+ end
91
+
92
+ # test for longest consecutive zeros when non-zero encountered or we're at the end
93
+ if (wd != 0 || i == 7)
94
+ if (consec0 > finalStart+finalLen-1)
95
+ finalStart = zeroStart
96
+ finalLen = consec0
97
+ end
98
+ zeroStart = -1
99
+ consec0 = 0
100
+ end
101
+ end
102
+
103
+ # compress if we've found a series of 0 words in a row
104
+ if (finalStart != -1)
105
+ head = hexStr[0,finalStart].join(":")
106
+ tailStart = finalStart + finalLen
107
+ tail = hexStr[tailStart..7].join(":")
108
+ return head + "::" + tail
109
+ end
110
+ return hexStr.join(":")
111
+ end
112
+
113
+ end # end class IPv6
114
+
115
+ end # end module
data/lib/ipv6net.rb ADDED
@@ -0,0 +1,262 @@
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.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
+
228
+ protected
229
+
230
+ # grow decreases the prefix length as much as possible without crossing a bit boundary.
231
+ def grow()
232
+ addr = self.network.addr
233
+ mask = self.netmask.mask
234
+ prefix_len = self.netmask.prefix_len
235
+ self.netmask.prefix_len.downto(0) do
236
+ mask = (mask << 1) & NetAddr::F128
237
+ if addr|mask != mask || prefix_len == 0 # // bit boundary crossed when there are '1' bits in the host portion
238
+ break
239
+ end
240
+ prefix_len -= 1
241
+ end
242
+
243
+ return IPv6Net.new(IPv6.new(addr),Mask128.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 = 128 - self.netmask.prefix_len
253
+ addr = ((self.network.addr>>shift) + nth) << shift
254
+ if addr > NetAddr::F128
255
+ return nil
256
+ end
257
+ return IPv6Net.new(IPv6.new(addr), self.netmask)
258
+ end
259
+
260
+ end # end class IPv6Net
261
+
262
+ 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