IP网际协议
IP提供的是不可靠、无连接的数据报传输服务。
- 不可靠:不能保证IP数据报能成功到达目的地,既可能丢包。可靠性需要由上层提供,如TCP的超时重传机制等。IP有一个简单的错误处理办法,当IP数据报被丢弃后,会发送ICMP消息告诉发送端错误情况。
- 无连接:不维护任何后续数据报的状态信息。每个数据报都是相互独立的,独立进行路由选择,到达目的地的顺序和发送顺序可以不同。虽然IP是无连接的,但TCP是面向连接的。
 举个例子,发送端TCP有四个数据包0 1 2 3,传给网络层,IP封装IP头部后将四个数据包发送出去;由于网络原因产生丢包或延时到达,在接收端只收到0 1 3三个数据包,由于IP是无连接的所以它不需要等2号包的到来就直接将三个数据包传给TCP。由于TCP是面向连接的,所以必须等到2号包的到达组装成完整的数据包后才能传入应用层。
IP头部分析
IP头部如图所示:

普通的IP头部长20字节(不包含选项字段)
- 版本(4位):常说的IPv4或IPv6
- 首部长度(4位):IP头部最长为60字节(一般为20字节)。用4位表示长度,那么最大为1111转换成十进制为15,那么最大则为15*4字节=60字节。
 需要注意的是:4个位里面,0001代表的不是数值1而是首部的1个单位长度,1个单位长度为32bit=4字节(如图所示一行为32bit)。所以首部长度表示首部占32bit字的数目,既首部长度能占多少行(每行32bit)。故一般IP头部长20字节,首部长度字段的值为5。
- 服务类型TOS(8位):TOS(8bit)=优先权子字段(3bit[已弃用])+TOS子字段(4bit)+保留位(1bit),服务类型字段主要可用在QOS上。
 4bitTOS子字段分别代表:1000——最小延时、0100——最大吞吐量、0010——最高可靠性、0001——最小费用
- 总长度(16位):指定整个IP数据报的长度(IP头部+数据),单位为字节。由于总长度字段占16位,所以IP数据报的最长总长度为2^16既65535字节。
- 标识(16位):唯一标识每个数据报。通常情况下每发一个数据报该字段加1。
 在IP分片的时候,比如说一个包该标识字段为1,此包过大需要被分片,分成三个小包1-1、1-2、1-3,这三个小包的标识字段依旧是1,而后面的1、2、3则和接下来的字段相关。
- 标志(3位):目前只用到2bit,第3位保留。
 标志(3bit)=0+DF+MF DF——Don’t Fragment MF——More Fragment
 010DF=1表示不允许分片(DF=1,若包过大则直接丢弃并用ICMP报文通知源端相关情况);DF=0则表示允许分片
 001MF=1表示后续还有分片;MF=0表示此分片包为若干分片的最后一个
- 片偏移(13位):分片在原数据报中的相对位置。相对于原数据报起点,该分片从何处开始。片偏移都是以8字节为偏移单位,既除最后一片外,其余所有分片长度皆为8字节的整数倍。由于IP是无连接的,分片后的数据报可能乱序到达所以需要用片偏移对分片进行正确重组。
 举例说明:一个数据报被分成1 2 3三个分片,1分片的长度80字节、2分片长度64字节、3分片长度10字节。
 1分片的片偏移为0
 2分片的片偏移为80,在原数据报中,2分片从第80字节开始
 3分片的片偏移为144(0+80+64)
- TTL生存时间(8位):设置数据报可经过的最多路由器数。经过一个路由器,TTL减一。当TTL=0时,数据报被丢弃并发送ICMP报文通知源端。
 TTL占8bit,所以TTL最大值为2^8既255。TTL设置的意义在于防止网络出现环路时,数据报被无限的传递。
- 协议(8位):表明上一层传输层用的协议类型。常见的协议及字段值如下所示:
 TCP——6、UDP——17、ICMP——1、EGP——8、IGP——9、OSPF——89
- 首部检验和(16位):IP头部的检验和
- 源地址(32位)
- 目标地址(32位)
- 选项:由于IP头部最大位60字节,以上的基础头部占用20字节,所以选项最多只有40字节
 选项位是一个可变长的字段,目前有如下这些定义:- 安全和处理限制
- 路径记录(记录每跳路由的IP)
- 时间戳
- 宽松源站路由
- 严格源站路由
 
IP路由选择
IP路由选择是逐跳进行的,所有的IP路由选择只为数据传输提供下一站路由的IP地址。IP路由选择需要注意以下两点:
- 数据报中的目的IP始终是不变的(除非在IP头部选项中使用源站路由,目的IP才可能会被改变)。
- 链路层的目的MAC始终都是在改变的(非直连路由),并且是下一跳路由的MAC地址。(下一跳路由的MAC通过ARP协议获得)
IP路由选择的大致过程如下:
- 检查IP头部的TTL值,若TTL=0则丢弃该数据报并发送ICMP报文通知源端
- 搜索路由表,优先匹配主机,寻找能与目的IP完全匹配的条目。
- 搜索路由表,匹配相同子网的条目,寻找能与目的网络号匹配的条目。
- 搜索路由表,寻找默认路由条目。
- 搜索路由表后都没有找到以上三种相关的条目,丢弃该数据报。
 路由选择的顺序:匹配主机——>匹配子网——>默认路由
 IP路由根据连接情况可粗略分成两类:直连路由和非直连路由
- 直连路由的寻址
 源IP、目的IP、源MAC、目的MAC由始到终都是固定的,数据报不经过路由器
- 非直连路由
 源IP、源MAC、目的IP都是不变的(IP头部非源站路由),但其中需要经过若干个路由器,每经过一次IP路由选择得出下一跳路由后,目的MAC就被修改成该下一跳路由接口的MAC
直连路由的目的MAC就是目标主机的MAC,而非直连路由的目的MAC是经路由选择后下一跳接口的MAC
ARP
ARP是为IP地址到对应的硬件地址(以太网中既MAC地址)之间提供动态映射的一种协议。
###实例分析
 书中举例一个FTP的例子,可以从这个例子中全局的理解TCP/IP一个整体的过程,例子图如下:

ftp bsdi
- 应用层:FTP应用程序需要获取FTP服务器的IP地址,FTP应用程序发现输入的是一个域名于是调用DNS域名解析将域名解析成32bit的IP地址并将数据传给传输层
- 传输层:由于FTP是使用TCP连接的,所以请求TCP进行三次握手建立连接,TCP将握手包封装好后传给网络层
- 网络层:如果目的IP是直连网络那么数据报可直接送给目的主机;如果目的IP是非直连网络则按IP路由选择获取下一跳地址并将数据报转发。
- 链路层(以太网):有了目的IP后需要知道目的IP对应的MAC(直连是目的主机的MAC/非直连是下一跳的MAC)才能将数据报真正的传给目的主机。这就需要ARP做IP——>MAC的解析。
- ARP解析过程- ARP发送一个二层的广播既ARP广播请求包,ARP广播包里的目的MAC为FF:FF:FF:FF:FF:FF全F,全网广播询问目的IPxxx.xxx.x.x对应的MAC是多少。本网段的所有主机都会收到此ARP广播包,然后根据广播包中的目的IP是否为自己的IP做出判断。如果目的IP不是自己的IP则丢弃该ARP广播包。
- 目的主机发现该ARP广播包中的目的IP是自己的IP,于是向源IP发送一个单播的ARP响应包。该ARP响应包中包含了自己的MAC地址
 
- ARP发送一个二层的广播既
- 如果是直连网络的话,源IP、源MAC、目的IP和目的MAC都齐全了并且建立连接后即可传输数据(数据的传输其实还是上述的过程)
- 如果是非直连网络的话,目的MAC就是下一跳的MAC,将数据报转发给下一跳的设备,经过不断的IP路由选择,数据报最终到达目的主机(期间不断重复3/4/5步骤)。在非直连网络中,数据报中的目的IP始终是不变的(非源站路由)而目的MAC总是在变换是下一跳设备接口的MAC,IP地址用于逻辑选路,MAC地址才是真正决定数据报要传到哪个设备或主机上。
 
 假若ARP解析失败,无法获取目的IP的MAC,那么网络层封装的IP包是不会从主机接口发出去的。
 在收到ARP应答的时候,操作系统或网络设备等都会将查询到的IP-MAC对应关系缓存起来,所以一般情况下系统会维护一张ARP表以供快速查询。Linux系统可以通过命令arp -a或cat /proc/net/arp查看缓存的arp记录
###ARP包结构
 在上一篇博客中讲到以太网帧的结构,其中类型字段用来表示上层所用的协议,0x0806表示的是ARP,具体以太网帧结构如下:1
2以太网帧结构:
  | 目的地址(6) | 源地址(6) | 0x0806 | ARP请求/应答数据(28) + PAD填充字节(18) | CRC(4) |
 其中以太网帧的数据部分是由ARP请求/应答数据(28) + PAD填充字节(18)组成,接下来要分析的是ARP请求/应答数据(28)这28字节的结构,具体的分组结构如下所示:1
2ARP包结构:
  | 硬件类型(2) | 协议类型(2) | 硬件地址长度(1) | 协议地址长度(1) | 操作(2) | 源MAC(6) | 源IP(4) | 目的MAC(6) | 目的IP(4) |
- 硬件类型(2):表示硬件地址的类型。1表示以太网地址
- 协议类型(2):此字段和以太网帧头部的类型字段是一样的。0x0800——IP。协议类型和硬件类型可以表面ARP是在做IP——MAC之间的映射。
- 硬件地址长度(1): 表明硬件地址的长度。以太网硬件地址(MAC)该字段值为6,表明以太网硬件地址(MAC)是占6字节/48位。
- 协议地址长度(1):表明协议地址的长度。IP协议地址长度该字段值为4,表明IP协议地址占4字节/32位
- 操作(2):表明操作的类型。以太网帧的类型字段对于ARP请求/应答都是0x0806,所以需要通过ARP分组结构中的操作字段区分ARP请求/ARP应答- 1——ARP请求
- 2——ARP应答
- 3——RARP请求
- 4——RARP应答
 
- 源MAC(6):源端的MAC地址
- 源IP(4):源端的IP地址
- 目的MAC(6):目的设备的MAC地址。该字段的值和以太网帧头部的目的地址在ARP请求时是不同的(尽管它们都表示硬件地址,但所填的值不同)。
 当是ARP请求的时候(操作字段为1)- 以太网帧头部的目的地址(6)字段值是FF:FF:FF:FF:FF(表明这是一个二层广播包,所有同网段的主机都会接收到该ARP请求包)
- ARP分组结构的目的MAC(6)字段值是00:00:00:00:00:00(表明ARP不知道目的MAC是多少,需要目的主机将自身MAC填入该字段)
 
- 以太网帧头部的
- 目的IP(4):目的IP地址
 所以包含ARP分组的完整以太网帧结构是这样的:1
|目的地址(6)|源地址(6)|0x0806||  硬件类型(2)|协议类型(2)|硬件地址长度(1)|协议地址长度(1)|操作(2)|源MAC(6)|源IP(4)|目的MAC(6)|目的IP(4)  || + PAD填充字节(18)|CRC(4)|
###代理ARP
 如果ARP请求是试图解析非本网络的一台主机,那么连接两个网络的路由器会代替目的主机应答该ARP请求,路由器将自身的接口MAC地址回应给源主机,此过程就称之为代理ARP或委托ARP。路由器其实是“欺骗”了源主机,ARP响应中的MAC地址并发真正目的主机的MAC地址而是与源主机相连的路由器接口的MAC,路由器让源主机误以为路由器是目的主机。因为ARP请求是一个二层的广播包,而路由器是隔离广播的,也就是说ARP请求广播包正常情况下是无法穿过路由器继续前进的,路由器知道如何到达目的主机(路由表有路由记录),所以“欺骗”源主机并接收之后的数据报,将这些数据报转发出去。
 举例说明代理ARP,如下所示:1
PC-A(192.168.1.1/mac-a)--------------(192.168.1.2/mac-r1) 1——Router——2 (10.1.1.1/mac-r2)-------------------(10.1.1.2/mac-b)PC-B
- 当PC-A发送ARP请求查询10.1.1.2对应的MAC地址是多少,ARP请求的广播包到达Router——1接口(一般情况下广播包就只能到达Router——1接口,不会经由Router——2再发出去)
- 如果普通主机接收到非自身IP的ARP请求包时,都是做丢弃处理的。但Router开启了代理ARP的功能,查询自身路由表发现知道如何到达10.1.1.2
- Router对PC-A的ARP请求做出回应,发送一个ARP响应给PC-A,此ARP响应包内的映射关系为10.1.1.2——mac-r1(但实际上10.1.1.2对应的MAC应该是mac-b才对)
- PC-A接收到Router发送的ARP响应包,误以为10.1.1.2的MAC地址是mac-r1,于是将所有目的IP为10.1.1.2的数据都发到Router——1接口上。Router接收到数据后再转发给PC-B
###免费ARP
 主机发送ARP请求查询IP对应的MAC,这个ARP请求就称之为免费ARP。免费ARP的作用主要有两个:
- 通过免费ARP来确定IP是否被占用,是否有IP冲突的情况。主机在系统重启或网络服务重启的时候会发送免费ARP,询问自身IP对应的MAC是多少,当网络上有主机已经配置了相同的IP则会回应该免费ARP的询问,源主机接收到ARP回应就能确定该IP已经被使用。
- 利用免费ARP更新arp表缓存。当主机发送免费ARP广播请求包时,本网络的所有主机都接收到该免费ARP。当发现目的IP不是自身IP时,主机会将该免费ARP丢弃,但在丢弃前会读取该免费ARP包内的源IP及源MAC,若发现和本机arp缓存表不同则更新映射。
###ICMP
 ICMP主要用于差错报文及其他消息的传递。ICMP报文通常被网络层或传输层使用,常用于将差错报文返回给客户进程,因为IP是不可靠的会出现丢包的情况,出现丢包现象的设备一般都会通过ICMP发送ICMP报文给源端,告诉出错的原因和问题等。
 ICMP报文封装在IP数据报内部,IP头部里的协议(8位)字段值为1就表示后面接的是ICMP报文:1
| IP头部(20字节) | ICMP报文 |
 ICMP报文的基本格式:1
2
3| 类型(8bit) | 代码(8bit) | 检验和(16bit) |
| -----------------------------------------------------|
| 不同类型和代码的内容				 |
 ICMP报文类型:
ICMP报文类型很多,但最常用的是0 0 回显应答、8 0 请求回显,这两个类型主要用在ping中。

ICMP差错报文必须包含该差错报文的IP首部(含
选项)和IP首部后面的前8个字节,之所以要这样,是因为需要根据IP首部判断特定的协议、根据前8个字节判断用户进程(前8字节包含源端口和目标端口)以下情况不会产生ICMP差错报文,目的是为了防止广播风暴:
- ICMP差错报文,ICMP差错报文不会再产生差错报文来通知源端ICMP差错报文丢失(ICMP查询报文会产生ICMP差错报文)
- 目的地址为广播或多播的IP数据报
- 链路层广播的数据报
- 非IP分片的第一片,因为只有第一片分片才包含前8字节所需的源端口和目的端口
- 源地址不是单个主机的数据报,既源地址不能为全零、环回地址、广播地址和多播地址
IP路由
路由选路是IP最为重要的功能之一,系统会维护一张包含路由条目的路由表,通过查询路由表得出下一跳地址然后进行路由转发及选路。
###路由更新方式
 路由表更新的方式有如下三种:
- 路由守护进程,运行着动态路由协议的用户进程,比如OSPF守护进程、RIP守护进程。
- route命令,可以通过route命令手动添加路由条目(静态路由就属于这种方式)。
- ICMP重定向,当收到ICMP重定向报文时,路由表也会被更新。
 除了更新路由表外,可以通过netstat -nr命令查看路由表中的路由条目,当查看路由表时会看到不同的标志(flag):
- U 该路由可用
- G 该路由是一个网关
- H 该路由是一个主机路由(直接到主机,目的地址是一个完整的主机地址)
- D 该路由是ICMP重定向报文创建的
- M 该路由被ICMP重定向报文修改
###路由原理
 路由原理其实之前也讲过,就是搜索路由表的优先顺序:
- 搜索匹配主机地址的路由
- 搜索匹配网络地址的路由
- 搜索默认路由
###ICMP重定向
 当路由器检测到某台主机使用非最优路由的时候,可以通过向该主机发送ICMP重定向报文,请求改变该主机的路由。(ICMP重定向报文一般由路由器生成,针对主机使用而不是路由器)
 Linux系统中控制ICMP重定向的相关文件/proc/sys/net/ipv4/conf/all/send_redirects、/proc/sys/net/ipv4/conf/default/send_redirects、/proc/sys/net/ipv4/conf/<interface_device>/send_redirects。0为关闭,1为开启。
 ICMP重定向报文结构:1
2
3
4
5
6
7————————————
|类型 5|代码 0~3|检验和|
————————————
|应该使用的路由器IP地址|
———————————————
|IP首部+原IP首部后的前8个字节|
———————————————
- 类型——5,表示是ICMP重定向(详见之前的表格)
- 代码——0~3,0——网络重定向、1——主机重定向、2——服务类型和网络重定向、3——服务类型和主机重定向