[Go] Try capturing TCP packets
Tags: go, pcap, beginners
Published: 2022-10-12 12:50:10

## Intro
I will try capturing TCP packets.

## TCP socket communication
To capture packets, I will create applications for TCP socket communication.

#### tcp-server-sample
```go
package main

import (
	"fmt"
	"io"
	"log"
	"net"
)

func main() {
	ln, err := net.Listen("tcp", ":8099")
	if err != nil {
		fmt.Println("cannot listen", err)
	}
	for {
		conn, e := ln.Accept()
		if e != nil {
			log.Fatal(e)
			return
		}
		go echo_handler(conn)
	}
}

func echo_handler(conn net.Conn) {
	defer conn.Close()
	io.Copy(conn, conn)
}
```

#### tcp-client-sample
```go
package main

import (
	"fmt"
	"log"
	"net"
	"time"
)

func main() {
	ticker := time.NewTicker(time.Millisecond * 1000)
	var conn net.Conn
	var err error
	defer func() {
		ticker.Stop()
		if conn != nil {
			conn.Close()
		}
	}()

	count := 0
	for range ticker.C {
		if count > 1000 {
			break
		}
		conn, err = net.Dial("tcp", ":8099")
		if err != nil {
			log.Fatal(err)
		}
		sendData := []byte(fmt.Sprintf("Hello_%d", count))
		_, err = conn.Write(sendData)
		if err != nil {
			log.Fatal(err)
		}
		buf := make([]byte, 1024)
		n, err := conn.Read(buf)
		if err != nil {
			log.Fatal(err)
		}
		log.Println(string(buf[:n]))
		conn.Close()
		conn = nil
		count += 1
	}
}
```

* [net package - net - Go Packages](https://pkg.go.dev/net)
* [Go言語を使ったTCPクライアントの作り方 - Qiita](https://qiita.com/tutuz/items/e875d8ea3c31450195a7)

## Capturing TCP packets

I will capturing TCP packets by [gopacket/pcap](https://github.com/google/gopacket).

### libpcap-dev
Because I had already installed Wireshark on Windows and Xubuntu, I didn't need install any other software for Windows.

But when I tried executing gopacket/pcap on Xubuntu, I would get error.

```
# github.com/google/gopacket/pcap
../../../pkg/mod/github.com/google/gopacket@v1.1.19/pcap/pcap_unix.go:34:10: fatal error: pcap.h: No such file or directory
   34 | #include <pcap.h>
      |          ^~~~~~~~
compilation terminated.
```

I should install "libpcap-dev".

```
sudo apt install -y libpcap-dev
```

* [ fatal error: pcap.h: No such file or directory #125 -  projectdiscovery/naabu - GitHub](https://github.com/projectdiscovery/naabu/issues/125)

### Capturing sample

#### tcp-capture-sample
```go
package main

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pcap"
)

func main() {
	loopbackDevice := getLoopbackDeviceName()
	if loopbackDevice == "" {
		log.Println("No loopback devices")
		return
	}
	handle, err := pcap.OpenLive(loopbackDevice, 1024, false, 3*time.Second)
	if err != nil {
		log.Fatal(err)
	}
	defer handle.Close()
	// Filtering capture targets
	err = handle.SetBPFFilter("tcp and port 8099")
	if err != nil {
		log.Fatal(err)
	}
	packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
   // Get decoded packets through chann
	for packet := range packetSource.Packets() {
		log.Println("S-------S")
		log.Println(packet)
		log.Println("E-------E")
	}
}
func getLoopbackDeviceName() string {
	// Find all devices
	devices, err := pcap.FindAllDevs()
	if err != nil {
		log.Fatal(err.Error())
	}
	for _, device := range devices {
		// for Windows
		if strings.Contains(strings.ToLower(device.Name), "loopback") ||
			// for Xubuntu
			device.Name == "lo" {
			return device.Name
		}
	}
	return ""
}
```

#### Results
```
...
2022/10/12 21:19:27 S-------S
2022/10/12 21:19:27 PACKET: 74 bytes, wire length 74 cap length 74 @ 2022-10-12 21:19:27.764308 +0900 JST
- Layer 1 (14 bytes) = Ethernet	{Contents=[..14..] Payload=[..60..] SrcMAC=00:00:00:00:00:00 DstMAC=00:00:00:00:00:00 EthernetType=IPv4 Length=0}
- Layer 2 (20 bytes) = IPv4	{Contents=[..20..] Payload=[..40..] Version=4 IHL=5 TOS=0 Length=60 Id=36925 Flags=DF FragOffset=0 TTL=64 Protocol=TCP Checksum=44156 SrcIP=127.0.0.1 DstIP=127.0.0.1 Options=[] Padding=[]}
- Layer 3 (32 bytes) = TCP	{Contents=[..32..] Payload=[..8..] SrcPort=8099 DstPort=57632 Seq=2020353806 Ack=775619635 DataOffset=8 FIN=false SYN=false RST=false PSH=true ACK=true URG=false ECE=false CWR=false NS=false Window=512 Checksum=65072 Urgent=0 Options=[TCPOption(NOP:), TCPOption(NOP:), TCPOption(Timestamps:3776381494/3776381494 0xe1170236e1170236)] Padding=[]}
- Layer 4 (08 bytes) = Payload	8 byte(s)

2022/10/12 21:19:27 E-------E
...
```

According to the result, the captured packet was decoded into several layer data.

```go
...

func main() {
...	
   // Get decoded packets through chann
	for packet := range packetSource.Packets() {
		printPacketInfo(packet)
	}
}
...
func printPacketInfo(packet gopacket.Packet) {
   // TCP
	tcpLayer := packet.Layer(layers.LayerTypeTCP)
	if tcpLayer != nil {
		tcp, _ := tcpLayer.(*layers.TCP)

		fmt.Printf("From port %d to %d\n", tcp.SrcPort, tcp.DstPort)
		fmt.Println("Sequence number: ", tcp.Seq)
	}
   // TCP/IPV4 Payload
	applicationLayer := packet.ApplicationLayer()
	if applicationLayer != nil {
		fmt.Println("Application layer/Payload found.")
		fmt.Printf("%s\n", applicationLayer.Payload())
	}

	// Check for errors
	if err := packet.ErrorLayer(); err != nil {
		fmt.Println("Error decoding some part of the packet:", err)
	}
}
```

* [google/gopacket - GitHub](https://github.com/google/gopacket)
* [GoPacket - github.com/google/gopacket - Go Packages](https://pkg.go.dev/github.com/google/gopacket)
* [Packet Capture, Injection, and Analysis with Gopacket - DevDungeon](https://www.devdungeon.com/content/packet-capture-injection-and-analysis-gopacket)
* [Goでパケットキャプチャを実践してみる - Qiita](https://qiita.com/kkyouhei/items/846e74c6a9653b069e5f)
* [RFC 9293 - Transmission Control Protocol (TCP)](https://www.rfc-editor.org/rfc/rfc9293.html)
*[RFC791 - INTERNET PROTOCOL](https://www.rfc-editor.org/rfc/rfc791.html)