Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Report DNS tcp rtt values when available #545

Open
nshopik opened this issue Jan 16, 2024 · 1 comment
Open

Report DNS tcp rtt values when available #545

nshopik opened this issue Jan 16, 2024 · 1 comment
Labels
enhancement New feature or request

Comments

@nshopik
Copy link

nshopik commented Jan 16, 2024

Is your feature request related to a problem? Please describe.
I would like to enrich DNS data with client latency using TCP connections.

Describe the solution you'd like
It possible to extract data from live capture or pcap file of TCP handshake RTT values of client and report information, that basically is user latency (not server latency).

Describe alternatives you've considered
Currently I'm doing that using separate simple code I wrote using GO.

Additional context
Even though TCP connection is usually only about 6-15% of all requests this is enough sample data to make conclusion quality of your service towards specifics prefixes (with aggregation ipv4 /24 and ipv6 /48)

This specific paper goes more into details
https://ant.isi.edu/~johnh/PAPERS/Moura20a.pdf

Here what I did for now

package main

import (
	"fmt"
	"log"
	"os"
	"time"

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

type HandshakeInfo struct {
	SrcIP       string
	DstIP       string
	SrcPort     string
	DstPort     string
	SYNTime     time.Time
	ACKTime     time.Time
	RTT         time.Duration
	Completed   bool
}

func main() {
	if len(os.Args) != 2 {
		fmt.Println("Usage: go run main.go pcap_file")
		return
	}

	// Open the pcap file for reading
	fileName := os.Args[1]
	handle, err := pcap.OpenOffline(fileName)
	if err != nil {
		log.Fatal(err)
	}
	defer handle.Close()

	packetSource := gopacket.NewPacketSource(handle, handle.LinkType())

	// Print CSV header
	fmt.Println("UnixTime,SourceIP,SourcePort,DestIP,DestPort,RTT")

	// Map to store handshake information based on unique key
	handshakes := make(map[string]*HandshakeInfo)

	// Iterate through packets
	for packet := range packetSource.Packets() {
		networkLayer := packet.NetworkLayer()
		transportLayer := packet.TransportLayer()

		if networkLayer != nil && transportLayer != nil && transportLayer.LayerType() == layers.LayerTypeTCP {
			tcpLayer := transportLayer.(*layers.TCP)

			srcIP := networkLayer.NetworkFlow().Src().String()
			dstIP := networkLayer.NetworkFlow().Dst().String()

			srcPort := fmt.Sprintf("%d", tcpLayer.SrcPort)
			dstPort := fmt.Sprintf("%d", tcpLayer.DstPort)

			key := fmt.Sprintf("%s:%s-%s:%s", srcIP, srcPort, dstIP, dstPort)

			if _, found := handshakes[key]; !found {
				// New handshake detected
				if tcpLayer.SYN && !tcpLayer.ACK {
					handshakes[key] = &HandshakeInfo{
						SrcIP:   srcIP,
						DstIP:   dstIP,
						SrcPort: srcPort,
						DstPort: dstPort,
						SYNTime: packet.Metadata().Timestamp,
					}
				}
			} else {
				// Existing handshake
				handshake := handshakes[key]

				if tcpLayer.ACK && !handshake.Completed {
					handshake.ACKTime = packet.Metadata().Timestamp
					handshake.RTT = handshake.ACKTime.Sub(handshake.SYNTime)
					handshake.Completed = true

					// Print CSV format
					fmt.Printf("%d,%s,%s,%s,%s,%s\n", handshake.SYNTime.Unix(), handshake.SrcIP, handshake.SrcPort, handshake.DstIP, handshake.DstPort, handshake.RTT)
				}
			}
		}
	}
}
@dmachard dmachard added the enhancement New feature or request label Jan 17, 2024
@dmachard
Copy link
Owner

Feel free to submit a pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants