网络知识

这里记录一些 ip, 包, 传输协议, 段, 和 7 层网络知识等等网络知识, 方便快速查阅.

参考资料 计算机网络 - Computer Network

  • 路由器和 3层交换机 等价.
  • 2层交换机 活动在数据链路层, 只是延长网络.
  • 每一层都加上自己的 header头部数据.

帧首部和尾部的大小并不一定.

IEEE 802.3-以太网 帧的首部通常是 14 个字节(6 个字节的目标 MAC 地址,6 个字节的源 MAC 地址和 2 个字节的类型/长度字段)。帧尾部是 4 个字节的校验和字段。Ethernet frame - Wikipedia

因此,整个以太网帧的首部和尾部共占据 18 个字节的空间。VLAN 等衍生技术可能会增加额外字节

IEEE 802.11 的帧结构在设计上考虑了无线信道特性、多跳传输、移动性以及安全性等因素,以适应无线局域网环境中的需求。IEEE 802.11 - Wikipedia

最小是 32 字节,最小的组成为:

  1. 帧头部(Frame Header):帧头部的长度为 28 个字节,包含以下字段:
    • Frame Control:2 个字节,用于指示帧类型、子类型和其他控制信息。
    • Duration/ID:2 个字节,用于指示帧的持续时间或标识符。
    • Address 1-4:每个地址字段占 6 个字节,用于指示目标 MAC 地址、源 MAC 地址和其他相关地址。
    • Sequence Control:0 或 2 个字节,用于指示帧的序列号和片段编号。
    • Qos: 0 或 2 字节,流控
    • HT Controler: 0 或 4 字节,高吞吐量
  2. 帧尾部(Frame Footer):帧尾部的长度为 4 个字节,包含 FCS(Frame Check Sequence)字段,用于进行帧的差错检测。

不同的原因:

  1. 帧类型字段: Wi-Fi 帧中包含一个帧类型字段,用于指示该帧的类型。帧类型字段可以表示数据帧、控制帧或管理帧。这有助于 Wi-Fi 设备在接收到帧时正确处理和解释其目的和功能。

  2. MAC 地址格式: 以太网使用 6 字节的 MAC 地址来唯一标识网络设备,而 Wi-Fi 引入了扩展的 MAC 地址格式。Wi-Fi 的帧首部包含 4 字节的接收者 MAC 地址和 4 字节的发送者 MAC 地址。这样的设计支持更复杂的无线网络拓扑,例如多跳传输和移动设备的漫游。

  3. 序列控制字段: Wi-Fi 帧中包含一个序列控制字段,用于维护帧的传输顺序和可靠性。序列控制字段包括分片编号、片段计数和帧序列号等信息,以确保帧在无线信道上传输和组装时的正确顺序。

  4. 帧校验序列(FCS): 以太网帧使用循环冗余校验(CRC)字段来检测帧的传输错误,而 Wi-Fi 帧使用一个更复杂的 FCS 字段来实现错误检测。FCS 字段包含了更强大的校验算法,以提高对无线信道上的干扰和误码的容错能力。

MTU 如果设置太大, 可能被路由器拒绝转发. 太小又会导致效率低.

windows 查看 MTU netsh.exe interface ipv4 show subinterfaces

linux 查看 MTU ip a

  • 版本:占 4 位,指 IP 协议的版本。(IPv4/IPv6)
  • 首部长度:占 4 位,单位是 4B,最小为 5(因为首部固定部分 20B,5 * 4B = 20B),最大为 15(15 * 4B = 60B)
  • 区分服务:占 8 位,用来获取更好的服务(如:优先级),但实际上一直没有用过
  • 总长度:占 16 位,首部 + 数据部分的总长度,单位是 1B,因此数据报的总长度为
  • 标识/标志/片偏移是分片专用. IP 数据报分片
  • 生存时间(TTL):占 8 位,IP 分组的保质期,经过一个路由器就 -1,变为 0 则丢弃
  • 协议:占 8 位,数据部分的协议,即传输层使用的是什么协议
  • 首部检验和:占 16 位,只检验数据报的首部,但不包含数据部分。每经过一个路由器,都会重新计算一下首部检验和。
  • 源地址:占 32 位
  • 目的地址:占 32 位
  • 可选字段:占 0 ~ 40 位,用来支持排错、测量以及安全措施
  • 填充:全 0,把首部长度补成 4B 的整数倍
协议名ICMPIGMPTCPEGPIGPUDPIPv6ESPOSPF
字段值1268917415089

由于 MTU 最大 1500 的限制, 如果发送大于 1500-20 (ip 头部最小为 20 byte)=1480 的数据, 就需要分片.不会考虑 TCP 头, 也就是说 TCP 头只会在第一个分片中存在.

  • 标识:占 16 位,同一数据报的分片使用同一标识
  • 标志:占 3 位,只有两位有意义 x _ _
    • 中间位 DF(Don’t Fragment)
      • DF = 1,禁止分片
      • DF = 0,允许分片
    • 最低位 MF(More Fragment)
      • MF = 1,后面还有分片
      • MF = 0,代表最后一片 / 没分片
  • 片偏移:指出较长分组分片后,某片在原分组中的相对位置。(以 8B 为单位)
    • 除了最后一片,每个分片长度一定是 8B 的整数倍

假设我要发送 3800 byte 的数据,tcp 头 24 byte,组成的数据包 3824 byte.

最大数据量为 1500-24 = 1476 byte. 要被 8 整除. 所以最大数据量为 1472/8=184 .

3800 / 1472 = 2.58…. = 3 次.

数据总长度标识MFDF片偏移
原始数据包3800+24=382412345000
数据包 11472+24=149612345100
数据包 21472+24=14961234510184
数据包 3856+24=8801234500368
CIDR 标识地址范围IP 个数
10.0.0.0/810.0.0.0~10.255.255.25516,777,216
172.16.0.0/12172.16.0.0~172.31.255.2551,048,576
192.168.0.0/16192.168.0.0~192.168.255.25565,536
  • ARP: 通过 ip 获取物理 mac 地址. 发送 arp 广播, 目标机器响应结果. 此协议互信, 所以可以伪造 ARP 响应包, 目标一旦缓存了错误的数据, 目标就会将数据发送给伪造者.
  • RARP: 通过 mac 地址获取 ip 地址. 广播 mac 地址, RARP 服务器检查后, 返回 IP 地址, 不存在则不响应.

DHCP 协议过程:

  1. 主机广播 DHCP 发现报文 “有没有 DHCP 服务器呀” 试图找到网络中的服务器,向服务器获得一个 IP 地址
  2. DHCP 服务器广播 DHCP 提供报文 “有!有!有!” 服务器拟分配给主机一个 IP 地址及相关配置,先到先得
  3. 主机广播 DHCP 请求报文 “我用你给我的 IP 地址啦?” 主机向服务器请求提供 IP 地址
  4. DHCP 服务器广播 DHCP 确认报文 “用吧!” 正式将 IP 地址分配给主机
  • Ping 测试连通性. 发送数据包, 通常远端会返回响应. 但防火墙之类的也可以不响应.
  • Tracertoute 跟踪数据包流转的路径. 每次经过节点都会 ttl-1 .同时如果为 0, 当前节点就会返回响应. 所以可以不断尝试, 拿到路由路径.
  • UDP 是无连接的,减少开销和发送数据之间的时延
  • UDP 使用最大努力交付,即不保证可靠交付
  • UDP 是面向报文的,适合一次性传输少量数据的网络应用. 对于应用层交付的报文,直接原封不动的封装到 UDP 数据报的数据部分,即一次发一个完整的报文
  • UDP 无拥塞控制,适合很多实时应用. 例如直播
  • UDP 首部开销很小,8 byte,TCP 需要 20 byte

UDP 的头部格式

头部最小 20 字节, 也就是说发送 1 字节的数据, ip 头 +tcp 头需要 41 字节.

  • **序号(seq):**在一个 TCP 连接中传送的字节流中的每一个字节都按序编号,本字段表示本报文段所发送数据的第一个字节的序号
  • **确认号(ack):**期望收到对方下一个报文段的第一个数据字节的序号。如果确认号为 N,则证明到序号 N - 1 为止的所有数据都已正确收到
  • **数据偏移(首部长度):**TCP 报文段的数据起始处距离 TCP 报文段的起始处有多远,以 4B 为单位,即 1 个数值是 4B
  • **紧急位 URG:**URG = 1 时,表示此报文段中有紧急数据,是高优先级的数据,应尽快传送,不用再缓存里排队,配合紧急指针字段使用
  • **确认位 ACK:**ACK = 1 时确认号有效,在连接建立之后所有传送的报文段都必须把 ACK 置为 1
  • **推送位 PSH:**PSH = 1 时,接收方尽快交付接受应用程序,不再等到缓存填满再向上交付(和紧急位是对应的,紧急位是发送发优先发送,推送位是接收方优先向上交付)
  • 复位 RST:RST = 1 时,表明 TCP 连接中出现严重差错,必须释放连接,然后再重新建立传输连接
  • 同步位 SYN:SYN = 1 时,表明是一个连接请求/连接接收报文
  • **终止位 FIN:**FIN = 1 时,表明此报文段发送方数据已发送完,要求释放连接
  • **窗口:**指发送本报文段的一方的接收窗口,即现在允许对方发送的数据量
  • **检验和:**检验首部 + 数据,检验时要加上 12B 伪首部,伪首部第四个字段是 6
  • **紧急指针:**URG = 1 时才有意义,指出本报文段中紧急数据的字节数
  • **选项:**最大报文段长度 MSS、窗口扩大、时间戳、选择确认……
KindLengthInfo
1 字节1 字节 (可选)n 字节 (可选)

  • 图中第一个字段是 MSS-Maximum Segment Size 最大报文长度
  • 第二个字段一个空的选项, 用于补齐 tcp 头部为 4 byte 的整数倍

刚开始客户端处于 closed 状态,服务端处于 listen 状态

  1. **第一次握手:**客户端发送连接请求报文段(SYN = 1,seq = x(随机)),无应用层数据。此时客户端处于 SYN_Send (同步发送)状态
  2. **第二次握手:**服务端收到客户端的连接请求报文后,为该 TCP 连接分配缓存和变量,并向客户端返回确认报文(SYN = 1,ACK = 1,seq = y(随机),ack = x + 1),允许连接,无应用层数据。此时服务端处于 SYN_REVD(同步接收)状态
  3. 第三次握手:客户端收到连接请求报文后,会发送一个对确认的确认报文(ACK = 1,seq = x + 1,ack = y + 1),可以携带数据。此时客户端处于 established 状态

服务器收到确认报文后,也会处于 established 状态。此时,双方建立了连接.

SYN 哄泛攻击:

  1. 攻击者发送握手第一个包
  2. 服务器响应后, 处于半连接. 发送消耗资源, 挂起消耗资源
  3. 服务器收不到响应, 重复发送, 持续消耗资源
  4. 短时间内服务器大量等待, 会崩掉

刚开始双方都处于 established 状态,假如是客户端先发起关闭请求,则:

  1. 第一次挥手:客户端发送连接释放报文段FIN = 1,seq = u(前面已传送数据的最后一个字节序号 + 1)),停止发送数据,主动关闭 TCP 连接。此时客户端处于 FIN-WAIT-1 状态
  2. 第二次挥手:服务端收到 FIN 报文后,返回一个确认报文段(ACK = 1,seq = v,ack = u + 1)。此时服务端处于 CLOSE-WAIT 状态
  3. 第三次挥手:服务端发完数据,就发出连接释放报文段(FIN = 1,ACK = 1,seq = w,ack = u + 1),主动关闭 TCP 连接。此时服务端处于 LAST-ACK 状态
  4. 第四次挥手:客户端返回一个确认报文段(ACK = 1,seq = u + 1,ack = w + 1),再等到时间等待计时器设置的 2MSL(最长报文段寿命)后,连接彻底关闭

在 TCP(传输控制协议)中,“Segment size”(段大小)和 “Packet”(包)是两个不同但相关的概念。

“Segment size” 指的是 TCP 协议中数据传输时每个 TCP 段(segment)的最大大小。TCP 使用分段(segmentation)将应用程序发送的数据划分为较小的片段进行传输。这些片段被称为 TCP 段,每个段都包含一个 TCP 头部和有效载荷(数据)。段的大小由操作系统或网络堆栈的配置参数确定,并且可以根据网络条件进行调整。通常情况下,段的大小在几百字节到几千字节之间。

而 “Packet”(包)是在网络层(如互联网协议 IP)上进行数据传输时使用的单位。包是由网络层负责封装和传递的,其中包括源地址、目标地址和有效载荷(即从传输层接收到的 TCP 段)。包的大小由网络层协议定义,例如在 IPv4 中,包的最大大小为 64KB。

区别:

  1. 概念层次不同:TCP 段是在传输层协议 TCP 中定义的,而包是在网络层协议(如 IP)中定义的。
  2. 功能不同:TCP 段负责将数据从应用程序发送到接收方的 TCP 协议栈,而包在网络中进行路由和传递,确保数据的正确交付。
  3. 大小限制不同:TCP 段的大小受到 TCP 协议栈的配置参数限制,而包的大小由网络层协议定义。

联系: 在传输过程中,应用程序发送的数据会被 TCP 协议分割成多个段(segment),每个段都会被封装为一个网络层的包(packet)进行传输。这两个概念都是为了实现可靠的数据传输和网络通信而存在的。

Nagle 算法规定包满足 MSS 立即发送, 否则需要发送 ACK 确认包. 而每个包都发送 ACK, 会降低性能.

DelayedAcknowledgment 不再针对单个包发送 ACK,而是一次确认两个包,或者在发送响应数据的同时捎带着发送 ACK,又或者触发超时时间后再发送 ACK.

而一旦这两点同时工作, 在发送小包的时候, 就需要等待 ack 回传, 才能发送. 为什么一般延迟是 40 ms 呢? redhat文档说默认是这个值, RFC 9293 说必须少于 0.5 seconds, 所以设置的 40 ms 吧.

如何解决呢? 启用 TCP_NODELAY , 禁用 Nagle 算法. nginx配置 就用了这个配置. 其中

  • TCP_NOPUSH 就是 TCP_CORK 参数, 可以和 TCP_DELAY 配置使用, 让 nginx 在发送文件的时候每个包尽量多的存放数据.
  • TCP_NODELAY 就是在其他情况下, 减少延迟用的.

参考资料 TCP_NODELAY 和 TCP_NOPUSH的解释 - wajika - 博客园