2024年最新gopacket reassembly源码分析_go packet accept(2),2024年最新HR的话扎心了

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事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

img
img
img

既有适合小白学习的零基础资料,也有适合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语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值