doutidi5037 2019-05-27 04:27
浏览 83

为什么当我在许多goroutine中发送一个TCP震动数据包(SYN)时,我在每个goroutine中都读取了许多响应数据包

I'm building a port scanner to check if some TCP port at remote machine is open.

To improve performance, I just build and send a TCP SYN packet to remote port instead of doing full 3-way handshake.And if I receive SYN-ACK packet successfully,then that port will be considered as open.

Here is a part of my code:

conn, _:= net.Dial("ip4:tcp", target)
tcpSynPacket := BuildTcpSynPacket() // here I build a tcp syn packet
conn.Write(tcpSynPacket.Marshal())
deadlineTime := time.NewTicker(time.Second * 2)
defer deadlineTime.Stop()
for {
    select {
case <-deadlineTime.C:
    return nil
default:
        bytes := make([]byte, 128)
        conn.SetReadDeadline(time.Now().Add(time.Millisecond * 200))
        readnum, err := conn.Read(bytes)
        responsePacket := parstTCPHeader(bytes[:readnum])
        matched := CHECK_IF_RESPONSE_MATCH_REQUEST(tcpSynPacket,responsePacket) // here I'll check if ack-no,src-port,dest-port in tcpSynPacket match seq-no,dest-port,src-port in response packet
        if !matched {
            // unmatched packets,may response for another routine
            continue
        }
        if responsePacket.rst_flg == 1 {
            // the port would be consider as close
            // build func return struct,and return
            ....
        }else {
            // the port would be consider as open
            // build func return struct,and return
            ....
        }
}

There are not for loop and CHECK_IF_RESPONSE_MATCH_REQUEST statement at old code.But when I do stress test, I found it's necessary.

Let's say we'll test if port 80 is open at 66.220.146.94.I open 1000 goroutines to call above code.

goroutine1: ack-no=11111

goroutine2: ack-no=22222

goroutine2: ack-no=33333

...

Then I found that in every goroutine,the following statement

readnum, err := conn.Read(bytes)
responsePacket := parstTCPHeader(bytes[:readnum])

will read all response packet even the read packet do not match the syn packet sent at current goroutine.

For example, at goroutine1, I sent syn packet(ack-no=11111),and read from conn. Then I found the seq-no in read packet could be 11112,22223,33334... So I add a loop and some check logic at CHECK_IF_RESPONSE_MATCH_REQUEST.

But the loop read and check logic make cpu so high.

Here is a test result(I run it every 60 seconds)

cpu%-runseconds

Top 10 cpu cost using 3-way shakehand when open 1000 goroutine(Duration: 60s, Total samples = 780ms ( 1.30%)):

flame graph(3-way shakehand port scan)

Top 10 cpu cost using tcp syn port scan when open 1000 goroutine(Duration: 60s, Total samples = 30.51s (50.85%)):

flame graph(tcp syn port scan)

What I want to know is that:

1.Why conn.Read(bytes) read all response packets in every goroutine?Is net.Dial("ip4:tcp", targetip) correct?

2.Is there a lower-cost way to do periodic port scan(every 60 seconds) without doing 3-way shakehand

  • 写回答

1条回答 默认 最新

  • douzhoubing2805 2019-05-28 10:00
    关注

    Quick answer:

    1. close conn at the end of the routine. net.Dial("ip4:tcp", targetip) is correct
    2. reuse local ephemeral port in the socket pairs local:port remote:port when you craft the syn packet

    Notes on 1:

    Closing conn might not be the best idea, should do 2 above for these benefits:

    1. not wasting cpu time for closing local ports
    2. when you make multiple conns to the same addr too quickly and let kernel assign local port, the kernel ip stack might actually plays up - for more details, digging in this keyword TCP simultaneous connect
    评论

报告相同问题?

悬赏问题

  • ¥50 导入文件到网吧的电脑并且在重启之后不会被恢复
  • ¥15 (希望可以解决问题)ma和mb文件无法正常打开,打开后是空白,但是有正常内存占用,但可以在打开Maya应用程序后打开场景ma和mb格式。
  • ¥20 ML307A在使用AT命令连接EMQX平台的MQTT时被拒绝
  • ¥20 腾讯企业邮箱邮件可以恢复么
  • ¥15 有人知道怎么将自己的迁移策略布到edgecloudsim上使用吗?
  • ¥15 错误 LNK2001 无法解析的外部符号
  • ¥50 安装pyaudiokits失败
  • ¥15 计组这些题应该咋做呀
  • ¥60 更换迈创SOL6M4AE卡的时候,驱动要重新装才能使用,怎么解决?
  • ¥15 让node服务器有自动加载文件的功能