diff --git a/bcap.go b/bcap.go index 1fd2e8b..0279bd1 100644 --- a/bcap.go +++ b/bcap.go @@ -2,151 +2,806 @@ package bcap import ( "fmt" - "github.com/google/gopacket" - "github.com/google/gopacket/layers" "net" + "strings" "sync" + "sync/atomic" + "time" + + "github.com/gopacket/gopacket" + "github.com/gopacket/gopacket/layers" ) -type Packets struct { - sync.RWMutex - cu map[string]PacketInfo +// ════════════════════════════════════════════════ +// 错误类型定义 / Error Type Definitions +// ════════════════════════════════════════════════ + +// ParseErrorType 解析错误类型 / Parse error type +type ParseErrorType int + +const ( + ErrTypeNone ParseErrorType = iota // 无错误 / No error + ErrTypeLinkLayer // 链路层错误 / Link layer error + ErrTypeNetwork // 网络层错误 / Network layer error + ErrTypeTransport // 传输层错误 / Transport layer error + ErrTypeUnsupported // 不支持的协议 / Unsupported protocol +) + +// ParseError 解析错误结构体,包含详细的错误信息 +// ParseError structure containing detailed error information +type ParseError struct { + Type ParseErrorType // 错误类型 / Error type + Layer string // 出错的层 / Layer where error occurred + Message string // 错误信息 / Error message + Err error // 原始错误 / Original error } -type PacketInfo struct { - Key string - ReverseKey string - Type string - SrcMac net.HardwareAddr - DstMac net.HardwareAddr - SrcIP string - SrcPort string - DstIP string - DstPort string - comment string - packet gopacket.Packet - tcpSeq uint32 - tcpAck uint32 - tcpWindow uint16 - tcpPayloads int - finState bool - synState bool - isFirst bool - //stateDescript 0=unknown 1=tcp_connect_1 2=tcp_connect_2 3=tcp_connect_3 - // 4=tcp_disconnect_1 5=tcp_disconnect_2 6=tcp_disconnect_2+3 7=tcp_disconnect_3 - // 8=tcp_disconnect_4 9=tcp_ack_ok 10=tcp_retransmit 11=tcp_ece 12=tcp_cwr 13=tcp_rst - // 14=tcp_keepalived 20=udp - stateDescript uint8 +func (e *ParseError) Error() string { + if e.Err != nil { + return fmt.Sprintf("[%s] %s: %v", e.Layer, e.Message, e.Err) + } + return fmt.Sprintf("[%s] %s", e.Layer, e.Message) } -func (p *Packets) Key(key string) PacketInfo { - p.RLock() - defer p.RUnlock() - return p.cu[key] -} - -func (p *Packets) SetComment(key, comment string) { - p.Lock() - c := p.cu[key] - c.comment = comment - p.cu[key] = c - p.Unlock() -} - -func (p *PacketInfo) SetComment(comment string) { - p.comment = comment -} - -func (p PacketInfo) StateDescript() uint8 { - return p.stateDescript -} - -func (p PacketInfo) TcpPayloads() int { - return p.tcpPayloads -} - -func (p PacketInfo) FinState() bool { - return p.finState -} - -func (p PacketInfo) SynState() bool { - return p.synState -} - -func (p PacketInfo) TcpWindow() uint16 { - return p.tcpWindow -} - -func (p PacketInfo) TcpAck() uint32 { - return p.tcpAck -} - -func (p PacketInfo) TcpSeq() uint32 { - return p.tcpSeq -} - -func (p PacketInfo) Packet() gopacket.Packet { - return p.packet -} - -func (p PacketInfo) Comment() string { - return p.comment -} - -func NewPackets() *Packets { - return &Packets{ - cu: make(map[string]PacketInfo), +// NewParseError 创建新的解析错误 / Create new parse error +func NewParseError(errType ParseErrorType, layer, message string, err error) *ParseError { + return &ParseError{ + Type: errType, + Layer: layer, + Message: message, + Err: err, } } +// ════════════════════════════════════════════════ +// 状态常量定义 / State Constants +// ════════════════════════════════════════════════════════════════════════════════ + +// TCP 状态描述符定义 / TCP state descriptor definitions +const ( + StateUnknown uint8 = 0 // 未知状态 / Unknown state + StateTcpConnect1 uint8 = 1 // 三次握手第一步 (SYN) / Three-way handshake step 1 (SYN) + StateTcpConnect2 uint8 = 2 // 三次握手第二步 (SYN-ACK) / Three-way handshake step 2 (SYN-ACK) + StateTcpConnect3 uint8 = 3 // 三次握手第三步 (ACK) / Three-way handshake step 3 (ACK) + StateTcpDisconnect1 uint8 = 4 // 四次挥手第一步 (FIN) / Four-way handshake step 1 (FIN) + StateTcpDisconnect2 uint8 = 5 // 四次挥手第二步 (ACK) / Four-way handshake step 2 (ACK) + StateTcpDisconnect23 uint8 = 6 // 四次挥手第二三步合并 (FIN-ACK) / Four-way handshake step 2+3 combined + StateTcpDisconnect3 uint8 = 7 // 四次挥手第三步 (FIN) / Four-way handshake step 3 (FIN) + StateTcpDisconnect4 uint8 = 8 // 四次挥手第四步 (ACK) / Four-way handshake step 4 (ACK) + StateTcpAckOk uint8 = 9 // 正常 ACK / Normal ACK + StateTcpRetransmit uint8 = 10 // TCP 重传 / TCP retransmission + StateTcpEce uint8 = 11 // TCP ECE 标志 / TCP ECE flag + StateTcpCwr uint8 = 12 // TCP CWR 标志 / TCP CWR flag + StateTcpRst uint8 = 13 // TCP RST 标志 / TCP RST flag + StateTcpKeepalive uint8 = 14 // TCP 保活 / TCP keepalive + StateUdp uint8 = 20 // UDP 数据包 / UDP packet + StateIcmp uint8 = 30 // ICMP 数据包 / ICMP packet + StateIcmpv6 uint8 = 31 // ICMPv6 数据包 / ICMPv6 packet +) + +// 默认配置常量 / Default configuration constants +const ( + DefaultConnectionTimeout = 5 * time.Minute // 默认连接超时时间 / Default connection timeout + DefaultCleanupInterval = 1 * time.Minute // 默认清理间隔 / Default cleanup interval + DefaultShardCount = 32 // 默认分片数量 / Default shard count +) + +// ════════════════════════════════════════════════ +// 回调函数类型定义 / Callback Function Types +// ════════════════════════════════════════════════ + +// ConnectionCallback 连接事件回调函数类型 +// ConnectionCallback connection event callback function type +type ConnectionCallback func(info PacketInfo) + +// ErrorCallback 错误事件回调函数类型 +// ErrorCallback error event callback function type +type ErrorCallback func(err *ParseError, packet gopacket.Packet) + +// ════════════════════════════════════════════════════════════════════════════════ +// 配置结构体 / Configuration Structures +// ════════════════════════════════════════════════════════════════════════════════ + +// PacketsConfig 数据包管理器配置 +// PacketsConfig configuration for packets manager +type PacketsConfig struct { + // 连接超时时间,超过此时间未活动的连接将被清理 + // Connection timeout, connections inactive beyond this time will be cleaned up + ConnectionTimeout time.Duration + + // 自动清理间隔,设置为 0 则禁用自动清理 + // Auto cleanup interval, set to 0 to disable auto cleanup + CleanupInterval time.Duration + + // 是否启用懒加载模式(按需解析详细信息) + // Whether to enable lazy parsing mode (parse details on demand) + LazyParsing bool + + // 是否启用调试日志 + // Whether to enable debug logging + EnableDebugLog bool + + // 分片数量(用于减少锁竞争) + // Shard count (for reducing lock contention) + ShardCount int + + // 新连接回调 / New connection callback + OnNewConnection ConnectionCallback + + // 连接关闭回调 / Connection closed callback + OnConnectionClosed ConnectionCallback + + // 错误回调 / Error callback + OnError ErrorCallback +} + +// DefaultConfig 返回默认配置 +// DefaultConfig returns default configuration +func DefaultConfig() *PacketsConfig { + return &PacketsConfig{ + ConnectionTimeout: DefaultConnectionTimeout, + CleanupInterval: DefaultCleanupInterval, + LazyParsing: false, + EnableDebugLog: false, + ShardCount: DefaultShardCount, + } +} + +// ════════════════════════════════════════════════ +// 统计信息结构体 / Statistics Structures +// ════════════════════════════════════════════════ + +// ErrorStats 错误统计信息 +// ErrorStats error statistics +type ErrorStats struct { + LinkLayerErrors uint64 // 链路层错误数 / Link layer error count + NetworkErrors uint64 // 网络层错误数 / Network layer error count + TransportErrors uint64 // 传输层错误数 / Transport layer error count + UnsupportedErrors uint64 // 不支持协议错误数 / Unsupported protocol error count + TotalErrors uint64 // 总错误数 / Total error count +} + +// ConnectionStats 连接统计信息 +// ConnectionStats connection statistics +type ConnectionStats struct { + ActiveConnections int64 // 当前活跃连接数 / Current active connections + TotalConnections uint64 // 历史总连接数 / Total historical connections + ClosedConnections uint64 // 已关闭连接数 / Closed connections + TimeoutConnections uint64 // 超时清理的连接数 / Timeout cleaned connections + TcpConnections int64 // TCP 连接数 / TCP connection count + UdpConnections int64 // UDP 连接数 / UDP connection count + IcmpConnections int64 // ICMP 连接数 / ICMP connection count +} + +// Stats 综合统计信息 +// Stats comprehensive statistics +type Stats struct { + Errors ErrorStats // 错误统计 / Error statistics + Connections ConnectionStats // 连接统计 / Connection statistics + StartTime time.Time // 启动时间 / Start time + LastCleanup time.Time // 上次清理时间 / Last cleanup time +} + +// ════════════════════════════════════════════════ +// 数据包信息结构体 / Packet Info Structure +// ════════════════════════════════════════════════════════════════════════════════ + +// PacketInfo 数据包信息结构体,包含完整的网络层和传输层信息 +// PacketInfo structure containing complete network and transport layer information +type PacketInfo struct { + Key string // 连接唯一标识 / Unique connection identifier + ReverseKey string // 反向连接标识 / Reverse connection identifier + Type string // 协议类型: tcp/udp/icmp/icmpv6 / Protocol type + SrcMac net.HardwareAddr // 源 MAC 地址 / Source MAC address + DstMac net.HardwareAddr // 目标 MAC 地址 / Destination MAC address + SrcIP string // 源 IP 地址 / Source IP address + SrcPort string // 源端口 (TCP/UDP) / Source port + DstIP string // 目标 IP 地址 / Destination IP address + DstPort string // 目标端口 (TCP/UDP) / Destination port + + // 时间戳相关字段 / Timestamp related fields + Timestamp time.Time // 数据包时间戳 / Packet timestamp + TimestampMicros int64 // 微秒级时间戳 / Microsecond timestamp + RelativeTime time.Duration // 相对于首包的时间偏移 / Time offset relative to first packet + + // ICMP 相关字段 / ICMP related fields + IcmpType uint8 // ICMP 类型 / ICMP type + IcmpCode uint8 // ICMP 代码 / ICMP code + IcmpChecksum uint16 // ICMP 校验和 / ICMP checksum + IcmpId uint16 // ICMP 标识符 / ICMP identifier + IcmpSeq uint16 // ICMP 序列号 / ICMP sequence number + + // 连接统计字段 / Connection statistics fields + PacketCount uint64 // 该连接的数据包计数 / Packet count for this connection + ByteCount uint64 // 该连接的字节计数 / Byte count for this connection + FirstSeen time.Time // 首次出现时间 / First seen time + LastSeen time.Time // 最后出现时间 / Last seen time + + // 内部状态字段 / Internal state fields + comment string // 用户自定义注释 / User-defined comment + packet gopacket.Packet // 原始数据包 / Raw packet + tcpSeq uint32 // TCP 序列号 / TCP sequence number + tcpAck uint32 // TCP 确认号 / TCP acknowledgment number + tcpWindow uint16 // TCP 窗口大小 / TCP window size + tcpPayloads int // 载荷长度 / Payload length + finState bool // TCP FIN 标志 / TCP FIN flag + synState bool // TCP SYN 标志 / TCP SYN flag + isFirst bool // 是否为首次出现 / Whether first occurrence + stateDescript uint8 // 状态描述符 / State descriptor +} + +// PacketInfo 的 Getter 方法 / PacketInfo getter methods + +func (p PacketInfo) StateDescript() uint8 { return p.stateDescript } +func (p PacketInfo) TcpPayloads() int { return p.tcpPayloads } +func (p PacketInfo) FinState() bool { return p.finState } +func (p PacketInfo) SynState() bool { return p.synState } +func (p PacketInfo) TcpWindow() uint16 { return p.tcpWindow } +func (p PacketInfo) TcpAck() uint32 { return p.tcpAck } +func (p PacketInfo) TcpSeq() uint32 { return p.tcpSeq } +func (p PacketInfo) Packet() gopacket.Packet { return p.packet } +func (p PacketInfo) Comment() string { return p.comment } +func (p *PacketInfo) SetComment(comment string) { p.comment = comment } + +// ════════════════════════════════════════════════ +// 分片 Map 实现(减少锁竞争)/ Sharded Map Implementation (reduce lock contention) +// ════════════════════════════════════════════════ + +// shard 单个分片 +// shard single shard +type shard struct { + sync.RWMutex + items map[string]PacketInfo +} + +// shardedMap 分片 Map,通过将数据分散到多个分片来减少锁竞争 +// shardedMap sharded map, reduces lock contention by distributing data across multiple shards +type shardedMap struct { + shards []*shard + shardCount int +} + +// newShardedMap 创建新的分片 Map +// newShardedMap creates a new sharded map +func newShardedMap(shardCount int) *shardedMap { + if shardCount <= 0 { + shardCount = DefaultShardCount + } + sm := &shardedMap{ + shards: make([]*shard, shardCount), + shardCount: shardCount, + } + for i := 0; i < shardCount; i++ { + sm.shards[i] = &shard{ + items: make(map[string]PacketInfo), + } + } + return sm +} + +// getShard 根据 key 获取对应的分片 +// getShard gets the shard for a given key +func (sm *shardedMap) getShard(key string) *shard { + hash := fnvHash(key) + return sm.shards[hash%uint32(sm.shardCount)] +} + +// fnvHash FNV-1a 哈希函数,用于快速计算字符串哈希 +// fnvHash FNV-1a hash function for fast string hashing +func fnvHash(key string) uint32 { + hash := uint32(2166136261) + for i := 0; i < len(key); i++ { + hash ^= uint32(key[i]) + hash *= 16777619 + } + return hash +} + +// Get 获取值 / Get value +func (sm *shardedMap) Get(key string) (PacketInfo, bool) { + shard := sm.getShard(key) + shard.RLock() + val, ok := shard.items[key] + shard.RUnlock() + return val, ok +} + +// Set 设置值 / Set value +func (sm *shardedMap) Set(key string, value PacketInfo) { + shard := sm.getShard(key) + shard.Lock() + shard.items[key] = value + shard.Unlock() +} + +// Delete 删除值 / Delete value +func (sm *shardedMap) Delete(key string) { + shard := sm.getShard(key) + shard.Lock() + delete(shard.items, key) + shard.Unlock() +} + +// DeleteBatch 批量删除 / Batch delete +func (sm *shardedMap) DeleteBatch(keys []string) { + // 按分片分组 keys / Group keys by shard + shardKeys := make(map[int][]string) + for _, key := range keys { + hash := fnvHash(key) + shardIdx := int(hash % uint32(sm.shardCount)) + shardKeys[shardIdx] = append(shardKeys[shardIdx], key) + } + + // 对每个分片批量删除 / Batch delete for each shard + for shardIdx, keys := range shardKeys { + shard := sm.shards[shardIdx] + shard.Lock() + for _, key := range keys { + delete(shard.items, key) + } + shard.Unlock() + } +} + +// Len 获取总长度 / Get total length +func (sm *shardedMap) Len() int { + total := 0 + for _, shard := range sm.shards { + shard.RLock() + total += len(shard.items) + shard.RUnlock() + } + return total +} + +// GetAll 获取所有项 / Get all items +func (sm *shardedMap) GetAll() []PacketInfo { + var result []PacketInfo + for _, shard := range sm.shards { + shard.RLock() + for _, v := range shard.items { + result = append(result, v) + } + shard.RUnlock() + } + return result +} + +// Range 遍历所有项 / Range over all items +func (sm *shardedMap) Range(f func(key string, value PacketInfo) bool) { + for _, shard := range sm.shards { + shard.RLock() + for k, v := range shard.items { + if !f(k, v) { + shard.RUnlock() + return + } + } + shard.RUnlock() + } +} + +// Clear 清空所有项 / Clear all items +func (sm *shardedMap) Clear() { + for _, shard := range sm.shards { + shard.Lock() + shard.items = make(map[string]PacketInfo) + shard.Unlock() + } +} + +// ════════════════════════════════════════════════ +// 对象池(减少内存分配)/ Object Pool (reduce memory allocation) +// ════════════════════════════════════════════════ + +// stringBuilderPool strings.Builder 对象池 +var stringBuilderPool = sync.Pool{ + New: func() interface{} { + return &strings.Builder{} + }, +} + +// getStringBuilder 从对象池获取 strings.Builder +// getStringBuilder gets strings.Builder from pool +func getStringBuilder() *strings.Builder { + return stringBuilderPool.Get().(*strings.Builder) +} + +// putStringBuilder 将 strings.Builder 放回对象池 +// putStringBuilder puts strings.Builder back to pool +func putStringBuilder(sb *strings.Builder) { + sb.Reset() + stringBuilderPool.Put(sb) +} + +// buildKey 使用对象池优化的 key 构建函数 +// buildKey optimized key building function using object pool +func buildKey(protocol, srcIP, srcPort, dstIP, dstPort string) string { + sb := getStringBuilder() + defer putStringBuilder(sb) + + sb.WriteString(protocol) + sb.WriteString("://") + sb.WriteString(srcIP) + if srcPort != "" { + sb.WriteString(":") + sb.WriteString(srcPort) + } + sb.WriteString("=") + sb.WriteString(dstIP) + if dstPort != "" { + sb.WriteString(":") + sb.WriteString(dstPort) + } + + return sb.String() +} + +// ════════════════════════════════════════════════ +// Packets 主结构体 / Packets Main Structure +// ════════════════════════════════════════════════ + +// Packets 数据包管理器,用于跟踪和解析网络数据包 +// Packets manager for tracking and parsing network packets +type Packets struct { + config *PacketsConfig // 配置 / Configuration + connections *shardedMap // 连接映射表 / Connection map + stats Stats // 统计信息 / Statistics + statsLock sync.RWMutex // 统计信息锁 / Statistics lock + startTime time.Time // 启动时间 / Start time + firstPacketTime time.Time // 首个数据包时间 / First packet time + cleanupTicker *time.Ticker // 清理定时器 / Cleanup ticker + stopCleanup chan struct{} // 停止清理信号 / Stop cleanup signal + cleanupOnce sync.Once // 确保只启动一次清理 / Ensure cleanup starts only once +} + +// NewPackets 创建新的数据包管理器实例(使用默认配置) +// NewPackets creates a new Packets manager instance (with default config) +func NewPackets() *Packets { + return NewPacketsWithConfig(DefaultConfig()) +} + +// NewPacketsWithConfig 使用自定义配置创建数据包管理器 +// NewPacketsWithConfig creates Packets manager with custom config +func NewPacketsWithConfig(config *PacketsConfig) *Packets { + if config == nil { + config = DefaultConfig() + } + + p := &Packets{ + config: config, + connections: newShardedMap(config.ShardCount), + startTime: time.Now(), + stopCleanup: make(chan struct{}), + } + + p.stats.StartTime = p.startTime + + // 启动自动清理 goroutine / Start auto cleanup goroutine + if config.CleanupInterval > 0 { + p.startAutoCleanup() + } + + return p +} + +// startAutoCleanup 启动自动清理 goroutine +// startAutoCleanup starts auto cleanup goroutine +func (p *Packets) startAutoCleanup() { + p.cleanupOnce.Do(func() { + p.cleanupTicker = time.NewTicker(p.config.CleanupInterval) + go func() { + for { + select { + case <-p.cleanupTicker.C: + p.CleanupExpiredConnections() + case <-p.stopCleanup: + p.cleanupTicker.Stop() + return + } + } + }() + }) +} + +// Stop 停止数据包管理器(停止自动清理) +// Stop stops the packets manager (stops auto cleanup) +func (p *Packets) Stop() { + close(p.stopCleanup) +} + +// ════════════════════════════════════════════════ +// 统计和查询方法 / Statistics and Query Methods +// ════════════════════════════════════════════════ + +// GetStats 获取统计信息 +// GetStats gets statistics +func (p *Packets) GetStats() Stats { + p.statsLock.RLock() + defer p.statsLock.RUnlock() + + stats := p.stats + stats.Connections.ActiveConnections = int64(p.connections.Len()) + return stats +} + +// GetConnectionCount 获取当前活跃连接数 +// GetConnectionCount gets current active connection count +func (p *Packets) GetConnectionCount() int { + return p.connections.Len() +} + +// GetAllConnections 获取所有活跃连接 +// GetAllConnections gets all active connections +func (p *Packets) GetAllConnections() []PacketInfo { + return p.connections.GetAll() +} + +// GetConnectionsByIP 根据 IP 地址查询连接 +// GetConnectionsByIP queries connections by IP address +func (p *Packets) GetConnectionsByIP(ip string) []PacketInfo { + var result []PacketInfo + p.connections.Range(func(key string, value PacketInfo) bool { + if value.SrcIP == ip || value.DstIP == ip { + result = append(result, value) + } + return true + }) + return result +} + +// GetConnectionsByPort 根据端口查询连接 +// GetConnectionsByPort queries connections by port +func (p *Packets) GetConnectionsByPort(port string) []PacketInfo { + var result []PacketInfo + p.connections.Range(func(key string, value PacketInfo) bool { + if value.SrcPort == port || value.DstPort == port { + result = append(result, value) + } + return true + }) + return result +} + +// GetConnectionsByType 根据协议类型查询连接 +// GetConnectionsByType queries connections by protocol type +func (p *Packets) GetConnectionsByType(protocolType string) []PacketInfo { + var result []PacketInfo + p.connections.Range(func(key string, value PacketInfo) bool { + if value.Type == protocolType { + result = append(result, value) + } + return true + }) + return result +} + +// Key 根据 key 获取连接信息 +// Key gets connection info by key +func (p *Packets) Key(key string) (PacketInfo, bool) { + return p.connections.Get(key) +} + +// SetComment 设置连接注释 +// SetComment sets connection comment +func (p *Packets) SetComment(key, comment string) { + if info, ok := p.connections.Get(key); ok { + info.comment = comment + p.connections.Set(key, info) + } +} + +// ════════════════════════════════════════════════ +// 连接清理方法 / Connection Cleanup Methods +// ════════════════════════════════════════════════ + +// CleanupExpiredConnections 清理过期连接 +// CleanupExpiredConnections cleans up expired connections +func (p *Packets) CleanupExpiredConnections() int { + now := time.Now() + timeout := p.config.ConnectionTimeout + var expiredKeys []string + + // 收集过期的 keys / Collect expired keys + p.connections.Range(func(key string, value PacketInfo) bool { + if now.Sub(value.LastSeen) > timeout { + expiredKeys = append(expiredKeys, key) + } + return true + }) + + // 批量删除 / Batch delete + if len(expiredKeys) > 0 { + p.connections.DeleteBatch(expiredKeys) + + // 更新统计 / Update statistics + p.statsLock.Lock() + p.stats.Connections.TimeoutConnections += uint64(len(expiredKeys)) + p.stats.LastCleanup = now + p.statsLock.Unlock() + } + + return len(expiredKeys) +} + +// ClearAllConnections 清空所有连接 +// ClearAllConnections clears all connections +func (p *Packets) ClearAllConnections() { + p.connections.Clear() + + p.statsLock.Lock() + p.stats.Connections.ActiveConnections = 0 + p.statsLock.Unlock() +} + +// ════════════════════════════════════════════════ +// 错误处理方法 / Error Handling Methods +// ════════════════════════════════════════════════ + +// recordError 记录错误统计 +// recordError records error statistics +func (p *Packets) recordError(errType ParseErrorType) { + p.statsLock.Lock() + defer p.statsLock.Unlock() + + p.stats.Errors.TotalErrors++ + switch errType { + case ErrTypeLinkLayer: + p.stats.Errors.LinkLayerErrors++ + case ErrTypeNetwork: + p.stats.Errors.NetworkErrors++ + case ErrTypeTransport: + p.stats.Errors.TransportErrors++ + case ErrTypeUnsupported: + p.stats.Errors.UnsupportedErrors++ + } +} + +// handleError 处理错误(记录统计并调用回调) +// handleError handles error (records statistics and calls callback) +func (p *Packets) handleError(parseErr *ParseError, packet gopacket.Packet) { + p.recordError(parseErr.Type) + + if p.config.EnableDebugLog { + fmt.Printf("[ERROR] %s\n", parseErr.Error()) + } + + if p.config.OnError != nil { + p.config.OnError(parseErr, packet) + } +} + +// ════════════════════════════════════════════════ +// 数据包解析方法 / Packet Parsing Methods +// ════════════════════════════════════════════════ + +// ParsePacket 解析数据包,支持 Ethernet、Linux SLL、Linux SLL2 等链路层格式 +// ParsePacket parses packets, supporting Ethernet, Linux SLL, Linux SLL2 and other link layer formats +// +// 参数 / Parameters: +// - packet: gopacket 数据包对象 / gopacket packet object +// - opts: 可选参数,opts[0] 可以是 *[]byte 类型的 MAC 地址(用于 nfqueue 等场景) +// / Optional parameters, opts[0] can be *[]byte type MAC address (for nfqueue scenarios) +// +// 返回 / Returns: +// - PacketInfo: 解析后的数据包信息 / Parsed packet information +// - error: 解析错误 / Parse error func (p *Packets) ParsePacket(packet gopacket.Packet, opts ...interface{}) (PacketInfo, error) { var info PacketInfo - //firstOpts is macaddr(nfqueue) - if ew := packet.Layer(layers.LayerTypeEthernet); ew != nil { - eth := ew.(*layers.Ethernet) - info.SrcMac = eth.SrcMAC - info.DstMac = eth.DstMAC + + // 提取时间戳 / Extract timestamp + metadata := packet.Metadata() + if metadata != nil { + info.Timestamp = metadata.Timestamp + info.TimestampMicros = metadata.Timestamp.UnixMicro() + + // 记录首个数据包时间 / Record first packet time + if p.firstPacketTime.IsZero() { + p.firstPacketTime = metadata.Timestamp + } + + // 计算相对时间 / Calculate relative time + info.RelativeTime = metadata.Timestamp.Sub(p.firstPacketTime) + } else { + info.Timestamp = time.Now() + info.TimestampMicros = info.Timestamp.UnixMicro() } + + // ──────────────────────────────────────────────── + // 链路层:提取 MAC 地址 / Link Layer: Extract MAC addresses + // 支持 Ethernet、Linux SLL、Linux SLL2 / Support Ethernet, Linux SLL, Linux SLL2 + // ──────────────────────────────────────────────── + var srcMac, dstMac net.HardwareAddr + + // 情况 1: 标准以太网层 / Case 1: Standard Ethernet layer + if ethLayer := packet.Layer(layers.LayerTypeEthernet); ethLayer != nil { + eth := ethLayer.(*layers.Ethernet) + srcMac = eth.SrcMAC + dstMac = eth.DstMAC + } else if sllLayer := packet.Layer(layers.LayerTypeLinuxSLL); sllLayer != nil { + // 情况 2: Linux SLL (cooked capture v1) / Case 2: Linux SLL (cooked capture v1) + sll := sllLayer.(*layers.LinuxSLL) + if sll.AddrType == 1 && sll.AddrLen == 6 { // 1 = ARPHRD_ETHER + srcMac = sll.Addr + } + } else if sll2Layer := packet.Layer(layers.LayerTypeLinuxSLL2); sll2Layer != nil { + // 情况 3: Linux SLL2 (cooked capture v2) / Case 3: Linux SLL2 (cooked capture v2) + sll2 := sll2Layer.(*layers.LinuxSLL2) + if sll2.ARPHardwareType == 1 && sll2.AddrLength == 6 { // 1 = ARPHRD_ETHER + srcMac = sll2.Addr + } + } + + // 可选:使用外部提供的 MAC 地址覆盖 / Optional: Override with externally provided MAC for k, v := range opts { - switch tp := v.(type) { - case *[]byte: - if tp != nil && k == 0 { - //nfqueue src mac addr - info.SrcMac = net.HardwareAddr(*tp) + if k == 0 { + if b, ok := v.(*[]byte); ok && b != nil && len(*b) == 6 { + srcMac = net.HardwareAddr(*b) } } } + + info.SrcMac = srcMac + info.DstMac = dstMac + + // ──────────────────────────────────────────────── + // 网络层:提取 IP 地址 / Network Layer: Extract IP addresses + // ──────────────────────────────────────────────── if nw := packet.NetworkLayer(); nw != nil { srcp, dstp := nw.NetworkFlow().Endpoints() info.SrcIP = srcp.String() info.DstIP = dstp.String() } else { - return info, fmt.Errorf("Failed to parse packet,not a valid network info") + parseErr := NewParseError(ErrTypeNetwork, "Network", "no valid network layer found", nil) + p.handleError(parseErr, packet) + return info, parseErr } - { - //tcp valid - layer := packet.Layer(layers.LayerTypeTCP) - if layer != nil { - if tcp, ok := layer.(*layers.TCP); ok { - return p.parseTcp(info, packet, layer, tcp) - } + + // ──────────────────────────────────────────────── + // 传输层:解析 TCP/UDP/ICMP / Transport Layer: Parse TCP/UDP/ICMP + // ──────────────────────────────────────────────── + + // TCP 协议 / TCP protocol + if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil { + if tcp, ok := tcpLayer.(*layers.TCP); ok { + return p.parseTcp(info, packet, tcpLayer, tcp) } } - { - layer := packet.Layer(layers.LayerTypeUDP) - if layer != nil { - if udp, ok := layer.(*layers.UDP); ok { - return p.parseUdp(info, packet, layer, udp) - } + + // UDP 协议 / UDP protocol + if udpLayer := packet.Layer(layers.LayerTypeUDP); udpLayer != nil { + if udp, ok := udpLayer.(*layers.UDP); ok { + return p.parseUdp(info, packet, udpLayer, udp) } } - //icmp - return info, fmt.Errorf("not support packet") + + // ICMPv4 协议 / ICMPv4 protocol + if icmpLayer := packet.Layer(layers.LayerTypeICMPv4); icmpLayer != nil { + if icmp, ok := icmpLayer.(*layers.ICMPv4); ok { + return p.parseIcmp(info, packet, icmpLayer, icmp) + } + } + + // ICMPv6 协议 / ICMPv6 protocol + if icmpv6Layer := packet.Layer(layers.LayerTypeICMPv6); icmpv6Layer != nil { + if icmpv6, ok := icmpv6Layer.(*layers.ICMPv6); ok { + return p.parseIcmpv6(info, packet, icmpv6Layer, icmpv6) + } + } + + // 不支持的协议类型 / Unsupported protocol type + parseErr := NewParseError(ErrTypeUnsupported, "Transport", + "unsupported packet type (not TCP/UDP/ICMP/ICMPv6)", nil) + p.handleError(parseErr, packet) + return info, parseErr } +// parseTcp 解析 TCP 数据包并进行状态跟踪 +// parseTcp parses TCP packets and performs state tracking func (p *Packets) parseTcp(info PacketInfo, packet gopacket.Packet, layer gopacket.Layer, tcp *layers.TCP) (PacketInfo, error) { - info.Key = fmt.Sprintf("tcp://%s:%d=%s:%d", info.SrcIP, tcp.SrcPort, info.DstIP, tcp.DstPort) - info.ReverseKey = fmt.Sprintf("tcp://%s:%d=%s:%d", info.DstIP, tcp.DstPort, info.SrcIP, tcp.SrcPort) + // 使用优化的 key 构建 / Use optimized key building + info.Key = buildKey("tcp", info.SrcIP, fmt.Sprintf("%d", tcp.SrcPort), info.DstIP, fmt.Sprintf("%d", tcp.DstPort)) + info.ReverseKey = buildKey("tcp", info.DstIP, fmt.Sprintf("%d", tcp.DstPort), info.SrcIP, fmt.Sprintf("%d", tcp.SrcPort)) info.Type = "tcp" info.SrcPort = fmt.Sprintf("%d", tcp.SrcPort) info.DstPort = fmt.Sprintf("%d", tcp.DstPort) @@ -157,10 +812,12 @@ func (p *Packets) parseTcp(info PacketInfo, packet gopacket.Packet, layer gopack info.finState = tcp.FIN info.synState = tcp.SYN info.tcpWindow = tcp.Window - p.RLock() - lastPacket := p.cu[info.Key] - p.RUnlock() - if lastPacket.Key != info.Key { + + // 获取上一个同方向的数据包 / Get the last packet in the same direction + lastPacket, exists := p.connections.Get(info.Key) + + // 如果是新连接,初始化状态 / If it's a new connection, initialize state + if !exists { lastPacket = PacketInfo{ Key: info.Key, ReverseKey: info.ReverseKey, @@ -169,8 +826,10 @@ func (p *Packets) parseTcp(info PacketInfo, packet gopacket.Packet, layer gopack SrcPort: info.SrcPort, DstIP: info.DstIP, DstPort: info.DstPort, - comment: "", - packet: packet, + FirstSeen: info.Timestamp, + LastSeen: info.Timestamp, + PacketCount: 0, + ByteCount: 0, tcpSeq: tcp.Seq, tcpAck: tcp.Ack, tcpWindow: tcp.Window, @@ -178,109 +837,523 @@ func (p *Packets) parseTcp(info PacketInfo, packet gopacket.Packet, layer gopack finState: tcp.FIN, synState: tcp.SYN, isFirst: true, - stateDescript: 0, + stateDescript: StateUnknown, + } + + // 更新统计 / Update statistics + p.statsLock.Lock() + p.stats.Connections.TotalConnections++ + atomic.AddInt64(&p.stats.Connections.TcpConnections, 1) + p.statsLock.Unlock() + + // 触发新连接回调 / Trigger new connection callback + if p.config.OnNewConnection != nil { + p.config.OnNewConnection(lastPacket) } - p.Lock() - p.cu[info.Key] = lastPacket - p.Unlock() } - p.RLock() - lastReverse := p.cu[info.ReverseKey] - p.RUnlock() + + // 更新连接统计 / Update connection statistics + info.FirstSeen = lastPacket.FirstSeen + info.LastSeen = info.Timestamp + info.PacketCount = lastPacket.PacketCount + 1 + info.ByteCount = lastPacket.ByteCount + uint64(len(packet.Data())) + + // 获取反向连接的数据包 / Get the reverse direction packet + lastReverse, _ := p.connections.Get(info.ReverseKey) + + // 继承上一个数据包的注释和 MAC 地址 / Inherit comment and MAC addresses from last packet if !lastPacket.isFirst { info.comment = lastPacket.comment if lastPacket.SrcMac != nil && len(info.SrcMac) == 0 { info.SrcMac = lastPacket.SrcMac } - if lastPacket.SrcMac != nil && len(info.SrcMac) == 0 { - info.SrcMac = lastPacket.SrcMac + if lastPacket.DstMac != nil && len(info.DstMac) == 0 { + info.DstMac = lastPacket.DstMac } } if lastReverse.SrcMac != nil && len(info.DstMac) == 0 { info.DstMac = lastReverse.SrcMac } - { - //state judge - if tcp.RST { - info.stateDescript = 13 - p.Lock() - delete(p.cu, info.Key) - delete(p.cu, info.ReverseKey) - p.Unlock() - return info, nil - } - if tcp.SYN && !tcp.ACK { - info.stateDescript = 1 - } else if tcp.SYN && tcp.ACK { - info.stateDescript = 2 - } else if tcp.ACK { - if !tcp.FIN { - if lastReverse.tcpSeq+1 == tcp.Ack && lastReverse.stateDescript == 2 { - info.stateDescript = 3 - } else if tcp.CWR { - info.stateDescript = 12 - } else if tcp.ECE { - info.stateDescript = 11 - } - if info.stateDescript != 0 { + // ──────────────────────────────────────────────── + // TCP 状态机判断 / TCP state machine + // ──────────────────────────────────────────────── + + connectionClosed := false + + // RST 标志:连接重置 / RST flag: connection reset + if tcp.RST { + info.stateDescript = StateTcpRst + p.connections.Delete(info.Key) + p.connections.Delete(info.ReverseKey) + connectionClosed = true + + // 更新统计 / Update statistics + p.statsLock.Lock() + p.stats.Connections.ClosedConnections++ + atomic.AddInt64(&p.stats.Connections.TcpConnections, -1) + p.statsLock.Unlock() + + // 触发连接关闭回调 / Trigger connection closed callback + if p.config.OnConnectionClosed != nil { + p.config.OnConnectionClosed(info) + } + + return info, nil + } + + // 三次握手 / Three-way handshake + if tcp.SYN && !tcp.ACK { + info.stateDescript = StateTcpConnect1 // SYN + } else if tcp.SYN && tcp.ACK { + info.stateDescript = StateTcpConnect2 // SYN-ACK + } else if tcp.ACK { + if !tcp.FIN { + // 三次握手第三步 / Three-way handshake step 3 + if lastReverse.tcpSeq+1 == tcp.Ack && lastReverse.stateDescript == StateTcpConnect2 { + info.stateDescript = StateTcpConnect3 + } else if tcp.CWR { + info.stateDescript = StateTcpCwr + } else if tcp.ECE { + info.stateDescript = StateTcpEce + } + + if info.stateDescript != StateUnknown { + goto savereturn + } + + // TCP 保活检测 / TCP keepalive detection + if info.tcpSeq == lastReverse.tcpAck-1 && info.tcpSeq == lastPacket.tcpSeq+uint32(lastPacket.tcpPayloads)-1 { + info.stateDescript = StateTcpKeepalive + goto savereturn + } + + // TCP 重传检测 / TCP retransmission detection + if !lastPacket.isFirst { + if info.tcpSeq < lastPacket.tcpSeq+uint32(lastPacket.tcpPayloads) { + info.stateDescript = StateTcpRetransmit goto savereturn } - if info.tcpSeq == lastReverse.tcpAck-1 && info.tcpSeq == lastPacket.tcpSeq+uint32(lastPacket.tcpPayloads)-1 { - //keepalive - info.stateDescript = 14 - goto savereturn + } + + // 四次挥手完成 / Four-way handshake completed + if lastReverse.finState && lastPacket.finState { + info.stateDescript = StateTcpDisconnect4 + p.connections.Delete(info.Key) + p.connections.Delete(info.ReverseKey) + connectionClosed = true + + // 更新统计 / Update statistics + p.statsLock.Lock() + p.stats.Connections.ClosedConnections++ + atomic.AddInt64(&p.stats.Connections.TcpConnections, -1) + p.statsLock.Unlock() + + // 触发连接关闭回调 / Trigger connection closed callback + if p.config.OnConnectionClosed != nil { + p.config.OnConnectionClosed(info) } - if !lastPacket.isFirst { - if info.tcpSeq < lastPacket.tcpSeq+uint32(lastPacket.tcpPayloads) { - //retransmit - info.stateDescript = 10 - goto savereturn - } - } - if lastReverse.finState && lastPacket.finState { - info.stateDescript = 8 - p.Lock() - delete(p.cu, info.Key) - delete(p.cu, info.ReverseKey) - p.Unlock() - return info, nil - } - if lastReverse.finState && lastReverse.tcpSeq+1 == info.tcpAck { - info.stateDescript = 5 - goto savereturn - } - info.stateDescript = 9 + + return info, nil + } + + // 四次挥手第二步 / Four-way handshake step 2 + if lastReverse.finState && lastReverse.tcpSeq+1 == info.tcpAck { + info.stateDescript = StateTcpDisconnect2 + goto savereturn + } + + // 正常 ACK / Normal ACK + info.stateDescript = StateTcpAckOk + } else { + // 四次挥手 / Four-way handshake + if !lastReverse.finState { + info.stateDescript = StateTcpDisconnect1 // FIN } else { - if !lastReverse.finState { - info.stateDescript = 4 + if lastReverse.finState && lastReverse.tcpSeq+1 == info.tcpAck && + lastPacket.tcpAck == info.tcpAck && lastPacket.tcpSeq == info.tcpSeq { + info.stateDescript = StateTcpDisconnect3 // FIN (step 3) } else { - if lastReverse.finState && lastReverse.tcpSeq+1 == info.tcpAck && lastPacket.tcpAck == info.tcpAck && lastPacket.tcpSeq == info.tcpSeq { - info.stateDescript = 7 - } else { - info.stateDescript = 6 - } + info.stateDescript = StateTcpDisconnect23 // FIN-ACK (step 2+3 combined) } } } } + savereturn: - p.Lock() - p.cu[info.Key] = info - p.Unlock() + // 只有在连接未关闭时才保存 / Only save if connection is not closed + if !connectionClosed { + p.connections.Set(info.Key, info) + } return info, nil } +// parseUdp 解析 UDP 数据包 +// parseUdp parses UDP packets func (p *Packets) parseUdp(info PacketInfo, packet gopacket.Packet, layer gopacket.Layer, udp *layers.UDP) (PacketInfo, error) { - info.Key = fmt.Sprintf("udp://%s:%d=%s:%d", info.SrcIP, udp.SrcPort, info.DstIP, udp.DstPort) - info.ReverseKey = fmt.Sprintf("udp://%s:%d=%s:%d", info.DstIP, udp.DstPort, info.SrcIP, udp.SrcPort) + // 使用优化的 key 构建 / Use optimized key building + info.Key = buildKey("udp", info.SrcIP, fmt.Sprintf("%d", udp.SrcPort), info.DstIP, fmt.Sprintf("%d", udp.DstPort)) + info.ReverseKey = buildKey("udp", info.DstIP, fmt.Sprintf("%d", udp.DstPort), info.SrcIP, fmt.Sprintf("%d", udp.SrcPort)) info.Type = "udp" info.SrcPort = fmt.Sprintf("%d", udp.SrcPort) info.DstPort = fmt.Sprintf("%d", udp.DstPort) info.packet = packet info.tcpPayloads = len(layer.LayerPayload()) - p.Lock() - p.cu[info.Key] = info - p.Unlock() + info.stateDescript = StateUdp + + // 获取已存在的连接信息 / Get existing connection info + lastPacket, exists := p.connections.Get(info.Key) + + if !exists { + // 新 UDP 连接 / New UDP connection + info.FirstSeen = info.Timestamp + info.LastSeen = info.Timestamp + info.PacketCount = 1 + info.ByteCount = uint64(len(packet.Data())) + + // 更新统计 / Update statistics + p.statsLock.Lock() + p.stats.Connections.TotalConnections++ + atomic.AddInt64(&p.stats.Connections.UdpConnections, 1) + p.statsLock.Unlock() + + // 触发新连接回调 / Trigger new connection callback + if p.config.OnNewConnection != nil { + p.config.OnNewConnection(info) + } + } else { + // 更新已存在的连接 / Update existing connection + info.FirstSeen = lastPacket.FirstSeen + info.LastSeen = info.Timestamp + info.PacketCount = lastPacket.PacketCount + 1 + info.ByteCount = lastPacket.ByteCount + uint64(len(packet.Data())) + info.comment = lastPacket.comment + + // 继承 MAC 地址 / Inherit MAC addresses + if lastPacket.SrcMac != nil && len(info.SrcMac) == 0 { + info.SrcMac = lastPacket.SrcMac + } + if lastPacket.DstMac != nil && len(info.DstMac) == 0 { + info.DstMac = lastPacket.DstMac + } + } + + p.connections.Set(info.Key, info) return info, nil } + +// parseIcmp 解析 ICMPv4 数据包 +// parseIcmp parses ICMPv4 packets +func (p *Packets) parseIcmp(info PacketInfo, packet gopacket.Packet, layer gopacket.Layer, icmp *layers.ICMPv4) (PacketInfo, error) { + info.Type = "icmp" + info.IcmpType = uint8(icmp.TypeCode.Type()) + info.IcmpCode = uint8(icmp.TypeCode.Code()) + info.IcmpChecksum = icmp.Checksum + info.IcmpId = icmp.Id + info.IcmpSeq = icmp.Seq + info.tcpPayloads = len(layer.LayerPayload()) + info.stateDescript = StateIcmp + + // ICMP 的 Key 格式:icmp://srcIP=dstIP:type:code:id:seq + // ICMP Key format: icmp://srcIP=dstIP:type:code:id:seq + sb := getStringBuilder() + defer putStringBuilder(sb) + + sb.WriteString("icmp://") + sb.WriteString(info.SrcIP) + sb.WriteString("=") + sb.WriteString(info.DstIP) + sb.WriteString(":") + sb.WriteString(fmt.Sprintf("%d:%d:%d:%d", info.IcmpType, info.IcmpCode, info.IcmpId, info.IcmpSeq)) + info.Key = sb.String() + + sb.Reset() + sb.WriteString("icmp://") + sb.WriteString(info.DstIP) + sb.WriteString("=") + sb.WriteString(info.SrcIP) + sb.WriteString(":") + sb.WriteString(fmt.Sprintf("%d:%d:%d:%d", info.IcmpType, info.IcmpCode, info.IcmpId, info.IcmpSeq)) + info.ReverseKey = sb.String() + + info.packet = packet + + // 获取已存在的连接信息 / Get existing connection info + lastPacket, exists := p.connections.Get(info.Key) + + if !exists { + // 新 ICMP 连接 / New ICMP connection + info.FirstSeen = info.Timestamp + info.LastSeen = info.Timestamp + info.PacketCount = 1 + info.ByteCount = uint64(len(packet.Data())) + + // 更新统计 / Update statistics + p.statsLock.Lock() + p.stats.Connections.TotalConnections++ + atomic.AddInt64(&p.stats.Connections.IcmpConnections, 1) + p.statsLock.Unlock() + + // 触发新连接回调 / Trigger new connection callback + if p.config.OnNewConnection != nil { + p.config.OnNewConnection(info) + } + } else { + // 更新已存在的连接 / Update existing connection + info.FirstSeen = lastPacket.FirstSeen + info.LastSeen = info.Timestamp + info.PacketCount = lastPacket.PacketCount + 1 + info.ByteCount = lastPacket.ByteCount + uint64(len(packet.Data())) + info.comment = lastPacket.comment + + // 继承 MAC 地址 / Inherit MAC addresses + if lastPacket.SrcMac != nil && len(info.SrcMac) == 0 { + info.SrcMac = lastPacket.SrcMac + } + if lastPacket.DstMac != nil && len(info.DstMac) == 0 { + info.DstMac = lastPacket.DstMac + } + } + + p.connections.Set(info.Key, info) + return info, nil +} + +// parseIcmpv6 解析 ICMPv6 数据包 +// parseIcmpv6 parses ICMPv6 packets +func (p *Packets) parseIcmpv6(info PacketInfo, packet gopacket.Packet, layer gopacket.Layer, icmpv6 *layers.ICMPv6) (PacketInfo, error) { + info.Type = "icmpv6" + info.IcmpType = uint8(icmpv6.TypeCode.Type()) + info.IcmpCode = uint8(icmpv6.TypeCode.Code()) + info.IcmpChecksum = icmpv6.Checksum + info.tcpPayloads = len(layer.LayerPayload()) + info.stateDescript = StateIcmpv6 + + // ICMPv6 的 Key 格式:icmpv6://srcIP=dstIP:type:code + // ICMPv6 Key format: icmpv6://srcIP=dstIP:type:code + sb := getStringBuilder() + defer putStringBuilder(sb) + + sb.WriteString("icmpv6://") + sb.WriteString(info.SrcIP) + sb.WriteString("=") + sb.WriteString(info.DstIP) + sb.WriteString(":") + sb.WriteString(fmt.Sprintf("%d:%d", info.IcmpType, info.IcmpCode)) + info.Key = sb.String() + + sb.Reset() + sb.WriteString("icmpv6://") + sb.WriteString(info.DstIP) + sb.WriteString("=") + sb.WriteString(info.SrcIP) + sb.WriteString(":") + sb.WriteString(fmt.Sprintf("%d:%d", info.IcmpType, info.IcmpCode)) + info.ReverseKey = sb.String() + + info.packet = packet + + // 获取已存在的连接信息 / Get existing connection info + lastPacket, exists := p.connections.Get(info.Key) + + if !exists { + // 新 ICMPv6 连接 / New ICMPv6 connection + info.FirstSeen = info.Timestamp + info.LastSeen = info.Timestamp + info.PacketCount = 1 + info.ByteCount = uint64(len(packet.Data())) + + // 更新统计 / Update statistics + p.statsLock.Lock() + p.stats.Connections.TotalConnections++ + atomic.AddInt64(&p.stats.Connections.IcmpConnections, 1) + p.statsLock.Unlock() + + // 触发新连接回调 / Trigger new connection callback + if p.config.OnNewConnection != nil { + p.config.OnNewConnection(info) + } + } else { + // 更新已存在的连接 / Update existing connection + info.FirstSeen = lastPacket.FirstSeen + info.LastSeen = info.Timestamp + info.PacketCount = lastPacket.PacketCount + 1 + info.ByteCount = lastPacket.ByteCount + uint64(len(packet.Data())) + info.comment = lastPacket.comment + + // 继承 MAC 地址 / Inherit MAC addresses + if lastPacket.SrcMac != nil && len(info.SrcMac) == 0 { + info.SrcMac = lastPacket.SrcMac + } + if lastPacket.DstMac != nil && len(info.DstMac) == 0 { + info.DstMac = lastPacket.DstMac + } + } + + p.connections.Set(info.Key, info) + return info, nil +} + +// ════════════════════════════════════════════════ +// 辅助方法和工具函数 / Helper Methods and Utility Functions +// ════════════════════════════════════════════════ + +// GetStateDescription 获取状态描述符的文本描述 +// GetStateDescription gets text description of state descriptor +func GetStateDescription(state uint8) string { + switch state { + case StateUnknown: + return "Unknown" + case StateTcpConnect1: + return "TCP SYN" + case StateTcpConnect2: + return "TCP SYN-ACK" + case StateTcpConnect3: + return "TCP ACK (Connected)" + case StateTcpDisconnect1: + return "TCP FIN (Step 1)" + case StateTcpDisconnect2: + return "TCP ACK (Step 2)" + case StateTcpDisconnect23: + return "TCP FIN-ACK (Step 2+3)" + case StateTcpDisconnect3: + return "TCP FIN (Step 3)" + case StateTcpDisconnect4: + return "TCP ACK (Closed)" + case StateTcpAckOk: + return "TCP ACK" + case StateTcpRetransmit: + return "TCP Retransmission" + case StateTcpEce: + return "TCP ECE" + case StateTcpCwr: + return "TCP CWR" + case StateTcpRst: + return "TCP RST" + case StateTcpKeepalive: + return "TCP Keepalive" + case StateUdp: + return "UDP" + case StateIcmp: + return "ICMP" + case StateIcmpv6: + return "ICMPv6" + default: + return fmt.Sprintf("Unknown(%d)", state) + } +} + +// FormatDuration 格式化时间间隔为人类可读格式 +// FormatDuration formats duration to human readable format +func FormatDuration(d time.Duration) string { + if d < time.Microsecond { + return fmt.Sprintf("%dns", d.Nanoseconds()) + } else if d < time.Millisecond { + return fmt.Sprintf("%.2fµs", float64(d.Nanoseconds())/1000.0) + } else if d < time.Second { + return fmt.Sprintf("%.2fms", float64(d.Microseconds())/1000.0) + } else if d < time.Minute { + return fmt.Sprintf("%.2fs", d.Seconds()) + } else if d < time.Hour { + return fmt.Sprintf("%.2fm", d.Minutes()) + } + return fmt.Sprintf("%.2fh", d.Hours()) +} + +// FormatBytes 格式化字节数为人类可读格式 +// FormatBytes formats bytes to human readable format +func FormatBytes(bytes uint64) string { + const unit = 1024 + if bytes < unit { + return fmt.Sprintf("%d B", bytes) + } + div, exp := uint64(unit), 0 + for n := bytes / unit; n >= unit; n /= unit { + div *= unit + exp++ + } + return fmt.Sprintf("%.2f %cB", float64(bytes)/float64(div), "KMGTPE"[exp]) +} + +// PrintStats 打印统计信息(用于调试) +// PrintStats prints statistics (for debugging) +func (p *Packets) PrintStats() { + stats := p.GetStats() + + fmt.Println("════════════════════════════════════════") + fmt.Println("Packet Statistics / 数据包统计") + fmt.Println("════════════════════════════════════════") + fmt.Printf("Start Time / 启动时间: %s\n", stats.StartTime.Format("2006-01-02 15:04:05")) + fmt.Printf("Uptime / 运行时间: %s\n", FormatDuration(time.Since(stats.StartTime))) + fmt.Println() + + fmt.Println("Connection Statistics / 连接统计:") + fmt.Printf(" Active / 活跃: %d\n", stats.Connections.ActiveConnections) + fmt.Printf(" Total / 总计: %d\n", stats.Connections.TotalConnections) + fmt.Printf(" Closed / 已关闭: %d\n", stats.Connections.ClosedConnections) + fmt.Printf(" Timeout / 超时: %d\n", stats.Connections.TimeoutConnections) + fmt.Printf(" TCP: %d\n", stats.Connections.TcpConnections) + fmt.Printf(" UDP: %d\n", stats.Connections.UdpConnections) + fmt.Printf(" ICMP: %d\n", stats.Connections.IcmpConnections) + fmt.Println() + + fmt.Println("Error Statistics / 错误统计:") + fmt.Printf(" Total / 总计: %d\n", stats.Errors.TotalErrors) + fmt.Printf(" Link Layer / 链路层: %d\n", stats.Errors.LinkLayerErrors) + fmt.Printf(" Network / 网络层: %d\n", stats.Errors.NetworkErrors) + fmt.Printf(" Transport / 传输层: %d\n", stats.Errors.TransportErrors) + fmt.Printf(" Unsupported / 不支持: %d\n", stats.Errors.UnsupportedErrors) + + if !stats.LastCleanup.IsZero() { + fmt.Println() + fmt.Printf("Last Cleanup / 上次清理: %s\n", stats.LastCleanup.Format("2006-01-02 15:04:05")) + } + + fmt.Println("════════════════════════════════════════") +} + +// ExportConnectionsToJSON 导出连接信息为 JSON 格式(示例) +// ExportConnectionsToJSON exports connections to JSON format (example) +func (p *Packets) ExportConnectionsToJSON() string { + connections := p.GetAllConnections() + + sb := getStringBuilder() + defer putStringBuilder(sb) + + sb.WriteString("[\n") + for i, conn := range connections { + if i > 0 { + sb.WriteString(",\n") + } + sb.WriteString(fmt.Sprintf(` { + "key": "%s", + "type": "%s", + "src_ip": "%s", + "src_port": "%s", + "dst_ip": "%s", + "dst_port": "%s", + "packet_count": %d, + "byte_count": %d, + "state": "%s", + "first_seen": "%s", + "last_seen": "%s", + "duration": "%s" + }`, + conn.Key, + conn.Type, + conn.SrcIP, + conn.SrcPort, + conn.DstIP, + conn.DstPort, + conn.PacketCount, + conn.ByteCount, + GetStateDescription(conn.stateDescript), + conn.FirstSeen.Format(time.RFC3339), + conn.LastSeen.Format(time.RFC3339), + FormatDuration(conn.LastSeen.Sub(conn.FirstSeen)), + )) + } + sb.WriteString("\n]") + + return sb.String() +} diff --git a/go.mod b/go.mod index 554df46..c722cb1 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,10 @@ module b612.me/bcap -go 1.20 +go 1.24.0 require ( - github.com/florianl/go-nfqueue/v2 v2.0.0 - github.com/google/gopacket v1.1.19 + github.com/florianl/go-nfqueue/v2 v2.0.2 + github.com/gopacket/gopacket v1.5.0 ) require ( @@ -12,7 +12,7 @@ require ( github.com/josharian/native v1.1.0 // indirect github.com/mdlayher/netlink v1.7.2 // indirect github.com/mdlayher/socket v0.4.1 // indirect - golang.org/x/net v0.25.0 // indirect + golang.org/x/net v0.39.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.20.0 // indirect + golang.org/x/sys v0.32.0 // indirect ) diff --git a/go.sum b/go.sum index 08539f5..ed1601b 100644 --- a/go.sum +++ b/go.sum @@ -1,30 +1,18 @@ -github.com/florianl/go-nfqueue/v2 v2.0.0 h1:NTCxS9b0GSbHkWv1a7oOvZn679fsyDkaSkRvOYpQ9Oo= -github.com/florianl/go-nfqueue/v2 v2.0.0/go.mod h1:M2tBLIj62QpwqjwV0qfcjqGOqP3qiTuXr2uSRBXH9Qk= +github.com/florianl/go-nfqueue/v2 v2.0.2 h1:FL5lQTeetgpCvac1TRwSfgaXUn0YSO7WzGvWNIp3JPE= +github.com/florianl/go-nfqueue/v2 v2.0.2/go.mod h1:VA09+iPOT43OMoCKNfXHyzujQUty2xmzyCRkBOlmabc= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= -github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= +github.com/gopacket/gopacket v1.5.0 h1:9s9fcSUVKFlRV97B77Bq9XNV3ly2gvvsneFMQUGjc+M= +github.com/gopacket/gopacket v1.5.0/go.mod h1:i3NaGaqfoWKAr1+g7qxEdWsmfT+MXuWkAe9+THv8LME= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= +golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= diff --git a/libpcap/libpcap.go b/libpcap/libpcap.go index d2fe634..6072a9c 100644 --- a/libpcap/libpcap.go +++ b/libpcap/libpcap.go @@ -3,8 +3,8 @@ package libpcap import ( "context" "fmt" - "github.com/google/gopacket" - "github.com/google/gopacket/pcap" + "github.com/gopacket/gopacket" + "github.com/gopacket/gopacket/pcap" ) type NetCatch struct { diff --git a/nfq/nfqueue.go b/nfq/nfqueue.go index 981ba68..6a7c7bf 100644 --- a/nfq/nfqueue.go +++ b/nfq/nfqueue.go @@ -4,8 +4,8 @@ import ( "context" "fmt" "github.com/florianl/go-nfqueue/v2" - "github.com/google/gopacket" - "github.com/google/gopacket/layers" + "github.com/gopacket/gopacket" + "github.com/gopacket/gopacket/layers" "time" )