network-infra-utility 0.3.0 → 0.6.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.
- checksums.yaml +4 -4
- data/.gitignore +21 -0
- data/GUIDE.md +212 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +70 -0
- data/Rakefile +1 -1
- data/bin/dns-query +836 -0
- data/bin/geo-doc +135 -0
- data/bin/geo-get +1 -1
- data/bin/geo-update +429 -0
- data/document/ASNum/346/250/241/345/235/227/345/212/237/350/203/275/350/257/264/346/230/216.md +242 -0
- data/document/DNSQuery/345/267/245/345/205/267/344/275/277/347/224/250/346/226/271/346/263/225.md +248 -0
- data/document/Geo/345/221/275/344/273/244/345/267/245/345/205/267/344/275/277/347/224/250/346/226/271/346/263/225.md +441 -0
- data/document/IP/346/250/241/345/235/227/345/212/237/350/203/275/350/257/264/346/230/216.md +297 -0
- data/document/MAC/346/250/241/345/235/227/345/212/237/350/203/275/350/257/264/346/230/216.md +296 -0
- data/document/SSH/350/277/236/346/216/245/345/256/242/346/210/267/347/253/257/344/275/277/347/224/250/346/226/271/346/263/225.md +764 -0
- data/network-infra-utility.gemspec +4 -2
- data/network.rb +3 -1
- data/service/geodb/geodb.rb +278 -1
- data/service/ssh/README.md +955 -0
- data/service/ssh/bin/ssh-client +198 -0
- data/service/ssh/design/SSH/350/277/236/346/216/245/345/256/242/346/210/267/347/253/257/345/212/237/350/203/275/351/234/200/346/261/202/346/226/207/346/241/243.md +292 -0
- data/service/ssh/design/SSH/350/277/236/346/216/245/345/256/242/346/210/267/347/253/257/350/257/246/347/273/206/350/256/276/350/256/241/346/226/207/346/241/243.md +1521 -0
- data/service/ssh/design/SSH/350/277/236/346/216/245/345/256/242/346/210/267/347/253/257/350/275/257/344/273/266/350/256/276/350/256/241/346/226/207/346/241/243.md +2493 -0
- data/service/ssh/ext/ssh_core/bin/ssh_core.cmd +28 -0
- data/service/ssh/ext/ssh_core/config/sys.config +0 -0
- data/service/ssh/ext/ssh_core/config/vm.args +0 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/CHECKSUM +1 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/LICENSE +21 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/README.md +696 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/VERSION +1 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/contents.tar.gz +0 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/metadata.config +15 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/rebar.config +17 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/rebar.lock +1 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx.app.src +10 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx.erl +506 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_config.erl +393 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_config.hrl +18 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_consult.erl +81 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_decoder.erl +1909 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_encoder.erl +116 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_parser.erl +1214 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_to_json.erl +408 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_to_term.erl +389 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/jsx_verify.erl +121 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx.erl +506 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_config.erl +393 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_config.hrl +18 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_consult.erl +81 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_decoder.erl +1909 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_encoder.erl +116 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_parser.erl +1214 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_to_json.erl +408 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_to_term.erl +389 -0
- data/service/ssh/ext/ssh_core/local_deps/jsx/src/src/jsx_verify.erl +121 -0
- data/service/ssh/ext/ssh_core/rebar.config +24 -0
- data/service/ssh/ext/ssh_core/rebar.lock +1 -0
- data/service/ssh/ext/ssh_core/src/ssh_auth_engine.erl +156 -0
- data/service/ssh/ext/ssh_core/src/ssh_channel_stm.erl +232 -0
- data/service/ssh/ext/ssh_core/src/ssh_codec.erl +83 -0
- data/service/ssh/ext/ssh_core/src/ssh_conn_sup.erl +48 -0
- data/service/ssh/ext/ssh_core/src/ssh_conn_worker.erl +535 -0
- data/service/ssh/ext/ssh_core/src/ssh_core.app.src +36 -0
- data/service/ssh/ext/ssh_core/src/ssh_core_app.erl +11 -0
- data/service/ssh/ext/ssh_core/src/ssh_core_sup.erl +46 -0
- data/service/ssh/ext/ssh_core/src/ssh_infra_sup.erl +117 -0
- data/service/ssh/ext/ssh_core/src/ssh_ipc.hrl +80 -0
- data/service/ssh/ext/ssh_core/src/ssh_ipc_coalesce.erl +94 -0
- data/service/ssh/ext/ssh_core/src/ssh_ipc_gateway.erl +467 -0
- data/service/ssh/ext/ssh_core/src/ssh_ipc_proto.erl +95 -0
- data/service/ssh/ext/ssh_core/src/ssh_jump_chain.erl +101 -0
- data/service/ssh/ext/ssh_core/src/ssh_keepalive_mgr.erl +222 -0
- data/service/ssh/ext/ssh_core/src/ssh_known_hosts_proxy.erl +67 -0
- data/service/ssh/ext/ssh_core/src/ssh_port_fwd.erl +225 -0
- data/service/ssh/ext/ssh_core/src/ssh_sftp_session.erl +250 -0
- data/service/ssh/ext/ssh_core/src/ssh_sftp_sup.erl +62 -0
- data/service/ssh/ext/ssh_core_rs/Cargo.lock +2345 -0
- data/service/ssh/ext/ssh_core_rs/Cargo.toml +30 -0
- data/service/ssh/ext/ssh_core_rs/bin/ssh_core_rs +34 -0
- data/service/ssh/ext/ssh_core_rs/bin/ssh_core_rs.cmd +40 -0
- data/service/ssh/ext/ssh_core_rs/src/channel.rs +296 -0
- data/service/ssh/ext/ssh_core_rs/src/coalesce.rs +143 -0
- data/service/ssh/ext/ssh_core_rs/src/codec.rs +71 -0
- data/service/ssh/ext/ssh_core_rs/src/conn.rs +628 -0
- data/service/ssh/ext/ssh_core_rs/src/gateway.rs +389 -0
- data/service/ssh/ext/ssh_core_rs/src/handler.rs +293 -0
- data/service/ssh/ext/ssh_core_rs/src/keepalive.rs +194 -0
- data/service/ssh/ext/ssh_core_rs/src/main.rs +351 -0
- data/service/ssh/ext/ssh_core_rs/src/portfwd.rs +378 -0
- data/service/ssh/ext/ssh_core_rs/src/proto.rs +198 -0
- data/service/ssh/ext/ssh_core_rs/src/sftp.rs +294 -0
- data/service/ssh/lib/network_infra_utility/ssh/automation/macro_engine.rb +213 -0
- data/service/ssh/lib/network_infra_utility/ssh/client.rb +257 -0
- data/service/ssh/lib/network_infra_utility/ssh/config/schema.rb +90 -0
- data/service/ssh/lib/network_infra_utility/ssh/config/settings.rb +103 -0
- data/service/ssh/lib/network_infra_utility/ssh/config/store.rb +90 -0
- data/service/ssh/lib/network_infra_utility/ssh/ipc/coalesce.rb +83 -0
- data/service/ssh/lib/network_infra_utility/ssh/ipc/errors.rb +36 -0
- data/service/ssh/lib/network_infra_utility/ssh/ipc/router.rb +212 -0
- data/service/ssh/lib/network_infra_utility/ssh/ipc/transport.rb +81 -0
- data/service/ssh/lib/network_infra_utility/ssh/security/host_key.rb +211 -0
- data/service/ssh/lib/network_infra_utility/ssh/security/vault.rb +211 -0
- data/service/ssh/lib/network_infra_utility/ssh/session/history.rb +56 -0
- data/service/ssh/lib/network_infra_utility/ssh/session/manager.rb +92 -0
- data/service/ssh/lib/network_infra_utility/ssh/session/session.rb +109 -0
- data/service/ssh/lib/network_infra_utility/ssh/session/tree.rb +95 -0
- data/service/ssh/lib/network_infra_utility/ssh/terminal/ansi_parser.rb +435 -0
- data/service/ssh/lib/network_infra_utility/ssh/terminal/buffer.rb +78 -0
- data/service/ssh/lib/network_infra_utility/ssh/terminal/emulator.rb +159 -0
- data/service/ssh/lib/network_infra_utility/ssh/terminal/logger.rb +195 -0
- data/service/ssh/lib/network_infra_utility/ssh/terminal/screen.rb +212 -0
- data/service/ssh/lib/network_infra_utility/ssh/terminal/theme.rb +127 -0
- data/service/ssh/lib/network_infra_utility/ssh/version.rb +7 -0
- data/service/ssh/lib/network_infra_utility/ssh.rb +44 -0
- data/support/basic/as_num.rb +221 -0
- data/support/basic/mac_address.rb +281 -0
- data/version.rb +1 -1
- metadata +142 -1
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
|
|
2
|
+
# IP 模块功能说明
|
|
3
|
+
|
|
4
|
+
> 源码位置:`support/basic/ip.rb` / `ipv4_address.rb` / `ipv6_address.rb`
|
|
5
|
+
> 加载方式:`require 'network'` 自动加载,或单独 `require_relative 'support/basic/ip'`
|
|
6
|
+
|
|
7
|
+
## 统一入口 `IP`
|
|
8
|
+
|
|
9
|
+
`IP` 模块提供 `v4` / `v6` / `range` / `cross` / `xross` 五个模块方法,内部按地址串是否含 `.` `:` 自动分派到 `IPv4` / `IPv6`。
|
|
10
|
+
|
|
11
|
+
### `IP.v4(string)` — 解析 IPv4 或 CIDR
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
require 'network'
|
|
15
|
+
|
|
16
|
+
# 单地址 → 返回 IPv4 对象
|
|
17
|
+
ip = IP.v4('10.37.214.42')
|
|
18
|
+
ip.to_d # => "10.37.214.42" 十进制点分
|
|
19
|
+
ip.number # => 173185606 32位整数
|
|
20
|
+
ip.class_a? # => true
|
|
21
|
+
ip.private? # => true
|
|
22
|
+
|
|
23
|
+
# CIDR → 返回 [IPv4, IPv4Mask]
|
|
24
|
+
ip2, mask = IP.v4('10.37.214.42/17')
|
|
25
|
+
mask.to_d # => "255.255.128.0"
|
|
26
|
+
mask.mask_counter # => 17 前缀长度
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### `IP.v6(string)` — 解析 IPv6 或 CIDR
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
# 单地址 → 返回 IPv6 对象
|
|
33
|
+
ip = IP.v6('2001:db8::1')
|
|
34
|
+
ip.to_s # => "2001:db8::1" 压缩形式
|
|
35
|
+
ip.to_s_full # => "2001:0db8:0000:...0001" 完整 8 段
|
|
36
|
+
ip.number # => 42540488161975842760550356425300241409 128位整数
|
|
37
|
+
|
|
38
|
+
# CIDR → 返回 [IPv6, IPv6Mask]
|
|
39
|
+
ip6, mask = IP.v6('2001:db8::/32')
|
|
40
|
+
mask.mask_counter # => 32
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### `IP.range(addr)` — 展开地址区间
|
|
44
|
+
|
|
45
|
+
支持三种输入,统一返回 `[start, end]`:
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
# CIDR → [网络地址, 广播地址]
|
|
49
|
+
start_ip, end_ip = IP.range('1.0.4.0/22')
|
|
50
|
+
start_ip.to_d # => "1.0.4.0"
|
|
51
|
+
end_ip.to_d # => "1.0.7.255"
|
|
52
|
+
|
|
53
|
+
# 区间串 → [起始, 结束]
|
|
54
|
+
s, e = IP.range('1.0.4.0-1.0.7.255')
|
|
55
|
+
s.to_d # => "1.0.4.0"
|
|
56
|
+
e.to_d # => "1.0.7.255"
|
|
57
|
+
|
|
58
|
+
# 单址 → [addr, addr]
|
|
59
|
+
s, e = IP.range('1.0.4.0')
|
|
60
|
+
[s.to_d, e.to_d] # => ["1.0.4.0", "1.0.4.0"]
|
|
61
|
+
|
|
62
|
+
# IPv6 同理
|
|
63
|
+
s6, e6 = IP.range('2001:db8::/64')
|
|
64
|
+
[s6.to_s, e6.to_s] # => ["2001:db8::", "2001:db8::ffff:ffff:ffff:ffff"]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### `IP.cross(range1, range2)` — 判断两区间是否相交
|
|
68
|
+
|
|
69
|
+
```ruby
|
|
70
|
+
r1 = IP.range('10.0.0.0/24') # [10.0.0.0, 10.0.0.255]
|
|
71
|
+
r2 = IP.range('10.0.0.128/25') # [10.0.0.128, 10.0.0.255]
|
|
72
|
+
r3 = IP.range('10.0.1.0/24') # [10.0.1.0, 10.0.1.255]
|
|
73
|
+
|
|
74
|
+
IP.cross(r1, r2) # => true 相交
|
|
75
|
+
IP.cross(r1, r3) # => false 不相交
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### `IP.xross(range1, range2, option)` — 求两区间合并端点
|
|
79
|
+
|
|
80
|
+
返回排序去重后的端点序列,从 0 地址起算转换为 IP 对象:
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
r1 = IP.range('10.0.0.0/24')
|
|
84
|
+
r2 = IP.range('10.0.0.128/25')
|
|
85
|
+
|
|
86
|
+
IP.xross(r1, r2, :v4).map(&:to_d)
|
|
87
|
+
# => ["10.0.0.0", "10.0.0.128", "10.0.0.255"]
|
|
88
|
+
|
|
89
|
+
# IPv6 用 :v6
|
|
90
|
+
r1v6 = IP.range('2001:db8::/64')
|
|
91
|
+
r2v6 = IP.range('2001:db8::8000:0/113')
|
|
92
|
+
IP.xross(r1v6, r2v6, :v6).map(&:to_s)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## IPv4 对象方法
|
|
98
|
+
|
|
99
|
+
### 创建
|
|
100
|
+
|
|
101
|
+
```ruby
|
|
102
|
+
ip = IPv4.new('10.37.214.42')
|
|
103
|
+
ip2, mask = IP.v4('192.168.1.0/24') # 推荐:通过 IP.v4 解析 CIDR
|
|
104
|
+
|
|
105
|
+
# IPv4 接受十六进制 / 二进制前缀
|
|
106
|
+
IPv4.new('0x0a.0x25.0xd6.0x2a').to_d # => "10.37.214.42"
|
|
107
|
+
IPv4.new('0b00001010.0.0.1').to_d # => "10.0.0.1"
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### 输出格式
|
|
111
|
+
|
|
112
|
+
```ruby
|
|
113
|
+
ip = IPv4.new('10.37.214.42')
|
|
114
|
+
|
|
115
|
+
ip.to_a # => [10, 37, 214, 42] 四段整数数组
|
|
116
|
+
ip.to_d # => "10.37.214.42" 十进制点分
|
|
117
|
+
ip.to_h # => "0a.25.d6.2a" 十六进制点分
|
|
118
|
+
ip.to_b # => "00001010.00100101.11010110.00101010" 二进制点分
|
|
119
|
+
ip.to_s # => "10.37.214.42" 同 to_d
|
|
120
|
+
ip.number # => 173185606 32位整数
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### 算术运算
|
|
124
|
+
|
|
125
|
+
```ruby
|
|
126
|
+
ip = IPv4.new('10.0.0.1')
|
|
127
|
+
|
|
128
|
+
(ip + 5).to_d # => "10.0.0.6" 加偏移量
|
|
129
|
+
(ip - 1).to_d # => "10.0.0.0" 减偏移量
|
|
130
|
+
|
|
131
|
+
# 按位与:可传 IPv4、IPv4Mask 或前缀长度
|
|
132
|
+
ip & IPv4.new('255.255.255.0') # 网络地址
|
|
133
|
+
ip & 24 # 同上,等价写法
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 地址分类
|
|
137
|
+
|
|
138
|
+
```ruby
|
|
139
|
+
IPv4.new('10.0.0.1').class_a? # => true A 类 (10-126)
|
|
140
|
+
IPv4.new('128.1.0.0').class_b? # => true B 类 (128-191)
|
|
141
|
+
IPv4.new('192.0.1.0').class_c? # => true C 类 (192-223)
|
|
142
|
+
IPv4.new('224.0.0.1').class_d? # => true D 类 (224-239) 组播
|
|
143
|
+
IPv4.new('240.0.0.1').class_e? # => true E 类 (240-255) 保留
|
|
144
|
+
|
|
145
|
+
IPv4.new('10.0.0.1').private? # => true 私有地址 (10/172.16-31/192.168)
|
|
146
|
+
IPv4.new('8.8.8.8').private? # => false
|
|
147
|
+
IPv4.new('127.0.0.1').loopback? # => true 环回地址
|
|
148
|
+
IPv4.new('0.0.0.0').special? # => true 特殊地址
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### 掩码与网络
|
|
152
|
+
|
|
153
|
+
```ruby
|
|
154
|
+
ip, mask = IP.v4('10.37.214.42/17')
|
|
155
|
+
|
|
156
|
+
# 掩码属性
|
|
157
|
+
mask.to_d # => "255.255.128.0"
|
|
158
|
+
mask.mask? # => true 是否合法网络掩码
|
|
159
|
+
mask.mask_counter # => 17 前缀长度
|
|
160
|
+
mask.anti_mask # => 反掩码对象
|
|
161
|
+
|
|
162
|
+
# 网络地址
|
|
163
|
+
net = ip.network_with(mask)
|
|
164
|
+
net.to_d # => "10.37.128.0"
|
|
165
|
+
|
|
166
|
+
# 网段完整区间
|
|
167
|
+
start_ip, end_ip = ip.range_with(mask)
|
|
168
|
+
start_ip.to_d # => "10.37.128.0" 网络地址
|
|
169
|
+
end_ip.to_d # => "10.37.255.255" 广播地址
|
|
170
|
+
|
|
171
|
+
# 判断是否为该网段的网络地址
|
|
172
|
+
ip.network_with?(mask) # => false
|
|
173
|
+
net.network_with?(mask) # => true
|
|
174
|
+
|
|
175
|
+
# 最长公共前缀长度
|
|
176
|
+
IPv4.new('10.0.0.1').prefix_with(IPv4.new('10.0.1.1')) # => 23
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### 子网划分 `delegation`
|
|
180
|
+
|
|
181
|
+
```ruby
|
|
182
|
+
# 把 10.0.0.0/24 划分为 /26 子网 → 4 个子网
|
|
183
|
+
base = IPv4.new('10.0.0.0')
|
|
184
|
+
subnets = base.delegation(24, 26)
|
|
185
|
+
subnets.map { |ip, m| "#{ip.to_d}/#{m.mask_counter}" }
|
|
186
|
+
# => ["10.0.0.0/26", "10.0.0.64/26", "10.0.0.128/26", "10.0.0.192/26"]
|
|
187
|
+
|
|
188
|
+
# 也可传 IPv4/IPv4Mask 作为参数
|
|
189
|
+
base.delegation(IPv4Mask.number(24), IPv4Mask.number(27))
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### 排序比较
|
|
193
|
+
|
|
194
|
+
```ruby
|
|
195
|
+
addrs = [IPv4.new('10.0.0.3'), IPv4.new('10.0.0.1'), IPv4.new('10.0.0.2')]
|
|
196
|
+
addrs.sort.map(&:to_d) # => ["10.0.0.1", "10.0.0.2", "10.0.0.3"]
|
|
197
|
+
addrs.min.to_d # => "10.0.0.1"
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## IPv6 对象方法
|
|
203
|
+
|
|
204
|
+
接口与 IPv4 对称,区别在于 128 位、8 段十六进制。
|
|
205
|
+
|
|
206
|
+
```ruby
|
|
207
|
+
ip6, mask6 = IP.v6('2001:db8::1/64')
|
|
208
|
+
|
|
209
|
+
# 输出
|
|
210
|
+
ip6.to_a # => [8193, 3512, 0, 0, 0, 0, 0, 1]
|
|
211
|
+
ip6.to_h # => "2001:0db8:0000:0000:0000:0000:0000:0001"
|
|
212
|
+
ip6.to_s # => "2001:db8::1" 压缩形式
|
|
213
|
+
ip6.to_s_full # => "2001:0db8:0000:...0001" 完整形式
|
|
214
|
+
ip6.number # => 42540488161975842760550356425300241409
|
|
215
|
+
|
|
216
|
+
# 算术 / 掩码 / 网络(同 IPv4)
|
|
217
|
+
ip6 & 64 # 网络地址
|
|
218
|
+
ip6.network_with(mask6).to_s # => "2001:db8::"
|
|
219
|
+
ip6.range_with(mask6).map(&:to_s) # => ["2001:db8::", "2001:db8::ffff:ffff:ffff:ffff"]
|
|
220
|
+
|
|
221
|
+
# 子网划分
|
|
222
|
+
base6 = IPv6.new('2001:db8::')
|
|
223
|
+
subnets6 = base6.delegation(64, 66)
|
|
224
|
+
subnets6.map { |ip, m| "#{ip.to_s}/#{m.mask_counter}" }
|
|
225
|
+
# => ["2001:db8::/66", "2001:db8:0:0:4000::/66", "2001:db8:0:0:8000::/66", "2001:db8:0:0:c000::/66"]
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## IPv4Mask / IPv6Mask 工厂
|
|
231
|
+
|
|
232
|
+
```ruby
|
|
233
|
+
# 按前缀长度创建网络掩码
|
|
234
|
+
IPv4Mask.number(24).to_d # => "255.255.255.0"
|
|
235
|
+
IPv4Mask.number(17).to_d # => "255.255.128.0"
|
|
236
|
+
IPv6Mask.number(64).to_h # => "ffff:ffff:ffff:ffff:0000:0000:0000:0000"
|
|
237
|
+
|
|
238
|
+
# 按字符串创建(校验合法性)
|
|
239
|
+
IPv4Mask.string('255.255.0.0').mask_counter # => 16
|
|
240
|
+
|
|
241
|
+
# 反掩码
|
|
242
|
+
IPv4Mask.anti_number(24).to_d # => "0.0.0.255"
|
|
243
|
+
IPv4Mask.anti_string('0.0.0.255').anti_mask_counter # => 24
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## 典型业务场景
|
|
249
|
+
|
|
250
|
+
### 1. 判断 IP 是否在某个网段内
|
|
251
|
+
|
|
252
|
+
```ruby
|
|
253
|
+
ip = IP.v4('10.37.214.42')
|
|
254
|
+
net_start, net_end = IP.range('10.37.128.0/17')
|
|
255
|
+
ip.number.between?(net_start.number, net_end.number) # => true
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### 2. CIDR 转区间 → 整数范围(geo-api 内部用法)
|
|
259
|
+
|
|
260
|
+
```ruby
|
|
261
|
+
# geodb.rb 把 CIDR 转为 [start_num, end_num] 作为 JSON 键
|
|
262
|
+
range = IP.range('1.0.4.0/22').map(&:number) # => [16777984, 16778239]
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### 3. 合并重叠网段
|
|
266
|
+
|
|
267
|
+
```ruby
|
|
268
|
+
ranges = [
|
|
269
|
+
IP.range('10.0.0.0/24'),
|
|
270
|
+
IP.range('10.0.0.128/25'),
|
|
271
|
+
IP.range('10.0.1.0/24')
|
|
272
|
+
]
|
|
273
|
+
|
|
274
|
+
# 去重合并
|
|
275
|
+
merged = [ranges.first]
|
|
276
|
+
ranges[1..].each do |r|
|
|
277
|
+
if IP.cross(merged.last, r)
|
|
278
|
+
merged[-1] = [merged.last.first, [merged.last.last, r.last].max]
|
|
279
|
+
else
|
|
280
|
+
merged << r
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
merged.map { |s, e| "#{s.to_d} - #{e.to_d}" }
|
|
285
|
+
# => ["10.0.0.0 - 10.0.1.255"]
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### 4. 子网规划
|
|
289
|
+
|
|
290
|
+
```ruby
|
|
291
|
+
# 从 172.16.0.0/16 中分配 /24 子网,取前 4 个
|
|
292
|
+
base = IPv4.new('172.16.0.0')
|
|
293
|
+
all_subnets = base.delegation(16, 24)
|
|
294
|
+
allocated = all_subnets.first(4)
|
|
295
|
+
allocated.map { |ip, m| ip.to_d + "/#{m.mask_counter}" }
|
|
296
|
+
# => ["172.16.0.0/24", "172.16.1.0/24", "172.16.2.0/24", "172.16.3.0/24"]
|
|
297
|
+
```
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
|
|
2
|
+
# MAC 模块功能说明
|
|
3
|
+
|
|
4
|
+
> 源码位置:`support/basic/mac_address.rb`
|
|
5
|
+
> 加载方式:`require 'network'` 自动加载,或单独 `require_relative 'support/basic/mac_address'`
|
|
6
|
+
|
|
7
|
+
## MAC 地址背景
|
|
8
|
+
|
|
9
|
+
MAC 地址是数据链路层 48 位硬件地址,通常用 6 组或 3 组十六进制表示。
|
|
10
|
+
|
|
11
|
+
### 支持的输入格式
|
|
12
|
+
|
|
13
|
+
| 分隔符 | 分组数 | 示例 | 说明 |
|
|
14
|
+
| ------ | ------ | ------------------- | ---------------- |
|
|
15
|
+
| `-` | 6 | `00-1A-2B-3C-4D-5E` | Windows 常用 |
|
|
16
|
+
| `:` | 6 | `00:1A:2B:3C:4D:5E` | Linux/Cisco 常用 |
|
|
17
|
+
| `.` | 3 | `001A.2B3C.4D5E` | 点分格式 |
|
|
18
|
+
| `-` | 3 | `001A-2B3C-4D5E` | 部分设备 3 段横杠 |
|
|
19
|
+
| 无 | — | `001A2B3C4D5E` | 无分隔符 |
|
|
20
|
+
|
|
21
|
+
### 特殊位
|
|
22
|
+
|
|
23
|
+
| 位 | 位置 | 说明 |
|
|
24
|
+
| ------ | ----------- | ----------------------------- |
|
|
25
|
+
| I/G 位 | 首字节 bit0 | 0=单播(个体),1=组播 |
|
|
26
|
+
| U/L 位 | 首字节 bit1 | 0=全球分配(OUI),1=本地分配 |
|
|
27
|
+
|
|
28
|
+
### EUI-64
|
|
29
|
+
|
|
30
|
+
MAC → EUI-64:在 OUI(前 3 字节)和 NIC(后 3 字节)之间插入 `FF:FE`。
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
00-1A-2B-3C-4D-5E → 00:1A:2B:FF:FE:3C:4D:5E
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
IPv6 SLAAC 接口标识符在此基础上翻转 U/L 位(首字节 XOR `0x02`)。
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 统一入口 `MAC`
|
|
41
|
+
|
|
42
|
+
`MAC.address` 接受 String、Array 或 Integer,返回 `MacAddress` 对象:
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
require 'network'
|
|
46
|
+
|
|
47
|
+
MAC.address("00:1A:2B:3C:4D:5E") # 6 段冒号
|
|
48
|
+
MAC.address("001A.2B3C.4D5E") # 3 段点分
|
|
49
|
+
MAC.address("001A-2B3C-4D5E") # 3 段横杠
|
|
50
|
+
MAC.address("001A2B3C4D5E") # 无分隔符
|
|
51
|
+
MAC.address(0x001A2B3C4D5E) # Integer
|
|
52
|
+
MAC.address([0x00, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E]) # Array
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
也可直接用 `MacAddress.new`,参数同上。
|
|
56
|
+
|
|
57
|
+
不同格式的同一地址等价:
|
|
58
|
+
|
|
59
|
+
```ruby
|
|
60
|
+
a = MAC.address("00:1A:2B:3C:4D:5E")
|
|
61
|
+
b = MAC.address("001A.2B3C.4D5E")
|
|
62
|
+
a == b # => true
|
|
63
|
+
a.number == b.number # => true
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 属性
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
mac = MacAddress.new("00-1A-2B-3C-4D-5E")
|
|
72
|
+
|
|
73
|
+
mac.numbers # => [0, 26, 43, 60, 77, 94] 6 字节整数数组
|
|
74
|
+
mac.number # => 112394521950 48 位整数
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 输出格式
|
|
80
|
+
|
|
81
|
+
### `to_hex(separator, groups)` — 通用格式化
|
|
82
|
+
|
|
83
|
+
`separator` 控制分隔符,`groups` 控制分组数(1 / 2 / 3 / 6):
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
mac = MacAddress.new("00-1A-2B-3C-4D-5E")
|
|
87
|
+
|
|
88
|
+
mac.to_hex("-", 6) # => "00-1a-2b-3c-4d-5e" 6 组(默认)
|
|
89
|
+
mac.to_hex(":", 6) # => "00:1a:2b:3c:4d:5e" Linux 格式
|
|
90
|
+
mac.to_hex(".", 3) # => "001a.2b3c.4d5e" Cisco 点分
|
|
91
|
+
mac.to_hex("", 1) # => "001a2b3c4d5e" 无分隔符
|
|
92
|
+
mac.to_hex("", 6) # => "001a2b3c4d5e" 无分隔符(6 组拼合)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 其他格式方法
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
mac.to_s # => "00-1a-2b-3c-4d-5e" 默认 6 组 - 分隔
|
|
99
|
+
mac.to_upcase(":", 6) # => "00:1A:2B:3C:4D:5E" 大写
|
|
100
|
+
mac.to_downcase # => "00-1a-2b-3c-4d-5e" 小写
|
|
101
|
+
mac.inspect # => "00-1a-2b-3c-4d-5e" 同 to_s
|
|
102
|
+
|
|
103
|
+
mac.to_a # => [0, 26, 43, 60, 77, 94] 6 字节整数数组
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## 属性判定
|
|
109
|
+
|
|
110
|
+
### 单播 / 组播(I/G 位)
|
|
111
|
+
|
|
112
|
+
```ruby
|
|
113
|
+
MacAddress.new("00-1A-2B-3C-4D-5E").multicast? # => false 单播
|
|
114
|
+
MacAddress.new("01-00-5E-00-00-01").multicast? # => true 组播
|
|
115
|
+
|
|
116
|
+
# 别名
|
|
117
|
+
mac.group? # 等价 multicast?
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 全球 / 本地分配(U/L 位)
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
MacAddress.new("00-1A-2B-3C-4D-5E").locally_administered? # => false 全球分配
|
|
124
|
+
MacAddress.new("02-00-00-00-00-01").locally_administered? # => true 本地分配
|
|
125
|
+
|
|
126
|
+
MacAddress.new("00-1A-2B-3C-4D-5E").universally_administered? # => true
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 特殊地址
|
|
130
|
+
|
|
131
|
+
```ruby
|
|
132
|
+
MacAddress.new("FF-FF-FF-FF-FF-FF").broadcast? # => true 全 1 广播
|
|
133
|
+
MacAddress.new("00-00-00-00-00-00").zero? # => true 全 0 空地址
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### OUI / NIC 分段
|
|
137
|
+
|
|
138
|
+
```ruby
|
|
139
|
+
mac = MacAddress.new("00-1A-2B-3C-4D-5E")
|
|
140
|
+
|
|
141
|
+
mac.oui # => "00-1a-2b" 前 3 字节(组织唯一标识符)
|
|
142
|
+
mac.nic # => "3c-4d-5e" 后 3 字节(网卡标识)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## EUI-64 / IPv6 SLAAC
|
|
148
|
+
|
|
149
|
+
### `to_eui64` — 扩展为 8 字节
|
|
150
|
+
|
|
151
|
+
在 OUI 和 NIC 之间插入 `FF:FE`,返回 8 字节整数数组:
|
|
152
|
+
|
|
153
|
+
```ruby
|
|
154
|
+
mac = MacAddress.new("00-1A-2B-3C-4D-5E")
|
|
155
|
+
|
|
156
|
+
mac.to_eui64
|
|
157
|
+
# => [0, 26, 43, 255, 254, 60, 77, 94]
|
|
158
|
+
|
|
159
|
+
mac.to_eui64_s # => "00:1a:2b:ff:fe:3c:4d:5e" 默认冒号分隔
|
|
160
|
+
mac.to_eui64_s("-") # => "00-1a-2b-ff-fe-3c-4d-5e"
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### `to_interface_id` — IPv6 SLAAC 接口标识符
|
|
164
|
+
|
|
165
|
+
在 EUI-64 基础上翻转 U/L 位(首字节 bit1 翻转),返回 8 字节整数数组:
|
|
166
|
+
|
|
167
|
+
```ruby
|
|
168
|
+
mac = MacAddress.new("00-1A-2B-3C-4D-5E")
|
|
169
|
+
|
|
170
|
+
mac.to_interface_id
|
|
171
|
+
# => [2, 26, 43, 255, 254, 60, 77, 94]
|
|
172
|
+
|
|
173
|
+
mac.to_interface_id_s # => "02:1a:2b:ff:fe:3c:4d:5e"
|
|
174
|
+
mac.to_interface_id_s("-") # => "02-1a-2b-ff-fe-3c-4d-5e"
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
> `00` 翻转后变 `02`,因为 U/L 位(bit1)从 0 变 1,表示本地生成。
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## 排序比较
|
|
182
|
+
|
|
183
|
+
`MacAddress` 包含 `Comparable`,按 48 位整数排序:
|
|
184
|
+
|
|
185
|
+
```ruby
|
|
186
|
+
a = MacAddress.new("00-00-00-00-00-01")
|
|
187
|
+
b = MacAddress.new("00-00-00-00-00-02")
|
|
188
|
+
c = MacAddress.new("FF-FF-FF-FF-FF-FF")
|
|
189
|
+
|
|
190
|
+
a < b # => true
|
|
191
|
+
a == a # => true
|
|
192
|
+
|
|
193
|
+
[a, c, b].sort.map(&:number)
|
|
194
|
+
# => [1, 2, 281474976710655]
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### 作为 Hash key
|
|
198
|
+
|
|
199
|
+
```ruby
|
|
200
|
+
h = {
|
|
201
|
+
MacAddress.new("00-00-00-00-00-01") => "网关",
|
|
202
|
+
MacAddress.new("00-00-00-00-00-02") => "终端"
|
|
203
|
+
}
|
|
204
|
+
h[MacAddress.new("00:00:00:00:00:01")] # => "网关"(不同分隔符等价)
|
|
205
|
+
|
|
206
|
+
MacAddress.new("00:1A:2B:3C:4D:5E").hash == MacAddress.new("00-1A-2B-3C-4D-5E").hash
|
|
207
|
+
# => true
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## 有效性检查
|
|
213
|
+
|
|
214
|
+
`MacAddress.valid?` 不抛异常,返回布尔值:
|
|
215
|
+
|
|
216
|
+
```ruby
|
|
217
|
+
MacAddress.valid?("00:1A:2B:3C:4D:5E") # => true
|
|
218
|
+
MacAddress.valid?("GG:1A:2B:3C:4D:5E") # => false
|
|
219
|
+
MacAddress.valid?("00-1A-2B-3C") # => false 段数错误
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## 兼容原接口
|
|
225
|
+
|
|
226
|
+
### `writing(groups, splitter)` — 原版格式化方法
|
|
227
|
+
|
|
228
|
+
```ruby
|
|
229
|
+
mac = MacAddress.new("00-1A-2B-3C-4D-5E")
|
|
230
|
+
|
|
231
|
+
mac.writing # => "001a-2b3c-4d5e" 默认 3 组 - 分隔
|
|
232
|
+
mac.writing(6, ":") # => "00:1a:2b:3c:4d:5e" 6 组冒号
|
|
233
|
+
mac.writing(10, "-") # => "00-1a-2b-3c-4d-5e" >=6 归为 6
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
> 推荐使用 `to_hex` 代替 `writing`,接口更清晰。
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## 典型业务场景
|
|
241
|
+
|
|
242
|
+
### 1. 批量格式转换
|
|
243
|
+
|
|
244
|
+
```ruby
|
|
245
|
+
# 设备导出的 MAC 格式不统一,统一转为冒号格式
|
|
246
|
+
macs = ["00-1A-2B-3C-4D-5E", "001A.2B3C.4D5E", "001A-2B3C-4D5E", "001A2B3C4D5E"]
|
|
247
|
+
macs.map { |m| MAC.address(m).to_hex(":", 6) }
|
|
248
|
+
# => ["00:1a:2b:3c:4d:5e", "00:1a:2b:3c:4d:5e", "00:1a:2b:3c:4d:5e", "00:1a:2b:3c:4d:5e"]
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### 2. 识别组播地址
|
|
252
|
+
|
|
253
|
+
```ruby
|
|
254
|
+
# 从抓包数据中筛出组播 MAC
|
|
255
|
+
mac_list = ["00-1A-2B-3C-4D-5E", "01-00-5E-00-00-01", "33-33-00-00-00-01"]
|
|
256
|
+
|
|
257
|
+
multicast = mac_list.select { |m| MAC.address(m).multicast? }
|
|
258
|
+
# => ["01-00-5E-00-00-01", "33-33-00-00-00-01"]
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### 3. 提取 OUI 做厂商归类
|
|
262
|
+
|
|
263
|
+
```ruby
|
|
264
|
+
# 按 OUI 前缀分组统计设备厂商
|
|
265
|
+
devices = [
|
|
266
|
+
"00-1A-2B-3C-4D-5E", # OUI: 00-1a-2b
|
|
267
|
+
"00-1A-2B-AA-BB-CC", # OUI: 00-1a-2b
|
|
268
|
+
"08-00-27-11-22-33", # OUI: 08-00-27
|
|
269
|
+
]
|
|
270
|
+
|
|
271
|
+
devices.group_by { |m| MAC.address(m).oui }
|
|
272
|
+
# => {"00-1a-2b" => ["00-1A-2B-3C-4D-5E", "00-1A-2B-AA-BB-CC"],
|
|
273
|
+
# "08-00-27" => ["08-00-27-11-22-33"]}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### 4. 生成 IPv6 SLAAC 地址
|
|
277
|
+
|
|
278
|
+
```ruby
|
|
279
|
+
# 已知 MAC 和 IPv6 前缀,生成 SLAAC 自动配置地址
|
|
280
|
+
mac = MAC.address("00-1A-2B-3C-4D-5E")
|
|
281
|
+
prefix = "2001:db8::"
|
|
282
|
+
|
|
283
|
+
interface_id = mac.to_interface_id_s(":")
|
|
284
|
+
slaac_address = "#{prefix}#{interface_id}"
|
|
285
|
+
# => "2001:db8::02:1a:2b:ff:fe:3c:4d:5e"
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### 5. MAC 地址去重
|
|
289
|
+
|
|
290
|
+
```ruby
|
|
291
|
+
# 不同格式记录的同一 MAC 需要去重
|
|
292
|
+
raw = ["00:1A:2B:3C:4D:5E", "00-1A-2B-3C-4D-5E", "001A.2B3C.4D5E", "001A-2B3C-4D5E", "08-00-27-11-22-33"]
|
|
293
|
+
|
|
294
|
+
unique = raw.map { |m| MAC.address(m) }.uniq.map(&:to_s)
|
|
295
|
+
# => ["00-1a-2b-3c-4d-5e", "08-00-27-11-22-33"]
|
|
296
|
+
```
|