提示

IP 地址转换与子网分析:劝你手算之前先看这篇

这篇文章不打算讲太深的网络原理(那得写一本书),而是聚焦在 IP 地址和子网的日常转换、计算、分析——包括你怎么在命令行、脚本、在线工具之间快速切换,以及那些我踩过的坑。
在线工具直连:www.viddown.cn/tools/ip/ip_converter/

一、你到底需要会什么

点分十进制 IP  整数(十进制/十六进制)互转

CIDR 表示法(比如 192.168.1.0/24)的含义

给定 IP 和掩码(或者前缀长度),算出网络地址、广播地址、第一个/最后一个可用地址

子网划分:把一个大的 /16 拆成若干个 /24 或者 /28

IPv6 的简单缩展转换

如果你只是偶尔配一下路由器,手算也行。但如果你要写脚本、做自动化、分析网络日志,那必须掌握几种转换和计算的方法。

二、IP 地址与整数互转(最常被忽略但巨有用)

为什么要把 IP 转成整数?两个场景:
1、在数据库里存 IP 时用 INT UNSIGNED 比字符串快得多。
2、判断两个 IP 是否在同一网段时,直接用位运算。

2.1 手动换算(了解一下就行)

192.168.1.1
= 192<<24 + 168<<16 + 1<<8 + 1
= 3232235521

这个公式谁都会,但手算容易错。强烈建议用工具。

2.2 用 Python 三行解决(最稳)

import ipaddress

# IP → 整数
int(ipaddress.IPv4Address('192.168.1.1'))   # 3232235521

# 整数 → IP
str(ipaddress.IPv4Address(3232235521))      # '192.168.1.1'

# IPv6 一样
int(ipaddress.IPv6Address('2001:db8::1'))   # 42540766411282592856903984951653826561

2.3 Linux 命令行(没有 Python 环境时)
用 printf 和算术运算,但很麻烦。我一般直接装一个 ipcalc,后面会讲。

2.4 一个小坑:符号问题
整数 IP 范围是 0 到 2^32-1,在 C 语言或某些环境里如果用了 int(有符号),192.168.1.1 会变成负数(-1062731519)。记得用无符号类型或 Python 自动处理。

三、子网计算:从 CIDR 到详细信息

给定 10.20.30.40/26,你能几秒钟说出:

子网掩码是什么?

网络地址?

广播地址?

可用 IP 范围?

如果还得拿笔算,那下面这些工具和命令你得收藏。

3.1 超实用的 ipcalc 命令
大多数 Linux 发行版都可以装:

sudo apt install ipcalc   # Ubuntu/Debian
# 或者用 net-tools 里的,但 ipcalc 是专用的

使用示例:

ipcalc 192.168.1.100/24

输出:

Address:   192.168.1.100        11000000.10101000.00000001.01100100
Netmask:   255.255.255.0 = 24   11111111.11111111.11111111.00000000
Wildcard:  0.0.0.255            00000000.00000000.00000000.11111111
=>
Network:   192.168.1.0/24       11000000.10101000.00000001.00000000
HostMin:   192.168.1.1          11000000.10101000.00000001.00000001
HostMax:   192.168.1.254        11000000.10101000.00000001.11111110
Broadcast: 192.168.1.255        11000000.10101000.00000001.11111111
Hosts/Net: 254                   Class C, Private Internet

注意那个 Wildcard 是通配符掩码(反掩码),给 OSPF 用的,一般我们用不到。
也可以直接用掩码:

ipcalc 10.1.2.3 255.255.240.0

输出一样的。
坑:老版本 ipcalc 对前缀大于 30 时的 HostMin/HostMax 会有误?我没遇到过,但要注意区分点分十进制掩码和 CIDR。

3.2 没有 ipcalc?用 sipcalc 或者在线
sipcalc 更细致,支持 IPv6:

sudo apt install sipcalc
sipcalc 192.168.1.100/24

输出更长,带网络区间、可用范围、反向掩码等。我比较喜欢 sipcalc,因为它能直接给出网络范围的两个边界。

3.3 Python ipaddress 模块(脚本化首选)
如果你要批量处理几百个网段,命令行一个个敲不现实。用 Python:

import ipaddress

net = ipaddress.IPv4Network('192.168.1.100/24', strict=False)
# strict=False 表示即使传入的是主机IP/前缀也会自动取网络地址

print("网络地址:", net.network_address)      # 192.168.1.0
print("广播地址:", net.broadcast_address)   # 192.168.1.255
print("掩码:", net.netmask)                 # 255.255.255.0
print("前缀长度:", net.prefixlen)           # 24
print("可用主机数:", net.num_addresses - 2) # 254

# 迭代所有主机IP(不包括网络地址和广播)
for host in net.hosts():
    print(host)   # 小心,如果/24就是254次循环

注意:ipaddress 模块在 Python 3.3+ 内置,不需要安装。但是 IPv6 的 hosts() 会返回巨量结果,小心内存。

3.4 我踩过的坑:严格模式与主机位
一开始我写 ipaddress.IPv4Network('192.168.1.100/24') 会报错:

ValueError: 192.168.1.100/24 has host bits set

因为 192.168.1.100 的二进制最后几位 01100100 不是全 0,严格模式下不允许。解决方法:
1、加 strict=False
2、或者手动把 IP 改成 192.168.1.0/24

两种都行,但 strict=False 更省事。

四、子网划分实践:把一个 /16 切成 4 个 /18

需求:总公司有 172.16.0.0/16(65536 个地址),要分成 4 个一样大的子网,每个支持大约 16000 个主机。

4.1 手算思路

2 位用于子网(因为 2^2 = 4),所以新前缀 = 16 + 2 = 18每个子网的地址块大小 = 2^(32-18) = 16384 个地址。

四个子网分别是:

172.16.0.0/18

172.16.64.0/18

172.16.128.0/18

172.16.192.0/18

4.2 用 ipcalc 验证子网边界
查第二个子网的广播地址:

ipcalc 172.16.64.0/18

输出 Network: 172.16.64.0/18, Host range: 172.16.64.1 - 172.16.127.254, Broadcast: 172.16.127.255。没毛病。

4.3 用 Python 自动划分子网

import ipaddress

supernet = ipaddress.IPv4Network('172.16.0.0/16')
subnets = list(supernet.subnets(new_prefix=18))
for sub in subnets:
    print(sub)

输出:

172.16.0.0/18
172.16.64.0/18
172.16.128.0/18
172.16.192.0/18

如果你想切成非等大小,或者按主机数量分配,那就得自己写逻辑了。但这个 subnets() 方法对统一划分非常方便。

五、IPv6 的地址转换与子网

IPv6 已经是公司网络的标配了(虽然很多还在混用)。最常用的两个操作:

5.1 展开/压缩 IPv6

2001:db8::1  2001:0db8:0000:0000:0000:0000:0000:0001 的压缩形式。
import ipaddress
ip = ipaddress.IPv6Address('2001:db8::1')
print(ip.exploded)     # 2001:0db8:0000:0000:0000:0000:0000:0001
print(ip.compressed)   # 2001:db8::1

5.2 IPv6 子网计算
IPv6 一般不用子网掩码,直接用前缀长度(通常 /64 是常见的最小分配单元)。

net6 = ipaddress.IPv6Network('2001:db8::/48')
# 分成 /64 子网
subnets = list(net6.subnets(new_prefix=64))
print(len(subnets))  # 65536 个 /64

注意:千万别尝试 list(net6.hosts()),一个 /64 有 2^64 个地址,会死机。

六、常用技巧汇总

6.1 判断两个 IP 是否在同一网段

import ipaddress
ip1 = ipaddress.IPv4Address('192.168.1.10')
ip2 = ipaddress.IPv4Address('192.168.1.200')
net = ipaddress.IPv4Network('192.168.1.0/24')
print(ip1 in net and ip2 in net)  # True

命令行 (ipcalc) 可以分别看 Network 地址是否相同。

6.2 从 IP 和掩码获取网络地址(纯 shell)
如果你在一个极其受限的环境(比如嵌入式系统),没 Python 没 ipcalc,可以用 bc 做位运算,但那酸爽……建议还是编译一个 ipcalc 的静态版本传进去。

6.3 批量转换 IP 列表
我有个日志文件,里面存了访问来源 IP,需要转成整数去数据库里查。一行 Python 搞定:

cat ips.txt | python3 -c 'import sys, ipaddress; [print(int(ipaddress.IPv4Address(line.strip()))) for line in sys.stdin]'

七、我踩过的坑总结

误以为 /24 的广播地址是 .255
对,/24  .255。但 /25 的广播地址可能是 .127  .255,取决于子网起点。要用工具算,别凭记忆。

掩码写成 255.255.255.255 时什么意思
那就是单个主机路由。我第一次看到时一脸懵逼。

IPv6  :: 只能出现一次
2001::db8::1 是非法的。Python  ipaddress 会报错,但有些手写配置可能直接传进路由器导致奇怪行为。

子网划分时忘了保留网络地址和广播地址
尤其是给点对点链路分配 /31(RFC 3021)时,可以用 2 个地址(没有网络和广播),但很多老设备不支持。我之前分配 /30 浪费了一半地址。

八、写在最后
IP 计算这件事,学会手算原理是为了面试和排错,但日常工作千万不要跟纸笔过不去。记住三件套:

ipcalc / sipcalc —— 快速查单个子网

Python ipaddress —— 脚本批量处理
在线工具(比如 https://www.viddown.cn/tools/ip/ip_converter/ 

你也可以像我一样,在 ~/.bashrc 里加个函数:

function ipinfo() {
    curl -s "https://ipinfo.io/$1"  # 需要联网,查公网 IP 归属地
}

但那是另一个话题了。

好了,下次再有同事拿着计算器算广播地址,你就把这篇文章甩给他。

顶部
×
🔖
收藏本站
将本站添加到浏览器书签,方便下次访问
Ctrl + D (Windows/Linux)
+ D (Mac)