知道 IP 之后,数据包下一步该往哪里走?

知道目的地,还不代表知道第一步

上一次,数据包终于弄清楚了 www.example.com 是怎么变成一个具体地址的。手里握着这个来之不易的 IP,它以为接下来的事情很简单——出发就是了。

但它很快卡住了。

它盯着这个 IP,发现一个问题:知道最终要去哪里,和知道这一步该往哪个方向迈,根本不是一回事。

方向感,不是名字带来的,也不是 DNS 带来的。

数据包意识到,自己需要先弄明白一件更基础的事:

这个目的地,离我近不近?

目标到底在不在这个网络里?

本机 IP 和子网掩码

数据包这才想起,自己出发的地方——也就是这台主机本身——同样有一个 IP 地址,而且这个地址通常还带着一个”/“后面的数字。比如:

1
192.168.1.100/24

这个 /24 不是随便写的。它划出了一条界线:前 24 位属于”网络部分”,剩下的部分才是”这个网络里具体是哪一台主机”。换句话说,/24 告诉这台主机:自己身处的这片直接可达的网络,范围是:

1
192.168.1.0/24

也就是从 192.168.1.0192.168.1.255 这一段。

同一个网络,意味着什么?

有了这条界线,数据包终于可以回答那个问题了——只要看目的 IP 是不是也落在这个范围里。

假设这次的目的地是:

1
192.168.1.50

这个地址落在 192.168.1.0/24 里面。

数据包明白了:

“原来它就在我身边。”

假设换一个目的地:

1
8.8.8.8

这个地址完全不在 192.168.1.0/24 的范围里。

数据包明白了:

“这一次,目标在很远的地方。”

同一个问题,答案却完全不同——这决定了接下来完全不同的两条路。

如果目标就在附近

目的主机就是下一跳

先看第一种情况。

如果目的 IP 是 192.168.1.50,而本机是 192.168.1.100/24——两者在同一个网络里——数据包发现一件让它有点意外的事:

不需要经过任何网关,直接把数据交给对方就行。

1
2
3
4
5
6
7
8
本机
192.168.1.100

│ 直接发送

目标
192.168.1.50

这里藏着一个很容易被忽略的事实:下一跳不一定是网关。在同一个直连网络里目的主机自己就可以是下一跳。

但我还不知道它的 MAC

方向倒是确定了——直接送过去就行。可数据包很快发现,自己手里只有一个 IP,192.168.1.50,并不知道对方在这条链路上到底是谁,该把数据交给哪块网卡。

这个问题,先放一放。现在只需要记住:方向已经确定了,细节留给以后。

如果目标在远方

数据包掏出自己的”小地图”

现在换成第二种情况——目的地是 8.8.8.8,一个明显不在本地网络里的地址。

这一次,直接发送行不通了。数据包需要一样新的东西来做决定:路由表

路由表可以理解成本机手里的一张小地图,上面记录着”不同的目的地,该往哪个方向送”。

路由表告诉它下一跳

Linux 上,可以直接看到这张地图:

1
ip route

输出大致是这样的:

1
2
3
4

192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.100
default via 192.168.1.1 dev eth0

第一行说的是:192.168.1.0/24 这个网络,直接从 eth0 出去就能到,不需要绕路——这正是前面那种”本地直连”的情况在路由表里的样子。

第二行是这次真正用得上的:default via 192.168.1.1 dev eth0。它的意思是:当没有更具体的路由能匹配目标时,把数据交给 192.168.1.1,从 eth0 这块网卡出去。

8.8.8.8 没有匹配到任何更具体的条目,于是落到了这条 default 路由上。

默认网关到底是什么?

这里正好可以说清楚一件容易被含糊过去的事:默认网关,就是默认路由指定的那个下一跳。它不是”所有远程流量的必经之路”,它只是”当没有其他更具体路由可用时”被选中的那一个。如果路由表里还有别的、更具体的条目能匹配某个目的地,数据就会走那条更具体的路,压根用不上默认网关。

对这次的 8.8.8.8 来说,匹配上的正好是默认路由,所以下一跳就是 192.168.1.1

让 Linux 告诉我们下一步往哪里走

查看本机地址

不妨亲自看一眼这一切是不是真的。先确认本机的地址和前缀:

1
ip addr

在输出里找到类似这样的一行:

1
2
3

inet 192.168.1.100/24

/24 就是前面说的那条网络边界线。

查看路由表

再看一眼路由表:

1
ip route

前面展示过的两行,大概率就出现在这里——一条直连网络的路由,一条默认路由。

使用 ip route get

最有意思的一步来了。与其自己在脑子里判断”这个目标在不在本地”,不如直接问 Linux:

1
ip route get 8.8.8.8

输出类似:

1
2
3

8.8.8.8 via 192.168.1.1 dev eth0 src 192.168.1.100

这一行信息量不小:

1
2
3
4
5
6

目的地: 8.8.8.8
下一跳: 192.168.1.1
出接口: eth0
源地址: 192.168.1.100

再试试一个本地目标:

1
ip route get 192.168.1.50

如果这个地址在本机的直连网络里,输出会明显不一样——不会再出现 via 某个网关,而是直接显示从哪块网卡送出去,因为目标本身就在这条链路上。

(如果手边的网络地址和示例不一样,把命令里的 IP 换成自己环境中的真实地址即可,原理是通用的。)

核心的一张图

把这一整套判断过程画出来,就是这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
          目的 IP


┌──────────────────┐
│ 本机 IP + 子网掩码 │
└────────┬─────────┘


是否同一网络?
/ \
是 否
│ │
▼ ▼
直接发送 查询路由表
│ │
│ ▼
│ 下一跳 IP
│ │
└───────┬───────┘


确定第一跳方向


下一篇:ARP / MAC

数据包终于知道第一步怎么走了

绕了一圈,数据包总算把最开始那个问题想明白了。

它手里的目的 IP,从一开始就只负责一件事——告诉它最终要去哪里。而这一步具体该交给谁,靠的是另一套东西:本机的 IP 和子网掩码,加上一张路由表。

如果目标就在身边,它自己就是下一跳。如果目标在远方,路由表会给出一个下一跳的地址——可能是默认网关,也可能是某条更具体的路由指向的地方。

有一件事让它印象很深:它完全不需要知道从这里到 8.8.8.8 的完整路线。它只需要弄清楚眼前这一步该交给谁——剩下的,交给下一跳去操心。

但是,下一跳的 IP 还不够

方向确定了。

对于这次的 8.8.8.8,数据包已经知道:下一跳是 192.168.1.1,从 eth0 出发。

可它低头一看,又卡住了。

手里这个 192.168.1.1,终究还只是一个 IP 地址。而在眼前这条链路上,数据真正要交出去的时候,靠的从来不是 IP——是这条链路上具体的那块网卡的身份。

数据包第一次认真地问自己:

知道了 IP,为什么还需要 MAC?

这个问题,留给下一次旅行。

知道 IP 之后,数据包下一步该往哪里走?

https://pengtech.net/network/packet-journey/next-hop.html

作者

鹏叔

发布于

2026-09-24

更新于

2026-09-24

许可协议

评论