重构代码
This commit is contained in:
+726
@@ -0,0 +1,726 @@
|
||||
# bcap API 手册
|
||||
|
||||
开发者参考文档。快速接入说明见 [`../README.md`](../README.md)。
|
||||
|
||||
## 1. 主包职责
|
||||
|
||||
主包负责:
|
||||
|
||||
- 统一的报文事实层
|
||||
- 统一的 flow 表达
|
||||
- 统一的轻量 hint 层
|
||||
- 以 TCP 为重点的轻状态跟踪
|
||||
|
||||
主包不负责:
|
||||
|
||||
- 报告生成
|
||||
- 业务规则判断
|
||||
- 用户侧展示文案
|
||||
- 动作编排
|
||||
- 深度流重组
|
||||
- 应用层协议解析框架
|
||||
|
||||
核心对象:
|
||||
|
||||
- `Decoder`
|
||||
- 单包事实解码
|
||||
- `Tracker`
|
||||
- flow 状态与轻量提示
|
||||
- `Analyzer`
|
||||
- `Decoder + Tracker` 组合入口
|
||||
|
||||
## 2. 公开入口
|
||||
|
||||
### 2.1 `Decoder`
|
||||
|
||||
构造:
|
||||
|
||||
```go
|
||||
decoder := bcap.NewDecoder()
|
||||
```
|
||||
|
||||
主要方法:
|
||||
|
||||
```go
|
||||
Decode(packet gopacket.Packet) (Packet, error)
|
||||
DecodeWithOptions(packet gopacket.Packet, opts DecodeOptions) (Packet, error)
|
||||
```
|
||||
|
||||
使用时机:
|
||||
|
||||
- 只想获得单包事实
|
||||
- 自己维护状态机
|
||||
- 想把 `bcap` 当作标准化事实提取层
|
||||
|
||||
### 2.2 `Tracker`
|
||||
|
||||
构造:
|
||||
|
||||
```go
|
||||
tracker := bcap.NewTracker()
|
||||
tracker := bcap.NewTrackerWithConfig(cfg)
|
||||
```
|
||||
|
||||
主要方法:
|
||||
|
||||
```go
|
||||
Observe(packet Packet) (Observation, error)
|
||||
CleanupExpiredFlows() int
|
||||
ActiveFlowCount() int
|
||||
Stop()
|
||||
```
|
||||
|
||||
使用时机:
|
||||
|
||||
- 已经在别处完成解码
|
||||
- 只想复用 `bcap` 的 flow / hint 跟踪能力
|
||||
|
||||
### 2.3 `Analyzer`
|
||||
|
||||
构造:
|
||||
|
||||
```go
|
||||
analyzer := bcap.NewAnalyzer()
|
||||
analyzer := bcap.NewAnalyzerWithConfig(cfg)
|
||||
```
|
||||
|
||||
主要方法:
|
||||
|
||||
```go
|
||||
ObservePacket(packet gopacket.Packet) (Observation, error)
|
||||
ObservePacketWithOptions(packet gopacket.Packet, opts DecodeOptions) (Observation, error)
|
||||
Decoder() *Decoder
|
||||
Tracker() *Tracker
|
||||
Stop()
|
||||
```
|
||||
|
||||
使用时机:
|
||||
|
||||
- 在线抓包
|
||||
- 离线遍历
|
||||
- 绝大多数直接接入型工具
|
||||
|
||||
## 3. `DecodeOptions`
|
||||
|
||||
`DecodeOptions` 目前包含两个字段:
|
||||
|
||||
```go
|
||||
type DecodeOptions struct {
|
||||
BaseTime time.Time
|
||||
SrcMACOverride net.HardwareAddr
|
||||
}
|
||||
```
|
||||
|
||||
字段:
|
||||
|
||||
- `BaseTime`
|
||||
- 用于计算 `Packet.Meta.RelativeTime`
|
||||
- `Analyzer.ObservePacket(...)` 未显式传入时,会自动以首包时间作为基准
|
||||
- `SrcMACOverride`
|
||||
- 用于覆盖源 MAC
|
||||
|
||||
## 4. 数据模型
|
||||
|
||||
### 4.1 `Packet`
|
||||
|
||||
`Packet` 表示单包事实。
|
||||
|
||||
```go
|
||||
type Packet struct {
|
||||
Meta Meta
|
||||
Link LinkFacts
|
||||
Network NetworkFacts
|
||||
Transport TransportFacts
|
||||
Raw RawFacts
|
||||
}
|
||||
```
|
||||
|
||||
不包含跨包推断结果。
|
||||
|
||||
### 4.2 `Meta`
|
||||
|
||||
```go
|
||||
type Meta struct {
|
||||
Timestamp time.Time
|
||||
TimestampMicros int64
|
||||
RelativeTime time.Duration
|
||||
CaptureLength int
|
||||
Length int
|
||||
}
|
||||
```
|
||||
|
||||
字段:
|
||||
|
||||
- `Timestamp` / `TimestampMicros`
|
||||
- 捕获时间
|
||||
- `RelativeTime`
|
||||
- 相对 `BaseTime` 的时间差
|
||||
- `CaptureLength`
|
||||
- 实际捕获长度
|
||||
- `Length`
|
||||
- 原始报文长度
|
||||
|
||||
### 4.3 `LinkFacts`
|
||||
|
||||
```go
|
||||
type LinkFacts struct {
|
||||
Kind LinkKind
|
||||
SrcMAC net.HardwareAddr
|
||||
DstMAC net.HardwareAddr
|
||||
}
|
||||
```
|
||||
|
||||
当前支持的链路类型:
|
||||
|
||||
- `LinkKindEthernet`
|
||||
- `LinkKindLinuxSLL`
|
||||
- `LinkKindLinuxSLL2`
|
||||
- `LinkKindUnknown`
|
||||
|
||||
### 4.4 `NetworkFacts`
|
||||
|
||||
```go
|
||||
type NetworkFacts struct {
|
||||
Family NetworkFamily
|
||||
SrcIP string
|
||||
DstIP string
|
||||
TTL uint8
|
||||
HopLimit uint8
|
||||
ProtocolNumber uint16
|
||||
ARP *ARPFacts
|
||||
}
|
||||
```
|
||||
|
||||
当前支持的网络族:
|
||||
|
||||
- `NetworkFamilyIPv4`
|
||||
- `NetworkFamilyIPv6`
|
||||
- `NetworkFamilyARP`
|
||||
- `NetworkFamilyUnknown`
|
||||
|
||||
备注:
|
||||
|
||||
- IPv4 报文主要填 `TTL`
|
||||
- IPv6 报文主要填 `HopLimit`
|
||||
- ARP 报文会同时填充 `ARP`
|
||||
|
||||
### 4.5 `TransportFacts`
|
||||
|
||||
```go
|
||||
type TransportFacts struct {
|
||||
Kind ProtocolKind
|
||||
Payload int
|
||||
TCP *TCPFacts
|
||||
UDP *UDPFacts
|
||||
ICMP *ICMPFacts
|
||||
Unknown *UnknownTransportFacts
|
||||
}
|
||||
```
|
||||
|
||||
当前支持协议:
|
||||
|
||||
- `ProtocolTCP`
|
||||
- `ProtocolUDP`
|
||||
- `ProtocolICMPv4`
|
||||
- `ProtocolICMPv6`
|
||||
- `ProtocolARP`
|
||||
- `ProtocolUnknown`
|
||||
|
||||
### 4.6 协议事实结构
|
||||
|
||||
#### `TCPFacts`
|
||||
|
||||
```go
|
||||
type TCPFacts struct {
|
||||
SrcPort string
|
||||
DstPort string
|
||||
Seq uint32
|
||||
Ack uint32
|
||||
Window uint16
|
||||
SYN bool
|
||||
ACK bool
|
||||
FIN bool
|
||||
RST bool
|
||||
ECE bool
|
||||
CWR bool
|
||||
PSH bool
|
||||
Checksum uint16
|
||||
Payload int
|
||||
}
|
||||
```
|
||||
|
||||
#### `UDPFacts`
|
||||
|
||||
```go
|
||||
type UDPFacts struct {
|
||||
SrcPort string
|
||||
DstPort string
|
||||
Length uint16
|
||||
Payload int
|
||||
}
|
||||
```
|
||||
|
||||
#### `ICMPFacts`
|
||||
|
||||
```go
|
||||
type ICMPFacts struct {
|
||||
Version int
|
||||
Type uint8
|
||||
Code uint8
|
||||
Checksum uint16
|
||||
ID uint16
|
||||
Seq uint16
|
||||
Payload int
|
||||
}
|
||||
```
|
||||
|
||||
#### `ARPFacts`
|
||||
|
||||
```go
|
||||
type ARPFacts struct {
|
||||
Operation uint16
|
||||
SenderMAC net.HardwareAddr
|
||||
TargetMAC net.HardwareAddr
|
||||
SenderIP string
|
||||
TargetIP string
|
||||
}
|
||||
```
|
||||
|
||||
### 4.7 `FlowKey` / `FlowRef`
|
||||
|
||||
推荐使用结构化 flow,不把字符串 key 当成唯一公共接口。
|
||||
|
||||
```go
|
||||
type Endpoint struct {
|
||||
IP string
|
||||
Port string
|
||||
}
|
||||
|
||||
type FlowKey struct {
|
||||
Family NetworkFamily
|
||||
Protocol ProtocolKind
|
||||
Src Endpoint
|
||||
Dst Endpoint
|
||||
}
|
||||
|
||||
type FlowRef struct {
|
||||
Forward FlowKey
|
||||
Reverse FlowKey
|
||||
Stable string
|
||||
}
|
||||
```
|
||||
|
||||
方法:
|
||||
|
||||
```go
|
||||
func (f FlowKey) StableString() string
|
||||
```
|
||||
|
||||
字段:
|
||||
|
||||
- `Forward`
|
||||
- 当前方向
|
||||
- `Reverse`
|
||||
- 反向方向
|
||||
- `Stable`
|
||||
- 稳定字符串表达,可用于日志和 map key
|
||||
|
||||
### 4.8 `Observation`
|
||||
|
||||
```go
|
||||
type Observation struct {
|
||||
Packet Packet
|
||||
Flow FlowRef
|
||||
Hints HintSet
|
||||
}
|
||||
```
|
||||
|
||||
- `Packet`
|
||||
- 报文事实
|
||||
- `Flow`
|
||||
- 流引用
|
||||
- `Hints`
|
||||
- 推断结果
|
||||
|
||||
## 5. Hint 模型
|
||||
|
||||
### 5.1 `HintSet`
|
||||
|
||||
```go
|
||||
type HintSet struct {
|
||||
Summary SummaryHint
|
||||
Tags []Tag
|
||||
|
||||
TCP *TCPHint
|
||||
UDP *UDPHint
|
||||
ICMP *ICMPHint
|
||||
ARP *ARPHint
|
||||
}
|
||||
```
|
||||
|
||||
- `Summary`
|
||||
- 摘要代码
|
||||
- `Tags`
|
||||
- 标签集合
|
||||
- 协议专属 hint
|
||||
- 放在对应子结构中
|
||||
|
||||
### 5.2 TCP hints
|
||||
|
||||
```go
|
||||
type TCPHint struct {
|
||||
Phase TCPPhase
|
||||
Event TCPEvent
|
||||
LegacyState uint8
|
||||
Seq uint32
|
||||
Ack uint32
|
||||
Window uint16
|
||||
Payload int
|
||||
Retransmission bool
|
||||
Keepalive bool
|
||||
KeepaliveResponse bool
|
||||
RST bool
|
||||
ECE bool
|
||||
CWR bool
|
||||
}
|
||||
```
|
||||
|
||||
Phase:
|
||||
|
||||
- `TCPPhaseHandshake`
|
||||
- `TCPPhaseEstablished`
|
||||
- `TCPPhaseTeardown`
|
||||
- `TCPPhaseSpecial`
|
||||
- `TCPPhaseUnknown`
|
||||
|
||||
Event:
|
||||
|
||||
- `TCPEventSYN`
|
||||
- `TCPEventSYNACK`
|
||||
- `TCPEventHandshakeACK`
|
||||
- `TCPEventACK`
|
||||
- `TCPEventRetransmission`
|
||||
- `TCPEventKeepalive`
|
||||
- `TCPEventKeepaliveResp`
|
||||
- `TCPEventFIN`
|
||||
- `TCPEventFINACK`
|
||||
- `TCPEventTeardownACK`
|
||||
- `TCPEventRST`
|
||||
- `TCPEventECE`
|
||||
- `TCPEventCWR`
|
||||
- `TCPEventUnknown`
|
||||
|
||||
常见 Tag:
|
||||
|
||||
- `TagTCPHandshakeSYN`
|
||||
- `TagTCPHandshakeSYNACK`
|
||||
- `TagTCPHandshakeACK`
|
||||
- `TagTCPTeardownFIN`
|
||||
- `TagTCPTeardownFINACK`
|
||||
- `TagTCPTeardownACK`
|
||||
- `TagTCPPacket`
|
||||
- `TagTCPRetransmit`
|
||||
- `TagTCPKeepalive`
|
||||
- `TagTCPKeepaliveResp`
|
||||
- `TagTCPRst`
|
||||
- `TagTCPEce`
|
||||
- `TagTCPCwr`
|
||||
|
||||
### 5.3 UDP / ICMP / ARP hints
|
||||
|
||||
#### `UDPHint`
|
||||
|
||||
```go
|
||||
type UDPHint struct {
|
||||
Payload int
|
||||
}
|
||||
```
|
||||
|
||||
对应 Tag:
|
||||
|
||||
- `TagUDPPacket`
|
||||
|
||||
#### `ICMPHint`
|
||||
|
||||
```go
|
||||
type ICMPHint struct {
|
||||
Version int
|
||||
Type uint8
|
||||
Code uint8
|
||||
IsEcho bool
|
||||
IsEchoReply bool
|
||||
IsUnreachable bool
|
||||
IsTimeExceeded bool
|
||||
}
|
||||
```
|
||||
|
||||
对应 Tag:
|
||||
|
||||
- `TagICMPPacket`
|
||||
- `TagICMPEchoRequest`
|
||||
- `TagICMPEchoReply`
|
||||
- `TagICMPUnreachable`
|
||||
- `TagICMPTimeExceeded`
|
||||
|
||||
#### `ARPHint`
|
||||
|
||||
```go
|
||||
type ARPHint struct {
|
||||
Operation uint16
|
||||
Request bool
|
||||
Reply bool
|
||||
}
|
||||
```
|
||||
|
||||
对应 Tag:
|
||||
|
||||
- `TagARPRequest`
|
||||
- `TagARPReply`
|
||||
|
||||
## 6. TCP 跟踪边界
|
||||
|
||||
`Tracker` 对 TCP 只做轻量跟踪,不做完整 TCP 栈模拟。
|
||||
|
||||
当前覆盖:
|
||||
|
||||
- 握手识别
|
||||
- 挥手识别
|
||||
- 普通 ACK 识别
|
||||
- 重传识别
|
||||
- keepalive 识别
|
||||
- keepalive response 识别
|
||||
- RST / ECE / CWR 识别
|
||||
|
||||
实现说明:
|
||||
|
||||
- 基于 flow 跟踪
|
||||
- 使用有限 segment 记忆辅助判断重传
|
||||
- keepalive 使用启发式判断
|
||||
- 支持超时清理
|
||||
|
||||
不覆盖:
|
||||
|
||||
- 严格 TCP 协议验证
|
||||
- 深度重组
|
||||
- 业务级根因结论
|
||||
|
||||
## 7. 配置
|
||||
|
||||
### 7.1 `PacketsConfig`
|
||||
|
||||
```go
|
||||
type PacketsConfig struct {
|
||||
ConnectionTimeout time.Duration
|
||||
CleanupInterval time.Duration
|
||||
}
|
||||
```
|
||||
|
||||
字段:
|
||||
|
||||
- `ConnectionTimeout`
|
||||
- flow 状态保留时长
|
||||
- `CleanupInterval`
|
||||
- 自动清理周期
|
||||
|
||||
默认值:
|
||||
|
||||
```go
|
||||
const (
|
||||
DefaultConnectionTimeout = 5 * time.Minute
|
||||
DefaultCleanupInterval = 1 * time.Minute
|
||||
)
|
||||
```
|
||||
|
||||
默认配置:
|
||||
|
||||
```go
|
||||
cfg := bcap.DefaultConfig()
|
||||
```
|
||||
|
||||
### 7.2 生命周期方法
|
||||
|
||||
`Tracker`:
|
||||
|
||||
- `CleanupExpiredFlows() int`
|
||||
- `ActiveFlowCount() int`
|
||||
- `Stop()`
|
||||
|
||||
`Analyzer`:
|
||||
|
||||
- `Decoder() *Decoder`
|
||||
- `Tracker() *Tracker`
|
||||
- `Stop()`
|
||||
|
||||
备注:
|
||||
|
||||
- 长生命周期进程退出前调用 `Stop()`
|
||||
- 如需自行控制清理节奏,可将 `CleanupInterval` 设为 `0`,再手动调用 `CleanupExpiredFlows()`
|
||||
|
||||
## 8. 错误模型
|
||||
|
||||
`bcap` 使用 `*ParseError` 表达解析问题:
|
||||
|
||||
```go
|
||||
type ParseError struct {
|
||||
Type ParseErrorType
|
||||
Layer string
|
||||
Message string
|
||||
Err error
|
||||
}
|
||||
```
|
||||
|
||||
错误类型:
|
||||
|
||||
- `ErrTypeLinkLayer`
|
||||
- `ErrTypeNetwork`
|
||||
- `ErrTypeTransport`
|
||||
- `ErrTypeUnsupported`
|
||||
|
||||
常见触发条件:
|
||||
|
||||
- 没有有效网络层
|
||||
- 协议层对象类型不匹配
|
||||
- 跟踪阶段缺少必须的协议事实
|
||||
|
||||
## 9. 子包
|
||||
|
||||
### 9.1 `libpcap`
|
||||
|
||||
`libpcap` 子包负责在线抓包输入。
|
||||
|
||||
入口:
|
||||
|
||||
- `FindAllDevs()`
|
||||
- `NewCatch(host, filter)`
|
||||
- `NewCatchEth(eth, filter)`
|
||||
- `SetRecall(func(gopacket.Packet))`
|
||||
- `Run()`
|
||||
- `Stop()`
|
||||
|
||||
常见组合:
|
||||
|
||||
- 子包接收 `gopacket.Packet`
|
||||
- 主包 `Analyzer` 做解析和提示
|
||||
- 上层工具做展示和统计
|
||||
|
||||
### 9.2 `nfq`
|
||||
|
||||
`nfq` 子包负责 NFQUEUE 输入适配。
|
||||
|
||||
入口:
|
||||
|
||||
- `NewNfQueue(ctx, queid, maxqueue)`
|
||||
- `SetRecall(func(id uint32, q *nfqueue.Nfqueue, p Packet))`
|
||||
- `Run()`
|
||||
- `Stop()`
|
||||
|
||||
备注:
|
||||
|
||||
- `nfq.Packet` 只是 NFQUEUE 输入包装
|
||||
- 它不是主包的 `bcap.Packet`
|
||||
- 一般仍然从其中取 `gopacket.Packet`,再交给 `Analyzer` 或 `Decoder`
|
||||
|
||||
## 10. 示例
|
||||
|
||||
### 10.1 直接用 `Analyzer`
|
||||
|
||||
```go
|
||||
analyzer := bcap.NewAnalyzer()
|
||||
defer analyzer.Stop()
|
||||
|
||||
obs, err := analyzer.ObservePacket(packet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(obs.Flow.Stable)
|
||||
fmt.Println(obs.Packet.Transport.Kind)
|
||||
fmt.Println(obs.Hints.Summary.Code)
|
||||
```
|
||||
|
||||
### 10.2 自己管理解码和跟踪
|
||||
|
||||
```go
|
||||
decoder := bcap.NewDecoder()
|
||||
tracker := bcap.NewTracker()
|
||||
defer tracker.Stop()
|
||||
|
||||
decoded, err := decoder.DecodeWithOptions(packet, bcap.DecodeOptions{
|
||||
BaseTime: firstPacketTS,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
obs, err := tracker.Observe(decoded)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
### 10.3 典型离线遍历
|
||||
|
||||
```go
|
||||
f, err := os.Open("sample.pcap")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
reader, err := pcapgo.NewReader(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
source := gopacket.NewPacketSource(reader, reader.LinkType())
|
||||
analyzer := bcap.NewAnalyzer()
|
||||
defer analyzer.Stop()
|
||||
|
||||
for packet := range source.Packets() {
|
||||
obs, err := analyzer.ObservePacket(packet)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
fmt.Println(obs.Packet.Network.SrcIP, obs.Hints.Summary.Code)
|
||||
}
|
||||
```
|
||||
|
||||
## 11. 辅助函数
|
||||
|
||||
主包还提供两个轻量格式化函数:
|
||||
|
||||
- `FormatDuration(time.Duration) string`
|
||||
- `FormatBytes(uint64) string`
|
||||
|
||||
用于轻量日志与终端展示。
|
||||
|
||||
## 12. 迁移说明
|
||||
|
||||
主包旧接口已经被清理,删除项包括:
|
||||
|
||||
- `Packets`
|
||||
- `PacketInfo`
|
||||
- `ParsePacket`
|
||||
- `NewPackets`
|
||||
- `NewPacketsWithConfig`
|
||||
- `LegacyPacketInfoFromObservation`
|
||||
- `GetStateDescription`
|
||||
- `PrintStats`
|
||||
- `ExportConnectionsToJSON`
|
||||
|
||||
迁移方向:
|
||||
|
||||
- 旧的单包解析入口迁到 `Decoder`
|
||||
- 旧的“包 + 状态”混合对象迁到 `Observation`
|
||||
- 旧的连接跟踪职责迁到 `Tracker`
|
||||
- 大部分调用场景直接迁到 `Analyzer`
|
||||
|
||||
旧代码如果大量依赖字符串 key:
|
||||
|
||||
- 新代码优先改为使用 `FlowRef.Forward` / `FlowRef.Reverse`
|
||||
- 如果只是为了日志或 map key,可以继续使用 `FlowRef.Stable`
|
||||
|
||||
## 13. 相关文档
|
||||
|
||||
- 快速入口见 [`../README.md`](../README.md)
|
||||
- 设计备忘见 [`dev.md`](./dev.md)
|
||||
+518
@@ -0,0 +1,518 @@
|
||||
# bcap 开发设计备忘
|
||||
|
||||
## 1. 这次重构的前提
|
||||
|
||||
当前已确认,直接使用 `bcap` 主包的范围很小,主要只有:
|
||||
|
||||
- `apps/tcp`
|
||||
- `apps/b612` 中的 `tcm`
|
||||
- `apps/b612` 中的 `tcpkill`
|
||||
|
||||
因此这次讨论 `bcap` 新架构时,可以明确采用以下前提:
|
||||
|
||||
1. 可以摒弃旧接口,不必为了兼容历史调用持续背负旧模型。
|
||||
2. `bcap` 子包暂时不动,尤其是 `libpcap`、`nfq` 这类输入适配层先保持现状。
|
||||
3. 重点只讨论 `bcap` 主包的新职责、新模型和新接口。
|
||||
|
||||
## 2. 当前主包存在的问题
|
||||
|
||||
当前 `bcap` 主包中,`Packets`、`PacketInfo`、`StateDescript` 这一套模型混合了承担以下几类职责:
|
||||
|
||||
- 报文事实解码
|
||||
- TCP 状态推断
|
||||
- 连接状态缓存
|
||||
- 统计汇总
|
||||
- 工具侧临时状态寄存
|
||||
- 文本格式化辅助
|
||||
|
||||
这几个职责混在一起,直接导致了以下问题:
|
||||
|
||||
### 2.1 `PacketInfo` 同时承载“事实”和“推断”
|
||||
|
||||
例如:
|
||||
|
||||
- `SrcIP`、`DstIP`、`SrcMac`、`DstMac` 属于原始事实
|
||||
- `TcpSeq`、`TcpAck`、`TcpWindow` 虽然也是事实,但只适用于 TCP
|
||||
- `StateDescript` 则是推断结果
|
||||
- `Comment` 又是工具业务的临时状态
|
||||
|
||||
这会让上层调用方无法区分:哪些字段是包里本来就有的,哪些字段是状态机推出来的,哪些字段甚至只是上层工具临时借位存进去的。
|
||||
|
||||
### 2.2 主包过于 TCP 化
|
||||
|
||||
当前主模型的核心字段和访问器明显偏向 TCP:
|
||||
|
||||
- `TcpSeq()`
|
||||
- `TcpAck()`
|
||||
- `TcpWindow()`
|
||||
- `TcpPayloads()`
|
||||
- `StateDescript()`
|
||||
|
||||
虽然已经开始支持 UDP、ICMP、ICMPv6,但整体接口风格仍然默认“TCP 才是主角,其他协议只是附带”。如果目标是做“通用 transport/protocol hint”,这个建模方式会持续妨碍扩展。
|
||||
|
||||
### 2.3 `Packets` 同时是解析器、状态仓库和工具 scratchpad
|
||||
|
||||
当前 `Packets` 既负责:
|
||||
|
||||
- 解析 gopacket.Packet
|
||||
- 保存连接状态
|
||||
- 维护统计
|
||||
- 暴露 `Key()` 查询
|
||||
- 暴露 `SetComment()` 给上层写入工具状态
|
||||
|
||||
这实际上把“协议解析层”和“工具业务状态层”耦死了。
|
||||
|
||||
### 2.4 字符串 key 被当成核心接口
|
||||
|
||||
当前以 `tcp://a:b-c:d` 这类字符串 key 作为主要流标识,这虽然方便快速实现,但不适合作为长期公共接口。
|
||||
|
||||
问题包括:
|
||||
|
||||
- 协议语义不够结构化
|
||||
- 上层反复自行拼 key
|
||||
- 不利于未来支持可逆 flow、单向 packet、无端口协议、ARP 等场景
|
||||
|
||||
### 2.5 文本格式化与 JSON 导出混入主包
|
||||
|
||||
像 `GetStateDescription`、`PrintStats`、`ExportConnectionsToJSON` 这种能力,不属于主包核心解析模型,应该降级为上层工具职责,或者至少转到辅助层,而不是继续成为主接口的一部分。
|
||||
|
||||
## 3. 新架构的总体目标
|
||||
|
||||
`bcap` 主包应收敛成:
|
||||
|
||||
- 一个统一的报文事实解码层
|
||||
- 一个统一的协议 hint 推断层
|
||||
- 一个以 TCP 为重点的轻量状态跟踪层
|
||||
|
||||
同时明确边界:
|
||||
|
||||
### 3.1 `bcap` 应负责的事
|
||||
|
||||
- 识别链路层、网络层、传输层协议
|
||||
- 提取结构化元数据
|
||||
- 输出通用 protocol hint
|
||||
- 对 TCP 做轻状态推断
|
||||
- 对 UDP/ICMP/ARP 做协议识别和元数据抽取
|
||||
- 对未知协议输出“尽量完整的基础事实”
|
||||
|
||||
### 3.2 `bcap` 不应负责的事
|
||||
|
||||
- 诊断报告拼装
|
||||
- 干预动作编排
|
||||
- CLI/TUI 展示文案
|
||||
- 工具侧临时状态寄存
|
||||
- 业务策略倒计时
|
||||
- 面向最终用户的统计打印
|
||||
|
||||
也就是说,`bcap` 的定位应是“提供事实和提示”,而不是“替工具做决策”。
|
||||
|
||||
## 4. 新架构的核心分层
|
||||
|
||||
建议主包收敛成 3 个核心对象:
|
||||
|
||||
1. `Decoder`
|
||||
2. `Tracker`
|
||||
3. `Analyzer`
|
||||
|
||||
### 4.1 `Decoder`
|
||||
|
||||
`Decoder` 是无状态组件,只负责把 `gopacket.Packet` 解码成结构化“事实”。
|
||||
|
||||
职责:
|
||||
|
||||
- 识别 L2/L3/L4 协议
|
||||
- 抽取 MAC、IP、端口、TTL、flags、payload 长度等事实
|
||||
- 不做跨包状态推断
|
||||
- 不维护连接表
|
||||
- 不暴露工具业务状态
|
||||
|
||||
### 4.2 `Tracker`
|
||||
|
||||
`Tracker` 是有状态组件,只负责基于连续观测结果做轻量 hint 推断。
|
||||
|
||||
职责:
|
||||
|
||||
- 维护按 flow 组织的轻状态
|
||||
- 针对 TCP 输出握手、挥手、重传、keepalive、RST 等 hint
|
||||
- 针对 ICMP/ARP 等协议补充轻量语义标签
|
||||
- 不负责最终展示
|
||||
- 不负责业务动作控制
|
||||
|
||||
### 4.3 `Analyzer`
|
||||
|
||||
`Analyzer` 是便捷组合层,内部持有 `Decoder + Tracker`,给上层一个简单入口。
|
||||
|
||||
典型用途:
|
||||
|
||||
- `tcm` / `tcml` 这种一边收包一边展示的工具
|
||||
- `diag` 这种离线分析工具
|
||||
- `show` 这种需要 packet facts + hints 的浏览工具
|
||||
|
||||
这样设计后:
|
||||
|
||||
- 只关心事实解码的调用方可直接用 `Decoder`
|
||||
- 需要 hint 的调用方可用 `Analyzer`
|
||||
- 需要更细粒度控制的调用方可分别持有 `Decoder` 和 `Tracker`
|
||||
|
||||
## 5. 新的数据模型
|
||||
|
||||
新模型建议明确拆成“事实”和“推断”两层。
|
||||
|
||||
### 5.1 第一层:Packet Facts
|
||||
|
||||
建议定义统一的结构化事实模型,例如:
|
||||
|
||||
```go
|
||||
type Packet struct {
|
||||
Meta Meta
|
||||
Link LinkFacts
|
||||
Network NetworkFacts
|
||||
Transport TransportFacts
|
||||
Raw RawFacts
|
||||
}
|
||||
```
|
||||
|
||||
其中:
|
||||
|
||||
- `Meta`:时间戳、捕获长度、原始长度、相对时间等
|
||||
- `LinkFacts`:链路层类型、源/目标 MAC、链路层协议等
|
||||
- `NetworkFacts`:IPv4/IPv6/ARP 等信息,包含地址、TTL/HopLimit、fragment、protocol number 等
|
||||
- `TransportFacts`:TCP/UDP/ICMP/unknown 等结构化内容
|
||||
- `RawFacts`:payload 长度、原始 packet 引用等
|
||||
|
||||
这层全部表示“包里本来就有”的事实,不包含推断结论。
|
||||
|
||||
### 5.2 第二层:Observation
|
||||
|
||||
建议把“经过跟踪器分析后的结果”定义为单独对象:
|
||||
|
||||
```go
|
||||
type Observation struct {
|
||||
Packet Packet
|
||||
Flow FlowRef
|
||||
Hints HintSet
|
||||
}
|
||||
```
|
||||
|
||||
这里:
|
||||
|
||||
- `Packet` 是原始事实
|
||||
- `Flow` 是结构化流引用
|
||||
- `Hints` 是推断结果
|
||||
|
||||
### 5.3 FlowKey / FlowRef
|
||||
|
||||
建议引入结构化 flow 标识,而不是继续把字符串 key 作为主接口。
|
||||
|
||||
```go
|
||||
type FlowKey struct {
|
||||
Family NetworkFamily
|
||||
Protocol TransportKind
|
||||
Src Endpoint
|
||||
Dst Endpoint
|
||||
}
|
||||
|
||||
type FlowRef struct {
|
||||
Forward FlowKey
|
||||
Reverse FlowKey
|
||||
Stable string
|
||||
}
|
||||
```
|
||||
|
||||
其中:
|
||||
|
||||
- `Forward` / `Reverse` 提供方向化 key
|
||||
- `Stable` 可以保留字符串形式,方便日志、map key、兼容旧调试习惯
|
||||
- `Endpoint` 应允许“只有地址没有端口”的协议
|
||||
|
||||
### 5.4 HintSet
|
||||
|
||||
建议将所有推断信息挂在 `HintSet` 上:
|
||||
|
||||
```go
|
||||
type HintSet struct {
|
||||
Summary SummaryHint
|
||||
Tags []Tag
|
||||
|
||||
TCP *TCPHint
|
||||
UDP *UDPHint
|
||||
ICMP *ICMPHint
|
||||
ARP *ARPHint
|
||||
}
|
||||
```
|
||||
|
||||
这样做的好处是:
|
||||
|
||||
- `Summary` 提供给 `tcm/tcml` 这类工具快速展示一句话
|
||||
- `Tags` 提供统一筛选、着色、统计能力
|
||||
- 协议专属 hint 由独立结构体承载,不再污染通用顶层对象
|
||||
|
||||
## 6. 协议专属 hint 的建议形态
|
||||
|
||||
### 6.1 `TCPHint`
|
||||
|
||||
`TCPHint` 是新架构里的重点能力,建议承载:
|
||||
|
||||
- seq / ack / window
|
||||
- flags
|
||||
- payload length
|
||||
- options 摘要
|
||||
- handshake / teardown 阶段
|
||||
- retransmission 判定
|
||||
- keepalive 判定
|
||||
- keepalive response 判定
|
||||
- rst / ece / cwr 等特殊标记
|
||||
- 是否疑似 out-of-order
|
||||
|
||||
同时建议把原来的 `StateDescript uint8` 弃用,改成更结构化的字段或 tag,例如:
|
||||
|
||||
- `Phase: TCPPhase`
|
||||
- `Event: TCPEvent`
|
||||
- `Tags: []Tag`
|
||||
|
||||
### 6.2 `UDPHint`
|
||||
|
||||
UDP 先做轻支持即可,建议包括:
|
||||
|
||||
- payload length
|
||||
- checksum presence / checksum value
|
||||
- zero-length payload
|
||||
- fragment 上下文关联信息(如果网络层提供)
|
||||
|
||||
不建议在主包内直接引入 DNS、QUIC 等应用层推断,除非后续确认收益足够大。
|
||||
|
||||
### 6.3 `ICMPHint`
|
||||
|
||||
建议至少包括:
|
||||
|
||||
- family: v4 / v6
|
||||
- type / code
|
||||
- id / seq
|
||||
- 是否 echo request / echo reply
|
||||
- 是否 destination unreachable / time exceeded
|
||||
|
||||
这层对 `show` 和 `tcm/tcml` 的“多协议展示”已经足够有价值。
|
||||
|
||||
### 6.4 `ARPHint`
|
||||
|
||||
如果目标是支持“展示其他协议”,ARP 必须成为一等公民。
|
||||
|
||||
建议至少包括:
|
||||
|
||||
- operation
|
||||
- sender MAC / sender IP
|
||||
- target MAC / target IP
|
||||
- request / reply tag
|
||||
|
||||
同时 `Decoder` 对 ARP 不应再因为没有 `NetworkLayer()` 而直接报错。
|
||||
|
||||
## 7. 标签体系建议
|
||||
|
||||
建议在主包内定义统一 tag 体系,供所有上层工具复用。
|
||||
|
||||
例如:
|
||||
|
||||
- `tcp.handshake.syn`
|
||||
- `tcp.handshake.synack`
|
||||
- `tcp.handshake.ack`
|
||||
- `tcp.teardown.fin`
|
||||
- `tcp.keepalive`
|
||||
- `tcp.keepalive.response`
|
||||
- `tcp.retransmit`
|
||||
- `tcp.rst`
|
||||
- `tcp.ece`
|
||||
- `tcp.cwr`
|
||||
- `udp.packet`
|
||||
- `icmp.echo-request`
|
||||
- `icmp.echo-reply`
|
||||
- `icmp.unreachable`
|
||||
- `arp.request`
|
||||
- `arp.reply`
|
||||
- `transport.unknown`
|
||||
|
||||
这比继续依赖一个不断膨胀的 `StateDescript` 枚举更稳。
|
||||
|
||||
## 8. 状态与并发语义
|
||||
|
||||
这是新架构里必须提前定死的部分。
|
||||
|
||||
### 8.1 `Decoder` 的并发语义
|
||||
|
||||
`Decoder` 无状态,应明确支持并发使用。
|
||||
|
||||
### 8.2 `Tracker` 的并发语义
|
||||
|
||||
`Tracker` 有状态,应明确:
|
||||
|
||||
- 同一 flow 的观测结果必须按顺序输入
|
||||
- 允许多 flow 并发,但内部状态更新语义以 flow 顺序为准
|
||||
- 如果调用方无法保证顺序,则 hint 结果可能退化
|
||||
|
||||
这点非常重要。当前的 `shardedMap` 只能提供 map 级线程安全,不能自动提供时序正确性。TCP hint 尤其依赖时序,因此新接口必须把这一点写进设计,而不是默认调用方自己猜。
|
||||
|
||||
### 8.3 工具侧临时状态必须外移
|
||||
|
||||
例如 `tcml` 的:
|
||||
|
||||
- delay countdown
|
||||
- block / allow 状态
|
||||
- 业务关键字命中后的动作状态
|
||||
|
||||
都不应再借用 `Comment/SetComment` 写进 `bcap` 状态仓库。
|
||||
|
||||
新架构里,`Tracker` 只维护协议分析所需状态,不维护工具策略状态。
|
||||
|
||||
## 9. 错误模型建议
|
||||
|
||||
建议错误也按层次整理:
|
||||
|
||||
- 解码失败
|
||||
- 支持范围外
|
||||
- 协议字段损坏
|
||||
- 状态推断降级
|
||||
|
||||
同时要允许“部分可用”的结果。
|
||||
|
||||
例如:
|
||||
|
||||
- 能识别到 IPv4,但 transport 未识别
|
||||
- 能识别到 ARP,但字段不完整
|
||||
- 能拿到 TCP flags,但因为缺少上下文无法判定 retransmit
|
||||
|
||||
这种情况下应尽量返回事实层结果,而不是简单整体失败。
|
||||
|
||||
## 10. 主包不再保留的旧概念
|
||||
|
||||
建议在新架构中明确移除或降级以下旧概念:
|
||||
|
||||
- `PacketInfo`
|
||||
- `Packets`
|
||||
- `StateDescript()`
|
||||
- `Comment()` / `SetComment()`
|
||||
- `GetStateDescription()`
|
||||
- `PrintStats()`
|
||||
- `ExportConnectionsToJSON()`
|
||||
- 各类以字符串 key 作为核心接口的模型
|
||||
|
||||
原因是:
|
||||
|
||||
- `PacketInfo` 混杂事实、推断和业务临时状态
|
||||
- `Packets` 角色过多,不适合作为长期主对象
|
||||
- `StateDescript` 无法优雅承载多协议 hint
|
||||
- `Comment/SetComment` 明显越界
|
||||
- 格式化和导出不应继续作为主包核心能力
|
||||
|
||||
## 11. 建议的新接口形态
|
||||
|
||||
建议主包至少提供两层使用方式。
|
||||
|
||||
### 11.1 低层接口
|
||||
|
||||
```go
|
||||
decoder := bcap.NewDecoder(opts)
|
||||
pkt, err := decoder.Decode(gpkt)
|
||||
|
||||
tracker := bcap.NewTracker(opts)
|
||||
obs, err := tracker.Observe(pkt)
|
||||
```
|
||||
|
||||
适合:
|
||||
|
||||
- 想分别控制解码与跟踪的调用方
|
||||
- 想单测某一层的调用方
|
||||
- 想把事实层和 hint 层分开使用的工具
|
||||
|
||||
### 11.2 便捷接口
|
||||
|
||||
```go
|
||||
analyzer := bcap.NewAnalyzer(opts)
|
||||
obs, err := analyzer.ObservePacket(gpkt)
|
||||
```
|
||||
|
||||
适合:
|
||||
|
||||
- `tcm`
|
||||
- `tcml`
|
||||
- `diag`
|
||||
- `show`
|
||||
|
||||
这类工具大多要的是“输入 gopacket.Packet,得到事实 + hint”。
|
||||
|
||||
## 12. 对现有调用方的迁移影响
|
||||
|
||||
### 12.1 `tcpkill`
|
||||
|
||||
迁移成本最低。
|
||||
|
||||
它主要只需要:
|
||||
|
||||
- TCP 四元组
|
||||
- seq / ack / window
|
||||
- MAC
|
||||
|
||||
因此它可以优先迁移到:
|
||||
|
||||
- `Decoder`
|
||||
- 或 `Analyzer` 中的 `Observation.Packet + Observation.Hints.TCP`
|
||||
|
||||
### 12.2 `tcm` / `tcml`
|
||||
|
||||
迁移重点在展示层。
|
||||
|
||||
它们当前主要依赖:
|
||||
|
||||
- `StateDescript`
|
||||
- `TcpSeq/TcpAck/TcpWindow/TcpPayloads`
|
||||
- `Key/ReverseKey`
|
||||
- `Comment/SetComment`
|
||||
|
||||
迁移后应改成:
|
||||
|
||||
- 用 `Observation.Hints.Summary` 或 `Tags` 做展示语义
|
||||
- 用结构化 `FlowRef` 替代字符串 key 拼接
|
||||
- 将 `tcml` 的动作状态迁出 `bcap`
|
||||
|
||||
### 12.3 `diag`
|
||||
|
||||
`diag` 会受益最大。
|
||||
|
||||
它本质上就是一个消费“事实 + hint”的离线分析器。等 `bcap` 能稳定输出结构化 TCP/ICMP/ARP/UDP hint 后,`diag` 内部很多启发式逻辑会更容易表达,也更少依赖旧的 TCP 枚举状态。
|
||||
|
||||
## 13. 与子包的边界
|
||||
|
||||
本次先不动子包,但应明确子包定位:
|
||||
|
||||
- `libpcap`:抓包输入适配层
|
||||
- `nfq`:NFQUEUE 输入适配层
|
||||
|
||||
它们不属于主模型核心,只是 `gopacket.Packet` 的来源适配。
|
||||
|
||||
换句话说:
|
||||
|
||||
- `bcap` 主包定义“如何理解 packet”
|
||||
- 子包定义“packet 从哪里来”
|
||||
|
||||
这个边界应该长期保持稳定。
|
||||
|
||||
## 14. 当前结论
|
||||
|
||||
如果重做 `bcap` 主包,最合理的方向是:
|
||||
|
||||
1. 主包已经从 `Packets/PacketInfo` 旧模型切换到 `Decoder/Tracker/Analyzer` 新模型。
|
||||
2. 明确分离“报文事实”和“推断 hint”。
|
||||
3. 引入结构化 `FlowKey/FlowRef`,不再把字符串 key 作为主接口。
|
||||
4. 用 `HintSet + Tags + protocol-specific hints` 取代 `StateDescript`。
|
||||
5. 删除 `Comment/SetComment` 这类工具业务越界能力。
|
||||
6. 让 `bcap` 成为“轻量协议事实 + hint 提供层”,而不是“工具状态仓库”。
|
||||
|
||||
## 15. 推荐的实施顺序
|
||||
|
||||
建议真正开始改实现时,按下面顺序推进:
|
||||
|
||||
1. 先定义新的公共类型草图:`Packet`、`Observation`、`FlowKey`、`HintSet`、`TCPHint`、`ICMPHint`、`ARPHint`。
|
||||
2. 再实现无状态 `Decoder`,保证多协议事实提取完整。
|
||||
3. 再实现有状态 `Tracker`,先把 TCP hint 迁进去。
|
||||
4. 再提供 `Analyzer` 便捷入口。
|
||||
5. 最后再让 `apps/tcp` 和 `apps/b612` 的调用方迁到新接口,并删除旧兼容 facade。
|
||||
|
||||
在这个顺序下,主架构会先稳定下来,后续迁移也更可控。
|
||||
Reference in New Issue
Block a user