欢迎光临
我们一直在努力

IP网络层

Linux socket 内核结构体关系

自上而下梳理调用链条:

  • task_struct(进程) 每个进程,里面有 files_struct,保存这个进程所有打开的文件描述符。

  • files_struct → fd_array[] 文件描述符数组。socket 也是文件,socket()系统调用,分配一个 fd,数组里存struct file*。

  • struct file 一切打开的文件都用这个结构体。socket 作为特殊文件,private_data指向 struct socket。

  • struct socket socket 层,系统调用相关接口API(eg:send() recv() accept())。 里面成员:struct sock *sk,指向真正网络协议层对象

  • struct sock 才是真正内核网络对象,socket 是上层封装给系统调用用的壳子。

         5.struct sock

    发送队列、接收队列、TCP 序号、拥塞控制、三次握手、重传、滑动窗口,全部在这里。

    C 没有类继承,用结构体嵌套实现继承: struct inet_sock 包含 struct sockstruct tcp_sock 包含 struct inet_connection_sock;一层套一层。(看图)

    • struct inet_sock:IP 层信息,源 IP、目的 IP 等。
    • struct inet_connection_sock:面向连接(TCP)专用(udp没有这一层,udp不需要建立连接)。
    • struct tcp_sock:TCP 专属,存序号、窗口、重传、拥塞控制全部 TCP 细节。

    struct sock 实现多态,体现在哪里

    多态体现在两点

    1. 指针强制转换

    拿到 struct sock *sk,可以直接强转成子类:struct tcp_sock *tp = (struct tcp_sock *)sk;或者

    struct udp_sock *tp = (struct udp_sock *)sk;同一个基类指针,实际指向不同子类对象,这就是多态。

    2. 函数指针(虚函数表)

    struct sock里面存一堆函数指针,比如: sk->sk_sendmsg、sk->sk_recvmsg

    • TCP 创建 socket 的时候,把这些函数指针赋值成 TCP 版本函数;
    • UDP 创建 socket 的时候,赋值成 UDP 版本函数。

    内核统一调用 sk->sk_sendmsg(sk,…),不用管是 TCP 还是 UDP,自动跑对应协议的函数。

    串起来的流程

    用户调用read/recv(fd,…) 进程 task_struct → files_struct → fd 找到 struct file → private_data 拿到 struct socket → socket 的 sk 指针找到 struct sock。 内核就访问 sk->sk_receive_queue,把队列里的 sk_buff 数据拷贝到用户空间。

    udp结构(没有struct inet_connection_sock这一层)

    IP网络层

    1.网络层 vs 传输层

    • IP(网络层)= 只有 “能力” IP 只管:把数据包从一台主机送到另一台主机。 只能做到尽力交付,不保证可靠,会丢包、乱序、重复,
    • TCP(传输层)= 负责 “策略”  在源主机、目的主机两端做:确认、重传、序号、流量控制,实现 100% 可靠交付。

    一个有策略一个有能力,就能把数据100%可靠的从一个源主机发到目标主机

    ip协议和核心作用和构成

  • IP 协议作用 提供尽力交付的能力:把数据报从 A 主机,跨多个网络送到 B 主机。
  • 只是 “很大概率送到”,不保证可靠,会丢包、乱序、重复。

        2.解决的问题:主机到主机

    • IP 地址用来唯一标识一台主机。
    • IP 地址 = 目标网络号 + 目标主机号。
    • 路由本质:数据包从一个子网,转发进入另一个子网

    对比:

    • IP(网络层):主机 → 主机,靠 IP 地址。
    • TCP/UDP(传输层):进程 → 进程,靠端口号。

         3.几个名词

    • 路由器:配 IP 地址,专门做路由转发;工作在网络层。

    现代路由器本身是小型计算机,也可以跑应用层程序,但转发数据包核心逻辑只到网络层。即不会再向上解析传输层 TCP/UDP 头部,更不会交给本机应用层程序处理,转发流程在底层就完成。

    • 节点:一个路由器,一个主机都可以叫做节点

    IP协议头格式

    类比:两地之间选什么样交通方式,根据业务需求选择走那一条路。

    • 4 位版本号 version 指定 IP 协议版本,IPv4:0100;IPv6:0110

    • 4 位头部长度 header length 单位:4 字节。 普通 IP 头部 20 字节:20/4 = 5,该字段填 5;最大值 15,对应 IP 头部最大 60 字节。

    • 8 位服务类型 TOS 3 位优先权(已废弃) + 4 位 TOS + 1 位保留位(必须 0)。 4 个 TOS 选项:最小延时、最大吞吐量、最高可靠性、最小成本,互斥只能选一个。

    • ssh/telnet:看重最小延时
    • ftp:看重最大吞吐量
    • 16 位总长度 total length 整个 IP 数据报(头部 + 数据)总字节数。

    • 16 位标识 id 标记主机发出的 IP 报文。一个报文被分片之后,所有分片拥有相同 id。

    • 3 位标志字段第 1 位:保留。第 2 位:禁止分片位。置 1 禁止分片;报文超过 MTU 直接丢弃。第 3 位:更多分片。最后一片置 0,其余分片置 1,相当于结束标记。

    • 13 位分片偏移 fragment offset 表示分片在原始报文的位置。 实际字节偏移 = 该字段 × 8。 除最后分片外,其余分片数据长度必须是 8 字节整数倍。

    • 8 位生存时间 TTL 最大跳数,常见初始值 64。 每经过一台路由器,TTL‑1;减为 0 还没到达目的地,报文直接丢弃。 主要是用来防防止出现路由循环

    • 8 位协议 标识上层是什么协议,例如 tcp、udp。

    • 16 位头部校验和 CRC 校验,只校验 IP 头部,检测头部是否损坏。

    • 32 位源地址、32 位目的地址 发送方 IP、接收方 IP。

    • 选项字段 长度不定,最多 40 字节,一般省略。

    同样2个问题

    分用问题:8位协议字段。IP 收到报文,剥掉 IP 头,看这个值,确定把数据交给上层 TCP/UDP/ICMP哪一个。

    解包问题:根据4位头部长度,确定头部有多长,data指针偏移这么多就是数据开始部分,并且还有16 位总长度 ,减去4位头部长度,还知道数据一共有多长

    理解子网划分

    把整个互联网类比成一所大学:

    • IP 地址 = 学号:院号+学号
    • 子网(网络号) = 学院编号(计算机学院、理学院、机械学院…)
    • 主机号 = 学院里面的学生个人编号

    eg:06 321 (06代表机械学院,321代表自己的学号,02理学院,03经管学院……)

    • 06 → 网络号(机械学院)
    • 321 → 主机号(李四本人,学院内部编号)

    IP = 目标网络号 + 目标主机号

    找目标主机,先找目标网络(学院),再找网络内部主机(学生)

    校园例子理解转发

    • 张三在计算机学院( 01)捡到了机械学院李四(子06)的钱包。
    • 钱包上写学号06321
    • 张三发现目标学号不属于本院,01才是计算机学院,06不是,但他单单知道不是本学院,不清楚是哪一个具体的学院(理学院,材料…..) 所以交给本院的院学生会主席,再由他发信息到校群询问  不走内网转发,交给出入口路由器,子网转发。
    • 机械学院的院学生会主席看到后发现06是本学院的标识,就再在机械学院的群里找到321对应的同学

    理解对应关系

    校园故事网络中的对应概念
    张三主机,在计算机学院(01) 源主机,处在源子网01
    钱包上面写学号06321 IP 报文头部写目标 IP 地址(网络号 06 + 主机号 321)
    张三对比:目标学院06≠自己学院01,不在同一个学院 源主机拿目标 IP,和自己子网掩码做运算,得到目标网络号,和本机网络号对比:不在同一个子网
    张三不知道机械学院具体在哪,自己送不到,交给本院学生会主席(出入口) 不能内网转发,交给路由器
    学生会主席在校园主干(校群)转发,跨学院传递钱包 公网转发。一路上各个路由器只看网络号 06,不关心主机号 321,一跳一跳转发
    钱包到达机械学院,机械学院学生会主席看到06是本院编号 数据包抵达目标子网;路由器识别目标网络号和自己接口网络号匹配,即子网匹配
    在机械学院内部,根据321找到李四同学 本子网内,根据主机号 321,交付给最终目标主机李四

    相关概念:

    内网转发(本子网内转发)

    源主机和目标主机在同一个子网(同一个学院),不需要走出本学院,不经过外部路由器,学院内部直接交付

    公网转发(跨子网转发)

    源主机和目标主机不在同一个子网(不同学院),本学院找不到目标主机,把数据包交给出入口路由器,跨多个子网转发。路上只看网络号,抵达目标子网才看主机号

    为什么要做子网划分(为什么大学要分学院)

    子网划分其实就是给网络预先做分组编号。

  • 如果不划分子网,全网所有主机全部平铺在一起。找一台主机就要在全部主机里线性遍历,查找效率极低。
  • 子网划分相当于预先分组。路由查找本质是淘汰过程:先把大量无关子网直接淘汰掉(比如要找01学院的我就可以直接淘汰02,03等等其他学院的),只留下目标所在子网,再在子网内部找主机,大幅提升查找效率。(先找学院再找学生)
  • 网段划分

    路由器

  • 核心能力:连接多个不同子网(构建子网) 路由器有多个网络接口,每个接口配一个 IP,每个 IP 属于不同子网。
  • 图中:一个接口192.168.128.1(属于 128 网段),另一个接口192.168.144.1(属于 144 网段)。 这两个 IP,就是各自子网的网关地址。

    网关地址就是路由器接入对应本子网的网口的 IP值。

    • 192.168.128.10 这台机器,网关填192.168.128.1
    • 192.168.144.11 这台机器,网关填192.168.144.1

    内网路由:

    所以加上掩码和网关的话上面的内网路由(192.全都是内网,不涉及公网转发)完整过程:

    源:192.168.128.10,目标:192.168.144.11

  • 源主机做分别那源ip和目标ip做掩码运算,发现目标网络号(192.168.144.0)和自己的网络号(192.168.128.0)不在同一个子网。
  • 不能内网转发,数据包交给网关192.168.128.1(路由器)。
  • 路由器收到包,从另一个接口192.168.144.1转发出去。
  • 到达192.168.144.0/24子网,根据主机号找到192.168.144.11主机
  • DHCP 协议

    为什么要有:如果没有 DHCP,就需要手动静态填写 IP、掩码、网关,家里设备多会很麻烦,还容易 IP 冲突。

  • 协议层级:应用层协议,运行在路由器上面。
  • 我们电脑手机的内网 IP,不是自己写死的,是 DHCP 服务器分配来的。

         2.干什么 主机刚接入网络,自己没有 IP。主机向全网广播 DHCP 请求,路由器的 DHCP 服务收到请求,给主机分配:

    • IP 地址
    • 子网掩码
    • 网关地址(路由器接口 IP)
    • DNS 服务器地址

    拿到这套参数,才可以正常上网。

    DHCP就是路由器在应用层的体现,路由器上运行着应用层的 DHCP 服务

    过去曾经提出⼀种划分网络号和主机号的方案,把所有IP地址分为五类,

    随着 Internet 的飞速发展,这种划分方案的局限性很快显现出来

    • A 类:一个大网,主机位 24 位,一个网络可以有一千六百多万台主机
    • B 类:主机位 16 位,一个网络最多 65534 台主机(6 万 5 千多)
    • C 类:主机位 8 位,一个网最多 254 台主机

    现实问题: 几乎没有哪家公司,一个子网里面真的放 6 万台机器。 很多企业规模就几千台设备,想要比 C 类(254 台)大一点的网段,只能申请一整个 B 类地址。

    申请一个 B 类,直接拿到 65534 个主机 IP。但公司实际只用 2000 个,剩下六万多个 IP 直接闲置,别人不能用,就白白浪费。 A 类网络更恐怖,一个 A 类一千六百多万地址,绝大多数机构根本用不完,大量地址被占住闲置。

    结果:

    • B 类地址被疯狂申请,很快全部耗尽;
    • A 类很多网络拿在手里,大片地址空着,没法回收分给别人。

    这就是有类 IP 划分的巨大缺陷:网络大小只能固定三档(A/B/C),不能灵活选网段大小,造成 IP 大量浪费。

    针对这种情况提出了新的划分方案,称为 CIDR (Classless Interdomain Routing)(无类别域间路由)

    CIDR

    CIDR 就是干掉 A/B/C 这个死板分类,可以自由规定网络位多长。

    CIDR 写法:IP / 前缀长度

    例子:192.168.1.0 /22 /22:前 22 位是网络位,剩下 32‑22 =10 位是主机位。

    简单计算

    IPv4 一共 32 位

    • /24:网络 24 位,主机 8 位 → 和传统 C 类一样
    • /16:网络 16 位,主机 16 位 → 和传统 B 类一样
    • /8:网络 8 位,主机 24 位 → 和传统 A 类一样

    但还可以写 /21 /22 /23,这是 ABC 分类做不到的(靠子网掩码实现)

    子网掩码和 CIDR 的对应

    CIDR 前缀子网掩码
    /24 255.255.255.0
    /23 255.255.254.0
    /22 255.255.252.0
    /16 255.255.0.0

    理解子网掩码

    网络号=子网掩码&IP地址

    IP 固定:140.252.20.68,这是一个 B 类 IP。

    例子 1:子网掩码 255.255.255.0

    掩码二进制:11111111.11111111.11111111.00000000

    • 前面 24 个1:网络位
    • 末尾 8 个0:主机位(主机号)

    ①求网络号(网络地址):IP & 掩码(按位与)

    只看最后 1 字节: IP 最后 1 字节:68 → 0100 0100 掩码最后 1 字节:0 → 0000 0000

    0100 0100
    & 0000 0000
    ——————————————
    0000 0000 → 0

    网络号:140.252.20.0

    主机号全部为 0 → 网络地址,代表这个子网本身,不能分配给电脑

    ②求广播地址:把全部主机号置 1

    主机位是后面 8 位,全部写 1:11111111 =255 广播地址:140.252.20.255

    主机号全部为 1 → 广播地址,用于广播,不能分配给电脑

    ③子网范围

    网络号 ~ 广播地址 → 140.252.20.0 ~ 140.252.20.255

    ④可用主机数量

    主机位 8 位:2^8-2 = 254台 减 2:扣掉【主机全 0 的网络地址】、【主机全 1 的广播地址】 真正能用 IP:140.252.20.1 ~ 140.252.20.254


    例子 2:子网掩码 255.255.255.240

    掩码二进制:11111111.11111111.11111111.11110000

    • 前面 28 个1:网络位
    • 末尾 4 个0:主机位(主机号)

    ①求网络号(按位与 &)

    IP 末尾字节68:0100 0100 掩码末尾字节240:1111 0000

    0100 0100
    & 1111 0000
    ——————————————
    0100 0000 → 64

    网络号:140.252.20.64

    这里主机 4 位全部是 0,网络地址,不给主机用。

    ②求广播地址:全部主机号置 1

    网络号末尾字节:0100 0000 把后面 4 位主机号改成 1:0100 1111 =79 广播地址:140.252.20.79

    主机 4 位全部是 1,广播地址,不给主机用。

    ③子网范围

    140.252.20.64 ~ 140.252.20.79

    ④可用主机数量

    主机位 4 位:2^4-2 =14台 真正能用 IP:140.252.20.65 ~ 140.252.20.78

    主机号全 0、全 1 是什么

  • 主机号全部比特为 0:网络地址(网段号),用来标识这整个子网,不能分配给主机。
  • 主机号全部比特为 1:广播地址,向本子网所有设备发广播数据包,不能分配给主机。
  • 子网掩码的作用(结合两张图理解)

    可以通过设置掩码二进制中 1 的个数,重新设置网络号,充分利用 IP 地址资源

    同样 IP:140.252.20.68

    • 掩码255.255.255.0(24 个 1) → 网络号140.252.20.0,子网很大,256 个 IP
    • 掩码255.255.255.240(28 个 1) → 网络号140.252.20.64,子网很小,16 个 IP
  • 改变掩码里面1的数量,同一个 IP 算出来的网络号直接变,子网大小就被我们人为切割
    • 1 越多 → 网络位变长,主机位变短,子网就越小(即一个子网内的主机数少)。
    • 靠子网掩码切出很多小小的子网,需要多大切多大,剩下 IP 留给别的子网使用,IP 就不会浪费。相当于是动态的形式了,不是以前固定写死了的类别

    IP 地址的数量限制

    我们知道,IP 地址 (IPv4) 是一个 4 字节 32 位的正整数。那么一共只有 2 的 32 次方 个 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 还没有普及

    私有 IP & 公网 IP

    私有 IP(内网 IP,局域网用)3 段固定范围

  • 10.*
  • 172.16.* ~ 172.31.*
  • 192.168.*
  • 私有 IP只能局域网内部通信,不能跑到互联网(公网)上面传输。(后面解释)

    公网 IP(全局 IP)

    除上面三段以外,其余全部是公网 IP。 公网 IP 全世界唯一,互联网上通信必须用公网 IP。

    NAT过程

    核心规则:每经过一台做 NAT 的路由器,就把报文的src源IP改成这台路由器自己 WAN 口对外 IP,目的 IP 全程不变。

    完整流程:

    家里电脑发出原始包

    电脑:192.168.1.201,访问抖音122.77.241.3

    src:192.168.1.201 dst:122.77.241.3

    源是内网私有 IP。

    2、经过自家家用路由器 NAT

    路由器 WAN 口 IP:10.1.1.2,把源 IP 替换成 WAN 口 IP

    src:10.1.1.2 dst:122.77.241.3

    私有 IP192.168.1.201从 IP 头部消失,路由器内部 NAT 表记录:哪个连接对应家里哪台主机。

    3、再经过运营商路由器(又一层 NAT)

    运营商路由器 WAN 口公网 IP:122.77.241.4,再次改写源 IP

    src:122.77.241.4 dst:122.77.241.3

    现在报文带着公网源 IP,进入广域网发给抖音服务器。

    抖音服务器收到包,只看见源是公网122.77.241.4,完全看不见原始内网192.168.1.201。

    4、抖音回复报文(回程)

    抖音发回来的报文:

    src:122.77.241.3 dst:122.77.241.4

    目标地址是运营商公网 IP。 互联网路由根据目标 IP,一定会把这个包送到这个公网 IP 所属的运营商路由器。

    运营商查自己 NAT 转换表,把目标 IP 改回10.1.1.2,转发给你家路由器; 你家路由器再查 NAT 表,目标改回内网192.168.1.201,内网转发交给电脑。

    为什么一定要NAT 地址转换

    因为内网 IP 可能重复(内网 IP 可以重复;但是同一个子网内部 IP 不能重复。)

    A 家电脑:192.168.1.201;B 家电脑也可以是 192.168.1.201,IP 一模一样。数据包根本无法正确回送根本不知道谁是哪一个,通信直接报废。(也解释了私有 IP不能直接在公网路由转发的原因)

    所以靠 NAT 转换,把重复的内网 IP 换成独一无二的公网 IP 去互联网通信

    NAT 解决 IP 地址不足,体现在哪里

  • 私有 IP 地址:可以全世界重复使用
  • 公网 IP 地址:全世界必须唯一,不能重复
  • 所以大量主机可以重复使用私有内网 IP,经过NAT 转换之后,一整个局域网内所有设备,共用同一个公网 IP 去访问外网。大大节约 IP 资源,

    补充:

  • 运营商掌控上网权限 家里所有上网报文,全部先经过运营商机房路由器。
    • 欠费:运营商直接丢弃你的报文,不让转发到公网,就上不了网。
    • 运营商可以决定你的数据包能不能去往互联网。
  • 报文会经过运营商设备,明文数据会被看到 你的数据包全部过运营商机房,所以如果携带有有些“敏感”信息就不能成功转到
  • 长城技术:代指网络审查、过滤,运营商可以拦截、丢弃特定报文。

    相关概念:

    局域网,内网,子网不用太过于纠结,可以当作一个东西

    LAN(局域网)LAN口的值就是网关

    WAN(广域网)WAN口的值可能是公网ip,也可能是运营商二级 NAT后的值(私网ip)

    网关:是一台具体设备的 IP 地址(路由器 LAN 口的值),是一个实实在在的主机 IP(192.168.1.1)

    网络号:是一个网段的编号,代表一整个局域网,不是一台设备(192.168.1.0)

    尝试理解公网

    真实的网络结构非常复杂,即涉及到划分公网 IP 的组织,ICANN,还要在全球范围内进行区域划分, 比如亚太,北美,欧洲等,又要考虑各个国家内部的 ISP 代理,整体拓扑非常复杂,我们简化所有过程,简单理解公网即可

    把公网类比成全世界城市交通网:

  • 最上层:俄罗斯、美国、英国等国家,各自占一个大网段(如1.0.0.0/8),国家之间靠国际骨干网相连,路由器跑 BGP 路由协议,互相交换全网路由信息。
  • 国家内部:再拆分各个省份(陕西5.1.0.0/16、河南5.2.0.0/16、浙江5.3.0.0/16),国内骨干网互通。
  • 省份内部:再拆各个城市(西安5.1.16.0/20、延安5.1.32.0/20)。
  • 城市下面才是普通家庭 / 公司内网。
  • 公网 IP 每一段,层层划分:国家→省份→城市,每一级路由器都有路由表,知道某个网段该往哪个接口转发。

    问题 1:国外俄罗斯要访问目标地址:5.1.16.X

    完整流程:

  • 俄罗斯主机发包,源是俄罗斯公网 IP,目标5.1.16.X。本机掩码运算,发现目标不在本国网段,交给本国网关。
  • 俄罗斯路由器查路由表:5.0.0.0/8属于中国,把数据包转发到国际骨干网,送到中国的国家级路由器。
  • 中国骨干路由器:目标5.1.16.X属于5.1.0.0/16(陕西省),转发给陕西省的出口路由器。
  • 陕西省路由器:目标5.1.16.X属于西安网段5.1.16.0/20,转发给西安的出入口路由器。
  • 西安路由器继续转发,把数据包送到5.1.16.X这台主机。
  • 全程:IP 源目 IP 不改变,没有 NAT,只有每一跳二层 MAC 帧头不断更换。 回程报文原路返回。


    问题 2:河北的报文,发往5.1.16.X

  • 河北主机发包,目标5.1.16.X,判断不在本网段,交给河北网关。
  • 河北路由器查路由:目标网段5.1.16.0/20属于陕西5.1.0.0/16,交给国内骨干网。
  • 国内骨干网转发给陕西省路由器。
  • 陕西路由器再交给西安路由器。
  • 最终送达主机5.1.16.X。
  • 关键点:每一级路由器只看目标 IP 的网络号,查表选择下一跳,不需要知道目标主机具体在哪条线下。

    路由

    • 路由表可以使用 route 命令查看
    • 如果目的 IP 命中了路由表,就直接转发即可;
    • 路由表里的default(缺省路由 / 默认路由)条目: 包含下一跳网关地址和本机发送接口。当数据包的目标 IP,和路由表里上面所有条目全都匹配不上的时候,就走这条缺省路由,从指定接口,把包发给这个下一跳网关。

    • 路由表的 Destination 是目的网络地址,Genmask 是子网掩码,Gateway 是下一跳地址,Iface 是发送接口,Flags 中的 U 标志表示此条目有效 (可以禁用某些条目),G 标志表示此条目的下一跳地址是某个路由器的地址,没有 G 标志的条目表示目的网络地址是与本机接口直接相连的网络,不必经路由器转发;

    转发过程例 1: 如果要发送的数据包的目的地址是 192.168.56.3

    • 跟第一行的子网掩码做与运算得到 192.168.56.0,与第一行的目的网络地址不符
    • 再跟第二行的子网掩码做与运算得到 192.168.56.0,正是第二行的目的网络地址,因此从 eth1 接口发送出去;

    转发过程例 2: 如果要发送的数据包的目的地址是 202.10.1.2

    • 依次和路由表前几项进行对比,发现都不匹配;
    • 按缺省路由条目,从 eth0 接口发出去,发往 192.168.10.1 路由器;
    • 由 192.168.10.1 路由器根据它的路由表决定下一跳地址;

    TCP的16 位标识 ID,3 位标志,13 位片偏移

    • 16 位标识 ID:同一个大 IP 数据报拆分出来的所有分片,拥有相同标识 ID;接收方靠这个 ID,把一堆分片识别成属于同一个原始报文。
    • 3 位标志
      • 最高位保留;
      • DF 位:不分片位。DF=1,路由器禁止分片,包太大直接丢弃,返回 ICMP;
      • MF 位:更多分片位。MF=1:后面还有分片;MF=0:这是最后一片。
    • 13 位片偏移:代表本片数据,相对于原始报文数据开头的偏移(以8 字节为单位)。接收方根据偏移,把分片按顺序拼回完整数据。
    • 核心结论:IP 报文可以进行分片与重组。 重要:只要任意一个分片丢失,整个原始报文直接作废,全部重传。接收方不会上交残缺数据,只会等待全部分片,缺一片就组装失败。

      MTU、MSS,为什么会分片

      • MTU(链路层最大传输单元):数据链路层一帧最多能承载的数据字节,以太网默认 1500 字节。
      • MSS(最大段大小):TCP 传给 IP 层的数据部分(不含报头)最大长度。 MSS = MTU − IP头部(20) − TCP头部(20) = 1460字节

      MSS 只属于 TCP,和 UDP 无关。UDP 没有 MSS 概念。

      分片发生过程

      传输层给网络层交出大数据包,IP 总长度 > MTU (1500),路由器就把这个 IP 报文切割成多个分片,一个个转发。 分片是网络层 (IP) 被迫做的事情,不是主流,不推荐(后面会再讲为什么不是主流)。

      分片坏处:只要丢任意一片,整个报文报废,丢包概率大幅上升。

      结论:过多分片,会导致丢包概率变大。

      怎么减少分片?

      分片发生在 IP 层;但避免分片的工作,交给传输层(TCP)做。(减少分片的本质在传输层) TCP 知道 MSS,每次交给 IP 的数据不超过 MSS (1460),加上 IP 头 + TCP 头,整个 IP 报文刚好 1500,就不会触发 IP 分片

      UDP 没有 MSS 机制,UDP 发大数据,直接交给 IP,就会触发 IP 分片。

      为什么滑动窗口不一次性发大报文,要多次发呢?

      所以现在看来主要原因就是为了避免IP分片,保证可靠性,二来就是如果有一段丢了,只重传丢失那一小段,不用重传全部,节省开销

      怎么做到IP 分片识别、收全判断、组装?

      1.怎么判断一个 IP 包是不是分片报文

      两种情况代表这是分片: a. MF(更多分片)=1:后面还有别的分片,肯定是分片包。

      b. MF=0,但是片偏移 > 0:说明这是最后一片分片,属于分片。

      那什么是原始完整不分片的包:MF=0 && 片偏移=0。两个条件同时满足,代表没有被分片。

      2、接收方怎么把分片归为一组,怎么判断是否全部收齐

      a. 16 位标识 ID 相同的分片,说明属于同一个原始大报文,能够放到一组。

      b. 判断有没有收全: 必须收到MF=0 的那一片(最后分片)。 只有拿到最后一片,才知道整个报文总长度。 三种收不全丢包场景:

    • 第一片丢失(片偏移 = 0 的分片丢了)
    • 中间某一片丢失(不太好判断,看第3步组装的过程)
    • 结尾分片丢失(MF=0 那片丢了)
    • 只要任意一片没到,接收方就组装失败,不会上交上层,等待超时直接全部丢

      3.组装(重组)过程

    • 同一标识 ID 的全部分片,按照片偏移从小到大升序排序。
    • 校验连续:当前分片片偏移 + 当前分片数据长度(按8字节单位) = 下一片的片偏移。(如果不连续不就相当于中间一片丢了吗)
    • 拼接到一起,还原完整 IP 数据,交给上层传输层。
    • eg:

      注意:片偏移单位 = 8 字节

      原始 IP 数据:4000 字节,被路由器分成 3 片:

      分片片偏移 (8 字节为单位)数据字节数MF 标志
      第 1 片 0 1480 1(还有分片)
      第 2 片 185 1480 1(还有分片)
      第 3 片 370 1040 0(最后一片)

      换算:1480 ÷ 8 = 185

    • 第 1 片片偏移 0,数据占 185 个 8 字节单元。
    • 0 + 185 = 185 → 正好等于第 2 片的片偏移 185,连续。
    • 第 2 片 1480 字节,同样 185 个单元;185+185 = 370 → 和第 3 片片偏移 370 对上。
    • 接收方重组步骤

    • 三个分片 ID 相同,把它们拿出来,按片偏移从小到大排:0 →185 →370。
    • 校验连续: 0+185 =185 ✔ 185+185 =370 ✔是连续的
    • 片偏移为0的第1片收到,加上连续所以中间也没有丢失,最后收到 MF=0 的最后一片,确认全部收齐。
    • 直接把三段数据拼接,恢复 4000 字节完整数据,上交 TCP/UDP
    • 丢包场景

      如果第 2 片丢了: 收到分片:0、370。 0+185 !=370,发现中间缺口。收不到全部分片,直接全部丢弃,不交给上层。

      MSS补充:

      TCP 希望发送报文段的时候,尽量避免在网络层发生 IP 分片,接收方也同样要保障分片处理的问题。 因此 TCP 在三次握手阶段,双方会互相通告自己的 MSS,最终取两者的最小值作为连接的 MSS。(所以一般就不会分片,即不是主流)

      握手阶段就协商确定 MSS,发送方按照这个 MSS 填充数据,绝大多数情况下就不会触发 IP 分片,大大减少分片发生的概率。

      注意:路径上的 MTU 是有可能动态变化的,但不影响 MSS 的基础逻辑,上层 TCP 会基于协商好的 MSS 控制报文大小。

      赞(0)
      未经允许不得转载:171主机测评 » IP网络层
      分享到: 更多 (0)

      评论 抢沙发

      • 昵称 (必填)
      • 邮箱 (必填)
      • 网址