网络层(network layer)是实现全球互联网的关键。网络层有多个协议,其中最重要的是 IP 协议。IP 协议的全称是 Internet Protocol,即“互联网协议”。借着 IP 协议,局域网可以相互连接,最终构成覆盖全球的互联网。IP 协议之外,网络层还有其他协议,如 ARP、RIP 和 BGP。这些协议起到了辅助 IP 协议的作用。
IP 包的格式
IP 协议最早可追溯到 1970 年代。IP 协议最早应用于互联网的老祖宗,美国国防部研发的阿帕网络(ARPANET)。随着互联网在民用领域的推广,IP 协议也越来越被人们熟知。和大多数网络协议一样,IP 协议同样把要传输的数据放入有固定格式的数据包,即符合 IP 协议规定的 0/1 序列。我们后面简称 IP 数据包为 IP 包。IP 包分为头部(header)和数据(Data)两部分。数据部分是要传送的信息,头部是为了能够实现传输而附加的信息。
IP 协议可以分为 IPv4 和 IPv6 两种。IPv6 是 IPv4 的改进版本,但 IPv4 依然使用广泛。这一篇中,我们可以暂时忽略两者的区别,只以 IPv4 为例。下面是 IPv4 的格式:

IPv4 包。按照 4 bytes 将整个序列折叠,以便更好的显示
与帧类似,IP 包的头部也有多个区域。我们将注意力放在红色的发出地(source address)和目的地(destination address)。它们都是 IP 地址。IPv4 的地址为 4 bytes 的长度,即 32 位(bit)。我们通常将 IPv4 的地址分为四个十进制的数,每个数的范围为 0-255,比如 192.0.0.1 就是一个 IP 地址。填写在 IP 包头部的是该地址的二进制形式。
IP 地址是全球地址,它可以识别成片房子的社区(局域网)和单个房子(一台主机)。这是通过将 IP 地址分类实现的。
IP class From To Subnet Mask
A 1.0.0.0 126.255.255.255 255.0.0.0
B 128.0.0.0 191.255.255.255 255.255.0.0
C 192.0.0.0 223.255.255.255 255.255.255.0
每个 IP 地址的 32 位可以分为两部分。第一部分用来区分局域网,第二个部分用来区分该局域网的主机。子网掩码(Subnet Mask)告诉我们这两部分的分界线,比如 255.0.0.0,换算成 0/1 序列是 8 个 1 和 24 个 0。它表示对应 IP 地址的前 8 位用于辨识局域网,后 24 位用于识别该局域网下的主机。由于 A、B、C 分类是已经规定好的,所以当一个 IP 地址属于 B 类范围时,我们就知道它的前 16 位和后 16 位分别表示局域网和主机。
网卡与路由器
IP 地址像是每个计算机的“邮编”。但这个说法并不精确。IP 地址真正识别的是网卡(NIC, Network Interface Card)。网卡是计算机的一个硬件,它在接收到网路信息之后,将信息交给计算机。当计算机需要发送信息的时候,也要通过网卡发送。一台计算机可以有不只一个网卡,比如笔记本就有一个以太网卡和一个 WiFi 网卡。计算机在接收或者发送信息的时候,要先决定想要通过哪个网卡。
路由器(router)是构成互联网的基础设备。它实际上就是一台配备有多个网卡的专用电脑。路由器的不同网卡接入到不同的网络,成为跨越网络的桥梁。比如下图是一个网络,位于中间位置的路由器有两个网卡,地址分别为 199.165.145.17 和 199.165.146.3。它们分别接入到两个网络:199.165.145 和 199.165.146。

跨越两个局域网的路由器。
IP 包接力
IP 包的传输要通过路由器的接力。每一个主机和路由中都存有一个路由表(routing table)。路由表根据目的地的 IP 地址,规定了等待发送的 IP 包所应该走的路线。就好像下图的路标,如果地址是“东京”,那么请转左;如果地址是“悉尼”,那么请向右。
如果我们从主机 145.17 生成发送到 146.21 的 IP 包,那么要采用如下的步骤。首先铺开信纸,写好信的开头。注明目的地 IP 地址(199.165.146.21)和发出地 IP 地址(199.165.145.17)。数据部分用来包含信息,可以是高层的 TCP 包或 UDP,也可以是任意乱写的字。现在,信写好了,要邮寄出去。主机 145.17 参照自己手中的路由表(routing table),看到里面有两行记录:
145.17 routing table (Genmask 为子网掩码,Iface 用于说明使用哪个网卡接口)
Destination Gateway Genmask Iface
199.165.145.0 0.0.0.0 255.255.255.0 eth0
0.0.0.0 199.165.145.17 0.0.0.0 eth0
除去注释说明部分,路由表中有两行记录。
第一行表示,如果 IP 目的地是 199.165.145.0 这个网络的任何主机,那么只需要自己在 eth0 上的网卡直接传送,在本地社区内直接送达,而不需要前往任何路由器。Gateway 0.0.0.0 的意思就是本地送信。
第二行表示所有不符合第一行的 IP 目的地,都应该送往 Gateway 199.165.145.17。这个 IP 地址是中间路由器接入在 eth0 的网卡 IP 地址,即邮局在 eth0 网络的分支。
我们的 IP 包目的地为 199.165.146.21,不符合第一行,所以按照第二行,发送到中间的路由器。主机 145.17 会将 IP 包放入帧的数据部分,并在帧的头部写上 199.165.145.17 对应的 MAC 地址,这样,就可以按照小喇叭中的方法在局域网中传送了。
中间的路由器收到以太协议的帧,然后从帧中的数据部分读取 IP 包。IP 包中有目的地 IP 地址。中间路由器现在可以对照自己的路由表,找到下一步的发送方向:
Destination Gateway Genmask Iface
199.165.145.0 0.0.0.0 255.255.255.0 eth0
199.165.146.0 0.0.0.0 255.255.255.0 eth1
0.0.0.0 199.165.146.8 0.0.0.0 eth1
记录的前两行分别对应 eth0 和 eth1 两个网络。由于路由器横跨 eth0 和 eth1 两个网络,它可以直接通过 eth0 和 eth1 上的网卡直接传送 IP 包。第三行表示,如果是前面两行之外的 IP 地址,则需要通过 eth1,送往 199.165.146.8,也就是右边的路由器。我们的目的地 199.165.146.21 符合第二行,所以将 IP 放入一个新的帧中。路由器在帧的头部写上 199.165.146.21 的 MAC 地址,直接发往主机 146.21。
在不同的操作系统下,需要用不同的命令来查看该主机的路由表:
Linux : $route -n
Mac OSX: $route -nr
Windows: $route print
如果 IP 地址指向更遥远的目的地,那么 IP 包可以进一步接力,到达更远的主机。IP 包从主机出发,根据沿途路由器的路由表指导,在路由器间接力。IP 包最终到达某个路由器,这个 router 与目标主机位于一个局域网中,可以直接建立连接层的通信。最后,IP 包被送到目标主机。这样一个过程叫做路由。我把路由称为叫“IP 包接力”,原因是路由这个词混合了太多的意思。
整个过程中,IP 包不断被主机和路由封装入帧再拆开,然后借助连接层,在局域网的各个 NIC 之间传送帧。整个过程中,我们的 IP 包的内容保持完整,没有发生变化。最终的效果是一个 IP 包从一个主机传送到另一个主机。利用 IP 包,我们不需要去操心底层发生了什么。
ARP 协议
在上面的过程中,我们实际上假设了,每一台主机和路由都能了解局域网内的 IP 地址和 MAC 地址的对应关系,这是实现 IP 包封装(encapsulation)到帧的基本条件。IP 地址与 MAC 地址的对应是通过 ARP 协议传播到局域网的每个主机和路由。每一台主机或路由中都有一个 ARP 缓存(ARP cache),用以存储局域网内 IP 地址和 MAC 地址如何对应。
ARP 介于连接层和网络层之间,ARP 包需要包裹在一个帧中。ARP 协议的工作方式如下:主机会发出一个 ARP 包,该 ARP 包中包含有自己的 IP 地址和 MAC 地址。通过 ARP 包,主机以广播的形式询问局域网上所有的主机和路由:我是 IP 地址 xxxx,我的 MAC 地址是 xxxx,有人知道 199.165.146.4 的 MAC 地址吗?拥有该 IP 地址的主机会回复发出请求的主机:哦,我知道,这个 IP 地址属于我的一个 NIC,它的 MAC 地址是 xxxxxx。由于发送 ARP 请求的主机采取的是广播形式,并附带有自己的 IP 地址和 MAC 地址,其他的主机和路由会同时检查自己的 ARP 缓存,如果不符合,则更新自己的 ARP 缓存。
这样,经过几次 ARP 请求之后,ARP cache 会达到稳定。如果局域网上设备发生变动,ARP 重复上面过程。
在 Linux 下,可以使用
a
r
p
来
查
看
A
R
P
的
过
程
。
在
M
a
c
O
S
X
下
用
arp来查看 ARP 的过程。在 Mac OSX 下用
arp来查看ARP的过程。在MacOSX下用arp -a。ARP 协议只用于 IPv4。IPv6 使用 Neighbor Discovery Protocol 来替代 ARP 的功能。
路由表的生成
我们还有另一个假设,就是每个主机和路由上都已经有了合理的路由表。这个路由表描述了网络的连接结构,也就是所谓的网络拓扑结构。如果你了解自己的网络连接,可以手写自己主机的路由表。但是,一个路由器可能有多个出口,所以路由表可能会很长。更重要的是,周围连接的其他路由器可能发生变动,比如新增路由器或者路由器坏掉,我们就需要路由表能及时将交通导向其他的出口。我们需要一种更加智能的探测周围的网络拓扑结构,并自动生成路由表。

北京地铁地图
我们以北京地铁为例子。如果从机场前往朝阳门,那么可以采取 2 号航站楼→三元桥→东直门→朝阳门。2 号航站楼和朝阳门分别是出发和目的主机。而三元桥和东直门为中间的两个路由器。如果三元桥→东直门段因为维修停运,我们需要更改三元桥的路由表,从而给前往朝阳门的乘客(IP 包)指示:请走如下路线三元桥→芍药居。然后依照芍药居的路由表前往朝阳门,也就是沿着芍药居→东直门→朝阳门的路线。
一种用来生成路由表的协议是 RIP(Routing Information Protocol)。它通过距离来决定路由表。对于 RIP 来说,所谓的距离是从出发地到目的地途径的路由器数目(hop number)。比如上面从机场到朝阳门,按照 2 号航站楼→三元桥→东直门→朝阳门路线,途径两个路由器,距离为 2。我们最初可以手动生成三元桥的 routing table。随后,根据 RIP 协议,三元桥向周围的路由器和主机广播自己前往各个 IP 的距离,到机场=0,团结湖=0,国贸=1,望京西=1,建国门=2 等等。收到 RIP 包的路由器和主机根据 RIP 包和自己到发送 RIP 包的主机的距离,算出自己前往各个 IP 的距离。东直门与三元桥的距离为 1。东直门收到三元桥的 RIP 包,包中记录了到机场的距离为 0。那么东直门途径三元桥前往机场的距离为 1+0=1。如果东直门自己的 RIP 记录都比这个远,比如东直门→芍药居→三元桥→机场 = 2。那么东直门更改自己的路由表:前往机场的交通都发往三元桥而不是芍药居。如果东直门自身的 RIP 记录并不差,那么东直门保持路由表不变。上述过程在各个点不断重复 RIP 广播/计算距离/更新路由表的过程,最终所有的主机和路由器都能生成最合理的路径。
简单的说,RIP 的基本逻辑是:如果 A 距离 B 为 6,而我距离 A 为 1,那么我途径 A 到 B 的距离为 7。
RIP 出于技术上的原因(looping hops),认为距离超过 15 的 IP 不可到达。所以 RIP 更多用于互联网的一部分(比如整个中国电信的网络)。这样一个互联网的部分往往属于同一个 ISP 或者有同一个管理机构,所以叫做自治系统(AS,autonomous system)。自治系统内部的主机和路由根据通向外部的边界路由器来和其它的自治系统通信。各个边界路由器之间通过 BGP(Border Gateway Protocol)来生成自己前往其它 AS 的路由表,而自治系统内部则参照边界路由器,使用 RIP 来决定路由表。BGP 的基本工作过程与 RIP 类似,但在考虑距离的同时,也权衡比如政策、连接性能等其他因素,再决定交通的走向。
总结
我们一开始讲述了 IP 包根据路由表进行接力的过程。为了顺利实现接力,我们又进一步深入到 ARP 和 RIP/BGP。这三个协议都协助了 IP 传输。ARP 让每台电脑和路由器知道自己局域网内 IP 地址和 MAC 地址的对应关系,从而顺利实现 IP 包到帧的封装。RIP 协议可以生成自治系统内部合理的路由表。BGP 协议可以生成自治系统外部的路由表。尽管讲解以 IPv4 为例,但上面的 IP 接力过程适用于 IPv6。