ip协议是tcp/ip协议族的核心协议,也是socket网络编程的基础之一。
ip头部信息:出现在每个ip数据报中,用于指定ip通信的源端ip地址、目的端ip地址,指导ip分片和重组,以及指定部分通信行为。
ip数据报的路由和转发:发生在除目标机器之外的所有主机和路由器上,他们决定数据报是否应该转发以及如何转发。
ip服务的特点
- ip协议是tcp/ip协议族的动力
他为上层协议提供无状态、无连接、不可靠的服务。
a.无状态:是指ip通信双方不同步传输数据的状态信息,因此所有ip数据的发送、传输和接收都是互相独立的、没有上下文关系的。
缺点是无法处理乱序和重复的ip数据报。
优点是简单、高效。无须为保持通信的状态而分配一些内核资源。也无需每次传输数据都携带状态信息。
b.无连接:是指ip通信双方都不长久地维持对方的任何信息,上层协议每次发送数据的时候,都必须明确置顶对方的ip地址。
c.不可靠:指ip协议不能保证ip数据报准确的达到接收端,他只是承诺尽最大努力。很多情况都会导致ip数据报发送失败。无论哪种情况,发送端ip模块一旦检测到ip数据报发送失败,就通知上层协议发送失败,而不会视图重传。因此,使用ip服务的上层协议(比如tcp协议)需要自己实现数据确认、超时重传等机制已达到可靠传输的目的。
ipv4头部结构
4位版本号:ipv4 值为4
4位头部长度:标识该ip头部有多少个32bit(4字节),因为4位最大能表示15,所以ip头部长度是60字节。
8位服务类型:包括3位优先权字段(现已经被忽略),4位TOS字段和1位保留字段(必须置0)。4位TOS字段表示:最小延时,最大吞吐量,最高可靠性和最小费用。其中最多有一个能置为1。应用程序需要根据实际需求来设置他。比如ssh、telent登录程序需要的是最小延时的服务,ftp需要最大吞吐量的服务。
16位总长度:指的是整个ip数据报的长度,以字节为单位。最大长度位65535(2的16次方-1)字节,但是由于,MTU限制,长度超过MTU的数据报都将被分片传输,所以实际传输的ip数据报的长度都远远没有达到最大值。截下来三个字段描述了如何实现分片。
16位标识:唯一的表示主机发送的每一个数据报。其初始值由系统随机生成:每发送一个数据报其值就+1。该值在数据分片时被复制到每一个分片中,因此同一个数据报的所有分片都具有相同的标识值。
3位标志:第一位保留,第二位表示“禁止分片”。如果设置了这个位,ip模块将不对数据报进行分片。在这种情况下,如果ip数据报的长度超过了MTU的话,ip模块将丢弃该数据报并返回一个ICMP差错报文。第三位表示“更多分片”。除了数据报的最后一个分片外,其他分片都置为1
13位分片偏移:是分片相对原始ip数据报开始处(仅指数据部分)的偏移。实际的偏移值是该值左移三位(乘8)后得到的。由于这个原因,除了最后一个ip分片外,每个ip分片的数据部分的长度必须是8的整数倍(这样才能保证后面的ip分片拥有一个合适的偏移量)。
8位生存时间:是数据报到达目的地之前允许经过的路由器跳数。TTL值被发送端设置(常见值64)。数据报在转发过程中。没经过一个路由,该值就被路由器-1。当TTL值为0时,路由器将丢失数据报,并向源端发送一个ICMP差错报文。TTL值可以防止数据报陷入路由循环。
8位协议:用来区分上层协议。/etc/protocols文件定义了所有上层协议对应的prococol字段的数值,其中ICMP是1、TCP是6、UDP是17。
16头部校验和:由发送端填充,接收端对其使用CRC算法以校验ip数据报头部(仅校验头部)在传输过程中是否损坏。
32位的源端ip地址和目的端ip地址用来标识数据报的发送端和接收端。一般情况下,这两个地址在整个数据报的传递过程中保持不变,而不论他中间经过多少中转路由器。
选项:是可变的可选信息,这部分最多包含40字节,因为ip头部最长是60字节(包含前面20字节固定部分)。可用的ip选项包含:1.记录路由:告诉数据报途径所有路由器都将自己的ip地址填入ip头部选项部分,这样我们就可以跟踪数据报的传递路径。2.时间戳:告诉每个路由器都将数据报被转发的时间(或时间与ip地址对)填入ip头部的选项部分,这样就可以测量途径路由之间的数据报传输时间。3.松散源路由选择:指定一个路由器ip地址列表,数据报发送过程中必须经过其中所有的路由器。4.严格源路由选择:和松散源路由选择类似,不过数据报只能经过被指定的路由器。
使用tcpdump观察ipv4头部结构
season@ubuntu:~$ sudo tcpdump -ntx -i lo
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
另开终端
season@ubuntu:~$ telnet 127.0.0.1
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
Ubuntu 16.04.3 LTS
ubuntu login: season
Password:
观察tcpdump输出的第一个数据包
IP 127.0.0.1.59410 > 127.0.0.1.23: Flags [S], seq 2072306840, win 43690, options [mss 65495,sackOK,TS val 190789224 ecr 0,nop,wscale 7], length 0
0x0000: 4510 003c bdf1 4000 4006 7eb8 7f00 0001
0x0010: 7f00 0001 e812 0017 7b84 e498 0000 0000
0x0020: a002 aaaa fe30 0000 0204 ffd7 0402 080a
0x0030: 0b5f 3668 0000 0000 0103 0307
....
该数据包描述的是一个ip数据报。由于我们是使用telent登录本机,所以ip数据报的源端ip地址和目的端ip地址都是127.0.0.1。telent服务器程序使用的端口号是23。telent客户端程序使用临时端口号59410与服务器通信。
Flags [S], seq , win , options 都是tcp头部信息
length指出该ip数据报所携带的应用程序数据的长度。
tcpdump中 -x 选项 使之输出数据包的二进制码。
参考表:
0x0000: 4510 003c bdf1(a5da) 4000 4006 7eb8(96cf) 7f00 0001
ip分片
MTU:通信术语 最大传输单元(Maximum Transmission Unit,MTU)是指一种通信协议的某一层上面所能通过的最大数据包大小(以字节为单位)
当ip数据报的长度超过帧的MTU时,他将被分片传输。分片可能发生在发送端,也可能发生在中转路由器上,而且可能在传输过程中被多次分片,但只有在最终的目标机器上,这些分片才会被内核中的ip模块重新组装。
ip头部中三个字段给ip的分片和重组提供了足够的信息:数据报标识、标志和片偏移。一个数据报的分片都具有自己的ip头部,他们具有相同的标识值,但具有不同的片偏移。并且除了最后一个分片外,其他分片都将设置MF标志。每个分片的ip头部的总长度字段被设置为该分片的长度。
发送一个1501字节的ip数据报
长度位1501字节的ip数据报被拆分成两个ip分片,第一个ip分片的长度位1500字节,第二个ip分片的长度为21字节,每个ip分片都包含自己的ip头部(20字节),且第一个ip分片的ip头部设置了MF标志,而第二个ip分片的ip头部则没有设置该标志,因为他已经是最后一个分片了。原始ip数据报中的ICMP头部内容被完整的复制到了第一个ip分片中,第二个ip分片不包含ICMP头部信息,因为ip模块重组该ICMP报文的时候只需要一份ICMP头部信息,重复传送这个信息没有任何益处。1473字节的ICMP报文数据的前1472字节被ip模块复制到第一个ip分片中,使其总长度位1500字节,从而满足MTU的要求,而多出的最后一个字节则被复制到第二个ip分片中。
- 使用tcpdump输出ip数据报的两个分片
season@ubuntu:~$ sudo tcpdump -ntv -i ens33 icmp
tcpdump: listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
另开终端
season@ubuntu:~$ ping 192.168.30.109 -c 1 -s 1473
PING 192.168.30.109 (192.168.30.109) 1473(1501) bytes of data.
1481 bytes from 192.168.30.109: icmp_seq=1 ttl=64 time=0.192 ms
--- 192.168.30.109 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.192/0.192/0.192/0.000 ms
输出信息:
IP (tos 0x0, ttl 64, id 16003, offset 0, flags [+], proto ICMP (1), length 1500)
192.168.30.108 > 192.168.30.109: ICMP echo request, id 4019, seq 1, length 1480
IP (tos 0x0, ttl 64, id 16003, offset 1480, flags [none], proto ICMP (1), length 21)
192.168.30.108 > 192.168.30.109: ip-proto-1
IP (tos 0x0, ttl 64, id 61550, offset 0, flags [+], proto ICMP (1), length 1500)
192.168.30.109 > 192.168.30.108: ICMP echo reply, id 4019, seq 1, length 1480
IP (tos 0x0, ttl 64, id 61550, offset 1480, flags [none], proto ICMP (1), length 21)
192.168.30.109 > 192.168.30.108: ip-proto-1
这两个IP 分片的标识值都是16003,说明它们是同一个IP数据报的分片。第一个分片的片偏移值为0,而第二个则是1480。很显然,第二个分片的片偏移值实际上也是第一个分片的ICMP报文的长度。第一个分片设置了MF 标志以表示还有后续分片,所以tcpdump输出“flags [+]”。而第二个分片则没有设置任何标志,所tcpdump 输出“flags [none]”。这个两个分片的长度分别为1500字节和21字节。
最后,IP层传递给数据链路层的数据可能是一个完整的IP数据报,也可能是一个IP分
片,它们统称为IP 分组(packet)。
ip模块的工作流程
从右往左分析:
当ip模块接收到来自数据链路层的ip数据报时,首先对该数据报的头部做CRC校验,确认无误之后就分析头部的具体信息
如果设置了源站选路选项(松散源/严格源理由选择)或者不是发送给本机,则ip模块调用数据报转发子模块来处理该数据报。如果数据报是发送给本机,则检测头部协议字段来决定他派给哪个上层应用(分用)。
数据报转发子模块:首先检测系统是否允许转发,如不允许就讲数据报丢弃。如允许将对数据报执行一些操作,然后将他交给ip数据报输出子模块。
计算下一跳路由:ip路由的过程,ip数据报应该发送到哪个下一跳(或者目标机器),以及经过哪个网卡来发送。
ip输出队列:存放的是所有等待发送的ip数据报,其中除了需要转发的ip数据报外,还包括封装了本机上层数据(ICMP报文、TCP报文段和UDP数据报)的ip数据报。
路由机制
使用route命令查看路由表
season@ubuntu:~$ route
内核 IP 路由表
目标 网关 子网掩码 标志 跃点 引用 使用 接口
default 192.168.30.2 0.0.0.0 UG 100 0 0 ens33
link-local * 255.255.0.0 U 1000 0 0 ens33
192.168.30.0 * 255.255.255.0 U 100 0 0 ens33
ip转发
上文提到,不是发送给本机的ip数据报将由数据报转发子模块处理。路由器都能执行数据报的转发操作,而主机一般只发送和接收数据报,这是因为主机上的
内核参数被设置为0
season@ubuntu:~$ cat /proc/sys/net/ipv4/ip_forward
0
可以修改为1,使他具有数据报转发功能。
转发操作:
a.检查数据报头部的TTL值,如果TTL为0,则丢弃该数据报。
b.检查数据报头部的严格源路由选项,如果该项被设置,则检测数据报的目标ip地址是否是本机的某个ip地址。如果不是,则发丝冰一个ICMP源站选路失败报文给发送端。
c.如果有必要,则给源端发送一个ICMP重定向报文,以告诉他一个更合理的下一跳路由器。
d.将TTL值减一。
e.处理ip头部选项。
f.如果有必要,则执行ip分片操作。
ipv6固定头部结构
ipv6头部是由40字节的固定头部和可变长的扩展头部(下面介绍)组成
4位版本号:指定ip协议的版本,ipv6是6。
8位通信类型:指示数据流通信类型或优先级,和ipv4中的TOS类似。
20位流标签:是ipv6新增加的字段。用于某些对连接的服务质量有特殊要求的通信,比如音频或视频等实时数据传输。
16位净荷长度:指的是ipv6扩展头部和应用程序数据长度之和,不包括固定头部长度。
8位下一个包头:指出紧跟ipv6固定头部后的包头类型,如扩展头或某个上层协议头(比如TCP、UDP或者ICMP),它类似于ipv4头部中的协议字段,且相同的取值有相同的含义。
8位跳数限制:和ipv4中的TTL含义一样。
ipv6用128位(16字节)来表示ip地址。
32位表示ipv4地址一般用点分十进制来表示,而ipv6地址则用十六进制字符串表示。比如:FE80:0000:0000:0000:1234:5678:0000:0012。ipv6用‘:’分割成8组,每组包含2字节,但是这种表示方法过于麻烦,可以使用所谓的零压缩法来将其简写,也就是省略连续的、全零的组。比如上面的例子可以表示位:FE80::1234:5678:0000:0012。不过零压缩法对一个ipv6地址只能使用一次。
ipv6扩展头部参考表
附:
TCP/IP:
数据链路层:ARP,RARP
网络层: IP,ICMP,IGMP
传输层:TCP ,UDP,UGP
应用层:Telnet,FTP,SMTP,SNMP
OSI:
数据链路层:Frame Relay, HDLC, PPP, IEEE 802.3/802.2, FDDI, ATM, IEEE 802.5/802.2
网络层:IP,IPX,AppleTalk DDP
传输层:TCP,UDP,SPX
会话层:RPC,SQL,NFS,NetBIOS,names,AppleTalk,ASP,DECnet,SCP
表示层:TIFF,GIF,JPEG,PICT,ASCII,EBCDIC,encryption,MPEG,MIDI,HTML
应用层:FTP,WWW,Telnet,NFS,SMTP,Gateway,SNMP