# 21.4 三层网络 --- Internet

Ethernet header足够在一个局域网中将packet发送到一个host。如果你想在局域网发送一个IP packet，那么你可以使用ARP获得以太网地址。但是IP协议更加的通用，IP协议能帮助你向互联网上任意位置发送packet。下图是一个IP packet的header，你们可以在lab配套的代码中的net.h文件找到。

![](https://1977542228-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MHZoT2b_bcLghjAOPsJ%2F-MO9AUbfTj-IlKsNex3Z%2F-MOESlRbJsr7WEE4pJzi%2Fimage.png?alt=media\&token=e355f6f3-0499-4622-a36c-8014782aac02)

如果IP packet是通过以太网传输，那么你可以看到，在一个以太网packet中，最开始是目的以太网地址，源以太网地址，以太网类型是0x0800，之后是IP header，最后是IP payload。

![](https://1977542228-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MHZoT2b_bcLghjAOPsJ%2F-MO9AUbfTj-IlKsNex3Z%2F-MOETibgLGajiB82bzej%2Fimage.png?alt=media\&token=2b19b3cf-ef83-41f3-b7b1-f902b5695b85)

在一个packet发送到世界另一端的网络的过程中，IP header会被一直保留，而Ethernet header在离开本地的以太网之后会被剥离。或许packet在被路由的过程中，在每一跳（hop）会加上一个新的Ethernet header。但是IP header从源主机到目的主机的过程中会一直保留。

IP header具有全局的意义，而Ethernet header只在单个局域网有意义。所以IP header必须包含足够的信息，这样才能将packet传输给互联网上遥远的另一端。对于我们来说，关键的信息是三个部分，目的IP地址（ip\_dst），源IP地址（ip\_src）和协议（ip\_p）。目的IP地址是我们想要将packet送到的目的主机的IP地址。地址中的高bit位是网络号，它会帮助路由器完成路由。IP header中的协议字段会告诉目的主机如何处理IP payload。

如果你们看到过MIT的IP地址，你们可以看到IP地址是18.x.x.x，虽然最近有些变化，但是在很长一段时间18是MIT的网络号。所以MIT的大部分主机的IP地址最高字节就是18。全世界的路由器在看到网络号18的时候，就知道应该将packet路由到离MIT更近的地方。

接下来我们看一下包含了IP packet的tcpdump输出。

![](https://1977542228-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MHZoT2b_bcLghjAOPsJ%2F-MO9AUbfTj-IlKsNex3Z%2F-MOEXfK0lywrVj1OVgD7%2Fimage.png?alt=media\&token=44fe34db-df05-4b46-9422-faee5fc47e89)

因为这个IP packet是在以太网上传输，所以它包含了以太网header。呃……，实际上这个packet里面有点问题，我不太确定具体的原因是什么，但是Ethernet header中目的以太网地址不应该是全f，因为全f是广播地址，它会导致packet被发送到所有的主机上。一个真实网络中两个主机之间的packet，不可能出现这样的以太网地址。所以我提供的针对network lab的方案，在QEMU上运行有点问题。不管怎么样，我们可以看到以太网目的地址，以太网源地址，以及以太网类型0x0800。0x0800表明了Ethernet payload是一个IP packet。

![](https://1977542228-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MHZoT2b_bcLghjAOPsJ%2F-MO9AUbfTj-IlKsNex3Z%2F-MOEZ4BXyrGBTXmWOQvC%2Fimage.png?alt=media\&token=04928ead-3b94-4e51-9043-89b26284ee89)

IP header的长度是20个字节，所以中括号内的是IP header，

![](https://1977542228-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MHZoT2b_bcLghjAOPsJ%2F-MOEZ9iqKkt92zbewl4r%2F-MOPw1qnEjiOAkSTe3Yi%2Fimage.png?alt=media\&token=913186dd-c85a-4fd8-bc3f-31e21b8744df)

从后向前看：

* 目的IP地址是0x0a000202，也就是10.0.2.2。
* 源IP地址是0x0a00020f，也就是10.0.2.15。
* 再向前有16bit的checksum，也就是0x3eae。IP相关的软件需要检查这个校验和，如果结果不匹配应该丢包。
* 再向前一个字节是protocol，0x11对应的是10进制17，表明了下一层协议是UDP
* 其他的就是我们不太关心的一些字段了，例如packet的长度。

IP header中的protocol字段告诉了目的主机的网络协议栈，这个packet应该被UDP软件处理。
