【Linux】网络层协议:IP

我们必须接受批评,因为它可以帮助我们走出自恋的幻象,不至于长久在道德和智识上自我陶醉,在自恋中走向毁灭,事实上我们远比自己想象的更伪善和幽暗。

文章目录


一、IP和TCP之间的关系(提供策略 和 提供能力)

我们之前已经学习了TCP,当时我们在进行讲解时,是站在上帝视角直接说A主机将数据段发送到B主机,可是A主机真的是直接将数据段发送到对方吗?其实并不是,传输层的数据段是要向下交付到网络层的,那TCP究竟在数据网络传输的过程中扮演了什么样的角色呢?网络层的核心工作又是什么呢?我该怎样理解这两层协议栈呢?

网络层能够提供将一个数据包从A主机跨网络发送到B主机的能力,但有能力一定能够做到吗?在我们生活的世界中,没有任何一件事情是100%能够发生的,因为任何事情都是有概率的,比如你们班的数学学霸张三,他具有每次数学考150的能力,但这能代表他每次考试一定能考150吗?这是不一定的!我们只能说它具有非常大的概率能够做到将数学考试的成绩达到150,但如果张三的父亲是学校的校长,他父亲要求张三必须每次考试都考到150,如果这次考试没到150,那他父亲就将这次考试作废,直到张三考到150为止,这叫他父亲提供给张三的策略。
同理,我们说网络层能够提供将一个数据包从A主机跨网络发送到B主机的能力,但这并不代表网络层一定能够做到!有没有可能网络出现拥塞,而数据包大面积丢失的情况呢?或者对方接受能力太小,网络层发送数据包的速度太快,导致对方来不及接收从而丢弃掉许多报文呢?或者对方迟迟没有返回报文,发送方又该怎么做呢?所以网络层不仅仅面临着只将数据包跨网络发送的要求,还有一些其他在网络传输过程中可能面临的问题,而这些问题由谁来解决呢?其实就是传输控制协议TCP来解决

所以我们说TCP提供了数据包跨网络发送的策略,比如超时重传,确认应答,流量控制,拥塞控制,滑动窗口,捎带应答,延时应答等等,这些都是数据包在网络发送时,如果出现了不可靠的问题,数据包应该怎么处理?这些都是由TCP来控制。
而IP层提供了数据包跨网络发送的能力,比如IP层会通过报头中的目的IP来查路由表,确定数据包的下一跳位置,IP层只负责将数据包交付到下一跳,至于传输过程中出现了什么问题,这是TCP层提供策略来进行解决的,IP层不关心,也不会做什么。
所以我们称,TCP提供数据跨网络传输的策略,而IP提供数据跨网络传输的能力,两者合在一起就一定能够保证数据包可靠的跨网络从主机A发送到主机B,这也就是为什么很多人叫TCP/IP协议的原因,因为这两个协议可以可靠的保证数据包跨网络送到目标主机,而这正就是网络通信的本质。

那么IP层究竟是如何做到将数据包跨网络发送的呢?(浅浅的简单理解一下)
路径选择中,目的ip是非常重要的,目的IP能够决定数据包以什么样的路径来进行路由,下图中的主机或者路由器,我们都将其称为一个节点,每个节点都会通过目的ip来决策数据包的下一跳位置(实际上是通过查路由表来决策的),而ip=目标网络+目标主机,32位的目的ip前半部分表示目标网络号,后半部分表示在目标网络中的目标主机号,因为一个局域网中是存在多台主机的,所以找到目标网段后,还需要在网段中找出目标主机。

下图是我家路由器的照片,可以看到路由器的ip地址和MAC地址。在进行数据包的路由时,不仅仅只有路由器才有路由的能力,主机本身也具有路由的能力,也可以进行数据包的路由选择。

二、IP报头的理解(再次理解面向数据报)

绿色部分的三个报头字段放到文章结尾处去讲。

学习协议,最离不开的两个问题是,如何将报头和有效载荷作分离?如何将有效载荷向上交付?
4位首部长度和TCP一样,都代表报头的长度大小,单位是4字节,4bit位最大是15,所以IP报头的大小范围是20~60字节,IP与TCP一样,都有自己的头部选项,封装报文时,选项可以带可以不带,具体看需求而定。同时IP还有16位总长度字段,所以只需要读取4位首部长度×4个字节的值,就可以拿到IP报头,然后再用16位总长度-IP报头的长度就可以拿到有效载荷,通过这两个字段值,就可以将报头和有效载荷作分离了。
8位协议表示网络层的上层的协议类型,ICMP协议是0000 0001,TCP是0000 0110,UDP是0001 0001,通过8位协议就可以将有效载荷向上分用,交付给上层协议了。

为什么UDP和IP一样,头部字段里面都有16位总长度呢?同时UDP和IP的报头长度也都能确定,UDP是定长,IP有4位首部长度。
其实道理很简单,因为UDP是面向数据报,同时IP也得是个报文啊,IP又不是面向字节流的,所以就算IP上面是TCP,你TCP玩的再花,再怎么面向字节流,到了IP这一层你也得是一个报文啊!IP发出去后,对端接收时,要确定出IP报文的有效载荷是多少啊!确定好之后,才能将有效载荷向上交付给TCP层,而到了TCP层,他这个时候才会不关心有效载荷是多少,因为TCP是面向字节流的,如何解释有效载荷,话语权在应用层。
所以,区分UDP面向数据报和TCP面向字节流,可以从协议头部字段能否得出有效载荷大小 和 通信socket接口2个方面来理解
(1)如果是面向数据报,那就要做到将有效载荷与报头分离后,能够将有效载荷准确的交给上层协议,因为你是一个报文!必须做到将一个完整报文的有效载荷,准确交付给上层。双方之间的数据包是一对一的。
如果是面向字节流,分离报头与有效载荷之后,可以不用将有效载荷准确的交给上层,只需要将有效载荷先放到接收缓冲区中,至于接收缓冲区中积压了多少有效载荷,从而导致无法区分哪个有效载荷是哪个报文的,这点TCP并不关心,这不是TCP要解决的事情,如何解释这些有效载荷的话语权在于应用层。
(2)recv/send 和 recvfrom/sendto不仅仅可以看出TCP是面向连接,UDP是无连接,还可以看出两者一个是面向数据报,一个是面向字节流。recvfrom和sendto每次发送和接收数据包时,都需要各自指出数据包的源socket地址和目的socket地址,这就是因为他们发送的报文是面向数据报的,每个报文到达对端后,都需要让对方的应用层能够准确的从传输层拿到一个完整报文的有效载荷,就像IP将一个报文的有效载荷准确的交给他的上层一样,这都是因为他们面向的是数据报,一个完整的报文,而不是字节流!
而recv和send发送数据时,是建立在有TCP连接的情况下进行通信的,所以send只需要将数据按照字节流的方式不停的塞到对端的接收缓冲区中即可,无须关心上层能否拿到一个完整报文的有效载荷,因为我是面向字节流的啊,你上层能不能拿到一份完整报文的有效载荷,关我什么事情?我只负责在我这一层将数据以字节流的方式进行疯狂传输即可!TCP的黏包问题什么的,我才不关心呢!又不是我TCP的问题,要解决那也是你应用层的事!

16位首部校验和由发送端填充,接收方在收到IP报文后,对IP报头(注意:仅检验头部)进行CRC校验算法,如果发现IP报头在传输过程中出现损坏,则直接丢弃该IP报文。
发送端的TCP层长时间没有收到应答报文时,会自动触发超时重传机制,重新发送滑动窗口内的一个或多个数据段。所以IP协议并不保证可靠性,至于数据在跨网络传输过程中出现不可靠的问题(比如丢包,数据段乱序,比特位翻转,数据段重复发送),IP协议并不关心,这些是TCP要解决的事情,比如提供数据段的排序+去重,超时重传......等等,而IP协议只负责跨网络发送数据即可。

8位生存时间TTL(Time To Live),指的是数据报在到达目的主机之前,允许经过的路由器跳数。
在复杂的网络拓扑结构中,没有人能够保证数据报一定能安全无误的到达目的主机,数据报有可能在路由的过程中不停的被环形转发(路由循环),那这样的报文其实就没有意义了,因为他永远无法到达目的主机,还有可能出现路径选择上的问题,比如数据报原本可以选择一条最优的路径进行到达目的主机,但可能由于某种网络环境原因,导致数据报绕了很长的路才会到达目的主机,浪费了很长的时间,那这样的报文其实也没有意义了,因为浪费了太长的时间,极大的降低了网络数据传输的效率,正确的做法就应该当网络环境良好时,重发数据报,让IP层选择一条最优的路径重新进行数据报的路由转发。
所以针对上面可能出现的问题,路由器应该具有直接丢弃报文的能力,而这种能力其实就体现在8位生存时间上,一旦IP报文在路由转发的过程中,经过的路由器跳数超出8位生存时间,那么路由器便可以直接放弃该报文的继续路由,直接丢弃掉它。
TTL的值一般被发送端设置为64,IP报文在转发的过程中,每经过一个路由器,该值就会减1,直到减为0的时候,路由器就会自动丢弃IP报文。在当今的网络环境中,一个报文在转发的过程中,最多也就经过8~9个路由器,了不起经过10几个路由器,一旦经过64个路由器,那就不用想了,一定是IP报文在转发的过程中出现了问题!所以TTL可以有效防止路由循环,以及网络中堆积大量无效报文的问题的产生。

之前我们当时写TCPsocket通信代码的时侯,服务器代码中bind的端口号,其实是交给了服务器主机的传输层,用于服务器对发送过来的消息,向上分用交给特定的进程。而客户端所指明的目的IP地址其实是交给了客户端主机的网络层,用于客户端主机将IP报文进行路由转发,跨网络发送到目的主机。

4位版本一般都是填0100,代表ipv4协议,由于ipv6和ipv4协议并不兼容,没有办法直接进行平替,所以4位版本的值都填4,基本没什么用,都是固定用法。
8位服务类型TOS(Type Of Service)指的是IP层能够根据应用层的不同需求,而提供的不同服务类型,包括一个3位的优先权字段,这个字段现在已经被忽略了,以及4位的TOS字段,和1位的保留字段(必须置为0),4位的TOS字段,其中最多只能有一个被置为1,分别表示最小延时,最大吞吐量,最高可靠性,和最小费用,应用程序可以根据实际的需求来设置TOS字段。
比如像ssh和telnet这样的登陆程序需要的是最小延时的服务,而文件传输程序ftp则需要的是最大吞吐量的服务。

三、网段划分

1.为什么要进行网段划分?(方便定位目标主机,以进行互联网对主机的分治管理)

举一个栗子,学校里面,会划分很多的学院,每个学院内又会细分很多专业,每个专业里面都有不同年级的学生,每个学生都有自己的学号,学校管理所有学生的前提是,先能够找到或者定位出任意一个学生,为了方便管理,学校会增设很多的职位,例如班长,院学生会主席,等等。
比如理学院的张三同学找到了一张学生卡,他一看就知道这张学生卡不是他们理学院的,因为我们知道学生卡的序列号都是经过设计的,前多少位代表院号,后多少位代表学生的专业号,后多少位代表专业里面学生的编号,入学年份等等信息,所以张三一看就知道这个学生卡不是他们学院的,张三此时就把学生卡交给他们院的学生会主席,各个院的学生会主席之间会有一个群,所以这张学生卡就被电子信息工程的学生会主席看到了,他一说这不就是我们院的学生卡吗?他就把这个学生卡拿到他们院,然后再向下问各个专业的主席,这张卡是哪个专业的,专业的主席认领之后,在本专业的群里面吼一声,谁的学生卡丢了?赶快过来认领来,最后李四就拿到了他的学生卡。

在上面的故事中,有一个非常重要的细节,就是查找学生卡所属的学生时,所做到的排除,当学生卡交给理学院的学生会主席时,我们立马可以排除掉除电子信息工程之外的其他所有学院,同时当学生卡交到电子信息工程的各个专业主席的群里时,立马就可以排除掉李四专业以外的其他专业,最后在李四专业的群里面,通过一个个学生的确认,最后让李四拿到自己的学生卡。
其实能够做到一下子排除一大批非目标的其他元素,主要就是因为分治思想,在宏观层面上做了很多的划分,互联网里面做了网段划分,国家里面做了很多省的划分,省内又做了很多城市的划分,城市里面做了各种区或县的划分,各个现或区又会做街道的划分,为什么要划分呢?其实就是为了方便国家能够快速定位一个人,从而对国家的人民进行管理。
对于互联网也是相同的,为什么要进行网段划分呢?其实就是为了方便互联网能够快速定位一台主机,因为每次确认主机时,排查的效率高,一次能够排除多个子网,这也是为什么IP要分为目标网络和目标主机的原因。

2.如何进行子网划分?

2.1 分类划分法(路由器组建局域网)

IP地址分为两个部分,网络号和主机号,路由器是组建局域网的重要设备,一个路由器最少都要桥接两个子网,所以路由器就会既在网段1,又在网段2,网段1和网段2的网络号一定是不能相同的,否则当路由器收到需要转发到自己所管理的网段内的报文时,就会出现问题!所以一个路由器桥接的各个网段,都需要有不同的网络号标识。一般路由器的ip,都是网络号.1,但并不是所有网段的路由器ip都是这样配置的,具体取决于网络管理员的设置和具体的网络拓扑结构。但一般家用的路由器都是这样配置的。


下面是我的笔记本连接移动热点时的ip地址,可以看到默认网关的地址是192.168.20.219,并不是所谓的网络号.1,可能是因为基站和路由器不太一样吧,毕竟基站那么大,分配管理ip地址的方式肯定也与路由器不同,自然网关的地址就不太一样了。

而所谓的不同网段,其实就是将网络号相同的主机放到一起,这群主机中要有一个出口路由器,这样就构成了一个网段。如果要在网段中新增一台主机,则新增的主机与其他主机必须网络号相同,但主机号必须唯一,通过合理的设置主机号和网络号,就可以保证在相互连接的网络中,每台主机的ip地址都不同,即使出现相同也没有关系,因为他们也有可能网络号不相同。
但手动管理子网内的ip是比较麻烦的一件事情,而有一种技术叫做DHCP(dynamic host configuration protocol)可以动态分配主机的ip地址,在一个子网中,管理子网ip地址的设备通常是路由器,而目前主流的路由器都自带了DHCP的功能。

分类划分法已经是比较老的划分方案了,这种网段划分的方案现在还有,但我们不作为重点研究,我们主要还是学新的网段划分方案,以子网掩码的方式来灵活划分网段。
下面这样的分类划分方案的粒度是非常粗的,就比如A类地址,很少会有一个局域网中存放167 77216台主机,这就是100多w台主机,所以申领A类地址的网段会很少,这类地址就被大量的浪费掉了,你说本来ipv4地址大概就43亿,全世界的人本来就不够分了,你还搁着浪费开了,这种子网划分的方案能好吗?其实申领最多的地址就是B类地址了,他划分出来的网段,每个网段内能存放65536台主机,所以就会大量的造成B类地址不足,A类地址浪费的问题,所以这种网段划分的方案不够灵活,很容易造成ipv4地址浪费的问题。
所以又有人提出了新的网段划分方案,CIDR。

2.2 CIDR(引入子网掩码)

CIDR(classless interdomain routing)无分类域间路由选择,指的是网段的地址在划分上是没有进行分类的,与传统分类划分法不同,CIDR将网络前缀调整为动态长度,具体表示为192.168.101.26/24,/前面的是ip地址,/后面的是子网掩码比特位为1的个数,代表ip地址的前24bit位是网络号,比如24如果转换成子网掩码,那就是255.255.255.0,/前面的是网络号192.168.101.0
实际上正确得到网络号的方式是,用ip地址与子网掩码进行按位与操作,按位与后的结果就是网络号,子网掩码的规则是从左到右比特位会由1逐渐变为0,通过调整从左到右bit位为1的个数,就可以动态调整网络号的比特位个数,自然主机号的个数也可以变化,所以调整网络掩码中从左到右bit位为1的个数,不仅能够进行网络号的调整,同时也能够调整网络号内主机的个数,极大程度上提高了ipv4地址的使用率,尽量不浪费ipv4地址。

下面我们来具体的划分两个子网ip地址,看看这两个子网的网络号和容纳的主机个数是多少。
例子1中,IP地址与子网掩码按位与之后,得到的结果就是140.252.20.0,这个结果就是网络号,子网掩码中后8位为0,则说明主机号就是这后8位,所以子网的地址范围为140.252.20.0 ~ 140.252.20.255,共有2^8次方种不同的主机号,也就是256种。
例子2种,子网掩码写成2进制的形式为11111111.11111111.11111111.11110000,所以网络号是前28个bit位,主机号是后4个bit位,ip地址和子网掩码按位与之后,得到的网络号结果为140.252.20.64,主机号只有4个比特位标识,所以共有16种主机号,子网的地址范围也就只有16个ipv4地址。

当ip地址与子网掩码按位与之后,主机号从全0到全1,就是这个子网范围内可供容纳的主机的ipv4地址个数,主机号为全0和全1都会用作特殊用途,全0保留下来不作为主机的ip地址进行分配,只作为网络号本身,全1也会保留下来不作为主机的ip地址进行分配,作为广播地址,向网络号所表示的网段内的所有主机进行广播消息,所有的主机都会收到。
像127.0.0.1这样的本地环回地址也不能在公网中进行使用,这种地址只作为本主机本身进行使用。所以2^32次方个地址并不是所有地址都会被用在公网中,有些IP是用作特殊用途的。

下面是我的windows主机查看到的无线LAN技术下,我的主机的ipv4地址和子网掩码,通过子网掩码就可以看到主机号为8个比特位,网络号为24个比特位,所以该网段中理论上能够支持的最大主机数是256台,这也是大部分家庭无线局域网中的支持数,但256个ipv4地址,并不是都会被该网段内的主机进行使用,部分地址是具有特殊用途的!比如网络号和广播地址,是会被保留下来,不被分配给局域网中的主机的。
按道理来说,我的电脑不应该有公网ip啊,我怎么会查到公网ip呢?我的电脑是在我家的局域网中啊,这个下面讲私有ip和公网ip就会明白了,实际上局域网主机查看的公网ip是局域网与公网通信时,与公网相连的路由器的WAN口IP。

四、私有IP和公网IP

1.IP地址的数量限制 和 私有IP地址划分

我们知道ipv4地址是4字节32位的整数,也就是说,ipv4地址的绝对上限也就是42亿9千万,如果真要是给全世界的入网设备各分配一个ip地址的话,那绝对是不够的!并且,ip地址也不是按照入网主机来分的,而是按照入网主机上面的网卡来分的,每个网卡都至少要有一个ip地址,一般情况下一张网卡都只配有一个ip地址,但如果你想给网卡配置多个ip地址这也是可以的,比如你想让这块网卡与多个子网(网段)进行通信。市面现在主流的笔记本计算机,都会配有两张网卡集成在主板上,一张是有线网卡,一张是无线网卡,所以如果真要是给每个入网设备都分配一个全球唯一的ip地址,那绝对是远远不够的!

有人可能会说,我们不是有CIDR技术吗?CIDR技术解决的是网段划分,他只是能够提高ip地址的使用率,不要出现某些ip地址被浪费的情况,但他并不能提高ip地址数量的绝对上限,所以他还是无法解决ip地址不够的问题。

常见解决和缓解ip地址不够的方式大概有3种
(1)DHCP,动态分配ip地址,只给入网的设备分配ip地址,电脑没有连接到网络时,一定是没有ip地址的,这在一定程度上也可以缓解ip地址不足的问题,但作用确实也是聊胜于无
(2)目前的互联网环境中,真正解决ip地址不够的主流方式还是通过NAT技术和私有ip的划分,NAT技术能够将私有ip地址转换为公网ip地址,然后进行访问公网上的服务,私有ip地址是可以大量重复的,在转换后,一定会出现的情况就是多个局域网主机共用一个公网ip来访问公网上的服务,NAT技术很重要,我们后面会详细去讲,这里先简单概述一下。
(3)最直接的方法就是使用ipv6协议,提升ip地址数量的绝对上限,但现在由于种种原因,我国的ipv6技术还没办法推广到全球,但国内现在已经有很多的公司内部开始使用ipv6协议了,在访问公网的时候,再将ipv6地址转换为ipv4地址,或许在未来的某一天我们能看到ipv6被推广到全球使用,但现在我们还是先好好学ipv4吧

ip地址相当于一块大蛋糕,私有ip划走了一部分,公网ip划走了一部分,还有一部分ip不给用户用,只能给网络的中间节点使用,例如路由器,基站等。
局域网是不直接连接到公网上的,所以理论上局域网使用任意的ip地址都可以,但RFC1918规定了组建局域网时,只能使用的ip地址,我们将这些ip地址称为私有ip,之前我们说ip地址具有唯一性,指的是公网ip具有唯一性,内网ip是可以重复的,这正好能解决ip地址不足的问题,因为大量的局域网主机都使用的是重复的内网ip地址。
内网ip地址可划分为三类:
(1)10.* ,前8位为固定网络号用法,共1677,7216个地址,后面的24个比特位,可以通过子网掩码来划分出具体的网络号位数和主机号位数(公司内网的ip该类地址比较常见)
(2)172.16. *到172.31. *,同样的,前16位为固定网络号用法,共104,8576个地址,后面的16个比特位,可以通过子网掩码来划分出具体的网络号位数和主机号位数(公司内网和学校中,该类ip地址比较常见)
(3)192.168. *,同样的,前16位为固定网络号用法,共65536个地址,后面的16个比特位,可以通过子网掩码来划分出具体的网络号位数和主机号位数(家庭中的ip地址一般这类比较常见)
第二类网络地址的个数是第三类的16倍,因为第二类ip地址固定网络号开头的种类有16个。
算一下私有ip大概有2亿,公网ip有41亿个

下面是我的云服务器上的私有ip和公网ip的截图,可以看到我的这台云服务器有自己的私有ip10.0.8.2,以及公网ip43.142.224.5,子网掩码为FF FF 1111 1100 0x00 0x00,后10个比特位为主机号,表示该子网内最多可容纳1024个ip地址,前22个比特位表示网络号10.0.8.0
我的这台云服务器上部署了很多服务,例如SSH服务(22端口号),FTP服务(21),SFTP服务(22),TELNET(23)服务等,SFTP服务通常使用SSH的默认端口22进行连接,与传统FTP相比,SFTP更安全。
云服务器一般会配置多块网卡,以支持多个ip地址,例如在公网中使用的公网ip和腾讯内网中使用的内网ip

2.内网数据包发送到公网的过程

2.1 NAT技术

下面是内网的数据报发送到公网上的服务器的过程,从这张图当中我们能够得到很多信息,也将理清很多容易混淆的概念。
(1)相互连接的两个网段的网络号是不能相同的,不连接的两个网段的网络号是可以相同的
由于相互连接的两个网段是会有桥接设备路由器的,当路由器收到来自外边的数据报要转发到他所管理的各个网段中的某一个网段时,如果这些网段的网络号都相同,那路由器就无法路由转发该数据报,而路由器在组建局域网的时候,是绝对不会这样组建的。
如果两个网段之间并没有任何桥接设备,他们各自用的路由器并不相同,就像下面左侧的两个网段,两个网段的网络号都是192.168.1.0,但两个网段并没有连接,并不会出现各自的路由器不知道该将数据报发送到哪个网段的情况,因为两个路由器各自都管着自己的家庭局域网,之间并没有交集,所以这两个家庭局域网的网络号是允许相同的。
(2)家用路由器桥接了两个子网,一个是家庭中的局域网,一个是与运营商路由器和其他路由器之间构建的局域网
像路由器这样的设备,一定是桥接了很多的子网的,所以路由器会配有多块网卡,每个网卡对应不同的子网接口,能够与不同的网段建立连接。
图中的路由器桥接了两个网段,则路由器在这两个网段中就应该各自有该网段中的ip地址,比如网段1右边的路由器,他既有在家庭局域网中的内网ip192.168.1.1,又有路由器和路由器之间所构成的局域网的内网ip10.1.1.2,前者一般称之为Lan口ip,指的是路由器对内管理的局域网中自己的ip,后者一般称之为Wan口ip,指的是路由器对外,自己作为局域网中的一台主机时,自己的ip。
像网段1右边的运营商的路由器同样也有自己的Lan口ip和Wan口ip,对内管理着其他路由器,所以Lan口ip是10.1.1.1,对外自己作为局域网中的一台主机,有着自己的对外的公网Wan口ip122.77.241.4
(3)不同的私有ip是可以一样的
不同的网段,如果是相互不桥接的网段,出现相同的私有ip是一件非常正常的事情,只要保证他们各自可以访问公网就可以了,这也是一定能够做到的,因为他们是互不连接的网段,之间并不会影响。
如果是相互桥接的网段,两个网段的网络号是不能重复的,同时也不能出现相同的私有ip,比如网段1的网络号是192.168.0.0,网段2的网络号是192.168.101.0,如果两个网段出现了相同的私有ip,则会导致连接两个网段的桥接路由器在转发来自外部报文时,出现不知道发给谁的问题,比如两个网段中,同时存在192.168.101.26ip地址,那当路由器在确定该ip地址应该去的网段时,网段1和网段2都能去,因为他们各自的子网掩码是FF FF 00 00和FF FF FF 00,一旦按位与后,两个网段都应该去,这一定是错误的!
所以相互桥接的网段中,各自是不能出现相同的私有ip的!路由器是可以控制这件事情不发生的,因为路由器具有DHCP的功能。
从所有的局域网的角度来看,无论各个局域网之间是否相互桥接,我们都是可以大面积,高频率的使用那三类私有ip地址的,所以,这就可以解决ipv4地址不足的问题,因为大量的私有ip地址可以在不同的网段中被重复的使用,这样就可以省下很多的公网ip留到公网环境中去使用,比如给大型的互联网公司这些公网ip,让各个局域网的主机都能访问到公网的同一台的服务器。


2.
局域网中的数据包在发送到公网的过程中,其实是要做源ip地址替换的,像图中绿色的箭头就是数据包的流向,这里我就先不说路由选择的事情,直接先说数据报跳到下一跳后,下一步要做的工作,至于路由选择的事情,后面会详细去讲。当数据报从客户端主机发出去的时候,会到达自己所在局域网的出口路由器,也就是图中的家用路由器,然后数据报中的IP层的源ip字段就会被路由器替换为路由器自己的Wan口ip10.1.1.2,当路由器将数据包发送到运营商路由器时,运营商路由器会将IP报头中的源ip字段又替换为自己的Wan口ip122.77.241.4,最终由运营商路由器将报文发送给公网上的服务器主机122.77.241.4。
这样不断将数据报中的源ip替换为路由器的Wan口ip的技术,其实就是NAT技术,而ip地址不足的问题能够得以解决,主要是有两个原因,一是通过局域网划分是私有ip大量重复的使用,二是私有ip可以通过NAT技术访问到公网上的服务器,如果往细说一说的话,其实能够让私有ip访问到公网上的服务器,并不是因为NAT技术,而是因为目的ip和网络中每个节点的路由表,NAT技术配套的还有NAPT技术,如果没有NAT技术就没有NAPT技术,所以说NAT实际上解决的是数据包如何从服务器返回的问题,并不是数据包如何从内网转发到公网。

2.2 产生的疑问

1.一个路由器可能会维护多个Lan口ip或多个Wan口ip吗?

一个路由器会桥接不同的子网,那路由器就会同时处于不同的网段中,在这些网段中,路由器都要有自己的ip地址,且这些ip地址都不能相同,同时路由器既可能对内管理多个网段,也可能对外同时处于多个路由器组建的局域网,所以路由器可能会维护多个Lan口ip或多个Wan口ip,在配置路由器时,只要给路由器多配几块网卡就可以实现路由器的多ip地址了。

2.为什么NAT技术要不断作源ip的替换?

如果不进行源ip的替换,就无法看到局域网主机的公网ip地址了,虽然源ip的替换无法解决数据报路由到下一跳的问题,因为这个问题实际上是由目的ip+节点路由表的方式来解决的,但源ip替换的价值其实并不是体现在数据包路由上的,而是解决ipv4地址不足的问题,它能够让不同的局域网主机共享一个公网ipv4地址。

多个局域网主机共享一个公网ipv4地址:这个功能是NAT最大的价值,这可以解决ipv4地址不足的问题,因为它可以让许多的私有ip共享同一个公网ipv4地址。

隐藏内部网络拓扑结构:这个作用可以算是捎带的,看一下就好,时间长了肯定也会忘,但上面的功能是NAT技术最大的价值,一定要记住!当内部设备通过 NAT 路由器访问互联网时,外部网络只能看到 NAT 路由器的公网IP地址,而无法直接获知内部设备的真实IP地址,这样就隐藏了内网的拓扑结构。

3.为什么局域网的主机还可以查到公网ip?

其实局域网中的所有主机都可以查到自己的公网ip,就像下面图中左侧四台客户端主机的公网ip就是运营商路由器的Wan口ip122.77.241.4/24,因为他们所处的局域网数据包最终转发到公网时,他们的源ip都会被替换成连接公网的路由器的Wan口ip,所以这些局域网的主机会共享同一个公网ip。同时右侧的两台客户端主机也是如此,他们也会共享同一个公网ip,正是右侧连接公网的路由器的Wan口ip122.77.241.5/24

4.局域网的主机会共享一个公网ip吗?

这是一定会发生的!因为我们知道ipv4地址不足的问题能够得以解决,靠的就是组建局域网的ip地址是能够大量高频率被使用的私有ip地址,那么这些私有ip地址对应的主机是一定要能够访问到互联网的,那么也就是说这些主机一定要有配套的公网ip,公网ip只有大概42亿,所以许多的局域网的主机一定会共享一个公网ip的。

下面是我的移动设备手机和笔记本连接我家的局域网时,查到的公网ip,左侧是通过windows命令行curl 4.ipw.cn和一个查询公网ip的网址查出来的结果,右侧是我的手机用同样的查询公网ip的网址查出来的结果,可以看到我的移动设备的公网ip和笔记本相同,我还看了我姐的手机和我妈的手机,他们的公网ip和我的两个设备的公网ip全部相同!

5.两个局域网中的主机能否直接进行通信?

两个局域网中的主机无法通过发送数据报的方式直接进行通信,因为我们说过目的ip大部分情况都得是公网ip,而局域网中的主机会存在大量重复私有ip的情况,如果数据报的目的ip是私有ip的话,那数据报其实是不知道该去哪里的,因为网络中可能很多的局域网中都有主机的地址是数据报的目的私有ip,所以我们称下面的两个局域网中的主机是没有办法直接进行通信的。

6.那我和我的朋友用QQ聊天是个什么逻辑呢?我和我的朋友都在局域网里面啊!

其实之前我们写过一个UDP版本的聊天室的socket通信代码,他的逻辑和QQ是类似的。

我们用的QQ的客户端和服务器端都是腾讯公司写的,当我们登录QQ时,其实就是本地的客户端进程和腾讯的QQ服务器建立了TCP连接,如果你此时给你的朋友发消息,其实你并不是直接将消息发送给你的朋友了,消息一定是先被发送到了腾讯的服务器上,然后再由腾讯的服务器把你发送的消息推送给你的朋友,如果你的朋友是在线状态,也和服务器建立了连接,那你的朋友会立刻收到由服务器转发的消息,如果你的朋友不在线,那服务器会先缓存这批消息,等下次你的朋友上线的时候,服务器会将之前你发的消息,再推送给你的已经上线的朋友。

推送的方式也很简单,只要你登录了QQ,服务器就与你建立了连接,那么你们之间就有通信可以使用的sockfd1,服务器只需要将你的消息暂存到一个buffer里面,当你的朋友与服务也建立连接之后,你的朋友的QQ客户端和服务器也会有通信时使用的sockfd2,服务器只需要将buffer中的数据通过调用send接口和sockfd2参数,就可以将消息推送到你的朋友那里。

所以我们说局域网中的客户端之间是无法直接进行通信的,必须由公网上的服务器主机来间接实现你们俩之间的通信,例如图中两台主机都和服务器建立了TCP连接,此后两台主机之间的通信,服务器就做一个消息的中转站,将消息发送给你的朋友。所以从一台客户端主机到服务器,和从服务器回到这台客户端主机,这条路径是具有唯一性的,但客户端主机之间是不具有唯一性的,因为他们的ip地址都是私有ip。

总结:
(1)内网中的数据包能够发送到公网,靠的是目的ip和网络中每个节点的路由表。
(2)NAT进行源ip的替换是为了解决ipv4地址不足的问题,能够让局域网中的主机共享一个ipv4地址
(3)数据包从公网服务器返回的时候,靠的是NAPT,不仅仅做源ip的替换,同时也会做port的替换,达到数据包能够从内网发到公网上的服务器,也能够回来的目的。

五、IP报文的路由(目的IP+节点的路由表)

路由是IP层最重要的核心工作,路由其实就是一跳一跳问路的过程,在问路的过程中,我们要去哪里当然是最重要的,而去的地方其实就是目的ip,目的ip大部分情况下都是公网ip,如果是内网ip,很可能是只在内网环境下进行内网主机之间的通信,比如军网,为了保证军网的网络安全以及敏感信息的安全性,军网会与互联网隔离,军网内部通信通常使用的就是私有ip地址,也就是内网ip,这些报文只会在军网内部进行转发,不会在互联网中路由。
回到主题上来,报文在进行路由时,其实就相当于问节点,我要去目的主机,下一步我应该怎么走?网络中的每个节点都有自己的网络层,都会维护一张路由表,每个节点在查询路由表之后,节点就会告诉数据包怎么走,一般节点的查询结果会有两种,一种是告诉数据包,你下一步到那个路由器A就可以了,后面怎么走我就不知道了,你到时候问A就可以了,另一种是,我也不知道你该怎么走,但你可以去问问那个路由器B,看看他知不知道怎么走,而这两种情况实际就分别对应了路由表的两种查询结果,一种是节点确实知道数据包的下一跳位置应该在哪,另一种是节点虽然不知道,但节点有默认网关,节点会自动把数据包交付到默认网关的位置。

其实IP层进行数据包的路由,最重要的两个部分就是目的ip和路由表。
我们先来看看路由表的大概组成部分和每个条目,需要说明的是,不止路由器有路由表,网络中的任何一个节点都有路由表,因为主机和路由器都有自己的网络层。
首先我自己的云服务器主机就有路由表,这个路由表很简单,只有三个条目,第一个条目其实就是前面我们所说的默认网关,Destination是指网络目标,指的是该台主机将把数据包送到哪个主机上,Gateway指的是网关地址,一般与本地网卡直接相连的网络是不需要网关地址的,本地网卡可能有多块,所以有许多直接相连的网络是不需要网关这一字段的,直接通过网卡对应的接口将数据包发送到直接相连的网络中即可。第二个条目其实就是我的云服务器所在的网段,网卡的接口本身就与该网段相连,第三个条目是链路本地地址,一般用在本地局域网中,他和本地环回地址是不一样的,本地环回是用于本主机的,链路本地地址可用于本地局域网中的各个设备之间的通信,链路本地地址是根据设备的MAC地址生成的,无须依靠DHCP,通常是自动配置的。

下面的两个网络接口分别对应了我的无线和有线网卡

下面来看一下,一个具体的路由表是如何将数据报进行转发的。
当路由器收到一个要转发和路由的数据报时,路由器会遍历自己的路由条目,拿目的ip地址与路由条目中的Genmask按位与,拿按位与的结果与Destination对比,如果相同,则从该条目对应的Iface接口将该数据报转发到Destination里面。
例1:目的ip192.168.56.3和255.255.255.0按位与后的结果与第一个路由条目不匹配,则继续向下遍历下一个路由条目,发现匹配后,则将数据报从eth1接口发到目标主机192.158.56.3上,因为目标网络192.168.56.0与当前主机直接相连,所以不需要通过路由器转发,可以直接发送到目的主机上
例2:目的ip202.10.1.2可以说与前三个路由条目都不匹配,直到与最后一个路由条目的Genmask按位与后,发现按位与的结果是0.0.0.0,此时将数据报按照缺省路由条目,从eth0接口发出去,目的地址是192.168.10.1,这个地址就是该局域网的出口路由器,而下次的下一跳位置再由出口路由器继续查自己的路由表进行决策。
数据报要去的目标网络号其实是在不断的变化的,因为路由过程中经过的每个局域网的子网掩码都是不同的,我们需要确定的是当我们的ip地址与子网掩码按位与后,是否与该条目的Destination相同,如果相同,则将该报文发送到目标网络即可,所以数据报在传输过程中,目标网络号由于每个局域网的Genmask不同,也会不断的变化。

在这张路由表中,可以看出Iface接口有三个,两个以太网接口,一个本地环回接口,所以可以看出,这张路由表对应的主机或路由器,其实是同时处在两个局域网当中的,一个是192.168.10.0,一个是192.168.56.0,在这两个不同的网段,该节点(喜欢把主机或路由器统称为节点)会各自有不同的私有ip地址,由此可见该节点很可能配有两块网卡,分别对应eth0和eth1接口,与两个不同的网段直接相连。默认网关的Flags一般为UG,U代表正在使用,G代表gateway默认网关的意思,默认网关最典型的特征就是Genmask为全0

六、IP报文的分片

1.MTU对IP协议的影响

真正在路由器和之间传递的确实是IP报文,但在一个局域网内部,真正传输的是MAC帧,这也就意味着,真正在局域网内传输的是数据帧,也就是说每个局域网内都会按照数据帧的方式传输,到达局域网中的下一跳位置后,下一跳位置的网络层来决定下下一条的位置应该在哪,确定好之后,在下一个网段中继续用数据帧来传输,所以真正在网线上跑的是数据帧,而不是IP报文。
而数据链路层有MAC帧协议,常见的就是以太网协议,以太网有规定,MAC帧的有效载荷不能超过MTU(maximum transmisson unit 最大传输单元)1500字节,IP报文能够决定传输数据的大小吗?并不能,控制传输数据大小的是TCP,TCP是面向字节流的,它可以控制什么时候发送数据,发送的时候发送多少,这也就是为什么滑动窗口中有多个数据段,而不是一个数据段,因为MTU会限制单个数据包的有效载荷不能超过1500字节,所以TCP在通过滑动窗口发送数据段时,都是发送多个数据段,而不是将多个数据段合并成一个大数据段进行发送。
而TCP的头部选项kind=2字段,就提供双方协商MSS(maximum segment size最大段大小)的大小的字段,这个MSS大小一般都是MTU-40,也就是1460字节,减去的40其实就是TCP报头和IP报头的大小。

但如果TCP不控制单个数据段的大小,就是要超过1460字节,甚至要发送3000字节的数据,那IP该怎么办呢?IP说,MAC帧不让我向下交给他的数据报超过1500字节,你TCP现在又给我这么大的数据段,这不就是为难我嘛!
TCP就好比公司的领导,IP相当于项目经理,MAC层相当于程序员,公司领导说我们现在要做和微信差不多的聊天软件,我们要和腾讯抢饭碗,你赶快去做吧,项目经理,项目经理对程序员说,领导说了,他让你们做微信,你们赶快做吧,程序员说,我们做不了,建议另请高明,项目经理就是个两头受气的受气包,领导让他这么干,结果底层程序员说我们干不了,那项目经理是不是就要有自己的解决措施呢?比如跟底层程序员说,我知道咋们这个项目难度非常大,我向大家保证,我们把项目的进度划分的细一些,你们每做完一个进度,我就向上层汇报,去和财务领奖金,奖金一定丰厚,等项目成功落实下来,我再自掏腰包给兄弟们每人包一个红包,再向公司领导申请,给兄弟们免费放3天假好好放松一下,你们看怎么样?程序员一听,又给钱又给假,抄起键盘就是干,别说微信了,重新再给咋们公司做个浏览器都行(开玩笑的哈),一定给公司卖命!

上面是生活的例子,而真实的IP层是怎么解决的呢?IP层会将TCP交给他的数据段进行分片,当对端收到后,会在自己的IP层对分片的一个个报文重新进行组装。
所以IP的分片与组装是IP层自己的行为,与上下层之间是解耦的,数据链路层和传输层压根就不关心,这纯纯就是IP自己的行为。

2.如何进行分片与组装?

其实解决如何要进行分片与组装,可以将这个问题细分为几个小问题,解决这些小问题之后,这个大问题自然就迎刃而解了。
(1)如何判断一个报文被分片了?
3位标志中,第一个字段被保留,第二位表示禁止分片,第三位表示更多分片标志位,设置第三位为1,则该报文被分片,分片的报文中,除了最后一个分片,其他分片都是1,只有最后一个分片的第三位是0。
所以当收到一个报文时,如果该报文的更多分片标志位是1,那说明这个报文是一个分片报文,如果该报文的更多分片标志位是0,但同时他的13位片偏移大于0,则说明这个报文是分片中的最后一个分片报文
(2)同一个报文的分片怎么能够识别出来?
16位标识,同一个数据报的所有分片都会具有相同的16位标识,因为这些分片原来都是出自一个数据报的,每个数据报都有自己的16位标识字段,自然一个数据段被分片后,每个分片的标识字段的值都是相同的。
(3)分片哪个在前,哪个在后,有没有收全或丢失?
13位片偏移,表示该报文的有效载荷在原来的数据段中的偏移量,所以我们可以通过偏移量的大小和报文自身的长度来确定分片的前后顺序以及是否收全或丢失
(4)如何将分片的报文重新组装为一个完整的报文?
将受到的分片报文可以先按照偏移量的大小做一个升序排序,后一个报文的偏移量大小,应该等于之前一个或多个报文在原来数据段中的长度之和。
(5)怎么保证我组装的报文是正确的?
把收到的分片合起来之后,合起来的报文的首部有16位IP头部校验和,除此之外,去掉IP报头向上交付后的TCP报文段的TCP首部也会有自己的16位头部校验和,通过这些校验和就可以判断组装好的报文是否是正确的。

3.分片对TCP和UDP的影响?(增大整体丢包概率)

首先需要说明的是分片不好,尤其是在跨网络通信的时候!这也是他并非主流情况的原因。
大部分情况下传输层向下交付的报文段是不会超过MTU-40的,同样IP报文也不会进行分片与组装。所以大部分情况领导是好领导,程序员是好程序员。

一个报文被拆分成了多个分片,如果在传输过程中,某一个分片丢失了,则会导致接收方组装分片失败,则接收方会直接丢弃收集到的所有分片,而发送方的TCP层长时间发现没有确认应答报文段,则会触发超时重传机制,重传时又会重新发送一整个报文段,该报文段在接收方的网络层又会进行分片,重新进行IP分片与组装的过程。
所以假设一个报文段到达对方的概率是99%,如果该报文段被分成了三片,那么整体丢包的概率就是99%×99%×99%大概是97%的概率,所以分片会增加整体丢包的概率,一般不建议传输层发送过大的报文段,应该合理的设置MSS字段的大小,在通信三次握手时就协商好该字段值

下面我们来分一个具体的IP报文,假设一个IP报文是3020字节大小,分出来的第一个分片就是1500,同时这个分片包含了原有IP报文的前20字节头部字段,第二个分片的有效载荷只能是原有IP报文的有效载荷中的1480B,然后再给他封装好新的IP头部,所以此时原有的IP报文只剩下最后的40B,那就只需要再进行一次分片即可,也给最后的40B前面封装好新的IP头部。

相关推荐
幻想编织者33 分钟前
Ubuntu实时核编译安装与NVIDIA驱动安装教程(ubuntu 22.04,20.04)
linux·服务器·ubuntu·nvidia
利刃大大1 小时前
【Linux入门】2w字详解yum、vim、gcc/g++、gdb、makefile以及进度条小程序
linux·c语言·vim·makefile·gdb·gcc
飞行的俊哥7 小时前
Linux 内核学习 3b - 和copilot 讨论pci设备的物理地址在内核空间和用户空间映射到虚拟地址的区别
linux·驱动开发·copilot
幽兰的天空9 小时前
介绍 HTTP 请求如何实现跨域
网络·网络协议·http
lisenustc9 小时前
HTTP post请求工具类
网络·网络协议·http
心平气和️9 小时前
HTTP 配置与应用(不同网段)
网络·网络协议·计算机网络·http
心平气和️9 小时前
HTTP 配置与应用(局域网)
网络·计算机网络·http·智能路由器
hunter2062069 小时前
ubuntu向一个pc主机通过web发送数据,pc端通过工具直接查看收到的数据
linux·前端·ubuntu
不会飞的小龙人9 小时前
Docker Compose创建镜像服务
linux·运维·docker·容器·镜像
不会飞的小龙人9 小时前
Docker基础安装与使用
linux·运维·docker·容器