一个很特别的TCP选项TCP_DEFER_ACCEPT

昨天自己在测试端口是否能链接,发现下载服务器链接后,不会在制定的时间内被踢下线。觉得是个bug,决定调试一下,看了一下Jovi下载服务的代码,里面有相应的超时处理,觉得有点怪,继续检查日志,发现日志里面完全没有Accept处理的过程。简单叙述症状就是客户端Telnet成功,服务器端没有任何相应。

于是开始怀疑防火墙,换IP,换地点测试了半天无果。还是Sonicmao找到了问题原因。

 

Jovi的代码上有这样一段:

 

#ifndef WIN32

 

    //TCP_DEFER_ACCEPT

 

    int val = 1;

 

    ret = peer_acceptor_.set_option(IPPROTO_TCP, TCP_DEFER_ACCEPT, &val, sizeof(val));

 

    ACE_DEBUG((LM_INFO," set_option TCP_DEFER_ACCEPT val(%d) ret(%d). /n", val, ret));

 

#endif

 

查询资料,TCP_DEFER_ACCEPT是一个很有趣的选项,

Linux 提供的一个特殊 setsockopt , 在 accept socket 上面,只有当实际收到了数据,才唤醒正在 accept 的进程,可以减少一些无聊的上下文切换。代码如下。

val = 5;

setsockopt(srv_socket->fd, SOL_TCP, TCP_DEFER_ACCEPT, &val, sizeof(val)) ;

里面 val 的单位是秒,注意如果打开这个功能,kernel val 秒之内还没有收到数据,不会继续唤醒进程,而是直接丢弃连接。

 

经过测试发现,设置TCP_DEFER_ACCEPT选项后,服务器受到一个CONNECT请求后,操作系统不会Accept,也不会创建IO句柄。操作系统应该在若干秒,(但肯定远远大于上面设置的1s) 后,会释放相关的链接。但没有同时关闭相应的端口,所以客户端会一直以为处于链接状态。如果Connect后面马上有后续的发送数据,那么服务器会调用Accept接收这个链接端口。

 

感觉了一下,这个端口设置对于CONNECT链接上来而又什么都不干的攻击方式处理很有效。我们原来的代码都是先允许链接,然后再进行超时处理,比他这个有点Out了。不过这个选项可能会导致定位某些问题麻烦。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
可以使用golang中的net库和raw socket来构造一个TCP SYN包并添加TCP选项字段。以下是一个示例代码: ```go package main import ( "net" "syscall" ) func main() { // 构造TCP SYN包 srcIP := net.ParseIP("192.168.1.100") dstIP := net.ParseIP("192.168.1.1") srcPort := 12345 dstPort := 80 seqNum := uint32(123456789) tcpHeaderLen := 20 tcpFlags := syscall.TCP_SYN // 构造TCP选项字段 tcpOptions := []byte{0x02, 0x04, 0x05, 0xb4, 0x04, 0x02, 0x08, 0x0a, 0x00, 0x0f, 0x48, 0x61, 0x63, 0x6b, 0x65, 0x72, 0x20, 0x57, 0x65, 0x62, 0x21} // 构造TCP头部 tcpHeader := make([]byte, tcpHeaderLen) tcpHeader[0] = byte(srcPort >> 8) tcpHeader[1] = byte(srcPort) tcpHeader[2] = byte(dstPort >> 8) tcpHeader[3] = byte(dstPort) tcpHeader[4] = byte(seqNum >> 24) tcpHeader[5] = byte(seqNum >> 16) tcpHeader[6] = byte(seqNum >> 8) tcpHeader[7] = byte(seqNum) tcpHeader[8] = byte(0) tcpHeader[9] = byte(0) tcpHeader[10] = byte(tcpHeaderLen >> 8) tcpHeader[11] = byte(tcpHeaderLen) tcpHeader[13] = byte(tcpFlags) tcpHeader[14] = byte(0x10) tcpHeader[15] = byte(0xff) // 添加TCP选项字段 copy(tcpHeader[tcpHeaderLen:], tcpOptions) // 构造IP头部 ipHeaderLen := 20 ipVersion := 4 ipHeader := make([]byte, ipHeaderLen) ipHeader[0] = byte(ipVersion<<4 | ipHeaderLen>>2) ipHeader[1] = byte(0) ipHeader[2] = byte(0) ipHeader[3] = byte(0) ipHeader[4] = byte(0) ipHeader[5] = byte(0) ipHeader[6] = byte(0) ipHeader[7] = byte(0) ipHeader[8] = byte(0) ipHeader[9] = byte(0) ipHeader[10] = byte(0xff) ipHeader[11] = byte(0xff) copy(ipHeader[12:16], srcIP.To4()) copy(ipHeader[16:20], dstIP.To4()) // 计算IP头部校验和 checksum := checksum(ipHeader) // 添加IP头部校验和 ipHeader[10] = byte(checksum >> 8) ipHeader[11] = byte(checksum) // 合并IP和TCP头部 packet := make([]byte, len(ipHeader)+len(tcpHeader)) copy(packet, ipHeader) copy(packet[len(ipHeader):], tcpHeader) // 发送TCP SYN包 conn, err := net.Dial("ip4:tcp", dstIP.String()) if err != nil { panic(err) } defer conn.Close() _, err = conn.Write(packet) if err != nil { panic(err) } } // 计算校验和 func checksum(data []byte) uint16 { var sum uint32 for i := 0; i < len(data)-1; i += 2 { sum += uint32(data[i])<<8 | uint32(data[i+1]) } if len(data)%2 != 0 { sum += uint32(data[len(data)-1]) << 8 } for (sum >> 16) > 0 { sum = (sum & 0xffff) + (sum >> 16) } return ^uint16(sum) } ``` 这段代码构造了一个源IP为192.168.1.100,目标IP为192.168.1.1,源端口为12345,目标端口为80的TCP SYN包,并添加了TCP选项字段。在发送包之前,需要使用`net.Dial("ip4:tcp", dstIP.String())`连接到目标IP地址,然后将构造好的TCP SYN包写入连接中即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值