全连接半连接
半连接:syn queue,完成第一次握手后,服务端收到后回复SYN+ACK后,服务端进入SYN_RCVD状态,连接会放入半连接队列。当服务端发送SYN_ACK后将会开启一个定时器,如果超时没有收到客户端的ACK,将会重发SYN_ACK包。重传的次数由/proc/sys/net/ipv4/tcp_synack_retries控制,默认是5次。
全连接:accept queue,完成三次握手后,连接会移除半连接队列,并将其添加到 accept 队列,等待进程调用 accept 函数时把连接取出来。
tcp与半连接全连接队列关系图
配置
// 半连接配置-Linux 默认 1024,jdk默认50,redis默认512
max(/proc/sys/net/ipv4/tcp_max_syn_backlog, 64)
// 第二次握手重传配置
/proc/sys/net/ipv4/tcp_synack_retries
// 全连接配置-Linux somaxconn默认128,mac 1024
min(/proc/sys/net/core/somaxconn, backlog)
默认情况下,全连接队列满以后,服务端会忽略客户端的 ACK,随后会重传SYN+ACK,也可以修改这种行为,修改配置: /proc/sys/net/ipv4/tcp_abort_on_overflow
- tcp_abort_on_overflow=0:表示三次握手最后一步全连接队列满以后服务端会丢掉客户端发过来的ACK,服务端随后会进行重传SYN+ACK。
- tcp_abort_on_overflow=1:表示全连接队列满以后服务端发送RST给客户端,直接释放资源
监控
netstat
// 查看网络按每秒粒度的统计数据
[dev@host0 ~]$ netstat -s | grep -i listen
1039 times the listen queue of a socket overflowed // 全连接溢出次数
1039 SYNs to LISTEN sockets ignored // 半连接溢出次数
ss
// 查看监听状态下本地端口小于14066全连接队列
ss -lnt sport \< :14066
// 查看监听状态下本地端口14066全连接队列
ss -lnt sport = :14066
Send-Q,Recv-Q含义见:https://blog.csdn.net/u010597819/article/details/108819166
syn-flood攻击
攻击原理:DDos攻击
syn-flood攻击(典型的DDos攻击),攻击原理为发送大量伪造sync报文,服务器响应确认报文至真实客户端,真实客户端没有发起该连接请求会丢弃确认报文,此时服务端继续重试(默认63s),由于超时导致服务器端半连接队列打满,致使服务器资源全部被占用,无法接收正常的连接,即服务不可用
最常见的SYN Flood攻击,客户端在短时间内发送大量的TCP SYN包至服务器,服务器会为每个TCP SYN包分配一个特定的数据区,只要这些SYN包具有不同的源地址(这一点对于攻击者来说是很容易伪造的)。这将给TCP服务器系统造成很大的系统负担,最终导致系统不能正常工作。
防御方案:SYN cookies 算法
引用:https://en.wikipedia.org/wiki/SYN_cookies
原理
可以理解为:syn cookies算法替换tcp协议中专门的数据区(半连接队列)
SYN Cookie是对TCP服务器端的三次握手协议作一些修改,专门用来防范SYN Flood攻击的一种手段。它的原理是,在TCP服务器收到TCP SYN包并返回TCP SYN+ACK包时,不分配一个专门的数据区,而是根据这个SYN包计算出一个cookie值。在收到TCP ACK包时,TCP服务器在根据那个cookie值检查这个TCP ACK包的合法性。如果合法,再分配专门的数据区进行处理未来的TCP连接。
实现
发起一个 TCP 连接时,客户端将一个 TCP SYN 包发送给服务器。作为响应,服务器将 TCP SYN + ACK 包返回给客户端。此数据包中有一个序号,它被 TCP 用来重新组装数据流。根据 TCP 规范,由端点发送的第一个序号可以是由该端点决定的任何值。
SYN Cookies 是根据以下规则构造的初始序号:
- t: 为一个缓慢递增的时间戳(通常为time()>>6,提供 64 秒的分辨率)(TCP是可靠协议,失败时会重传报文,默认重试5次,重试间隔时间分别为:1s + 2s + 4s + 8s +16s = 31s,第5次发出后还要等32s才知道第5次也超时了,所以一共是31 + 32 = 63s)
- m: 为服务器会在 SYN 队列条目中存储的最大分段大小(Maximum segment size)
- s: 为一个加密散列函数对服务器和客户端各自的 IP 地址和端口号以及t进行运算的结果(即s = hash(sip,dip,sport,dport,t))。返回得到的数值s必须是一个24位值
初始 TCP 序号,也就是所谓的SYN cookie,按照如下算法得到:
- 高五位:**t ** mod 32(对32取余)
- 中三位:**m ** 编码后的数值
- 低24位:**s ** 本身
注:由于_m_必须用 3 位进行编码,服务器在启用了 SYN Cookie 时只能为_m_发送八种不同的数值。
根据 TCP 规范,当客户端发回 TCP ACK 包给服务器以响应服务器的 SYN + ACK 包时,客户端必须使用由服务器发送的初始序号加1作为数据包中的确认号。服务器接着从确认号中减去 1 以便还原向客户端发送的原始 SYN Cookie。
接下来服务器进行以下检查:
- 配合现在时间t来检查连接是否过期。
- 重新计算s来确认这是不是一个有效的 SYN Cookie。
- 从 3 位编码中解码m,以便之后用来重建 SYN 队列条目。在此之后,连接照常进行。