Files
yoloyolo/.planning/phases/01-capture-and-classification/01-01-PLAN.md
T

12 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
01-capture-and-classification 01 execute 1
go.mod
go.sum
classify/rules.go
classify/classifier.go
classify/classifier_test.go
classify/types.go
true
CLAS-01
truths artifacts key_links
Each of 10+ protocol classes (ICMP, DNS, HTTPS, HTTP, SSH, SMTP, NTP, DHCP, other-TCP, other-UDP, unknown) is correctly identified from packet headers
Classification rules are stored in a config-driven slice/struct, not hardcoded switch statements
Unrecognized traffic is grouped as 'unknown' class
path provides exports
classify/types.go TrafficClass type, ClassifiedPacket struct, WindowSnapshot struct
TrafficClass
ClassifiedPacket
WindowSnapshot
path provides contains
classify/rules.go Rule struct and DefaultRules slice with 10+ protocol rules DefaultRules
path provides exports
classify/classifier.go Classify function that matches packets against rule table
Classify
NewClassifier
path provides min_lines
classify/classifier_test.go Unit tests for all 10+ protocol classes 80
from to via pattern
classify/classifier.go classify/rules.go Classifier uses DefaultRules slice DefaultRules
from to via pattern
classify/classifier.go classify/types.go Returns TrafficClass from Classify function TrafficClass
Bootstrap Go project and implement the protocol classification engine with full test coverage.

Purpose: Establish the Go module, core types shared across all packages, and the config-driven classifier that maps packets to one of 10+ traffic classes. This is the foundation every other plan depends on.

Output: Compiling Go module with classify package, passing unit tests for all protocol classes.

<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/01-capture-and-classification/01-CONTEXT.md @.planning/phases/01-capture-and-classification/01-RESEARCH.md Task 1: Install Go 1.24, initialize module, and create shared types go.mod, classify/types.go .planning/phases/01-capture-and-classification/01-RESEARCH.md 1. Install Go 1.24+ from https://go.dev/dl/ (NOT apt which gives 1.22). Download the linux-arm64 tarball: - wget https://go.dev/dl/go1.24.1.linux-arm64.tar.gz - sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.24.1.linux-arm64.tar.gz - Ensure /usr/local/go/bin is in PATH (export PATH=$PATH:/usr/local/go/bin) - Verify: `go version` must show go1.24.x
  1. Initialize the Go module:

    • cd /home/dev/workspace/yoloyolo
    • go mod init github.com/netsynth/netsynth
    • mkdir -p classify capture aggregate cmd/netsynth
  2. Create classify/types.go with the following types (these are the contracts for all packages):

package classify

// TrafficClass represents a classified network traffic category.
type TrafficClass string

const (
    ClassICMP     TrafficClass = "ICMP"
    ClassDNS      TrafficClass = "DNS"
    ClassHTTPS    TrafficClass = "HTTPS"
    ClassHTTP     TrafficClass = "HTTP"
    ClassSSH      TrafficClass = "SSH"
    ClassSMTP     TrafficClass = "SMTP"
    ClassNTP      TrafficClass = "NTP"
    ClassDHCP     TrafficClass = "DHCP"
    ClassOtherTCP TrafficClass = "other-TCP"
    ClassOtherUDP TrafficClass = "other-UDP"
    ClassUnknown  TrafficClass = "unknown"
)

// AllClasses returns all known traffic classes in display order.
func AllClasses() []TrafficClass {
    return []TrafficClass{
        ClassICMP, ClassDNS, ClassHTTPS, ClassHTTP, ClassSSH,
        ClassSMTP, ClassNTP, ClassDHCP, ClassOtherTCP, ClassOtherUDP, ClassUnknown,
    }
}

// ClassifiedPacket pairs a raw packet's classification result with metadata.
type ClassifiedPacket struct {
    Class     TrafficClass
    SrcPort   uint16
    DstPort   uint16
    Protocol  string // "tcp", "udp", "icmp"
    Length    int
}

// WindowSnapshot holds aggregated packet counts for a time window.
// This is the contract between the aggregation stage and future audio synthesis (Phase 2).
type WindowSnapshot struct {
    Counts      map[TrafficClass]int64
    TotalPackets int64
    WindowIndex  int
}
  1. Fetch Phase 1 dependencies:
    • go get github.com/gopacket/gopacket@v1.5.0
    • go get github.com/packetcap/go-pcap@latest
    • go get github.com/spf13/cobra@v1.10.2
    • go mod tidy cd /home/dev/workspace/yoloyolo && go version | grep -q "go1.24" && go build ./classify/... <acceptance_criteria>
    • go version output contains "go1.24"
    • go.mod contains module github.com/netsynth/netsynth
    • go.mod contains github.com/gopacket/gopacket
    • go.mod contains github.com/packetcap/go-pcap
    • go.mod contains github.com/spf13/cobra
    • classify/types.go contains type TrafficClass string
    • classify/types.go contains ClassICMP, ClassDNS, ClassHTTPS, ClassHTTP, ClassSSH, ClassSMTP, ClassNTP, ClassDHCP, ClassOtherTCP, ClassOtherUDP, ClassUnknown
    • classify/types.go contains type ClassifiedPacket struct
    • classify/types.go contains type WindowSnapshot struct
    • go build ./classify/... exits 0 </acceptance_criteria> Go 1.24 installed, module initialized with all Phase 1 deps, classify/types.go compiles with all 11 traffic classes and shared types.
Task 2: Implement config-driven classifier with tests for all protocol classes classify/rules.go, classify/classifier.go, classify/classifier_test.go classify/types.go .planning/phases/01-capture-and-classification/01-RESEARCH.md - TestClassifyICMP: ICMP packet (no port) -> ClassICMP - TestClassifyDNS_UDP: UDP dst port 53 -> ClassDNS - TestClassifyDNS_TCP: TCP dst port 53 -> ClassDNS - TestClassifyHTTPS: TCP dst port 443 -> ClassHTTPS - TestClassifyHTTP: TCP dst port 80 -> ClassHTTP - TestClassifySSH: TCP dst port 22 -> ClassSSH - TestClassifySMTP: TCP dst port 25 -> ClassSMTP - TestClassifyNTP: UDP dst port 123 -> ClassNTP - TestClassifyDHCP: UDP dst port 67 or 68 -> ClassDHCP - TestClassifyOtherTCP: TCP dst port 8080 (unmatched) -> ClassOtherTCP - TestClassifyOtherUDP: UDP dst port 9999 (unmatched) -> ClassOtherUDP - TestClassifyUnknown: Packet with no TCP/UDP/ICMP layer -> ClassUnknown - TestRulesAreOrderDependent: First matching rule wins 1. Create classify/rules.go with Rule struct and DefaultRules per D-01 and D-02:
package classify

// Rule defines a classification rule. Protocol is "tcp", "udp", or "icmp".
// DstPort 0 means match any port for this protocol.
// Rules are evaluated in order; first match wins.
type Rule struct {
    Protocol string
    DstPort  uint16
    Class    TrafficClass
}

// DefaultRules is the D-01 protocol map — ordered, first-match-wins.
// Per D-02: config-driven slice, not a switch statement.
var DefaultRules = []Rule{
    {Protocol: "icmp", DstPort: 0, Class: ClassICMP},
    {Protocol: "udp", DstPort: 53, Class: ClassDNS},
    {Protocol: "tcp", DstPort: 53, Class: ClassDNS},
    {Protocol: "tcp", DstPort: 443, Class: ClassHTTPS},
    {Protocol: "tcp", DstPort: 80, Class: ClassHTTP},
    {Protocol: "tcp", DstPort: 22, Class: ClassSSH},
    {Protocol: "tcp", DstPort: 25, Class: ClassSMTP},
    {Protocol: "udp", DstPort: 123, Class: ClassNTP},
    {Protocol: "udp", DstPort: 67, Class: ClassDHCP},
    {Protocol: "udp", DstPort: 68, Class: ClassDHCP},
    // Catch-alls (must be last):
    {Protocol: "tcp", DstPort: 0, Class: ClassOtherTCP},
    {Protocol: "udp", DstPort: 0, Class: ClassOtherUDP},
}
  1. Create classify/classifier.go:
package classify

import (
    "github.com/gopacket/gopacket"
    "github.com/gopacket/gopacket/layers"
)

// Classifier classifies packets against a rule table.
type Classifier struct {
    rules []Rule
}

// NewClassifier creates a classifier with the given rules.
func NewClassifier(rules []Rule) *Classifier {
    return &Classifier{rules: rules}
}

// Classify inspects a gopacket.Packet and returns a ClassifiedPacket.
// It checks ICMP first, then extracts TCP/UDP port info and matches against rules.
// Per D-03: returns ClassUnknown if no rule matches.
func (c *Classifier) Classify(pkt gopacket.Packet) ClassifiedPacket {
    result := ClassifiedPacket{
        Class:  ClassUnknown,
        Length: len(pkt.Data()),
    }

    // Check ICMP
    if pkt.Layer(layers.LayerTypeICMPv4) != nil || pkt.Layer(layers.LayerTypeICMPv6) != nil {
        result.Protocol = "icmp"
        // Match against rules
        for _, rule := range c.rules {
            if rule.Protocol == "icmp" {
                result.Class = rule.Class
                return result
            }
        }
        return result
    }

    // Check TCP
    if tcpLayer := pkt.Layer(layers.LayerTypeTCP); tcpLayer != nil {
        tcp := tcpLayer.(*layers.TCP)
        result.Protocol = "tcp"
        result.SrcPort = uint16(tcp.SrcPort)
        result.DstPort = uint16(tcp.DstPort)
        for _, rule := range c.rules {
            if rule.Protocol == "tcp" && (rule.DstPort == 0 || rule.DstPort == result.DstPort) {
                result.Class = rule.Class
                return result
            }
        }
        return result
    }

    // Check UDP
    if udpLayer := pkt.Layer(layers.LayerTypeUDP); udpLayer != nil {
        udp := udpLayer.(*layers.UDP)
        result.Protocol = "udp"
        result.SrcPort = uint16(udp.SrcPort)
        result.DstPort = uint16(udp.DstPort)
        for _, rule := range c.rules {
            if rule.Protocol == "udp" && (rule.DstPort == 0 || rule.DstPort == result.DstPort) {
                result.Class = rule.Class
                return result
            }
        }
        return result
    }

    // No recognized transport layer -> ClassUnknown per D-03
    return result
}
  1. Write tests in classify/classifier_test.go using gopacket test packet construction:
    • Build synthetic packets using gopacket.SerializeBuffer with layers.Ethernet + layers.IPv4 + layers.TCP/UDP/ICMP headers
    • Test each of the 10+ protocol classes plus ClassUnknown
    • Test that rule order matters (first match wins)

Run RED phase first: write all tests, verify they fail (classifier not yet imported or logic wrong). Then GREEN: implement until all pass. cd /home/dev/workspace/yoloyolo && go test -v -count=1 ./classify/... <acceptance_criteria> - classify/rules.go contains type Rule struct - classify/rules.go contains var DefaultRules = []Rule{ - classify/rules.go contains at least 12 Rule entries (10 specific + 2 catch-all) - classify/classifier.go contains func (c *Classifier) Classify(pkt gopacket.Packet) ClassifiedPacket - classify/classifier.go contains func NewClassifier(rules []Rule) *Classifier - classify/classifier.go does NOT contain switch keyword (per D-02) - classify/classifier_test.go contains TestClassify with subtests for ICMP, DNS, HTTPS, HTTP, SSH, SMTP, NTP, DHCP, OtherTCP, OtherUDP, Unknown - go test -v -count=1 ./classify/... exits 0 with all subtests passing </acceptance_criteria> Classifier passes tests for all 11 traffic classes. Rules stored as config-driven slice (D-02). First-match-wins ordering verified. No switch statements in classification logic.

- `go version` shows 1.24+ - `go build ./classify/...` compiles without errors - `go test -v -count=1 ./classify/...` all tests pass - `go vet ./classify/...` no issues - No import of `google/gopacket` anywhere (must be `gopacket/gopacket`)

<success_criteria>

  • Go 1.24 installed and working
  • Module initialized with gopacket, go-pcap, cobra dependencies
  • 11 TrafficClass constants defined
  • ClassifiedPacket and WindowSnapshot types defined (shared contracts)
  • Config-driven classifier with 12+ rules in a slice (not switch)
  • All protocol class unit tests passing </success_criteria>
After completion, create `.planning/phases/01-capture-and-classification/01-01-SUMMARY.md`