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
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:
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.
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.