修订历史
- 2024.03.08 创建笔记
expr-lang 是方便与 go 应用集成的表达式引擎:
env := map[string]interface{}{
"greet": "Hello, %v!",
"names": []string{"world", "you"},
"sprintf": fmt.Sprintf,
}
code := `sprintf(greet, names[0])`
program, err := expr.Compile(code, expr.Env(env))
output, err := expr.Run(program, env) // output: Hello, world!
gopacket 提供了一组 API,可以用于捕获、解析、分析和构造各种类型的网络数据包
https://colobu.com/2019/06/01/packet-capture-injection-and-analysis-gopacket/
内核网络通信模块
nfqueue
回调函数,将网络数据传给 opengfwgopacket
分析网络数据的协议,抽取出所需字段expr-lang
编译配置文件中的规则,结合协议字段的值,执行规则获得裁决结果go-nfqueue
将数据裁决结果通知给内核ruleset/
规则编译、运行,内嵌函数定义、注册modifier/
数据解码、修改、重新打包io/
封装内核与用户态进程之间的网络数据交互engine/
主循环,多个 worker 接受数据、分析特征、执行规则、返回裁决结果cmd/
入口函数。配置读取analyzer/
分析流量特征,抽取协议字段值// engine/engine.go
func (e *engine) Run(ctx context.Context) error {
...
for _, w := range e.workers {
go w.Run(ioCtx)
}
for _, i := range e.ioList {
ioEntry := i
err := ioEntry.Register(ioCtx, func(p io.Packet, err error) bool {
...
return e.dispatch(ioEntry, p)
})
...
}
...
}
func (e *engine) dispatch(ioEntry io.PacketIO, p io.Packet) bool {
...
e.workers[index].Feed(&workerPacket{
StreamID: p.StreamID(),
Packet: packet,
SetVerdict: func(v io.Verdict, b []byte) error {
return ioEntry.SetVerdict(p, v, b)
},
})
}
// engine/worker.go
func (w *worker) Feed(p *workerPacket) {
w.packetChan <- p
}
func (w *worker) Run(ctx context.Context) {
...
for {
select {
case wPkt := <-w.packetChan:
...
v, b := w.handle(wPkt.StreamID, wPkt.Packet)
_ = wPkt.SetVerdict(v, b)
}
...
}
}
// engine/tcp.go
func (s *tcpStream) ReassembledSG(sg reassembly.ScatterGather, ac reassembly.AssemblerContext) {
...
// feed 每一个 analyzer,抽取出字段
for i := len(s.activeEntries) - 1; i >= 0; i-- {
entry := s.activeEntries[i]
update, closeUpdate, done := s.feedEntry(entry, rev, start, end, skip, data)
...
}
if updated || s.virgin {
// 字段集合作为输入,执行每一条表达式
result := s.ruleset.Match(s.info)
...
}
...
}