在下方公众号后台回复:【网络安全】,可获取给你准备的最新网安教程全家桶。
IP 协议
IP 协议全称为 “网际互连协议(Internet Protocol)”,IP 协议是 TCP/IP 体系中的网络层协议。
基本概念
网络层解决的问题
TCP 作为传输层控制协议,其保证的是数据传输的可靠性和传输效率,但 TCP 提供的仅仅是数据传输的策略,而真正负责数据在网络中传输的则传输层之下的网络层和链路层。
双方在进行网络通信时,发送的数据并不是直接从一方的传输层直接发送到了另一方的传输层,而是需要传输层将数据继续向下进行交付,在网络层和链路层经过数据封装后再通过网络发送到对方主机,对方主机收到数据后也同样需要在链路层和网络层进行数据解包,此时对方的传输层才拿到了发送过来的数据,然后再继续将该数据向上进行交付。
网络通信的过程,就像两个人在送互相送数据,这两个人分别在两栋楼的四楼,如果一个人要将数据交给对方,那么这个人就必须先从四楼走到一楼,然后再在路上经过路径选择到达对方楼下,最后再上到四楼将数据交给对方。
其中,送数据的这个人从四楼下来的过程就是数据封装的过程,这个人在路上经过路径选择到达对方楼下的过程就是数据路由的过程,而这个人再上到四楼将数据交给对方的过程就是数据解包的过程。
而网络层要解决的问题就是,将数据从一台主机送到另一台主机,也就是数据的路由。
保证数据可靠的从一台主机送到另一台主机的前提
当双方在进行基于 TCP 的网络通信时,要保证将数据可靠的从一台主机送到另一台主机,前提是发送方要有将数据送到对方主机的能力,要是发送方连将数据发送给对方的能力都没有,那就更不用谈可靠的将数据送给对方主机了。
也就是说,在网络层有能力将数据送到对方主机的情况下,虽然网络层不能保证每次都能将数据成功送到对方主机,但在 TCP 提供的可靠性策略的保证下,最终网络层就一定能够将数据可靠的发送到对方主机。
说明一下:
路径选择
数据进行的网络传输一般都是跨网络的,而路由器就是连接多个网络的硬件设备,因此数据在进行跨网络传输时一定需要经过多个路由器。
数据路由就像我们旅游一样,当确定了要到达的目标主机后,就需要寻找最短的路径到达该目的地。
确定数据路由的目的地后,数据就可以在网络中进行路由了,但数据在路由时无法自行进行路径选择,因为这个数据本身是 “不认识路” 的,因此数据在路由的过程中需要不断 “找路人问路”,而这里所谓的“路人” 就是网络当中的一台台路由器。
网络当中的路由器是 “认识路的”,它们将自己的“认路经验” 都记录到路由表当中,因此路由器可以通过查路由表找到去特定点的最短路径。因此数据在路由时,会不断通过路由器来进行路径选择,以此来一步步靠近目标网络或目标主机。
主机和路由器
IP 协议格式
IP 协议格式如下:
IP 报头在内核当中本质就是一个位段类型,给数据封装 IP 报头时,实际上就是用该位段类型定义一个变量,然后填充 IP 报头当中的各个属性字段,最后将这个 IP 报头拷贝到数据的首部,至此便完成了 IP 报头的封装。
IP 如何将报头与有效载荷进行分离?
IP 分离报头与有效载荷的方法与 TCP 是一模一样的,当 IP 从底层获取到一个报文后,虽然 IP 不知道报头的具体长度,但 IP 报文的前 20 个字节是 IP 的基本报头,并且这 20 字节当中涵盖 4 位首部长度。
因此 IP 是这样分离报头与有效载荷的:
IP 就是通过这种 “定长报头 + 自描述字段” 的方式进行报头和有效载荷的分离的。但需要注意的是,IP 报头当中的 4 位首部长度描述的基本单位与 TCP 报头当中的 4 位首部长度一样,都是以 4 字节为单位进行描述的,这也恰好是报文的宽度。
4 位二进制的取值范围是 0000 ~ 1111,因此 IP 报头的最大长度为15 × 4 = 60 15\times 4=6015×4=60 字节,因为基本报头的长度是 20 字节,所以 IP 报头中选项字段的长度最多是 40 字节。如果 IP 报头当中不携带选项字段,那么 IP 报头的长度就是 20 字节,此时报头当中的 4 位首部长度字段所填的值就是20 ÷ 4 = 5 20\div 4=520÷4=5,即 0101。
IP 如何决定将有效载荷交付给上层的哪一个协议?
基于 IP 协议的传输层协议不止一种,因此当 IP 从底层获取到一个报文并对其进行解包后,IP 需要知道应该将分离后得到的有效载荷交付给上层的哪一个协议。
在 IP 报头当中有一个字段叫做 8 位协议,该字段表示的就是上层协议的类型,IP 就是根据该字段判定应该将分离出来的有效载荷交付给上层的哪一个协议的。该字段是发送方的 IP 层从上层传输层获取到数据后填充的,比如是上层 TCP 交给 IP 层的数据,那么该数据在封装 IP 报头时的 8 位协议填充的就是 TCP 对应的编号。
32 位源 IP 地址和 32 位目的 IP 地址
IP 报头当中的 32 位源 IP 地址和 32 位目的 IP 地址,分别代表的就是该报文的发送端和接收端对应的 IP 地址。
数据在网络传输过程中会遇到一个个的路由器,这些路由器会帮助网络当中的数据进行路由转发,使得网络中的数据慢慢趋近于目标主机。路由器在帮助数据进行路由转发时,会提取出该数据的 IP 报头当中的目的 IP 地址,并以此作为数据路由转发的重要依据。
当接收端收到了发送端发来的数据后,接收端可能也想要给发送端发送数据,因此发送端在发送数据时除了需要指明该数据的目的 IP 地址,还需要指明该数据的源 IP 地址,也就是发送端的 IP 地址。即便接收端收到数据后没有数据想要发送给发送端,但至少接收端需要向发送端发送一个响应报文,表明发送端发送的数据已经被接收端可靠的收到了,因此发送出去的数据除了需要指明该数据的目的 IP 地址,还需要指明该数据的源 IP 地址。
理解 socket 编程:
8 位生存时间
报文在网络传输过程中,可能因为某些原因导致报文无法到达目标主机,比如报文在路由时出现了环路路由的情况,或者目标主机已经异常离线了,此时这个报文就成了一个废弃的游离报文。
为了避免网络当中出现大量的游离报文,于是在 IP 的报头当中就出现了一个字段,叫做 8 位生存时间(Time To Live,TTL)。8 位生存时间代表的是报文到达目的地的最大报文跳数,每当报文经过一次路由,这里的生存时间就会减一,当生存时间减为 0 时该报文就会被自动丢弃,此时这个报文就会在网络中消散。
分片与组装
数据链路层解决的问题
IP 能够将数据跨网络从一台主机送到另一台主机,而数据在进行跨网络传送时,需要经过一个个的路由器进行路由转发,最终才能到达目标主机。
比如要将数据从主机 B 跨网络传送到主机 C,那么主机 B 需要先将数据交给路由器 F,路由器 F 再将数据交给路由器 G,…,最终由路由器 D 将数据交给主机 C。
因此 IP 进行数据跨网络传送的前提是,需要先将数据从一个节点传送到和自己相连的下一个节点,这个问题实际就是由 IP 之下的数据链路层解决的,其中数据链路层最典型的代表协议就是 MAC 帧。
而两个节点直接相连也就意味着这两个节点是在同一个局域网当中的,因此要讨论两个相邻节点的数据传送时,实际讨论的就是局域网通信的问题。
最大传输单元 MTU
MAC 帧作为数据链路层的协议,它会将 IP 传下来的数据封装成数据帧,然后发送到网络当中。但 MAC 帧携带的有效载荷的最大长度是有限制的,也就是说 IP 交给 MAC 帧的报文不能超过某个值,这个值就叫做最大传输单元(Maximum Transmission Unit,MTU),这个值的大小一般是 1500 字节。
在 Linux 下使用ifconfig命令可以查看对应的 MTU。
由于 MAC 帧无法发送大于 1500 字节的数据,因此 IP 层向下交付的数据的长度不能超过 1500 字节,这里所说的数据包括 IP 的报头和 IP 的有效载荷。
分片与组装
如果 IP 层要传送的数据超过了 1500 字节,那么就需要先在 IP 层对该数据进行分片,然后再将分片后的数据交给下层 MAC 帧进行发送。
如果发送数据时在 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 报头字节数 | 数据字节数 |
---|---|---|---|
1 | 1500 | 20 | 1480 |
2 | 1500 | 20 | 1480 |
3 | 1500 | 20 | 1480 |
4 | 80 | 20 | 60 |
分片报文到达对方的 IP 层后需要被重新组装起来,因此 IP 层在对数据进行分片时需要记录分片的信息,而 IP 报头当中的 16 位标识、3 位标志和 13 位片偏移实际就是与数据分片相关的字段。
因此上述四个分片报文对应的 16 位标识都是一样的,假设四个分片报文的 16 位标识都是 123,则这四个报文对应的 16 位标识、3 位标志中的 “更多分片” 和 13 位片偏移分别如下:
分片报文 | 总字节数 | IP 报头字节数 | 数据字节数 | 16 位标识 | “更多分片” | 13 位片偏移 |
---|---|---|---|---|---|---|
1 | 1500 | 20 | 1480 | 123 | 1 | 0 |
2 | 1500 | 20 | 1480 | 123 | 1 | 185 |
3 | 1500 | 20 | 1480 | 123 | 1 | 370 |
4 | 80 | 20 | 60 | 123 | 0 | 555 |
需要注意的是,13 位片偏移当中记录的字节数是当前分片在原数据开始处的偏移字节数的值÷ 8 \div 8÷8 得到的,比如分片报文 2 在原始数据开始处的偏移字节数是 1480,其对应的 13 位片偏移的值就是1480 ÷ 8 = 185 1480\div 8=1851480÷8=185。
组装的过程
MAC 帧交给 IP 层的数据可能来自世界各地,这些数据可能是经过分片后发送的,也可能是没有经过分片直接发送的,因此 IP 必须要通过某种方式来区分收到的各个数据。
对于各个分片报文来说:
根据分片报文的这三个特点就能够将分片报文合理的组装起来。
分片报文丢包的问题
分片后的报文在网络传输过程中也可能会出现丢包问题,但接收端有能力判断是否收到了全部分片报文,比如假设某组分片报文对应的 16 位标识值为 x:
需要注意的是,未分片报文的 “更多分片” 标志位为 0,最后一个分片报文的 “更多分片” 标志位也为 0,但当接收端只收到分片报文中的最后一个分片报文时,接收端不会将其识别成一个未分片的报文,因为未分片的报文所对应的 13 位片偏移的值也应该是 0,而最后一个分片报文所对应的 13 位片偏移的值不为 0。
因此只有当一个报文的 13 位片偏移为 0,并且该报文的 “更多分片” 标志位也为 0 时,该报文才会被识别成一个没有被分片的独立报文,否则该报文就会被识别成一个分片报文。
为什么不建议进行分片?
虽然传输层并不关心 IP 层的分片问题,但分片对传输层也是有影响的。
需要注意的是,只要分片报文当中的某一个出现了丢包,此时传输层都需要将数据整体进行重传,因为传输层并不知道底层 IP 对数据进行了分片,当传输层发送出去的数据得不到应答时传输层就只能将数据整体进行重传,因此数据在发送时不建议进行分片。
如何尽可能避免分片?
实际数据分片的根本原因在于传输层一次向下交付的数据太多了,导致 IP 无法直接将数据向下交给 MAC 帧,如果传输层控制好一次交给 IP 的数据量不要太大,那么数据在 IP 层自然也就不需要进行分片。
MAC 帧的有效载荷最大为 MTU,TCP 的有效载荷最大为 MSS,由于 TCP 和 IP 常规情况下报头的长度都是 20 字节,因此一般情况下 MSS = MTU - 20 - 20,而 MTU 的值一般是 1500 字节,因此 MSS 的值一般就是 1460 字节。
所以一般建议 TCP 将发送的数据控制在 1460 字节以内,此时就能够降低数据分片的可能性。之所以说是降低数据分片的可能性,是因为每个网络的链路层对应的 MTU 可能是不同的,如果数据在传输过程中进入到了一个 MTU 较小的网络,那么该数据仍然可能需要在路由器中进行分片。
网段划分
IP 地址的构成
IP 地址由网络号和主机号两部分构成:
例如,下图中路由器连接了两个网段。对于网络标识来讲,同一网段内主机的网络标识是相同的,不同网段内主机的网络标识是不同的。而对于主机标识来讲,同一网段内主机的主机标识是不同的,不同网段内主机的主机标识是可以相同的。
不同的子网其实就是把网络号相同的主机放到一起。
如果在子网中新增一台主机,则这台主机的网络号和这个子网的网络号一致,但是主机号必须不能和子网中的其他主机重复。
DHCP 协议
实际手动管理 IP 地址是一个非常麻烦的事情,当子网中新增主机时需要给其分配一个 IP 地址,当子网当中有主机断开网络时又需要将其 IP 地址进行回收,便于分配给后续新增的主机使用。
当我们连接 WiFi 时需要输入密码,本质就是因为路由器需要验证你的账号和密码,如果验证通过,那么路由器就会给你动态分配了一个 IP 地址,然后你就可以基于这个 IP 地址进行各种上网动作了。
先找目标网络,再找目标主机
当 IP 要将数据跨网络从一台主机发送到另一台主机时,其实不是直接将数据发送到了目标主机,而是先将数据发送到目标主机所在的网络,然后再将数据发送到目标主机。
因此数据在路由时的第一目的并不是找到目标主机,而是找到目标网络所在的网络,然后再在目标网络当中找到目标主机。
数据路由时之所以不一开始就以找目标主机为目的,因为这样效率太低了。
因此,为了提高数据路由的效率,我们对网络进行了网段划分。
网段划分
过去曾经提出一种划分网络号和主机号的方案,就是把所有 IP 地址分为五类,如下图所示:
因此,各类 IP 地址的取值范围如下:
子网划分
但随着网络的飞速发展,这种划分方案的局限性很快就显现出来了。
为了避免这种情况,于是又提出了新的划分方案,称为 CIDR(Classless Interdomain Routing):
此时一个网络就被更细粒度的划分成了一个个更小的子网,通过不断的子网划分,子网中 IP 地址对应的主机号就越来越短,因此子网当中可用 IP 地址的个数也就越来越少,这也就避免了 IP 地址被大量浪费的情况。
因此一个数据在路由的时候,随着数据不断路由进入更小的子网,其网络号的位数是在不断变化的,准确来说其网络号的位数是在不断增加的,这也就意味着 IP 地址当中的主机号的位数在不断减少。最终当数据路由到达目标主机所在的网络时,就可以在该网络当中找到对应的目标主机并将数据交给该主机,此时该数据的路由也就结束了。
特殊的 IP 地址
并不是所有的 IP 地址都能够作为主机的 IP 地址,有些 IP 地址本身就是具有特殊用途的。
也就是说,IP 地址中主机号为全 0 的代表的是当前局域网的网络号,IP 地址中主机号为全 1 的代表的是广播地址,这两个 IP 地址都是不能作为主机的 IP 地址的。因此在某个局域网中最多能存在的主机个数是 2 主机号位数 − 2 2^{主机号位数}-22主机号位数−2。
本机环回基本原理
本机环回会将数据贯穿网络协议栈,但最终并不会将数据发送到网络当中,相当于本机环回时不会将数据写到网卡上面。
本机环回的目的就是将数据自顶向下贯穿协议栈,进行一次数据封装的过程的过程,然后再自底向上贯穿协议栈,进行一次数据的解包和分用,用于测试本地的网络功能是否正常。
本机环回的基本原理:
loopback 设备:
IP 地址的数量限制
IP 地址数量不足问题
我们知道,IP 地址(IPv4)是一个 4 字节 32 位的正整数,因此一共有2 32 2^{32}232 个 IP 地址,也就是将近 43 亿个 IP 地址。但 TCP/IP 协议规定,每个主机都需要有一个 IP 地址。
CIDR 虽然在一定程度上缓解了 IP 地址不够用的问题,因为 CIDR 提高了 IP 地址的利用率,减少了浪费,但 IP 地址的绝对上限并没有增加。
如何解决 IP 地址不足问题
解决 IP 地址不足有以下几种方式:
私网 IP 地址和公网 IP 地址
私网 IP 地址的种类
如果一个组织内部组建局域网,IP 地址只用于局域网内的通信,而不直接连到 Internet 上,理论上使用任意的 IP 地址都可以,但是 RFC 1918 规定了用于组建局域网的私有 IP 地址。
包含在这个范围中的,都称为私网 IP,其余的则称为公网 IP(或全局 IP)。
我们连接云服务器时,连接的这个 IP 地址就是云服务器的公网 IP 地址。
我们可以通过ifconfig命令来查看我们这台机器的私网 IP,其中网络接口 lo(loop)代表的是本地环回,而 eth0 代表的就是我这台机器的网络接口,可以看到我的私网 IP 地址是 172.21.0.15。
需要注意的是,这里连接云服务器时的 IP 地址 49.232.66.206,是云服务器的公网 IP,由于我使用的是腾讯云,因此这里的 172.21.0.15 是我这个云服务器在腾讯内部的私网 IP,可以看到这个 IP 正好在第二种私网 IP 范围内。
此外,打开 Windows 当中的 cmd 窗口,通过ipconfig命令可以看到大量以 192.168 开头的私网 IP。
我们为什么要给运营商交钱?
我们享受的是互联网公司提供服务,但为什么需要向运营商交钱呢?
也就是说,用户上网的数据首先必须经过运营商的相关网络设备,然后才能发送到互联网公司对应的服务器。因此所谓的网段划分、子网划分等工作实际都是运营商做的。
数据是如何发送到服务器的
路由器是连接两个或多个网络的硬件设备,在路由器上有两种网络接口,分别是 LAN 口和 WAN 口:
我们将 LAN 口的 IP 地址叫做 LAN 口 IP,也叫做子网 IP,将 WAN 口的 IP 地址叫做 WAN 口 IPO,也叫做外网 IP。
我们使用的电脑、家用路由器、运营商路由器、广域网以及我们要访问的服务器之间的关系大致如下:
由于私网 IP 不能出现在公网当中,因此子网内的主机在和外网进行通信时,路由器会不断将数据包 IP 首部中的源 IP 地址替换成路由器的 WAN 口 IP,这样逐级替换,最终数据包中的源 IP 地址成为一个公网 IP,这种技术成为 NAT(Network Address Translation,网络地址转换)。
为什么私网 IP 不能出现在公网当中?
两个局域网当中的主机不能不跨公网进行通信
所以数据要从一个局域网发送到另一个局域网,如果不经过公网是基本上不可能的。我们在和别人聊天的时候,也不是直接将数据从一个局域网直接发送到了另一个局域网,而是先将数据经过公网发送到了服务器,然后再由服务器将数据经过公网转发到了另一个局域网。
但实际确实存在一些技术能够使数据包在发送过程中不进行公网 IP 的替换,而将数据正确送到目标主机,这种技术叫做内网穿透,也叫做 NAT 穿透。
路由
数据 “问路” 的过程
数据在路由的过程中,实际就是一跳一跳(Hop by Hop)“问路”的过程。所谓 “一跳” 就是数据链路层中的一个区间,具体在以太网中指从源 MAC 地址到目的 MAC 地址之间的帧传输区间。
IP 数据包的传输过程中会遇到很多路由器,这些路由器会帮助数据包进行路由转发,每当数据包遇到一个路由器后,对应路由器都会查看该数据的目的 IP 地址,并告知该数据下一跳应该往哪跳。
路由器的查找结果可能有以下三种:
路由表查询的具体过程
每个路由器内部会维护一个路由表,我们可以通过route命令查看云服务器上对应的路由表。
当 IP 数据包到达路由器时,路由器就会用该数据的目的 IP 地址,依次与路由表中的子网掩码 Genmask进行 “按位与” 操作,然后将结果与子网掩码对应的目的网络地址Destination进行比对,如果匹配则说明该数据包下一跳就应该跳去这个子网,此时就会将该数据包通过对应的发送接口Iface发出。
如果将该数据包的目的 IP 地址与子网掩码进行 “按位与” 后,没有找到匹配的目的网络地址,此时路由器就会将这个数据包发送到默认路由,也就是路由表中目标网络地址中的default。可以看到默认路由对应的Flags是UG,实际就是将该数据转给了另一台路由器,让该数据在另一台路由器继续进行路由。
数据包不断经过路由器路由后,最终就能到达目标主机所在的目标网络,此时就不再根据该数据包目的 IP 地址当中的网络号进行路由了,而是根据目的 IP 地址当中的主机号进行路由,最终根据该数据包对应的主机号就能将数据发送给目标主机了。
路由表生成算法
路由可分为静态路由和动态路由:
路由表相关生成算法:距离向量算法、LS 算法、Dijkstra 算法等。