feat(01-01): implement config-driven classifier with 12 protocol rules
- Add classify/rules.go: Rule struct and DefaultRules slice with 12 rules (10 specific protocol rules + 2 catch-alls for other-TCP/other-UDP) - Add classify/classifier.go: Classifier with NewClassifier and Classify methods - Config-driven slice (not switch) per D-02; first-match-wins ordering - Returns ClassUnknown for non-TCP/UDP/ICMP traffic per D-03 - All 14 tests pass covering all 11 traffic classes
This commit is contained in:
@@ -0,0 +1,72 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
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},
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user