如何使用Golang组成原始TCP数据包(使用gopacket)并通过原始套接字发送它

时间:2014-11-30 22:23:16

标签: sockets go tcp raw-sockets gopacket

我想使用gopacket制作自定义TCP数据包然后 使用原始套接字发送它们。

这是一个简短易读的示例程序 演示了我想做的事情:

package main

import (
    "code.google.com/p/gopacket"
    "code.google.com/p/gopacket/examples/util"
    "code.google.com/p/gopacket/layers"
    "log"
    "net"
)

func main() {
    defer util.Run()()

    // XXX create tcp/ip packet
    srcIP := net.ParseIP("127.0.0.1")
    dstIP := net.ParseIP("192.168.0.1")
    //srcIPaddr := net.IPAddr{
    //  IP: srcIP,
    //}
    dstIPaddr := net.IPAddr{
        IP: dstIP,
    }
    ipLayer := layers.IPv4{
        SrcIP:    srcIP,
        DstIP:    dstIP,
        Protocol: layers.IPProtocolTCP,
    }
    tcpLayer := layers.TCP{
        SrcPort: layers.TCPPort(666),
        DstPort: layers.TCPPort(22),
        SYN:     true,
    }
    tcpLayer.SetNetworkLayerForChecksum(&ipLayer)
    buf := gopacket.NewSerializeBuffer()
    opts := gopacket.SerializeOptions{
        FixLengths:       true,
        ComputeChecksums: true,
    }
    err := gopacket.SerializeLayers(buf, opts, &ipLayer, &tcpLayer)
    if err != nil {
        panic(err)
    }
    // XXX end of packet creation

    // XXX send packet
    ipConn, err := net.ListenPacket("ip4:tcp", "0.0.0.0")
    if err != nil {
        panic(err)
    }
    _, err = ipConn.WriteTo(buf.Bytes(), &dstIPaddr)
    if err != nil {
        panic(err)
    }
    log.Print("packet sent!\n")
}

但是运行此程序不起作用... SerializeLayer失败。 这是恐慌:

  

恐慌:无效的src IP 127.0.0.1

     

goroutine 16 [running]:runtime.panic(0x5bb020,0xc2090723e0)           /home/human/golang-empire/go/src/pkg/runtime/panic.c:279 + 0xf5 main.main()           /home/human/golang-empire/gopkg/src/github.com/david415/HoneyBadger/packetSendTest.go:41 + 0x464

     

goroutine 19 [终结者等待]:runtime.park(0x413cc0,0x7bc6c0,   0x7bb189)           /home/human/golang-empire/go/src/pkg/runtime/proc.c:1369+ 0x89 runtime.parkunlock(0x7bc6c0,0x7bb189)           /home/human/golang-empire/go/src/pkg/runtime/proc.c:1385+ 0x3b runfinq()           /home/human/golang-empire/go/src/pkg/runtime/mgc0.c:2644 + 0xcf runtime.goexit()           /home/human/golang-empire/go/src/pkg/runtime/proc.c:1445

1 个答案:

答案 0 :(得分:9)

您的问题是“工艺客户[原文如此] TCP数据包”,但您的代码清楚地表明您还希望制作自定义IP第3层标头,两者之间存在差异。此外,您没有提到IPv4与IPv6,但您的代码似乎也是IPv4特定的。

鉴于您的示例代码,我假设您要设置完整的IPv4标头。

从Go 1.3.3和即将发布的Go 1.4开始,你无法使用Core Go软件包做你想做的事。为了达到你想要的目标,你需要做两件事:

  1. 您需要在Go中创建一个原始套接字。与此网站上的其他错误答案相反,您可以使用net.ListenPacketnet.DialIPnet.ListenIP之一在Go中创建原始套接字。
  2. 例如:

    conn, err := net.ListenIP("ip4:tcp", netaddr)
    if err != nil {
        log.Fatalf("ListenIP: %s\n", err)
    }
    

    创建一个原始套接字。

    1. 如果要设置自己的IPv4第3层标头,则需要设置套接字选项以启用功能。
    2. 您的问题没有说明您正在使用的操作系统和架构。在运行Mac OS X的笔记本电脑上:

      % man ip
      . . .
      Outgoing packets automatically have an IP header prepended to them
      (based on the destination address and the protocol number the socket is created with),
      unless the IP_HDRINCL option has been set.
      

      IP_HDRINCL也可在Linux上使用。不幸的是,Core Go没有办法设置IP_HDRINCL套接字选项,也没有办法设置其他IP套接字选项,例如IP_TTL。我有一组私有补丁,可以通过Core Go启用此功能,但这对您没有帮助。

      我相信以下软件包具有您所需的所有功能ipv4。请注意它是一个大包装,我自己没有使用它。我做了grep,它在多个平台上支持IP_HDRINCL。您想调用NewRawConn来创建原始连接,此函数创建一个原始套接字并设置IP_HDRINCL套接字选项。

      另请参阅此处:raw-sockets-in-go以及他在此处编写的代码latency,以便了解一种更简单的方法,如果您只想设置TCP标头,则可能符合您的需求。但请注意,此代码不允许您在IPv4 IP标头中设置我怀疑您要执行的IP地址。