网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
connPool: pool,
AssemblerOptions: DefaultAssemblerOptions,
}
}
### AssembleWithContext
AssembleWithContext 将给定的 TCP 数据包重新组合到其适当的Stream中。
传入的时间戳必须是看到数据包的时间戳。对于从网络上读取的数据包,time.Now() 应该没问题。对于从 PCAP 文件读取的数据包,应传入CaptureInfo.Timestamp。此时间戳将影响通过调用 FlushCloseOlderThan 刷新哪些流。
func (a *Assembler) AssembleWithContext(netFlow gopacket.Flow, t *layers.TCP, ac AssemblerContext) {
var conn *connection
var half *halfconnection
var rev *halfconnection
a.ret = a.ret[:0]
key := key{netFlow, t.TransportFlow()}
ci := ac.GetCaptureInfo()
timestamp := ci.Timestamp
// 获取/创建一个conn
conn, half, rev = a.connPool.getConnection(key, false, timestamp, t, ac)
if conn == nil {
if *debugLog {
log.Printf("%v got empty packet on otherwise empty connection", key)
}
return
}
// 锁的范围那么大吗
conn.mu.Lock()
defer conn.mu.Unlock()
if half.lastSeen.Before(timestamp) {
half.lastSeen = timestamp
}
a.start = half.nextSeq == invalidSequence && t.SYN
if *debugLog {
if half.nextSeq < rev.ackSeq {
log.Printf("Delay detected on %v, data is acked but not assembled yet (acked %v, nextSeq %v)", key, rev.ackSeq, half.nextSeq)
}
}
// 判断是否要直接丢弃该包
if !half.stream.Accept(t, ci, half.dir, half.nextSeq, &a.start, ac) {
if *debugLog {
log.Printf("Ignoring packet")
}
return
}
// 连接被关闭不处理
if half.closed {
// this way is closed
if *debugLog {
log.Printf("%v got packet on closed half", key)
}
return
}
seq, ack, bytes := Sequence(t.Seq), Sequence(t.Ack), t.Payload
if t.ACK {
half.ackSeq = ack
}
// TODO: push when Ack is seen ??
action := assemblerAction{
nextSeq: Sequence(invalidSequence),
queue: true,
}
a.dump("AssembleWithContext()", half)
if half.nextSeq == invalidSequence {
// 一般来说只有第一个包才会nextSeq== invalidSequence
// 然后又
if t.SYN {
if *debugLog {
log.Printf("%v saw first SYN packet, returning immediately, seq=%v", key, seq)
}
seq = seq.Add(1)
half.nextSeq = seq
action.queue = false
} else if a.start {
if *debugLog {
log.Printf("%v start forced", key)
}
half.nextSeq = seq
action.queue = false
} else {
if *debugLog {
log.Printf("%v waiting for start, storing into connection", key)
}
}
} else {
diff := half.nextSeq.Difference(seq)
if diff > 0 {
if *debugLog {
log.Printf("%v gap in sequence numbers (%v, %v) diff %v, storing into connection", key, half.nextSeq, seq, diff)
}
} else {
if *debugLog {
log.Printf("%v found contiguous data (%v, %v), returning immediately: len:%d", key, seq, half.nextSeq, len(bytes))
}
action.queue = false
}
}
action = a.handleBytes(bytes, seq, half, t.SYN, t.RST || t.FIN, action, ac)
if len(a.ret) > 0 {
action.nextSeq = a.sendToConnection(conn, half, ac)
}
if action.nextSeq != invalidSequence {
half.nextSeq = action.nextSeq
if t.FIN {
half.nextSeq = half.nextSeq.Add(1)
}
}
if *debugLog {
log.Printf("%v nextSeq:%d", key, half.nextSeq)
}
}
## key
作为conn的key,其第一个应该是netflow,第二个应该是transportFlow
type key [2]gopacket.Flow
Reverse:反转src和dst
func (k *key) Reverse() key {
return key{
k[0].Reverse(),
k[1].Reverse(),
}
}
String:打印
func (k *key) String() string {
return fmt.Sprintf(“%s:%s”, k[0], k[1])
}
## StreamPool
StreamPool 存储由 Assemblers 创建的所有Stream,允许多个Assembler在Stream处理上协同工作,同时强制执行单个Stream串行接收其数据的事实。它对并发是安全的,可供多个Assembler同时使用。
StreamPool 处理一个或多个 Assembler 对象使用的 Stream 对象的创建和存储。当 Assembler 找到一个新的 TCP 流时,它会通过调用 StreamFactory 的 New 方法创建一个关联的 Stream。此后(直到流关闭),该 Stream 对象将通过 Assembler 对流的 Reassembled 函数的调用接收组装的 TCP 数据。
与 Assembler 一样,StreamPool 尝试最小化分配。但是,与 Assembler 不同的是,它确实必须做一些锁定以确保它存储的连接对象可以被多个 Assembler 访问。
type StreamPool struct {
conns map[key]*connection
users int
mu sync.RWMutex
factory StreamFactory
free []*connection
all [][]connection
nextAlloc int
newConnectionCount int64
}
const initialAllocSize = 1024
func NewStreamPool(factory StreamFactory) *StreamPool {
return &StreamPool{
conns: make(map[key]*connection, initialAllocSize),
free: make([]*connection, 0, initialAllocSize),
factory: factory,
nextAlloc: initialAllocSize,
}
}
### connections-获取所有的连接
func (p *StreamPool) connections() []*connection {
p.mu.RLock()
conns := make([]*connection, 0, len(p.conns))
for _, conn := range p.conns {
conns = append(conns, conn)
}
p.mu.RUnlock()
return conns
}
### remove - 删除连接
func (p *StreamPool) remove(conn *connection) {
p.mu.Lock()
if _, ok := p.conns[conn.key]; ok {
delete(p.conns, conn.key)
p.free = append(p.free, conn)
}
p.mu.Unlock()
}
### grow - 分配一组连接
默认为1024个
func (p *StreamPool) grow() {
conns := make([]connection, p.nextAlloc)
p.all = append(p.all, conns)
for i := range conns {
p.free = append(p.free, &conns[i])
}
if *memLog {
log.Println(“StreamPool: created”, p.nextAlloc, “new connections”)
}
p.nextAlloc *= 2
}
### dump - 打印剩余的连接数和当前连接的信息
func (p *StreamPool) Dump() {
p.mu.Lock()
defer p.mu.Unlock()
log.Printf(“Remaining %d connections: “, len(p.conns))
for _, conn := range p.conns {
log.Printf(”%v %s”, conn.key, conn)
}
}
### newConnection - 创建新的连接
func (p *StreamPool) newConnection(k key, s Stream, ts time.Time) (c *connection, h *halfconnection, r *halfconnection) {
if *memLog {
p.newConnectionCount++
if p.newConnectionCount&0x7FFF == 0 {
log.Println(“StreamPool:”, p.newConnectionCount, “requests,”, len(p.conns), “used,”, len(p.free), “free”)
}
}
if len(p.free) == 0 {
p.grow()
}
index := len(p.free) - 1
c, p.free = p.free[index], p.free[:index]
c.reset(k, s, ts)
return c, &c.c2s, &c.s2c
}
### getHalf - 获取一个conn
func (p *StreamPool) getHalf(k key) (*connection, *halfconnection, *halfconnection) {
conn := p.conns[k]
if conn != nil {
return conn, &conn.c2s, &conn.s2c
}
rk := k.Reverse()
conn = p.conns[rk]
if conn != nil {
return conn, &conn.s2c, &conn.c2s
}
return nil, nil, nil
}
### getConnection - 获取一个conn,当conn不存在时会创建
func (p *StreamPool) getConnection(k key, end bool, ts time.Time, tcp *layers.TCP, ac AssemblerContext) (*connection, *halfconnection, *halfconnection) {
p.mu.RLock()
conn, half, rev := p.getHalf(k)
p.mu.RUnlock()
if end || conn != nil {
return conn, half, rev
}
s := p.factory.New(k[0], k[1], tcp, ac)
p.mu.Lock()
defer p.mu.Unlock()
conn, half, rev = p.newConnection(k, s, ts)
conn2, half2, rev2 := p.getHalf(k)
if conn2 != nil {
if conn2.key != k {
panic(“FIXME: other dir added in the meantime…”)
}
// FIXME: delete s ?
return conn2, half2, rev2
}
p.conns[k] = conn
return conn, half, rev
}
## connection
type connection struct {
key key // client->server
c2s, s2c halfconnection
mu sync.Mutex
}
### reset - 设置连接信息(复用)
func (c *connection) reset(k key, s Stream, ts time.Time) {
c.key = k
base := halfconnection{
nextSeq: invalidSequence,
ackSeq: invalidSequence,
created: ts,
lastSeen: ts,
stream: s,
}
c.c2s, c.s2c = base, base
c.c2s.dir, c.s2c.dir = TCPDirClientToServer, TCPDirServerToClient
}
### lastSeen
func (c *connection) lastSeen() time.Time {
if c.c2s.lastSeen.Before(c.s2c.lastSeen) {
return c.s2c.lastSeen
}
return c.c2s.lastSeen
}
### String
func (c *connection) String() string {
return fmt.Sprintf(“c2s: %s, s2c: %s”, &c.c2s, &c.s2c)
}
## halfconnection - 单向的连接
type halfconnection struct {
dir TCPFlowDirection
pages int // Number of pages used (both in first/last and saved)
saved *page // Doubly-linked list of in-order pages (seq < nextSeq) already given to Stream who told us to keep
first, last *page // Doubly-linked list of out-of-order pages (seq > nextSeq)
nextSeq Sequence // sequence number of in-order received bytes
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
er pages (seq > nextSeq)
nextSeq Sequence // sequence number of in-order received bytes
[外链图片转存中…(img-1QbRHQ0Q-1715739591845)]
[外链图片转存中…(img-R8p9Ipeu-1715739591845)]
[外链图片转存中…(img-1C8QotiJ-1715739591845)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新