在snort3.0中,数据包捕获的过程相对2.x的版本发生了一些变化,这里不再是直接调用libpcap了。 简单介绍一下,在snort3中,daq作为获取数据的抽象层,代替了之前直接对libpcap的调用,官方文档这样说的:
The Data AcQuisition library (DAQ), provides pluggable packet I/O. LibDAQ replaces direct calls to libraries like libpcap with an abstraction layer that facilitates operation on a variety of hardware and software interfaces without requiring changes to Snort. It is possible to select the DAQ module and mode when invoking Snort to perform pcap readback or inline operation, etc. The DAQ library may be useful for other packet processing applications and the modular nature allows you to build new modules for other platforms.
大致意思是这么做的话,会有助于在各种硬件和软件接口上进行操作,而无需更改Snort。
在第一章关于启动与运行的部分,最后提到了daq_instance->acquire(0, main_func)
,简单介绍一下daq_instance的初始化操作,这里不对libdaq做深入的介绍了,它是另外一个库。
首先它是前文中提到的Analyzer类的一个成员变量,在启动Analyzer线程时进行赋值
void Analyzer::operator()(Swapper* ps, uint16_t run_num)
{
..............
if (Snort::thread_init_privileged(source.c_str()))
{
daq_instance = SFDAQ::get_local_instance();
...................
}
.....................
}
而get_local_instance中返回的daq_instance,是在thread_init_privileged进行初始化的
bool Snort::thread_init_privileged(const char* intf)
{
............
SFDAQInstance *daq_instance = new SFDAQInstance(intf); //创建该实例对象时,使用到的intf,即为接口,在现在我的使用场景下这里是网卡
SFDAQ::set_local_instance(daq_instance);
.............
return true;
}
它的初始化主要就是和一个接口(网卡设备)进行绑定,然后调用acquire
函数对该网卡进行监听,当有数据包到来的时候,调用main_func
函数。
它是snort处理数据包的入口,每当有数据包到来的时候其实都会调用它,先看看它是在什么地方初始化的。
src/main/analyzer.cc
static THREAD_LOCAL PacketCallback main_func = Snort::packet_callback;
可以看到它作为analyzer这个文件中的一个全局变量被初始化,而Snort::packet_callback
,就是真正被调用到的函数。
这里结合代码简单分析下
DAQ_Verdict Snort::packet_callback(
void*, const DAQ_PktHdr_t* pkthdr, const uint8_t* pkt)
{
set_default_policy();
Profile profile(totalPerfStats);
pc.total_from_daq++;
packet_time_update(&pkthdr->ts);
if ( SnortConfig::get_conf()->pkt_skip && pc.total_from_daq <= SnortConfig::get_conf()->pkt_skip )
return DAQ_VERDICT_PASS;
s_switcher->start();
s_packet = s_switcher->get_context()->packet;
s_packet->context->packet_number = pc.total_from_daq;
DetectionEngine::reset();
sfthreshold_reset();
ActionManager::reset_queue(s_packet);
DAQ_Verdict verdict = process_packet(s_packet, pkthdr, pkt); //数据包解析,以及规则匹配
ActionManager::execute(s_packet); //这里对这个包进行一些action的操作,主要是基于Action接口,具体见下文
int inject = 0;
verdict = update_verdict(s_packet, verdict, inject);
if (PacketTracer::is_active())
{
PacketTracer::log("Policies: Network %u, Inspection %u, Detection %u\n",
get_network_policy()->user_policy_id, get_inspection_policy()->user_policy_id,
get_ips_policy()->user_policy_id);
PacketTracer::log("Verdict: %s\n", SFDAQ::verdict_to_string(verdict));
PacketTracer::dump(pkthdr);
}
HighAvailabilityManager::process_update(s_packet->flow, pkthdr);
Stream::timeout_flows(pkthdr->ts.tv_sec);
HighAvailabilityManager::process_receive();
s_packet->pkth = nullptr; // no longer avail upon sig segv
if ( SnortConfig::get_conf()->pkt_cnt && pc.total_from_daq >= SnortConfig::get_conf()->pkt_cnt )
SFDAQ::break_loop(-1);
// Check for resume(n)
else if ((s_pause.pause_cnt && pc.total_from_daq >= s_pause.pause_cnt)
#ifdef REG_TEST // pause-after-n
|| ( SnortConfig::get_conf()->pkt_pause_cnt && !s_pause.was_paused &&
pc.total_from_daq >= SnortConfig::get_conf()->pkt_pause_cnt )
#endif
)
{
SFDAQ::break_loop(0);
s_pause.was_paused = s_pause.pause = true;
}
s_switcher->stop();
return verdict;
}
Action具体有三个
class ReactAction : public IpsAction 这个看了一下代码,是回应一些防御的报文的,例如重置链接,以及tcp的rst包等,说明在这里send response to client and terminate session
class ReplaceAction : public IpsAction overwrite packet contents 重写包内容的
class RejectAction : public IpsAction terminate session with TCP reset or ICMP unreachable
具体的规则匹配等等操作,都是从这个函数走入的,具体的细节后续再分析下。
这部分能写的东西并不是很多,因为我其实没有仔细研究snort用于捕获数据包的那个libdaq库,而在snort代码中,捕获数据包就是使用daq进行回调。在捕获到数据包之后,理所应当的会进行规则匹配等操作,当然这里还有一些预处理的部分,包括tcp流重组,ip碎片重组等等。