network-infra-utility 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,88 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ # IP 统一入口模块。
5
+ #
6
+ # 提供 v4 / v6 / range / cross / xross 五个模块方法,
7
+ # 内部按地址串是否含 '.' ':' 自动分派到 IPv4 / IPv6。
8
+ require_relative 'ipv4_address'
9
+ require_relative 'ipv6_address'
10
+
11
+ module IP
12
+ module_function
13
+
14
+ # 解析 IPv4 地址或 CIDR。
15
+ # string 不含 '/' → 返回 IPv4
16
+ # string 含 '/' → 返回 [IPv4, IPv4Mask]
17
+ def v4(string)
18
+ ip, msk = string.split('/')
19
+ return IPv4.new(ip) unless msk
20
+
21
+ mask = msk.include?('.') ? IPv4Mask.string(msk) : IPv4Mask.number(msk.to_i)
22
+ [IPv4.new(ip), mask]
23
+ end
24
+
25
+ # 解析 IPv6 地址或 CIDR。
26
+ # string 不含 '/' → 返回 IPv6
27
+ # string 含 '/' → 返回 [IPv6, IPv6Mask]
28
+ def v6(string)
29
+ ip, msk = string.split('/')
30
+ return IPv6.new(ip) unless msk
31
+
32
+ mask = msk.include?(':') ? IPv6Mask.string(msk) : IPv6Mask.number(msk.to_i)
33
+ [IPv6.new(ip), mask]
34
+ end
35
+
36
+ # 展开一个地址区间,返回 [start, end]。
37
+ # 支持三种输入:
38
+ # CIDR '1.0.4.0/22' → [网络地址, 广播地址]
39
+ # 区间 '1.0.4.0-1.0.7.255' → [起始, 结束]
40
+ # 单址 '1.0.4.0' → [addr, addr]
41
+ # 自动按 '.' ':' 区分 v4 / v6。
42
+ def range(addr)
43
+ return expand_dash_range(addr) if addr.include?('-')
44
+
45
+ if addr.include?('.')
46
+ gateway, netmask = v4(addr)
47
+ elsif addr.include?(':')
48
+ gateway, netmask = v6(addr)
49
+ else
50
+ raise ArgumentError, "Unrecognized address #{addr}"
51
+ end
52
+
53
+ if netmask
54
+ network = gateway.network_with(netmask)
55
+ network.range_with(netmask)
56
+ else
57
+ [gateway, gateway]
58
+ end
59
+ end
60
+
61
+ # 两个闭区间是否相交。参数为 [start, end] 形式,元素需可比较。
62
+ def cross(range1, range2)
63
+ s1, e1 = range1
64
+ s2, e2 = range2
65
+ (s1 <= s2 && s2 <= e1) ||
66
+ (s1 <= e2 && e2 <= e1) ||
67
+ (s2 <= s1 && s1 <= e2) ||
68
+ (s2 <= e1 && e1 <= e2)
69
+ end
70
+
71
+ # 求两个区间的合并并集端点。
72
+ # 返回排序去重后的端点序列,转换为 IP 对象。
73
+ # option: :v4 或 :v6,决定从 0 地址起算。
74
+ def xross(range1, range2, option = :v4)
75
+ s1, e1 = range1
76
+ s2, e2 = range2
77
+ base_addr = option == :v6 ? '::' : '0.0.0.0'
78
+ [s1, e1, s2, e2].sort.uniq.map { |n| IP.send(option, base_addr) + n }
79
+ end
80
+
81
+ # 处理 'start-end' 形式的区间串。
82
+ # 递归解析两端各自可能是 CIDR 或单址,最终 flatten 成 [start, end]。
83
+ def expand_dash_range(addr)
84
+ sddr, eddr = addr.split('-')
85
+ [range(sddr).first, range(eddr).last]
86
+ end
87
+ module_function :expand_dash_range
88
+ end
@@ -0,0 +1,336 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ # IPv4 地址与掩码工具。
5
+ #
6
+ # 用法:
7
+ # ip, mask = IP.v4('10.37.214.42/17')
8
+ # net = ip.network_with(mask)
9
+ # p ip.to_d, mask.to_d, net.to_d
10
+ # p ip.mask?, mask.mask?, net.network_with?(mask)
11
+ # start_ip, end_ip = ip.range_with(mask)
12
+ # p start_ip.to_d, end_ip.to_d
13
+ #
14
+ # a = IP.v4('10.37.214.21/8')[0]
15
+ # b = IP.v4('145.217.33.14/16')[0]
16
+ # c = IP.v4('221.25.43.72/24')[0]
17
+ # d = IP.v4('238.13.189.51')
18
+ # e = IP.v4('246.211.74.63')
19
+ # p a.class_a?, b.class_b?, c.class_c?, d.class_d?, e.class_e?
20
+ # p a.private?, b.private?
21
+
22
+ require 'ipaddr'
23
+
24
+ # IPv4 地址。
25
+ #
26
+ # 持有四个 8 位段 @numbers 和 32 位整数 @number。
27
+ # 支持 CIDR 解析(通过 IP.v4)、掩码运算、范围展开、地址分类判断。
28
+ class IPv4
29
+ attr_reader :numbers, :number
30
+
31
+ # 解析地址串。支持十进制、0x 十六进制、0b 二进制三种前缀进制,
32
+ # 前缀作用于整串所有段。形如 '10.0.0.1' / '0x0a.0.0.1' / '0b00001010.0.0.1111'。
33
+ # 非法格式发 warn 并返回 self,使 IP.v4 仍拿到一个可判空的对象。
34
+ def generate(string)
35
+ unless /^(0[bdx])?([0-9a-fA-F]+\.){3}[0-9a-fA-F]+$/.match?(string)
36
+ warn "Abnormal format string #{string}!"
37
+ return self
38
+ end
39
+ @content, @mode = mode(string)
40
+ fours = format_parts(@content, @mode)
41
+ if valid?(fours)
42
+ @numbers = fours
43
+ @number = to_i
44
+ else
45
+ warn "Abnormal decimal string #{string}!"
46
+ end
47
+ self
48
+ end
49
+
50
+ def initialize(string)
51
+ generate(string)
52
+ end
53
+
54
+ # ---- 进制解析 ----
55
+
56
+ # 拆出前缀与本体。'0b'/'0x'/'0d' 前缀原样保留为 mode,否则按十进制。
57
+ def mode(string)
58
+ if ['0b', '0x', '0d'].include?(string[0..1])
59
+ [string[2..], string[0..1]]
60
+ else
61
+ [string, '0d']
62
+ end
63
+ end
64
+
65
+ # 按 mode 把点分四段各自 eval 成整数。mode 前缀会被拼到每段前。
66
+ def format_parts(string, mode)
67
+ string.split('.').map { |part| eval("#{mode}#{part}") }
68
+ end
69
+
70
+ # 判断四段是否全落在 0..255 且共 4 段。
71
+ def valid?(nums)
72
+ nums.size == 4 && nums.all? { |n| (0..255).include?(n) }
73
+ end
74
+
75
+ # ---- 输出格式 ----
76
+
77
+ def to_a
78
+ @numbers
79
+ end
80
+
81
+ def to_d
82
+ @numbers.map { |n| n.to_s(10) }.join('.')
83
+ end
84
+
85
+ def to_h
86
+ @numbers.map { |n| '%02x' % n }.join('.')
87
+ end
88
+
89
+ def to_b
90
+ @numbers.map { |n| '%08b' % n }.join('.')
91
+ end
92
+
93
+ # 兼容原接口:to_s 即十进制点分。
94
+ def to_s
95
+ to_d
96
+ end
97
+
98
+ private
99
+
100
+ # 32 位整数表示,内部缓存计算结果。
101
+ def to_i
102
+ @numbers.map { |n| '%08b' % n }.join.to_i(2)
103
+ end
104
+
105
+ # 从 32 位整数值构造新地址。
106
+ def rebuild_from_number(n)
107
+ bin = '%032b' % (n & 0xFFFFFFFF)
108
+ dotted = bin.unpack('a8a8a8a8').join('.')
109
+ IPv4.new('0b' + dotted)
110
+ end
111
+
112
+ public
113
+
114
+ # ---- 算术运算(非破坏性,返回新对象)----
115
+
116
+ # 加 num 个地址,返回新 IPv4。num 可为 Integer 或 IPv4(取其 number)。num 为负则等价于减法。
117
+ def +(num)
118
+ delta = num.is_a?(IPv4) ? num.number : num
119
+ new_number = (@number + delta) & 0xFFFFFFFF
120
+ rebuild_from_number(new_number)
121
+ end
122
+
123
+ # 减 num 个地址,返回新 IPv4。
124
+ def -(num)
125
+ self.+(-num)
126
+ end
127
+
128
+ # 按位与。another 可为 IPv4(含 Mask 子类)或前缀长度 Integer。
129
+ def &(another)
130
+ target = case another
131
+ when IPv4 then another
132
+ when Integer then IPv4Mask.number(another)
133
+ else return self
134
+ end
135
+ new_addr = @numbers.each_with_index.map { |n, i| n & target.numbers[i] }
136
+ IPv4.new(new_addr.join('.'))
137
+ end
138
+
139
+ # 子网划分。把当前网段按 sub_pref 前缀长度划分为若干子网,
140
+ # 返回 [[subnet_ip, mask], ...]。base_pref/sub_pref 可为前缀长度或 IPv4/IPv4Mask。
141
+ def delegation(base_pref, sub_pref)
142
+ base_len = base_pref.is_a?(IPv4) ? base_pref.mask_counter : base_pref
143
+ sub_len = sub_pref.is_a?(IPv4) ? sub_pref.mask_counter : sub_pref
144
+ return [] unless base_len && sub_len
145
+ return [] unless base_len < sub_len
146
+ return [] unless base_len <= 32 && sub_len <= 32
147
+
148
+ base = self & base_len
149
+ pref = IPv4Mask.number(sub_len)
150
+ (0..(2**(sub_len - base_len) - 1)).map do |i|
151
+ sub_ip = base + (i << (32 - sub_len))
152
+ [sub_ip, pref]
153
+ end
154
+ end
155
+
156
+ # ---- 地址分类 ----
157
+
158
+ def class_a?
159
+ (IPv4.new('10.0.0.0').number..IPv4.new('126.255.255.255').number).include?(@number)
160
+ end
161
+
162
+ def class_b?
163
+ (IPv4.new('128.1.0.0').number..IPv4.new('191.255.255.255').number).include?(@number)
164
+ end
165
+
166
+ def class_c?
167
+ (IPv4.new('192.0.1.0').number..IPv4.new('223.255.255.255').number).include?(@number)
168
+ end
169
+
170
+ def class_d?
171
+ (IPv4.new('224.0.0.0').number..IPv4.new('239.255.255.255').number).include?(@number)
172
+ end
173
+
174
+ def class_e?
175
+ (IPv4.new('240.0.0.0').number..IPv4.new('255.255.255.254').number).include?(@number)
176
+ end
177
+
178
+ def private?
179
+ n = @number
180
+ (IPv4.new('10.0.0.0').number..IPv4.new('10.255.255.255').number).include?(n) ||
181
+ (IPv4.new('172.16.0.0').number..IPv4.new('172.31.255.255').number).include?(n) ||
182
+ (IPv4.new('192.168.0.0').number..IPv4.new('192.168.255.255').number).include?(n)
183
+ end
184
+
185
+ def loopback?
186
+ (IPv4.new('127.0.0.0').number..IPv4.new('127.255.255.255').number).include?(@number)
187
+ end
188
+
189
+ # 特殊地址:环回、全 1 广播、全 0。
190
+ def special?
191
+ [IPv4.new('127.0.0.1').number, IPv4.new('255.255.255.255').number,
192
+ IPv4.new('0.0.0.0').number].include?(@number)
193
+ end
194
+
195
+ # 保留 is_another? 作为 special? 的兼容别名。
196
+ alias is_another? special?
197
+
198
+ # ---- 掩码判定 ----
199
+
200
+ # 是否为合法网络掩码(前 1 后 0,无 01 交错)。
201
+ def mask?
202
+ !to_b.gsub('.', '').include?('01')
203
+ end
204
+
205
+ # 是否为反掩码(前 0 后 1,无 10 交错)。
206
+ def anti_mask?
207
+ !to_b.gsub('.', '').include?('10')
208
+ end
209
+
210
+ # 网络掩码的前缀长度。非掩码返回 nil。
211
+ def mask_counter
212
+ return nil unless mask?
213
+ counter, msk = 0, to_b.gsub('.', '')
214
+ while msk[-1] == '0'
215
+ counter += 1
216
+ msk = msk[0..-2]
217
+ end
218
+ 32 - counter
219
+ end
220
+
221
+ # 反掩码的尾部 1 个数。非反掩码返回 nil。
222
+ def anti_mask_counter
223
+ return nil unless anti_mask?
224
+ counter, msk = 0, to_b.gsub('.', '')
225
+ while msk[-1] == '1'
226
+ counter += 1
227
+ msk = msk[0..-2]
228
+ end
229
+ counter
230
+ end
231
+
232
+ # 掩码对应的反掩码。非掩码返回 nil。
233
+ def anti_mask
234
+ return nil unless (counter = mask_counter)
235
+ IPv4Mask.anti_number(counter)
236
+ end
237
+
238
+ # 反掩码对应的掩码。非反掩码返回 nil。
239
+ def mask
240
+ return nil unless (counter = anti_mask_counter)
241
+ IPv4Mask.number(32 - counter)
242
+ end
243
+
244
+ # ---- 保留 is_ 前缀别名,兼容旧接口 ----
245
+
246
+ alias is_mask? mask?
247
+ alias is_anti_mask? anti_mask?
248
+ alias is_private? private?
249
+ alias is_class_a? class_a?
250
+ alias is_class_b? class_b?
251
+ alias is_class_c? class_c?
252
+ alias is_class_d? class_d?
253
+ alias is_class_e? class_e?
254
+
255
+ # ---- 网络/范围 ----
256
+
257
+ # 与掩码相与得到网络地址。
258
+ def network_with(mask)
259
+ new_addr = mask.numbers.each_with_index.map { |n, i| n & @numbers[i] }
260
+ IPv4.new(new_addr.join('.'))
261
+ end
262
+
263
+ # 该网段的完整地址区间,含网络地址与广播地址。
264
+ # 前缀 /32 时返回 [self, self]。
265
+ def range_with(mask)
266
+ return [self, self] if mask.mask_counter.to_i == 32
267
+ net = network_with(mask)
268
+ prefix = mask.mask_counter.to_i
269
+ offset = 2**(32 - prefix) - 1
270
+ [net, net + offset]
271
+ end
272
+
273
+ # 判断自身是否可作 mask 所在网段的网络地址。
274
+ def network_with?(mask)
275
+ msk_counter, msk = 0, mask.to_b.gsub('.', '')
276
+ while msk[-1] == '0'
277
+ msk_counter += 1
278
+ msk = msk[0..-2]
279
+ end
280
+ nt_counter, nt = 0, to_b.gsub('.', '')
281
+ while nt[-1] == '0'
282
+ nt_counter += 1
283
+ nt = nt[0..-2]
284
+ end
285
+ nt_counter >= msk_counter
286
+ end
287
+ alias is_network_with? network_with?
288
+
289
+ # 两个地址的最长公共前缀长度。
290
+ def prefix_with(another_ip)
291
+ ip1 = to_b.gsub('.', '')
292
+ ip2 = another_ip.to_b.gsub('.', '')
293
+ count = 0
294
+ count += 1 while ip1[count] == ip2[count] && count < 32
295
+ count
296
+ end
297
+
298
+ # ---- 排序比较 ----
299
+
300
+ include Comparable
301
+ def <=>(other)
302
+ @number <=> other.number
303
+ end
304
+ end
305
+
306
+ # IPv4 掩码工厂,按前缀长度或字符串构造 IPv4(带掩码语义)。
307
+ class IPv4Mask
308
+ class << self
309
+ # 前缀长度 → 网络掩码。num 范围 0..32。
310
+ def number(num)
311
+ raise ArgumentError, "Invalid prefix length #{num}" unless (0..32).include?(num)
312
+ str = '0b' + ('1' * num + '0' * (32 - num)).unpack('a8a8a8a8').join('.')
313
+ IPv4.new(str)
314
+ end
315
+
316
+ # 字符串 → 网络掩码,校验其为合法掩码。
317
+ def string(str)
318
+ temp = IPv4.new(str)
319
+ raise "Invalid net mask #{str}!" unless temp.mask?
320
+ temp
321
+ end
322
+
323
+ # 反掩码前缀长度 (尾部 1 的个数) → 反掩码。
324
+ def anti_number(num)
325
+ str = '0b' + ('0' * num + '1' * (32 - num)).unpack('a8a8a8a8').join('.')
326
+ IPv4.new(str)
327
+ end
328
+
329
+ # 字符串 → 反掩码,校验其为合法反掩码。
330
+ def anti_string(str)
331
+ temp = IPv4.new(str)
332
+ raise "Invalid anti mask #{str}!" unless temp.anti_mask?
333
+ temp
334
+ end
335
+ end
336
+ end
@@ -0,0 +1,244 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ # IPv6 地址与掩码工具。
5
+ #
6
+ # 用法:
7
+ # ip, mask = IP.v6('2001:db8::1/64')
8
+ # net = ip.network_with(mask)
9
+ # start_ip, end_ip = ip.range_with(mask)
10
+
11
+ require 'ipaddr'
12
+
13
+ # IPv6 地址。
14
+ #
15
+ # 持有八个 16 位段 @numbers 和 128 位整数 @number。
16
+ # 解析借助标准库 IPAddr 展开压缩形式,再统一为点分十六进制段。
17
+ class IPv6
18
+ attr_reader :numbers, :number
19
+
20
+ # 解析地址串。借助 IPAddr 还原 '::' 压缩为完整 8 段十六进制;
21
+ # 非法地址抛异常(与 IPv4 的 warn 行为不同,沿用原作者设计)。
22
+ def generate(string)
23
+ @content = IPAddr.new(string).to_string
24
+ @mode = '0x'
25
+ eights = format_parts(@content, @mode)
26
+ @numbers = eights
27
+ @number = to_i
28
+ self
29
+ rescue IPAddr::InvalidAddressError => e
30
+ raise "Abnormal format string #{string}! #{e.message}"
31
+ end
32
+
33
+ def initialize(string)
34
+ generate(string)
35
+ end
36
+
37
+ # ---- 进制解析 ----
38
+
39
+ def mode(string)
40
+ [IPAddr.new(string).to_string, '0x']
41
+ end
42
+
43
+ def format_parts(string, mode = '0x')
44
+ string.split(':').map { |part| eval("#{mode}#{part}") }
45
+ end
46
+
47
+ private
48
+
49
+ def to_i
50
+ @numbers.map { |n| '%016b' % n }.join.to_i(2)
51
+ end
52
+
53
+ def rebuild_from_hex(hex_str)
54
+ IPv6.new(hex_str.unpack('a4a4a4a4a4a4a4a4').join(':'))
55
+ end
56
+
57
+ public
58
+
59
+ # ---- 输出格式 ----
60
+
61
+ def to_a
62
+ @numbers
63
+ end
64
+
65
+ def to_d
66
+ @numbers.map { |n| n.to_s(10) }.join(':')
67
+ end
68
+
69
+ def to_h
70
+ @numbers.map { |n| '%04x' % n }.join(':')
71
+ end
72
+
73
+ def to_b
74
+ @numbers.map { |n| '%016b' % n }.join(':')
75
+ end
76
+
77
+ # 压缩形式( '::' 形态),借助 IPAddr 还原。
78
+ def to_s
79
+ IPAddr.new(@content).to_s
80
+ end
81
+
82
+ alias to_s_short to_s
83
+
84
+ def to_s_full
85
+ to_h
86
+ end
87
+
88
+ # ---- 算术运算(非破坏性)----
89
+
90
+ def +(num)
91
+ delta = num.is_a?(IPv6) ? num.number : num
92
+ new_number = (@number + delta)
93
+ new_number &= (1 << 128) - 1 if delta < 0 && new_number < 0
94
+ rebuild_from_hex('%032x' % new_number)
95
+ end
96
+
97
+ def -(num)
98
+ self.+(-num)
99
+ end
100
+
101
+ def &(another)
102
+ target = case another
103
+ when IPv6 then another
104
+ when Integer then IPv6Mask.number(another)
105
+ else return self
106
+ end
107
+ new_addr = @numbers.each_with_index.map { |n, i| n & target.numbers[i] }
108
+ IPv6.new(new_addr.map { |n| '%04x' % n }.join(':'))
109
+ end
110
+
111
+ def delegation(base_pref, sub_pref)
112
+ base_len = base_pref.is_a?(IPv6) ? base_pref.mask_counter : base_pref
113
+ sub_len = sub_pref.is_a?(IPv6) ? sub_pref.mask_counter : sub_pref
114
+ return [] unless base_len && sub_len
115
+ return [] unless base_len < sub_len
116
+ return [] unless base_len <= 128 && sub_len <= 128
117
+
118
+ base = self & base_len
119
+ pref = IPv6Mask.number(sub_len)
120
+ (0..(2**(sub_len - base_len) - 1)).map do |i|
121
+ sub_ip = base + (i << (128 - sub_len))
122
+ [sub_ip, pref]
123
+ end
124
+ end
125
+
126
+ # ---- 掩码判定 ----
127
+
128
+ def mask?
129
+ !to_b.gsub(':', '').include?('01')
130
+ end
131
+
132
+ def anti_mask?
133
+ !to_b.gsub(':', '').include?('10')
134
+ end
135
+
136
+ def mask_counter
137
+ return nil unless mask?
138
+ counter, msk = 0, to_b.gsub(':', '')
139
+ while msk[-1] == '0'
140
+ counter += 1
141
+ msk = msk[0..-2]
142
+ end
143
+ 128 - counter
144
+ end
145
+
146
+ def anti_mask_counter
147
+ return nil unless anti_mask?
148
+ counter, msk = 0, to_b.gsub(':', '')
149
+ while msk[-1] == '1'
150
+ counter += 1
151
+ msk = msk[0..-2]
152
+ end
153
+ counter
154
+ end
155
+
156
+ def anti_mask
157
+ return nil unless (counter = mask_counter)
158
+ IPv6Mask.anti_number(counter)
159
+ end
160
+
161
+ def mask
162
+ return nil unless (counter = anti_mask_counter)
163
+ IPv6Mask.number(128 - counter)
164
+ end
165
+
166
+ alias is_mask? mask?
167
+ alias is_anti_mask? anti_mask?
168
+
169
+ # ---- 网络/范围 ----
170
+
171
+ def network_with(mask)
172
+ new_addr = mask.numbers.each_with_index.map { |n, i| n & @numbers[i] }
173
+ IPv6.new(new_addr.map { |n| '%04x' % n }.join(':'))
174
+ end
175
+
176
+ # 完整地址区间,含网络地址与全 1 广播地址。
177
+ def range_with(mask)
178
+ return [self, self] if mask.mask_counter.to_i == 128
179
+ net = network_with(mask)
180
+ prefix = mask.mask_counter.to_i
181
+ offset = 2**(128 - prefix) - 1
182
+ [net, net + offset]
183
+ end
184
+
185
+ def network_with?(mask)
186
+ msk_counter, msk = 0, mask.to_b.gsub(':', '')
187
+ while msk[-1] == '0'
188
+ msk_counter += 1
189
+ msk = msk[0..-2]
190
+ end
191
+ nt_counter, nt = 0, to_b.gsub(':', '')
192
+ while nt[-1] == '0'
193
+ nt_counter += 1
194
+ nt = nt[0..-2]
195
+ end
196
+ nt_counter >= msk_counter
197
+ end
198
+ alias is_network_with? network_with?
199
+
200
+ def prefix_with(another_ip)
201
+ ip1 = to_b.gsub(':', '')
202
+ ip2 = another_ip.to_b.gsub(':', '')
203
+ count = 0
204
+ count += 1 while ip1[count] == ip2[count] && count < 128
205
+ count
206
+ end
207
+
208
+ # ---- 排序比较 ----
209
+
210
+ include Comparable
211
+ def <=>(other)
212
+ @number <=> other.number
213
+ end
214
+ end
215
+
216
+ # IPv6 掩码工厂。
217
+ class IPv6Mask
218
+ class << self
219
+ def number(num)
220
+ raise ArgumentError, "Invalid prefix length #{num}" unless (0..128).include?(num)
221
+ hex = '%032x' % ('1' * num + '0' * (128 - num)).to_i(2)
222
+ str = hex.unpack('a4a4a4a4a4a4a4a4').join(':')
223
+ IPv6.new(str)
224
+ end
225
+
226
+ def string(str)
227
+ temp = IPv6.new(str)
228
+ raise "Invalid net mask #{str}!" unless temp.mask?
229
+ temp
230
+ end
231
+
232
+ def anti_number(num)
233
+ hex = '%032x' % ('0' * num + '1' * (128 - num)).to_i(2)
234
+ str = hex.unpack('a4a4a4a4a4a4a4a4').join(':')
235
+ IPv6.new(str)
236
+ end
237
+
238
+ def anti_string(str)
239
+ temp = IPv6.new(str)
240
+ raise "Invalid anti mask #{str}!" unless temp.anti_mask?
241
+ temp
242
+ end
243
+ end
244
+ end
data/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NetworkInfraUtility
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end