Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

like image 390
David Stainton Avatar asked Nov 30 '14 22:11

David Stainton


People also ask

What is raw TCP packet?

Basically when you are capturing packets on an interface you have an associated link type to it (ethernet, 802.11, 802.15. 4, etc). Raw packet is used when you dont have any, the first bytes captured are directly the IPv6 or IPv4 header.

How do you code a raw socket?

Raw tcp socketsint s = socket (AF_INET, SOCK_RAW, IPPROTO_TCP); The above function call creates a raw socket of protocol TCP. This means that we have to provide the TCP header along with the data. The kernel or the network stack of Linux shall provide the IP header.

What is raw socket connection?

A raw socket is a type of socket that allows access to the underlying transport provider. This topic focuses only on raw sockets and the IPv4 and IPv6 protocols. This is because most other protocols with the exception of ATM do not support raw sockets.


1 Answers

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.

like image 177
user3813751 Avatar answered Oct 09 '22 10:10

user3813751