A-A+

suricata 3.1 源码分析14 (流查找分配)

2018年12月06日 suricata 暂无评论

流查找/分配

通过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;
}
标签:

给我留言

Copyright © 九毛的官方博客 保留所有权利.   Theme  Ality

用户登录