【问题标题】:How to use Golang to compose raw TCP packet (using gopacket) and send it via raw socket如何使用 Golang 组合原始 TCP 数据包(使用 gopacket)并通过原始套接字发送
【发布时间】:2015-01-28 21:40:52
【问题描述】:

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

这是一个简短易读的 go 程序示例, 展示我想做的事:

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 [运行]: 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

【问题讨论】:

  • OK...我修复了“invalid src ip”错误...这是由于 ParseIP 返回了错误的类型...它需要使用它的 .To4() 方法进行转换;- )
  • 目前我的简单示例是发送精心制作的 TCP/IP 数据包......但是端口号和其他部分格式不正确。也许 TCP 端口的“字节序”设置错误?这里简单的简洁示例:github.com/david415/HoneyBadger/blob/…
  • 这个新的提交表明,通过将 srcPort 和 dstPort 与数据包解析后的预期值进行比较,生成的 TCP/IP 数据包格式错误:github.com/david415/HoneyBadger/blob/…

标签: sockets go tcp raw-sockets gopacket


【解决方案1】:

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

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

从 Go 1.3.3 和即将发布的 Go 1.4 开始,您无法使用 Core Go 包做您想做的事情。要完成您的愿望,您需要做两件事:

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

例如:

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

创建一个原始套接字。

  1. 如果要设置自己的 IPv4 第 3 层标头,则需要设置套接字选项以启用功能。

您的问题没有说明您使用的是什么操作系统和架构。在我运行 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 地址,我怀疑您想要这样做。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-02
  • 2018-11-16
  • 2012-10-11
  • 1970-01-01
相关资源
最近更新 更多