suricata 3.1 源码分析20 (数据包封装)

在Suricata中,用来封装数据包的结构体为Packet,核心字段如下:

字段含义
src/dst、sp/dp、proto五元组信息:源/目的地址,源/目的端口号,传输层协议(TCP/UDP/…)。
flow数据包所属的流指针(类型为Flow_ *)。
ip4h、ip6h网络层数据指针。
tcph、udph、sctph、icmpv4/6h传输层数据指针。
payload、payload_len应用层负载指针及长度。
next、prev前一个/后一个数据包指针,用于组成双向链表。

PcapCallbackLoop函数中第一步就是完成对数据包的封装。其函数原型如下:
void PcapCallbackLoop(char *user, struct pcap_pkthdr *h, u_char *pkt)
其中,user为用户数据,即之前传入的PcapThreadVars,h为pcap包结构体头,pkt为包数据指针。

void PcapCallbackLoop(char *user, struct pcap_pkthdr *h, u_char *pkt)
{
    SCEnter();

    PcapThreadVars *ptv = (PcapThreadVars *)user;
    Packet *p = PacketGetFromQueueOrAlloc();
/*调用PacketGetFromQueueOrAlloc获取一个Packet结构。该函数会首先尝试调用
PacketPoolGetPacket从packet pool中直接获取,如果失败(已经用完了),
那么就调用PacketGetFromAlloc新分配一个。
注:与pool中取出的数据包最终可以回收不同,这种使用malloc动态分配的数据包
最后需要free,因此为了区分会在其flags中打上标记PKT_ALLOC。*/

    struct timeval current_time;

    if (unlikely(p == NULL)) {
        SCReturn;
    }

    PKT_SET_SRC(p, PKT_SRC_WIRE);
    p->ts.tv_sec = h->ts.tv_sec;
    p->ts.tv_usec = h->ts.tv_usec;
    SCLogDebug("p->ts.tv_sec %"PRIuMAX"", (uintmax_t)p->ts.tv_sec);
    p->datalink = ptv->datalink;

    ptv->pkts++;
    ptv->bytes += h->caplen;
    (void) SC_ATOMIC_ADD(ptv->livedev->pkts, 1);
    p->livedev = ptv->livedev;
/*填充Packet结构体的部分字段:数据包源(PKT_SRC_WIRE)、时间戳、
所属数据链路/设备*/

    if (unlikely(PacketCopyData(p, pkt, h->caplen))) {
/*调用PacketCopyData复制数据包内容到pkt字段中,并设置pktlen为相应的长度。
注:为什么需要做复制这种开销大的操作呢?man pcap_dispatch给出了答案:The 
struct pcap_pkthdr… are not guaranteed to be valid after the 
callback routine returns; if the code needs them to be valid after 
the callback, it must make a copy of them. 而由于Suricata的多线程和
异步特性,数据包在callback中会送入outq中等待后续线程继续处理,因此这里必须
进行复制。对于一些支持zero copy的抓包模式,如netmap,pfring等使用
PacketSetData直接将p->ext_pkt指向pkt*/
        TmqhOutputPacketpool(ptv->tv, p);
        SCReturn;
    }

    switch (ptv->checksum_mode) {
        case CHECKSUM_VALIDATION_AUTO:
            if (ptv->livedev->ignore_checksum) {
                p->flags |= PKT_IGNORE_CHECKSUM;
            } else if (ChecksumAutoModeCheck(ptv->pkts,
                        SC_ATOMIC_GET(ptv->livedev->pkts),
                        SC_ATOMIC_GET(ptv->livedev->invalid_checksums))) {
                ptv->livedev->ignore_checksum = 1;
                p->flags |= PKT_IGNORE_CHECKSUM;
            }
            break;
        case CHECKSUM_VALIDATION_DISABLE:
            p->flags |= PKT_IGNORE_CHECKSUM;
            break;
        default:
            break;
    }
/*校验和相关的处理。若checksum_mode为DISABLE,将会给包的flags打上
PKT_IGNORE_CHECKSUM标志,表示后续不再对其进行校验和验证。若
checksum_mode为AUTO,则调用ChecksumAutoModeCheck进行统计分析,
满足条件则后续该设备的数据包都会关闭校验和验证。目前是的关闭条件是:
1000个包中若有超过10%的包校验和不正确,则认为网卡开启了
checksum offloading,因而关闭检验和验证*/

    if (TmThreadsSlotProcessPkt(ptv->tv, ptv->slot, p) != TM_ECODE_OK) {
/*调用TmThreadsSlotProcessPkt让本线程中包含的其他slot中的模块对数据包进行后续处理。
若处理返回失败,则调用pcap_breakloop中断抓包。*/
        pcap_breakloop(ptv->pcap_handle);
        ptv->cb_result = TM_ECODE_FAILED;
    }

    /* Trigger one dump of stats every second */
    TimeGet(&current_time);
    if (current_time.tv_sec != ptv->last_stats_dump) {
        PcapDumpCounters(ptv);
        ptv->last_stats_dump = current_time.tv_sec;
/*调用PcapDumpCounters设置抓包统计信息(stats counters)。
这个打印保证每秒只触发一次,机制是:获取当前时间,
只有当前秒数与上一次记录的秒数不同时才调用*/
    }

    SCReturn;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

高晓伟_Steven

相逢即是有缘,动力源于金钱。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值