​​


作者:threedayman​


内容提要

  • 什么是TCP?

  • 在OSI模型中,TCP属于哪一层?

  • TCP得头格式

  • 怎样唯一确认一个TCP连接

  • 一台服务器下,某应用 TCP最大连接数是多少?

  • TCP连接怎样建立

  • SYN Flood攻击
  • TCP连接断开

TCP

传输控制协议(英语:Transmission Control Protocol,缩写:TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由 IETF 的 RFC 793 定义。在简化的计算机网络 OSI 模型中,它完成第四层传输层所指定的功能。

TCP 报文头

这就是TCP?_transmission

  • 源端口: 2 字节 发送端口

  • 目标端口:2 字节 接收端口


    TCP 协议中没有源 ip 和目标 ip,那是 ip 层相关信息。源 ip、源端口、目标 ip、目标端口构成了 TCP 连接的​四元组​ 。一个四元组可以唯一标识一个 TCP 连接。


  • 序列号: 4 字节 TCP 是面向字节流的协议,通过 TCP 传输的字节流的每个字节都分配了序列号,序列号指的是本报文段第一个字节的序列号

  • 确认号:期望接收下一个序列号,小于此确认号的所有字节都已经收到。

  • 头部长度:

  • 保留: 占 6bits 保留为今后使用,但目前应置为 0


  • TCP Flags


  • NS: 1bit ECN-nonce 隐藏保护

  • CWR: 1bit 减少拥塞窗口(CWR)标志由发送主机设置,以指示它收到了设置了 ECE 标志的 TCP 段并在拥塞控制机制中作出了响应

  • ECE: 如果设置了 SYN 标志(1),则 TCP 对等点可以使用 ECN;如果 SYN 标志为 clear(0),则说明 IP 报头中具有拥塞经历标志的包在正常传输过程中收到(ECN=11)。这可以作为 TCP 发送方网络拥塞(或即将发生拥塞)的指示。

  • URG: 紧急比特

  • ACK: 确认数据包

  • PSH:告知对方数据包收到以后应马上交给上层应用,不能缓存。

  • RST: 用来强制断开连接,之前建立的连接已经不在了、包不合法、或者无法处理。

  • SYN: 发起连接数据包同步双方的初始序列号。

  • FIN: 准备断开连接,不会再发数据包。

  • 窗口大小: 标识窗口大小。

  • 校验和: 检查数据是否被篡改或损坏。

  • 紧急指针: 当 URG=1 时,标识紧急数据所在的位置。

  • 选项、填充:常用的选项有 MSS、SACK、Window Scale 等,填充为了补齐数据到 32 位。


    通过四元组(源 ip、源端口、目标 ip、目标端口)可以唯一确认一个TCP连接。假设某应用得ip和端口 都固定,客户端得ip和端口是可变得,理论上 最大TCP连接数 = 客户端IP数 * 客端端口数。IPv4,客户端ip数最多为2^32,端口数最多2^16,那理论最大单机TCP为2^48。当然tcp连接数还受其他因素得限制,比如在linux操作系统中,受​文件描述符限制​ ,可以通过ulimit设置,另外受到机器​内存​ 限制。


三次握手

这就是TCP?_python_02

  • Sender 发送 SYN(seq = x);
  • Reciver 接收到 SYN 后 返回 SYN+ACK(seq=y,ack = x+1);
  • Sender 接收到 SYN 和 ACK 之后 返回给 Reciver 一个 ACK(seq=x+1,ack=y+1);

三次握手优点

  • 防止旧得连接重复初始化。
  • 同步双方序列号。(序列号用于数据去重,按序接收处理数据,标识哪些数据已经被处理)
  • 避免资源浪费。(每收到一个SYN就会创建一个连接)

SYN_SENT 状态模拟

packetdrill简介 (​​https://github.com/google/packetdrill)​

  • 执行系统调用(system call),对比返回值是否符合预期
  • 把数据包(packet)注入到内核协议栈,模拟协议栈收到包
  • 比较内核协议栈发出的包与预期是否相符
  • shell 命令
  • python 命令

SYN_SENT状态模拟脚本

//0s 创建一个 socket
+0 socket(...,SOCK_STREAM,IPPROTO_TCP)=3
//连接
+0 connect(3,...,...)= -1

tcpdump 抓包导出syn_sent.pcap 文件。通过wireshark打开

这就是TCP?_python_03


从抓包结果上看发送syn报文后,没有收到syn+ack报文,发送端会重试发送syn报文。重试次数可以在 ​/proc/sys/net/ipv4/tcp_syn_retries​ 中设置。


查看当时得tcp连接状态

这就是TCP?_github_04

SYN Flood 攻击

这就是TCP?_github_05

正常流程

  • Receiver接收到Sender的SYN 报文时,会将其加入到​SYN队列​;
  • 发送 SYN + ACK 给Sender,等待Sender回应 ACK 报文;
  • Receiver接收到 ACK 报文后,从​SYN队列​移除放入到​Accept队列​;
  • 应用调用 accpet()接口,从​Accept队列​取出的连接

异常流程

  • 受到SYN攻击得时候,Receive将收不到Sender发送得ACK报文,将导致​SYN队列​中的数据不能转移到​Accept队列
  • SYN队列​满之后,将会影响正常得连接请求

SYN Flood攻击防御

  • 通过增加​SYN 队列​长度,长度受到net.ipv4.tcp_max_syn_backlog、net.core.somaxconn、backlog 参数影响。再长得队列,面对恶意攻击也有被填满得时刻。
  • 减少SNY+ACK 重试次数,重试时间指数级避退(1s、2s、4s、8s、16s),重试完成之后才会将SYN-RECV连接关闭 通过/proc/sys/net/ipv4/tcp_synack_retries 设置。该选项对于误传SYN能有一点作用,面对恶意SYN请求,也顶不住。
  • SYN-Cookie 机制
  • SYN 队列​满之后,后续Receiver收到 SYN 包,不进入​SYN 队列​;
  • 计算出一个 cookie 值,再以 SYN + ACK 中的​序列号​返回Sender,
  • Receiver接收到Sender的应答报文时,Receiver会检查这个 ACK 包的合法性。如果合法,直接放入到​Accept 队列​。
  • SYN-Cookie 通过/proc/sys/net/ipv4/tcp_syncookies设置 默认1表示队列满时启用,0表示禁用,2表示始终启用。

四次挥手

这就是TCP?_python_06