JavaScript 计算 IPv4 网段:别让 32 位有符号运算把地址算成负数
配置 VPN 或安全组之前,经常要确认三件事:一个 CIDR 实际覆盖哪些地址,一个 IP 是否在里面,两个网段有没有交集。
例如,192.168.1.42/24 的网段边界不是从 .42 开始。它归一化后是 192.168.1.0/24,范围是 192.168.1.0 到 192.168.1.255。这个区别看起来简单,但手动复制配置时很容易漏掉。
为此做了一个离线小工具“网段有界”。核心计算使用纯函数,界面只负责输入和展示,不调用云 API,也不修改网络配置。下面介绍其中两个值得注意的实现细节。
先把 IPv4 转成安全整数
JavaScript 的普通位运算会把数值转换为 32 位有符号整数。IPv4 却覆盖从 0 到 4294967295 的无符号范围。直接用左移拼接地址,如果没有处理无符号转换,高位地址就可能得到负数。
这里采用算术累加,避免混用有符号结果:
function ipv4ToNumber(ip) {
const parts = ip.split('.');
if (parts.length !== 4 || parts.some(part =>
!/^(0|[1-9]\d{0,2})$/.test(part) || Number(part) > 255
)) throw new Error('无效 IPv4 地址');
return parts.reduce((value, part) => value * 256 + Number(part), 0);
}
function cidrRange(ip, prefix) {
if (!Number.isInteger(prefix) || prefix < 0 || prefix > 32) {
throw new Error('前缀必须为 0 到 32 的整数');
}
const size = 2 ** (32 - prefix);
const start = Math.floor(ipv4ToNumber(ip) / size) * size;
return { start, end: start + size - 1, size };
}
IPv4 最大整数远低于 JavaScript 的最大安全整数,因此这里的整数运算可以精确表示。输入校验明确拒绝前导零、缺少地址段和大于 255 的地址段,避免把含糊输入悄悄纠正成另一个地址。
/0 包含全部 IPv4 地址;/32 只包含一个地址。统一使用块大小公式,就不必为这两种情况硬凑掩码位移逻辑。
把网段交集当成区间问题
将两个 CIDR 都转换成闭区间后,交集的起点是两个起点的较大值,终点是两个终点的较小值。起点大于终点时没有交集:
function intersection(a, b) {
const start = Math.max(a.start, b.start);
const end = Math.min(a.end, b.end);
return start <= end ? { start, end, size: end - start + 1 } : null;
}
例如 192.168.1.0/24 和 192.168.1.128/25 的交集,是后者的全部 128 个地址。判断一个 IP 的归属,也只需要比较它是否位于起点和终点之间。
但“有交集”不等于“配置有错”。安全组还涉及协议、端口和动作,路由也有前缀长度及优先级等规则。工具只回答地址覆盖关系,不能替代完整配置审查。
验收不能只测常见的 /24
这个工具的核心测试共通过 537 项断言,包括 /0、/31、/32、最大 IPv4、主机位归一、相邻网段无交集、包含关系和非法输入。另对完整 /24 的地址转换往返,以及 /25 的成员关系进行了枚举检查。
界面还有一个容易被忽略的问题:用户把有效输入改错后,旧结果必须清空,报告导出也应禁用。否则用户可能把上一组地址的报告当成当前输入的结果。
最后,页面展示的是“地址总数”,没有统一减去两个地址后写成“可用主机数”。不同前缀及云平台的地址保留规则需要单独确认,尤其不能把 /31、/32 和普通局域网网段混为一谈。
这类小工具最实用的地方,是在提交配置前把范围算清楚。它不需要联网扫描,也不需要账号密钥。
- 点赞
- 收藏
- 关注作者
评论(0)