How to use Golang to compose raw TCP packet (using gopacket) and send it via raw socket

huangapple go评论87阅读模式
英文:

How to use Golang to compose raw TCP packet (using gopacket) and send it via raw socket

问题

我想使用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 创建TCP/IP数据包
	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 结束数据包创建

	// XXX 发送数据包
	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 失败了。这是 panic 的信息:

panic: invalid 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 [finalizer wait]: 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

英文:

I would like to craft custome TCP packets using gopacket and then
send them using raw sockets.

Here is a short and readable example go program that
demonstrates what I'd like to do:

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")
}

However running this program doesn't work... the SerializeLayer fails.
Here's the panic:

> panic: invalid 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 [finalizer wait]: 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

得分: 13

你的问题中提到了“构造自定义的TCP数据包”,但是你的代码明确表明你还想构造自定义的IP层3头部,这两者是有区别的。另外,你没有提到IPv4和IPv6的区别,但是你的代码似乎是针对IPv4的。

根据你的示例代码,我假设你想设置完整的IPv4头部。

截至Go 1.3.3和即将发布的Go 1.4,你不能使用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上也是可用的。不幸的是,Go核心包没有设置IP_HDRINCL套接字选项的方法,也没有设置其他IP套接字选项(如IP_TTL)的方法。我有一组私有补丁,可以在Go核心包中启用这个功能,但这对你没有帮助。

我相信以下包具有你所需的所有功能ipv4。请注意,这是一个庞大的包,我自己没有使用过。我进行了grep,并且它支持多个平台上的IP_HDRINCL。你可以调用NewRawConn来创建一个原始连接,这个函数会创建一个原始套接字并设置IP_HDRINCL套接字选项。

另请参阅这里:raw-sockets-in-go,以及他在这里编写的代码latency,以了解一个更简单的方法,可能适合你的需求,如果你只想设置TCP头部。然而,请注意,这段代码不允许你设置IPv4 IP头部中的IP地址,我猜你可能想要这样做。

英文:

You question says "craft custome[sic] TCP packets" but your code makes it clear you also want to craft custom IP layer 3 headers and there is a difference between the two. Also, you don't mention IPv4 vs IPv6, but again your code seems IPv4 specific.

Given your example code I'll assume you want to be set the full IPv4 header.

As of Go 1.3.3 and the soon to be released Go 1.4 you can't do what you want using the Core Go packages. To accomplish what you desire you need to do two things:

  1. You need to create a raw socket in Go. Contrary to other incorrect answers on this website you can create a raw socket in Go using one of net.ListenPacket, net.DialIP, or net.ListenIP.

For example:

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

creates a raw socket.

  1. If you want to set your own IPv4 layer 3 header you will need to set a socket option to enable functionality.

Your question don't state what OS and architecture you are using. On my laptop running 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 is also available on Linux. Unfortunately, Core Go does not have a way to set the IP_HDRINCL socket option nor does it have a way to set other IP socket options such as IP_TTL. I have a set of private patches that enable this functionality with Core Go but that won't help you.

I believe the following package has all the functionality you desire ipv4. Please note it's a large package and I haven't used it myself. I did grep and it supports IP_HDRINCL on multiple platforms. You want to call NewRawConn to create a raw connection and this function creates a raw socket and sets the IP_HDRINCL socket option.

See also here: raw-sockets-in-go and the code he wrote here latency to get a feel for a much simpler approach that might suit your needs if you just want to set TCP headers. However, please note this code doesn't let you set the IP addresses in the IPv4 IP header which I suspect you want to do.

huangapple
  • 本文由 发表于 2014年12月1日 06:23:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/27218989.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定