LabHub
学习 学习路径 课程

地址、子网与网关

做一个算地址的工具

在 LabHub 中继续学习

目标

使用 Python 编写一个工具,根据地址和掩码计算网络地址、广播地址、可用地址范围和主机数, 再利用该工具判断地址是否属于同一子网、对地址段分类并检查重叠。最后根据所需主机数 选择合适的前缀,并留下设计说明。

为什么重要

防火墙规则、网络策略和 VPC 设计全都建立在这些计算之上。 但如果反复手算,迟早一定会出错,而错误通常要等到故障发生后才会暴露。 因此,更好的做法是制作一次工具并持续复用。

尤其需要注意 /31/32。如果主机数一律按“总数减 2”计算, 两者会分别得到 0 和 -1,而工具不会报错,只会原样返回错误结果。 这是一种检查器无法发现的谎言,只有了解例外的人才能避免。

步骤

  1. 手工计算三个地址段,并逐行写入 /root/cidr/01-hand.txt。地址段为 192.168.40.130/2610.10.6.75/20172.20.200.9/23,每行必须包含 CIDR、网络地址、广播地址、第一个主机地址、最后一个主机地址和主机数量。
  2. 创建 /root/cidr/subnet.pypython3 subnet.py 203.0.113.77/28python3 subnet.py 203.0.113.77 255.255.255.240 必须给出相同答案,输出为 prefix= network= broadcast= first= last= hosts= 六行。将两次运行结果保存到 /root/cidr/02-tool.txt
  3. 修改工具,使其正确处理 /31/32。两者均为 broadcast=none/31 的两个地址都可作为主机(hosts=2),/32 只有自身一个地址(hosts=1)。将运行 10.0.0.5/3110.0.0.5/32 的结果及其原因写入 /root/cidr/03-edge.txt
  4. 创建 /root/cidr/same.pypython3 same.py <주소/프리픽스> <상대주소> 只能输出 samedifferent 其中一个单词。运行五组地址并保存到 /root/cidr/04-same.txt,采用 주소/프리픽스 상대주소 판정 格式。
  5. 创建 /root/cidr/classify.pypython3 classify.py <주소> 输出 private link-local loopback cgnat multicast public 中的一个。运行九个地址并保存到 /root/cidr/05-class.txt,采用 주소 분류 格式。
  6. 10.60.8.0/22 拆分为 /24 网络,在 /root/cidr/06-split.txt 中逐行按 네트워크/24 first= last= hosts= 格式记录。
  7. 判断候选网络是否与现有网络重叠,并写入 /root/cidr/07-overlap.txt。重叠时写 후보 overlap=겹치는대역,不重叠时写 후보 free
  8. /root/cidr/08-report.md 中编写设计说明。内容必须包括满足三种主机数需求(120 台、600 台、30 台)的最小前缀、连续分配地址段的好处,以及何时使用 /31/32

参考

手工计算三个地址段

前缀不是 8 的倍数时最容易出错。先计算块大小(2 的主机位数次方),再从其倍数中找出包含该地址的范围,其起点就是网络地址。

例如,/26 的块大小为 64,因此边界为 0、64、128、192。130 位于从 128 开始的块中。

使用 python3 -c "import ipaddress; n=ipaddress.ip_network('192.168.40.130/26', strict=False); print(n.network_address, n.broadcast_address, n.num_addresses-2)" 进行复核。

制作计算工具

/26255.255.255.192 表达的是同一含义。如果传入两个参数,可以将它们拼接成 주소/마스크,再交给同一个函数。

例如 ipaddress.ip_network("203.0.113.77 255.255.255.240".replace(' ', '/'), strict=False)

评分器会原样查找输出中的键名(prefix= network= broadcast= first= last= hosts=),因此不要修改。主机数等于总地址数减去网络地址和广播地址。

处理 /31 与 /32 的例外

num_addresses - 2/31 会得到 0,对 /32 会得到 -1。工具不会报错,只会原样返回这些值,因此只有了解例外的人才能避开错误。

当前缀大于等于 31 时,请进入另一个分支。在该分支中不设置广播地址(broadcast=none),第一个主机地址就是网络地址,最后一个主机地址是该网络的最后一个地址,主机数等于总地址数。

请在说明文件中写明哪一份 RFC 针对哪类链路允许这种用法。

判断是否属于同一子网

判断只需一行——对方地址是否位于我的网络中。即 ipaddress.ip_address(other) in ipaddress.ip_network(mine, strict=False)

前两组的地址相同,只有掩码不同。在这里可以看出,只要掩码错一位,旁边的计算机就会变成位于地球另一端。

输出必须只有 samedifferent 一个单词。评分器还会使用说明中没有给出的地址组再询问一次。

对地址段的性质分类

建立规则列表,从上到下寻找匹配项;如果均不匹配,则为 public

需要的地址段如下——环回 127.0.0.0/8、链路本地 169.254.0.0/16、CGNAT 100.64.0.0/10、组播 224.0.0.0/4、私有 10.0.0.0/8·172.16.0.0/12·192.168.0.0/16

172.32.0.1100.100.5.5 是陷阱。172 私有地址段是 /12,范围只从 172.16 到 172.31;100.64.0.0/10 是运营商分配给用户的地址,既不属于私有地址,也不属于公网地址。

细分地址段

/22 划分为 /24 时,会得到 2 的 (24-22) 次方,也就是四个网络。ipaddress.ip_network("10.60.8.0/22").subnets(new_prefix=24) 会依次给出这四个网络。

每行应包含网络(含前缀)、第一个主机地址、最后一个主机地址和主机数量。每个子网都有 254 个主机地址。

检查地址段是否重叠

靠肉眼判断重叠一定会出错。ipaddressoverlaps() 会告诉你两个网络是否共享哪怕一个地址。

列表中包含 172.17.0.0/16 是有原因的。Docker 的默认网桥使用该地址段;如果公司内网也使用 172.17,容器就无法访问内网服务器。

重叠时写 후보 overlap=겹치는대역,不重叠时写 후보 free

留下设计说明

找到能够容纳“所需主机数加 2”的最小 2 的幂即可。以 120 台为例,至少需要 122 个地址,因此选择 128,即 7 个主机位,前缀为 /25。

连续分配是指可以将相邻网络合并为一条路由进行通告。这也是有类网络最终消失的实际原因。

最好一并注明,在云环境中还需要扣除更多保留地址。