suricata 3.1 原始碼分析14 (流查詢分配)
阿新 • • 發佈:2019-02-14
流查詢/分配
通過FlowWorker執行緒函式中呼叫FlowHandlePacket來找到流。
下面將按照FlowHandlePacket的流程,分析flow engine對於新送入的解碼後的資料包是如何處理的。
對於一個Packet,首先在流表中查詢其所屬的流是否已經存在,若存在,則直接返回該流的引用即可,否則就需要分配一個新流。
該過程的實現由FlowGetFlowFromHash完成,函式會返回一個Flow指標,表示找到的或新分配的流。
/** \brief Get Flow for packet
*
* Hash retrieval function for flows. Looks up the hash bucket containing the
* flow pointer. Then compares the packet with the found flow to see if it is
* the flow we need. If it isn't, walk the list until the right flow is found.
*
* If the flow is not found or the bucket was emtpy, a new flow is taken from
* the queue. FlowDequeue() will alloc new flows as long as we stay within our
* memcap limit.
*
* The p->flow pointer is updated to point to the flow.
*
* \param tv thread vars
* \param dtv decode thread vars (for flow log api thread data)
*
* \retval f *LOCKED* flow or NULL
*/
Flow *FlowGetFlowFromHash(ThreadVars *tv, DecodeThreadVars *dtv, const Packet *p, Flow **dest)
{
Flow *f = NULL;
/* get our hash bucket and lock it */
const uint32_t hash = p->flow_hash;
//獲取包的hash,這裡的hash是由p帶來的。p的flow_hash是通過在FlowSetupPacket函式呼叫FlowGetHash,又通過FlowGetHash中的hashword生成的,對於 hash_key的獲取方法以後另行分析。
FlowBucket *fb = &flow_hash[hash % flow_config.hash_size];
//以獲取到的key為索引,在流表flow_hash中獲取到一個FlowBucket的指標
FBLOCK_LOCK(fb);
//使用FBLOCK_LOCK對該bucket上鎖。實際使用的鎖可能為spin lock或mutext,取決於FBLOCK_SPIN是否定義。
SCLogDebug("fb %p fb->head %p", fb, fb->head);
/* see if the bucket already has a flow */
if (fb->head == NULL) {
//說明這個bucket中還沒有流
f = FlowGetNew(tv, dtv, p);
//呼叫FlowGetNew新分配一個流
if (f == NULL) {
FBLOCK_UNLOCK(fb);
return NULL;
}
/* flow is locked */
fb->head = f;
fb->tail = f;
/* got one, now lock, initialize and return */
FlowInit(f, p);
//使用Packet的資訊初始化這個新流
f->flow_hash = hash;
f->fb = fb;
//設定流的flow_hash和fb
/* update the last seen timestamp of this flow */
COPY_TIMESTAMP(&p->ts,&f->lastts);
//記錄最後一次更新流的時間
FlowReference(dest, f);
//使用FlowReference將p->flow指向剛獲取流,該函式內部會使用FlowIncrUsecnt增加該流的使用計數。注意,該機制的目的與通常的引用計數不同,不是為了在沒有引用時回收資源,而是為了避免出現誤刪除等問題
FBLOCK_UNLOCK(fb);
//FBLOCK_UNLOCK解鎖並返回這個流。由於FlowGetNew中會呼叫FLOWLOCK_WRLOCK對flow進行上鎖(因為後面需要使用),因此這裡就不需要再鎖了。
return f;
}
//以下程式碼為fb->head不為NULL,即bucket中有流時,嘗試從其中的Flow連結串列中查詢該packet所屬的Flow
/* ok, we have a flow in the bucket. Let's find out if it is our flow */
f = fb->head;
/* see if this is the flow we are looking for */
if (FlowCompare(f, p) == 0) {
//代表f和p不匹配,為1匹配,為0不匹配
Flow *pf = NULL; /* previous flow */
while (f) {
pf = f;
f = f->hnext;
if (f == NULL) {
f = pf->hnext = FlowGetNew(tv, dtv, p);
if (f == NULL) {
FBLOCK_UNLOCK(fb);
return NULL;
}
fb->tail = f;
/* flow is locked */
f->hprev = pf;
/* initialize and return */
FlowInit(f, p);
f->flow_hash = hash;
f->fb = fb;
/* update the last seen timestamp of this flow */
COPY_TIMESTAMP(&p->ts,&f->lastts);
FlowReference(dest, f);
FBLOCK_UNLOCK(fb);
return f;
}
//若未找到,則與4類似,獲取一個新流並初始化,然後掛到連結串列尾部再返回。注意,這裡並沒有移到頭部,因為新流不代表就是活躍流。
if (FlowCompare(f, p) != 0) {
/* we found our flow, lets put it on top of the
* hash list -- this rewards active flows */
if (f->hnext) {
f->hnext->hprev = f->hprev;
}
if (f->hprev) {
f->hprev->hnext = f->hnext;
}
if (f == fb->tail) {
fb->tail = f->hprev;
}
f->hnext = fb->head;
f->hprev = NULL;
fb->head->hprev = f;
fb->head = f;
/* found our flow, lock & return */
FLOWLOCK_WRLOCK(f);
if (unlikely(TcpSessionPacketSsnReuse(p, f, f->protoctx) == 1)) {
f = TcpReuseReplace(tv, dtv, fb, f, hash, p);
if (f == NULL) {
FBLOCK_UNLOCK(fb);
return NULL;
}
}
/* update the last seen timestamp of this flow */
COPY_TIMESTAMP(&p->ts,&f->lastts);
FlowReference(dest, f);
FBLOCK_UNLOCK(fb);
return f;
}
//使用FlowCompare比較head flow與packet,若相匹配,則說明已經找到了,且這個流已經在頭部不需要再調整,則先鎖上該flow再解鎖bucket,然後返回。
}
}
/* lock & return */
FLOWLOCK_WRLOCK(f);
if (unlikely(TcpSessionPacketSsnReuse(p, f, f->protoctx) == 1)) {
f = TcpReuseReplace(tv, dtv, fb, f, hash, p);
if (f == NULL) {
FBLOCK_UNLOCK(fb);
return NULL;
}
}
/* update the last seen timestamp of this flow */
COPY_TIMESTAMP(&p->ts,&f->lastts);
//記錄最後一次更新流的時間
FlowReference(dest, f);
//將dest指向f,此處dest為p->flow,由函式傳入。
FBLOCK_UNLOCK(fb);
return f;
}