Linux网络协议之IP协议(网络层)
文章目录
1.IP协议基本概念
IP协议全称为“网际互连协议(Internet Protocol)”,IP协议是TCP/IP体系中的网络层协议
三个基本概念:
主机:配有IP地址,但是不进行路由控制的设备
路由器:既有IP地址,又进行路由控制的设备
节点:主机和路由器的统称
2.IPV4协议格式
IPV4协议格式如下:
- 4位版本号(version):指定IP协议版本,对于IPV4来说就是4,对于IPV6来说就是6
- 4位首部长度(header length):IP头部的长度是个32bit, 也就是 length * 4 的字节数,4bit能表示最大的数字是15(2^4-1), 因此IP头部最大长度是60字节(4x15=60)
- 8位服务类型(type of service):
- 3位优先权字段(已经弃用), 4位TOS字段, 和1位保留字段(必须置为0). 4位TOS分别表示: 最小延时, 最大吞吐量, 最高可靠性, 最小成本. 这四者相互冲突, 只能选择一个. 对于ssh/telnet这样的应用程序, 最小延时比较重要; 对于ftp这样的程序, 最大吞吐量比较重要
- 16位总长度(total length):IP数据报整体占多少个字节
- 16位标识(Id):唯一的标识主机发送的报文。如果IP报文在数据链路层被分片了, 那么每一个片里面的这个id都是相同的
3位标志字段:第一位保留(保留的意思是现在不用, 但是还没想好说不定以后要用到). 第二位置为1表示禁止分片, 这时候如果报文长度超过MTU, IP模块就会丢弃报文. 第三位表示"更多分片", 如果分片了的话,最后一个分片置为1, 其他是0. 类似于一个结束标记
第一位保留(保留的意思是现在不用, 但是还没想好说不定以后要用到). 第二位置为1表示禁止分片, 这时候如果报文长度超过MTU, IP模块就会丢弃报文. 第三位表示"更多分片", 如果分片了的话,最后一个分片置为1, 其他是0. 类似于一个结束标记
- 13位分片偏移(framegament offset):分片相当于在原始数据当中的偏移量
8位生存时间(Time To Live,TTL):
数据报到达目的地的最大报文跳数. 一般是64. 每次经过一个路由, TTL-= 1, 一直减到0还没到达, 那么就丢弃了. 这个字段主要是用来防止出现路由循环
- 8位协议:表示上层协议的类型
- 16位头部校验和:使用CRC进行校验, 来鉴别头部是否损坏
- 32位源地址和32位目标地址:表示发送端和接收端
- 选项字段:不定长,最多40字节
3.分片与组装
分片与组装过程:
- 如果IP层要传送的数据超过了1500字节,那么就需要先在IP层对该数据进行分片,然后再将分片后的数据交给下层MAC帧进行发送
- 如果发送数据时在IP层进行了分片,那么当这些分片数据到达对端主机的IP层后就需要先进行组装,然后再将组装好的数据交付给上层传输层
注意:
- 数据的分片不是经常需要做的,实际在网络通信过程中不分片才是常态,因为数据分片会存在一些潜在的问题,比如分片可能会增加丢包的概率
- 数据的分片和组装发生在IP层,不仅源端主机可能会对数据进行分片,数据在路由过程中的路由器也可能对数据进行分片。因为不同网络的MTU是不一样的,如果传输路径上的某个网络的MTU比源端网络的MTU小,那么路由器就可能对IP数据报再次进行分片
- 分片数据的组装只会发生在目的端的IP层
- 在分片的数据中,每一个分片在IP层都会被添加上对应的IP报头,而传输层添加的报头只会出现在第一个分片中,因此网络中传输的数据包可能没有传输层的报头
数据的分片和组装都是由IP层完成的:
数据的分片和组装都是在IP层完成的,上层的传输层和下层的链路层并不关心
传输层只负责为数据传送提供可靠性保证,比如当数据传送失败后,传输层的TCP协议可以组织进行数据重传
当TCP将待发送的数据交给IP后,TCP并不关心该数据是否会在IP层进行分片,即TCP并不关心数据具体的发送过程
当TCP从IP获取到数据后,TCP也不关心该数据是否在IP层经过了组装
而链路层的MAC帧只负责,将数据从一个节点传送到和自己相连的下一个节点
当IP将待发送的数据交给MAC帧后,MAC帧并不知道该数据是IP经过分片后的某个分片数据,还是一个没有经过分片的数据,MAC帧只知道它一次最多只能发送MTU大小的数据,如果IP交给MAC帧大于MTU字节的数据,那MAC帧就无法进行发送
MAC帧从网络中获取到数据后,MAC帧也不关心这个数据是否需要进行组装,MAC帧只需要将该数据的MAC帧报头去掉后直接上交给上层IP就行了,而至于该数据的组装问题则是IP需要解决的
因此,数据的分片和组装完全是由IP协议自己完成的,传输层和链路层不必关心也不需要关心
分片的过程:
- 假设IP层要发送4500字节的数据,由于该数据超过了MAC帧规定的MTU,因此IP需要先将该数据进行分片,然后再将一个个的分片交给MAC帧进行发送
- IP报头如果不携带选项字段,那么其大小就是20字节,假设IP层添加的IP报头的长度就是20字节,并按下列方式将数据分片后形成了四个分片报文,如下图:
- 需要注意的是,分片后的每一个分片数据都需要封装上对应的IP报头,因此4500字节的数据至少需要分为四个分片报文进行发送
- 分片报文到达对方的IP层后需要被重新组装起来,因此IP层在对数据进行分片时需要记录分片的信息,而IP报头当中的16位标识、3位标志和13位片偏移实际就是与数据分片相关的字段
- 16位标识:唯一标识主机发送的报文,如果数据在IP层进行了分片,那么每一个分片报文的16位标识是相同的
- 3位标志:第一位保留,表示暂时没有规定该字段的意义。第二位表示禁止分片,表示如果报文长度超过MTU,IP模块就会丢弃该报文。第三位表示“更多分片”,如果报文没有进行分片,则该字段设置为0,如果报文进行了分片,则除了最后一个分片报文设置为0以外,其余分片报文均设置为1
- 13位片偏移:分片相对于原始数据开始处的偏移,表示当前分片在原数据中的偏移位置,实际偏移的字节数是这个值× 8 \times 8×8得到的。因此除了最后一个报文之外,其他报文的长度必须是8的整数倍,否则报文就不连续了
- 因此上述四个分片报文对应的16位标识都是一样的,假设四个分片报文的16位标识都是123,则这四个报文对应的16位标识、3位标志中的“更多分片”和13位片偏移分别如下:
- 需要注意的是,13位片偏移当中记录的字节数是当前分片在原数据开始处的偏移字节数的值÷ 8 \div 8÷8得到的,比如分片报文2在原始数据开始处的偏移字节数是1480,其对应的13位片偏移的值就是1480÷8=185
组装的过程:
- MAC帧交给IP层的数据可能来自世界各地,这些数据可能是经过分片后发送的,也可能是没有经过分片直接发送的,因此IP必须要通过某种方式来区分收到的各个数据
- IP报头当中有32位源IP地址,源IP地址记录了发送端所对应的IP地址,因此通过IP报头当中的32位源IP地址就可以区分来自不同主机的数据
- IP报头当中有16位标识,未分片的数据各自的16位标识都是不同的,而由同一个数据分片得到的各个分片报文所对应的16位标识都是相同的,因此通过IP报头当中16位标识就可以判断哪些报文是没有经过分片的独立报文,哪些报文是经过分片后的分片报文
- 因此IP可以通过IP报头当中的32位源IP地址和16位标识,将经过分片的数据各自聚合在一起,聚合在一起后就可以开始进行组装了
- 对于各个分片报文来说:
- 第一个分片报文中的13位片偏移的值一定为0
- 最后一个分片报文中的“更多分片”标志位一定为0
- 对于每一个分片报文来说,当前报文的13位片偏移加上当前报文的数据字节数 ÷ 8所得到的值,就是下一个分片报文的所对应的13位片偏移
- 根据分片报文的这三个特点就能够将分片报文合理的组装起来
- 先找到分片报文中13位片偏移为0的分片报文,然后提取出其IP报头当中的16位总长度字段,通过计算即可得出下一个分片报文所对应的13位片偏移,按照此方式依次将各个分片报文拼接起来
- 直到拼接到一个“更多分片”标志位为0的分片报文,此时表明分片报文组装完毕
分片报文丢包的问题:
- 分片后的报文在网络传输过程中也可能会出现丢包问题,但接收端有能力判断是否收到了全部分片报文,比如假设某组分片报文对应的16位标识值为x:
- 如果分片报文中的第一个分片报文丢包了,那么接收端收到的分片报文中就找不到对应16位标识为x,并且13位片偏移为0的分片报文
- 如果分片报文中的最后一个分片报文丢包了,那么接收端收到的分片报文中就找不到对应16为标识为x,并且“更多分片”标志位为0的分片报文
- 如果分片报文中的其它分片报文丢包了,那么接收端在进行分片报文的组装时就会找不到对应13位片偏移为特定值的分片报文
- 需要注意的是,未分片报文的“更多分片”标志位为0,最后一个分片报文的“更多分片”标志位也为0,但当接收端只收到分片报文中的最后一个分片报文时,接收端不会将其识别成一个未分片的报文,因为未分片的报文所对应的13位片偏移的值也应该是0,而最后一个分片报文所对应的13位片偏移的值不为0
- 因此只有当一个报文的13位片偏移为0,并且该报文的“更多分片”标志位也为0时,该报文才会被识别成一个没有被分片的独立报文,否则该报文就会被识别成一个分片报文
为什么不建议进行分片?
- 虽然传输层并不关心IP层的分片问题,但分片对传输层也是有影响的
- 如果一个数据在网络传输过程中没有经过分片,那么只要接收端收到了这一个报文,我们就可以认为该数据被对方可靠的收到了
- 而如果一个数据在网络传输过程中进行了分片,那么只有当接收端收到了全部的分片报文并将其成功组装起来,这时我们才认为该数据被对方可靠的收到了。但如果众多的分片报文当中有一个报文出现了丢包,就会导致接收端就无法将报文成功组装起来,这时接收端会将收到的分片报文全部丢弃,此时传输层TCP会因为收不到对方应答而进行超时重传
- 假设在网络传输时丢包的概率是万分之一,如果将数据拆分为一百份进行发送,那么此时丢包的概率就上升到了百分之一。因为只要有一个分片报文丢包了也就等同于这个报文整体丢失了,因此分片会增加传输层重传数据的概率
- 需要注意的是,只要分片报文当中的某一个出现了丢包,此时传输层都需要将数据整体进行重传,因为传输层并不知道底层IP对数据进行了分片,当传输层发送出去的数据得不到应答时传输层就只能将数据整体进行重传,因此数据在发送时不建议进行分片
如何尽可能避免分片?
- 实际数据分片的根本原因在于传输层一次向下交付的数据太多了,导致IP无法直接将数据向下交给MAC帧,如果传输层控制好一次交给IP的数据量不要太大,那么数据在IP层自然也就不需要进行分片
- 因此TCP作为传输控制协议,它需要控制一次向下交付数据不能超过某一阈值,这个阈值就叫做MSS(Maximum Segment Size,最大报文段长度)
- 通信双方在建立TCP连接时,除了需要协商自身窗口大小等概念之外,还会协商后续通信时每一个报文段所能承载的最大报文段长度MSS
- MAC帧的有效载荷最大为MTU,TCP的有效载荷最大为MSS,由于TCP和IP常规情况下报头的长度都是20字节,因此一般情况下 MSS = MTU - 20 - 20,而MTU的值一般是1500字节,因此MSS的值一般就是1460字节
- 所以一般建议TCP将发送的数据控制在1460字节以内,此时就能够降低数据分片的可能性。之所以说是降低数据分片的可能性,是因为每个网络的链路层对应的MTU可能是不同的,如果数据在传输过程中进入到了一个MTU较小的网络,那么该数据仍然可能需要在路由器中进行分片
4.IP网段划分
4.1 IP地址组成
路由查找目标主机过程:路由查找是先找到目标网络,在找到目标主机
IP地址可以分为两个部分:网络号和主机号
- 网络号:保证相互连接的两个网段具有不同的标识
- 主机号:在同一网段内,主机具有相同的网络号,但是具有不同的主机号。标识相同网段里的不同主机
- 不同的子网其实就是把网络号相同的主机连接到了一起
- 如果在子网中新增一台主机,这台主机的网络号和这个子网的网络号一致,但是主机号必须不同和子网中的其它主机重复
由上可知,在子网中新增一台主机需要为其分配对应且正确的IP,去除一台主机的话,需要回收IP。但是手动管理子网IP很麻烦
- 有一种技术DHCP,能够自动的给子网内新增主机结点分配IP地址,避免了手动管理的不便
- 一般现在的路由器都带有DHCP功能,因此路由器可以看作一个DHCP服务器
4.2 IP地址分类
过去曾经提出一种划分网络号和主机号的方案,就是把所有IP地址分为五类,如下图所示:
因此,各类IP地址的取值范围如下:
A类:
0.0.0.0 ~ 127.255.255.255B类:
128.0.0.0 ~ 191.255.255.255C类:
192.0.0.0 ~ 223.255.255.255D类:
224.0.0.0 ~ 239.255.255.255E类:
240.0.0.0 ~ 247.255.255.255
当要判断一个IP地址是属于哪一类时,只需要遍历IP地址的前五个比特位,第几个比特位最先出现0值,那么这个IP地址对应就属于A、B、C、D、E类地址
这种划分方法,我们发现 A,B,C类越往下网络号表示越来越多,主机号表示越来越少。随着互联网的发展,大多数组织都申请B类网络地址,导致B类很快就用完了,针对上面的情况,提出了一个新的方案,称为CIDR(无类域间路)。只与子网掩码有关,通过子网掩码要区分网络号和主机号
CIDR(Classless Interdomain Routing):又叫无类域间路
- 引入一个额外的子网掩码(subnet mask)来区分网络号和主机号
- 子网掩码也是一个32位的正整数。通常用一串 “0” 来结尾
- 将IP地址和子网掩码进行 “按位与” 操作, 得到的结果就是网络号
- 网络号和主机号的划分与这个IP地址是A类、B类还是C类无关
CIDR划分法如何计算当前网络的网络号:
如何得到当前子网中的IP地址使用多少个比特位作为主机号?
比如:
4.3 特殊的IP地址
- 在一个网段中都会有一个网络号的和广播号,即 192.0.0.0 ~ 192.0.0.255
- 网络号:主机号全为0的ip地址,就为该网段中的网络号,代表整个局域网
- 广播号:主机号中全为1的ip地址,就为该网段中的广播号,代表广播地址
- 127.0.0.1 :本地回环网卡地址(通常用来测试自己机器的网络连通性)
- 0.0.0.0:代表本地所有的网卡地址
4.4 IP地址的数量限制
- 我们知道, IP地址(IPv4)是一个4字节32位的正整数。那么一共只有232个IP地址, 大概是43亿左右。而TCP/IP协议规定, 每个主机都需要有一个IP地址,这意味着, 一共只有43亿台主机能接入网络么?
- 实际上, 由于一些特殊的IP地址的存在, 数量远不足43亿; 另外IP地址并非是按照主机台数来配置的, 而是每一个网卡都需要配置一个或多个IP地址
- CIDR在一定程度上缓解了IP地址不够用的问题(提高了利用率, 减少了浪费。但是IP地址的绝对上限并没有增加), 仍然不是很够用。 这时候有三种方式来解决:
动态分配IP地址: 只给接入网络的设备分配IP地址. 因此同一个MAC地址的设备, 每次接入互联网中, 得到的IP地址不一定是相同的
NAT技术
IPv6: IPv6并不是IPv4的简单升级版. 这是互不相干的两个协议, 彼此并不兼容; IPv6用16字节128位来表示一个IP地址; 但是目前IPv6还没有普及
4.5 私网IP地址与公网IP地址
首先了解一下运营商:运营商为我们建设了网络通信的基础设施。当我们在进行通信的时候,都需要经过运营商为我们建立的基础设施
- 我们知道IPv4的IP地址个数是一个确定的数,某些组织有将所有的IP地址分成了两大类,私网IP地址和公网IP地址
- 私网IP地址和公网IP地址的区别:
- 私网就可以理解成是一个局域网。私网可以有很多个,而公网只有一个
- 顾名思义,私网IP就是在私网中使用的IP地址,公网IP就是在公网中使用的IP地址
- 不同的私网私网IP可以相同,但是在公网中,公网IP只有一个,是唯一的
- 同一私网内的IP地址不能是重复的
- RFC1918规定,包含在以下范围的IP地址称为私有IP地址,其余的都是公网IP地址
- 私有IP地址:
10.*
,前8位是网络号,共16777216个地址172.16.*到172.31.*
,前12位是网络号,共1048576个地址192.168.*
前16位是网络号,共65536个地址
让我们来了解下数据是如何发送到服务器的:
路由器是连接两个或多个网络的硬件设备,在路由器上有两种网络接口,分别是LAN口和WAN口:
- LAN口(Local Area Network):表示连接本地网络的端口,主要与家庭网络中的交换机、集线器或PC相连
- WAN口(Wide Area Network):表示连接广域网的端口,一般指互联网
我们将LAN口的IP地址叫做LAN口IP,也叫做子网IP,将WAN口的IP地址叫做WAN口IPO,也叫做外网IP
我们使用的电脑、家用路由器、运营商路由器、广域网以及我们要访问的服务器之间的关系大致如下:
- 不同的路由器,子网IP其实都是一样的(通常都是192.168.1.1),子网内的主机IP地址不能重复,但是子网之间的IP地址就可以重复了
- 每一个家用路由器,其实又作为运营商路由器的子网中的一个节点,这样的运营商路由器可能会有很多级,最外层的运营商路由器的WAN口IP就是一个公网IP了
- 如果希望我们自己实现的服务器程序,能够在公网上被访问到,就需要把程序部署在一台具有外网IP的服务器上,这样的服务器可以在阿里云/腾讯云上进行购买
- 由于私网IP不能出现在公网当中,因此子网内的主机在和外网进行通信时,路由器会不断将数据包IP首部中的源IP地址替换成路由器的WAN口IP,这样逐级替换,最终数据包中的源IP地址成为一个公网IP,这种技术称为NAT(Network Address Translation,网络地址转换)
让我们再来思考两个问题:
问题一:为什么私网IP不能出现在公网当中?
- 不同的局域网中主机的IP地址可能是相同的,所以私网IP无法唯一标识一台主机,因此不能让私网IP出现在公网上,因为IP地址要能唯一标识公网上的一台主机
- 但由于IP地址不足的原因,我们不能让主机直接使用公网IP而让主机使用私网IP,因为私网IP可以重复也就意味着我们可以在不同的局域网使用相同的IP地址,缓解了IP的不足
- 此外,我们不能直接使用公网IP还有一个原因就是,因为我们的数据包必须要经过运营商的路由器,如果我们发送的数据直接到了公网,那也就意味着我们再也不用交网费了,这是不现实的
问题二:为什么两个局域网当中的主机不能不跨公网进行通信
- 两个局域网当中的主机理论上是不能不跨公网进行通信的,因为一个主机要将数据发送给另一台主机的前提是得先知道另一台主机的IP地址
- 即便现在这个主机知道了另一台主机的IP地址,但有可能这两台主机的IP地址是一样的,因为它们的IP地址都是私网IP地址
- 当这一台主机发送数据时将目的IP地址填成和自己相同的IP地址,操作系统就会认为这个数据就是要发给自己的,而不会向外进行发送了
- 所以数据要从一个局域网发送到另一个局域网,如果不经过公网是基本上不可能的。我们在和别人聊天的时候,也不是直接将数据从一个局域网直接发送到了另一个局域网,而是先将数据经过公网发送到了服务器,然后再由服务器将数据经过公网转发到了另一个局域网
- 但实际确实存在一些技术能够使数据包在发送过程中不进行公网IP的替换,而将数据正确送到目标主机,这种技术叫做内网穿透,也叫做NAT穿透
5.对路由的了解
主机A跨网络通信主机B,首先需要找到主机B所在的局域网,再在该局域网中找到主机B。路由就是找下一跳往哪跳,下一次发送给那个节点
- IP数据包的传输过程中会遇到很多路由器,这些路由器会帮助数据包进行路由转发,每当数据包遇到一个路由器后,对应路由器都会查看该数据的目的IP地址,并告知该数据下一跳应该往哪跳
- 路由器的查找结果可能有以下三种:
- 路由器经过路由表查询后,得知该数据下一跳应该跳到哪一个子网
- 路由器经过路由表查询后,没有发现匹配的子网,此时路由器会将该数据转发给默认路由
- 路由器经过路由表查询后,得知该数据的目标网络就是当前所在的网络,此时路由器就会将该数据转给当前网络中对应的主机
- 路由表:路由表存储着指向特定网络地址的路径
- 注意:路由表不仅仅存在于路由器中,还存在于联网的计算机中
- 路由表可以通过
route
命令查看
- 路由过程:根据IP地址和子网掩码,按位与得到目的网络号,找路由表中找是否有匹配的目的网络号。有匹配的目的网络号,通过Iface接口发送出去,去找目标主机。没有匹配到,会发送给默认网络号(default),一般是路由器,然后该路由器循环上面操作,直到找到主机
一点扩展:路由表生成算法
路由可分为静态路由和动态路由:
- 静态路由:是指由网络管理员手工配置路由信息
- 动态路由:是指路由器能够通过算法自动建立自己的路由表,并且能够根据实际情况进行调整
路由表相关生成算法:距离向量算法、LS算法、Dijkstra算法等