1. 程式人生 > >suricata 3.1 原始碼分析14 (流查詢分配)

suricata 3.1 原始碼分析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; }