TCP/IP(四):IP 地址、子网与路由

写在前面

前几篇走完了分层模型、链路层,又把网络层的 IP 数据报整体结构(头部字段、TTL 的作用)过了一遍。这一篇聚焦 IP 协议里被引用最多、面试最爱考、工程上最常踩坑的部分:IP 地址到底怎么编排、子网怎么划分、路由器怎么查表转发,以及 ICMP 怎么撑起 ping 和 traceroute

这些知识在云原生里天天见——配置 Pod 的 CIDR、规划 VPC 子网、写路由表、排查"为什么 ping 不通",全靠它。


一、IPv4 地址:32 位与点分十进制

IPv4 地址是一个 32 位的无符号整数,为了方便记忆,把它每 8 位切一段、转成十进制、用点连起来,这就是"点分十进制":

1
2
3
4
192      .168      .1        .10
11000000 .10101000 .00000001 .00001010
 └─8位─┘  └─8位─┘  └─8位─┘  └─8位─┘
   第1字节   第2字节   第3字节   第4字节

所以 IPv4 地址总共只有 2³² = 4294967296(约 43 亿)个,这正是地址紧缺、要搞 IPv6 和 NAT 的根本原因。

注意 IP 地址是网络层地址,跨网可路由;而 MAC 地址是链路层地址,只在同一链路内有效。这两套地址体系互不替代,后面讲 ARP 会把它们接起来。


二、网络位与主机位:地址的两段结构

光知道 32 位还不够,关键在于这 32 位要拆成两段

1
2
3
4
   IPv4 地址(32 位)= 网络位 | 主机位
                      └──┬─┘   └──┬─┘
                  标识"哪个网络"  标识"网络里的哪台主机"
                  (路由器靠它转发)(路由到目的网后用它在链路内定位)
  • 网络位(network prefix) —— 决定这个地址属于哪个网段,路由器逐跳转发时只看这部分;
  • 主机位(host part) —— 在同一网段内区分不同主机,只有"到了最后一跳、进了目的网络"之后才用得上。

历史上有 A/B/C 三类地址,靠固定的前几位决定网络位长度(A 类 /8、B 类 /16、C 类 /24)。但这种"定长分类"太死板:一个 /16 网段有 65534 个主机地址,对一个几十台机器的小公司是巨大浪费;一个 /252 又装不下大企业。于是有了子网划分CIDR(无类别域间路由)——网络位长度不再由"类别"决定,而是可以任意指定。


三、子网掩码与 CIDR 记法

既然网络位长度可变,就需要一种方式告诉别人"前多少位是网络位"。有两种等价写法:

1. 子网掩码(dotted mask) —— 把网络位全置 1、主机位全置 0,也写成点分十进制:

1
2
3
4
5
前 24 位是网络位:
掩码 = 11111111.11111111.11111111.00000000 = 255.255.255.0

前 22 位是网络位:
掩码 = 11111111.11111111.11111100.00000000 = 255.255.252.0

2. CIDR 记法 —— 在 IP 地址后面直接写 /n,n 就是网络位的位数:

1
2
192.168.1.10/24  →  前 24 位是网络位,掩码 255.255.255.0
10.0.0.5/22      →  前 22 位是网络位,掩码 255.255.252.0

工程里几乎都用 CIDR 记法,因为它一眼就能看出网络位长度。常见的 CIDR 与掩码对应:

CIDR子网掩码主机位可用主机数典型用途
/8255.0.0.02416777214A 类整段、10/8 私有
/16255.255.0.01665534中型网络、172.16/12 子段
/24255.255.255.08254最常见的子网大小
/28255.255.255.240414小网段、云上分配合
/30255.255.255.25222点对点链路
/32255.255.255.25501单机路由、主机精确匹配

两个特例:可用主机数 = 2^(主机位) − 2 ,因为主机位全 0 的那个是"网络地址"、全 1 的那个是"广播地址",都不能分配给主机。/31 是个特殊例外(RFC 3021),点对点链路上只有两台主机、不需要广播,所以两个地址都可用;/32 则表示单个主机,没有主机位。


四、动手算:网络地址、广播地址、可用主机数

这是面试高频题,也是规划子网的基本功。规则只有三条:

  1. 网络地址 = IP 地址 AND 子网掩码(按位与,主机位清零);
  2. 广播地址 = 网络地址 OR (主机位全 1);
  3. 可用主机数 = 2^(主机位) − 2,范围是"网络地址 + 1"到"广播地址 − 1"。

例 1:192.168.1.10/24

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/24 → 主机位 = 32 − 24 = 8,掩码 255.255.255.0

IP  : 192.168.1.10  = 11000000.10101000.00000001.00001010
掩码: 255.255.255.0 = 11111111.11111111.11111111.00000000
                                  AND ─────────────────────
网络地址: 192.168.1.0  = 11000000.10101000.00000001.00000000

广播地址: 192.168.1.255 = 11000000.10101000.00000001.11111111   (主机位全 1)

可用主机数 = 2^8 − 2 = 254
可用范围  : 192.168.1.1  ~  192.168.1.254

例 2:10.0.0.5/22(非字节边界,更接近真实场景)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/22 → 主机位 = 32 − 22 = 10,掩码 255.255.252.0

第 3 字节的掩码是 11111100 = 252(注意不是 255,跨了字节边界)

IP  : 10.0.0.5     = 00001010.00000000.00000000.00000101
掩码: 255.255.252.0 = 11111111.11111111.11111100.00000000
                                    AND ──────────────────
网络地址: 10.0.0.0   = 00001010.00000000.00000000.00000000

把后 10 位主机位全置 1 得广播地址:
广播地址: 10.0.3.255 = 00001010.00000000.00000011.11111111
                                    ↑第3字节后2位=11=3

可用主机数 = 2^10 − 2 = 1022
可用范围  : 10.0.0.1  ~  10.0.3.254

第二个例子最容易出错的地方是第 3 字节的边界:掩码 252 说明第 3 字节里只有前 2 位属于网络位,后 2 位属于主机位。所以广播地址的第 3 字节是 00000000 | 00000011 = 3,整个网段横跨 10.0.0.0 ~ 10.0.3.255 这四个"第 3 字节"。

一个速算技巧:对 /24 以上(即掩码在第 4 字节内)的子网,“块大小”= 256 − 掩码字节。比如 /28 掩码是 .240,块大小 = 256 − 240 = 16,所以子网的第 4 字节从 0 开始按 16 递增:.0、.16、.32……这台主机落在哪个块里,网络地址就是那个块的起点。


五、VLSM 与私有地址段

VLSM:可变长子网掩码

CIDR 让网络位长度任意,而 VLSM(Variable Length Subnet Masking,可变长子网掩码) 进一步允许对同一个主网,按不同子网的实际大小切出不同掩码长度

举个规划例子:一个公司拿到 192.168.10.0/24,要分给几个不同规模的部门:

1
2
3
4
5
部门 A(120 台): 192.168.10.0/25    → 126 个可用(最省的能盖住 120 的子网)
部门 B(50 台) : 192.168.10.128/26  → 62 个可用
部门 C(25 台) : 192.168.10.192/27  → 30 个可用
点对点互联(2台): 192.168.10.224/30 → 2 个可用
剩余未分配     : 192.168.10.228/30 ...

没有 VLSM 之前(只能用定长子网掩码 FLSM),所有子网都得按最大的那个来切,地址浪费严重。VLSM 让掩码长度"按需伸缩",是现代网络和云上 VPC 子网规划的基础。

私有地址段(RFC 1918)

不是所有地址都能在公网路由。RFC 1918 划出三段私有地址,供内网自由使用、不用申请,出公网时靠 NAT 转换:

名称网段CIDR容量
A 类私有10.0.0.0 ~ 10.255.255.25510.0.0.0/82²⁴(约 1677 万)
B 类私有172.16.0.0 ~ 172.31.255.255172.16.0.0/122²⁰(约 100 万)
C 类私有192.168.0.0 ~ 192.168.255.255192.168.0.0/162¹⁶(65536)

家里路由器的 LAN、公司办公网、数据中心内网、云上 VPC、Docker 默认网桥——绝大多数内网都落在这三段里。还有几个特殊段也值得知道:169.254.0.0/16(链路本地,DHCP 拿不到地址时的 fallback)、127.0.0.0/8(环回,localhost)。


六、路由表与最长前缀匹配

IP 地址规划好之后,路由器靠"路由表"决定每个包往哪转发。一条路由条目至少包含三要素:

1
目的网络(含掩码) | 下一跳(Next Hop) | 出接口

比如一台主机的路由表可能长这样:

1
2
3
4
目的网络         掩码              下一跳         接口
0.0.0.0          0.0.0.0           192.168.1.1   eth0   ← 默认路由(兜底)
192.168.1.0      255.255.255.0     0.0.0.0(本机)  eth0   ← 直连子网
10.0.0.0         255.255.252.0     192.168.1.254 eth0   ← 去往 10.0.0.0/22

最长前缀匹配(Longest Prefix Match)

当一个目的地址能匹配多条路由时,路由器永远选掩码最长(最具体)的那条——因为掩码越长、网络位越精确,指向的网段越小。

举例:要发往 10.0.0.50,路由表里有 10.0.0.0/2210.0.0.0/24 两条。两者都能匹配(50 落在这两个网段里),但 /24 比 /22 更长更具体,于是走 /24 那条。

1
2
3
目的 10.0.0.50
   ├─ 命中 10.0.0.0/22   (网络位 22)
   └─ 命中 10.0.0.0/24   (网络位 24) ← 更长,优先选这条

默认路由与默认网关

如果没有任何具体路由能匹配目的地址怎么办?交给默认路由 0.0.0.0/0。它的掩码是 0 位,能匹配任意地址,所以它是"兜底"——按最长前缀匹配规则,它永远排在最后才被选中。

家宽场景里,主机的默认路由下一跳就是默认网关(家用路由器的 LAN 口 IP),所有"非本网段"的流量都甩给它,由它继续往运营商转发。ip routeroute -n 看到的 default via ... 就是这条。

1
default via 192.168.1.1 dev eth0      ← 等价于 0.0.0.0/0 via 192.168.1.1

七、ICMP:ping 与 traceroute 的底层

IP 协议本身只管"尽力把包送到",不反馈任何状态。ICMP(Internet Control Message Protocol,互联网控制报文协议) 就是 IP 层的"信使",用来报告差错和探测网络——ping 和 traceroute 都建立在它之上。

严格说 ICMP 工作在网络层(和 IP 同层,封装在 IP 包里,协议号 1),没有端口号,不涉及传输层。所以"ping 某个端口"在 ICMP 意义上是不成立的——ping 只能测主机可达性。

ping:Echo Request / Echo Reply

ping 的原理极其简单——发一个 ICMP Echo Request(类型 8,代码 0) 给目标,目标收到后回一个 Echo Reply(类型 0,代码 0),一来一回就能算 RTT 和丢包:

1
2
3
4
5
6
本机                                目标主机
  │                                   │
  │── Echo Request (Type 8) ─────────►│
  │                                   │
  │◄──────── Echo Reply (Type 0) ──────┤
  │   记录往返时间 RTT                 │

ping 不通的原因很多:目标离线、路由不可达、防火墙丢弃了 ICMP(很多生产环境出于安全只放行业务端口、禁 ping,所以"ping 不通 ≠ 服务不可用",这点排查时要记牢)。

traceroute:靠 TTL 递增一跳跳摸路径

traceroute 巧妙利用了 IP 头部里的 TTL(Time To Live,生存时间) 字段。每经过一台路由器,TTL 减 1;一旦减到 0,路由器就丢弃该包并回送一个 ICMP Time Exceeded(类型 11) 给源。traceroute 正是靠这个回包"自报家门"的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
源                          路由器A        路由器B        目标
 │  TTL=1 ─────────────────►│
 │                          │ TTL-1=0,丢弃
 │◄──── ICMP Time Exceeded──┤           (记录 A 的地址 + 耗时)
 │  TTL=2 ─────────────────►│转发, TTL=1►│
 │                          │            │ TTL-1=0,丢弃
 │◄────────── ICMP Time Exceeded─────────┤          (记录 B)
 │  TTL=3 ─────────────────►│ ─────────►│ ─────────►│
 │                                             到达目标
 │◄────────── Echo Reply / Port Unreachable ───┤     (结束)

过程是:先发 TTL=1,第一台路由器把 TTL 减到 0、回 Time Exceeded,于是知道了第一跳;再发 TTL=2,第二台路由器回包,知道第二跳……如此递增,直到某个探测包真正到达目标,目标回一个应答(ICMP Echo Reply 或 UDP 端口不可达),路径就摸完了。

一个跨平台要注意的细节:探测包用什么协议,不同系统不一样——

  • Linux 的 traceroute(传统默认)发 UDP 探测包,目的端口用一个递增的高端口,目标通常因端口未监听而回 ICMP Port Unreachable(类型 3)
  • Windows 的 tracertICMP Echo Request 探测包,目标回 Echo Reply

两者定位每一跳都靠中间路由器回的 ICMP Time Exceeded(类型 11),只是"到达目标后收到的那个结束应答"不同。现代 Linux 也可以用 traceroute -I 强制走 ICMP、mtr 做持续式路径探测,在排查抖动时更顺手。


小结

  • IPv4 地址是 32 位整数,点分十进制只是写法;核心在于它被切成网络位 + 主机位
  • 子网掩码CIDR /n 是描述网络位长度的两种等价方式,可用主机数 = 2^(主机位) − 2;
  • 网络地址 = IP AND 掩码,广播地址 = 主机位全 1;非字节边界(如 /22)的算法要练熟;
  • VLSM 让同一主网按需切出不同掩码;私有地址段(10/8、172.16/12、192.168/16)供内网自由使用;
  • 路由器按最长前缀匹配选路,默认路由 0.0.0.0/0 是兜底;
  • ICMP 是 IP 层信使,ping 用 Echo Request/Reply 测可达性,traceroute 靠 TTL 递增 + Time Exceeded 逐跳摸路径。

下一篇:TCP/IP(五):传输层与 UDP —— 从"主机到主机"上升到"进程到进程",讲清端口号、套接字,以及 UDP 这个"极简"传输层协议的头部、校验和和适用场景。


参考:

Licensed under CC BY-NC-SA 4.0