Suricata IPS-NFQ模式
Suricata IPS-NFQ模式
注:本文写于2019年,文中的相关概念的介绍摘抄出自哪里已经不记得,如有侵权请指出,本文将补充相关部分的引用。
1.Suricata运行模式
Suricata有两个运行模式的概念。
一个运行模式封装在RunModes(runmmode.c)结构体里,一个RunModes对应一组运行模式。Suricata支持的所有运行模式存储在runmodes数组中,定义为RunModes runmodes[RUNMODE_USER_MAX]。模式包括:“IDS+Pcap”模式组、“File+Pcap”模式组、“IPS+NFQ”模式组、“UnixSocket”模式组等(另外还有其他一些内部模式,如:“列出关键字”模式、“打印版本号”模式等,这些没有存储在runmodes数组中)。模式组的选择决定了Suricata的数据来源、实现功能(IDS或IPS)、和输出的方式。本小节就把这种运行模式的概念称作:运行模式组。
另一个运行模式的概念来源于配置文件里的runmode字段。Suricata由几个称为线程,线程模块和队列的“构建块”组成。Suricata是多线程的,因此多个线程同时处于活动状态。线程模块是功能的一部分。一个模块例如用于解码分组,另一个模块是检测模块,另一个模块是输出模块。数据包可以由多个线程处理。数据包将通过队列传递到下一个线程。数据包将由一个线程一次处理,但引擎一次可以处理多个数据包(Max-pending-packets)。一个线程可以有一个或多个线程模块。如果他们有更多的模块,他们只能一次活动。
这种线程,模块和队列排列在一起的方式就是配置文件里这个runmode的含义。Suricata从官方介绍文件看目前有三种:Single、Autofp、Workers。
附上官网关于这三种运行模式的介绍:https://suricata.readthedocs.io/en/suricata-4.1.4/performance/runmodes.html ,本小节称这种配置文件里的运行模式就叫运行模式,区分运行模式组。
官方文件表明,通常情况下,Workers运行模式表现最佳。在此模式下,NIC /驱动程序确保数据包在Suricata的处理线程上正确平衡,每个分组处理线程包含完整的分组管道。
运行模式和运行模式组的关系是:每一个模式组,可以包含若干个运行模式,运行模式的注册,则是为各个模式组(如RunModeIpsNFQRegister)添加其所支持的运行模式(通过调用RunModeRegisterNewRunMode),并定义该组的默认运行模式,以及非常重要的:注册各个模式下的初始化函数,等后续初始化阶段确定了具体的运行模式后,就会调用这里注册的对应的初始化函数,对该模式下的运行环境进行进一步配置。
IPS-NFQ模式组在Runmode-nfq.c 的RunModeIpsNFQRegister() 函数中注册了两种运行模式:autofp 和 workers 模式,其中autofp是默认的运行模式。
2. Suricata IPS-NFQ模式
2.1 IPS-NFQ模式与网络防火墙iptables/netfilter
Suricata IPS-NFQ模式是通过和linux下通用的网络防火墙iptables/netfilter的连通来达到阻断网络的目的。通过iptables 将网络报文发送到特定的队列中去,放入到用户态中,然后通过suricata进行规则匹配,最后返回对报文的处理。
一般在iptables中的target有以下六种:
值 | 宏定义 | 含义 |
---|---|---|
0 | NF_DROP | 丢弃数据包 |
1 | NF_ACCEPT | 数据包通过,继续迭代 |
2 | NF_STOLEN | 模块接管该数据报,告诉Netfilter“忘掉”该数据报。该回调函数将从此开始对数据包的处理,并且Netfilter应当放弃对该数据包做任何的处理。但是,这并不意味着该数据包的资源已经被释放。这个数据包以及它独自的sk_buff数据结构仍然有效,只是回调函数从Netfilter 获取了该数据包的所有权 |
3 | NF_QUEUE | 将数据包注入不同的队列(目标队列号位于判决的高16位) |
4 | NF_REPEAT | 再次迭代相同的循环(再次调用该钩子函数,即重新跳到该表中的第一条规则?) |
5 | NF_STOP | 接受,但不要继续迭代,不再进入链表中后续的hook节点,而NF_ACCEPT则还需要进入后续hook点检查。 |
注:
1.不得使用判决NF_STOLEN,因为它在内核中具有特殊含义。
2.使用NF_REPEAT时,防止同一数据包重新排队的一种方法是使用nfq_set_verdict2设置nfmark,并设置nefilter规则,以便仅在尚未设置标记时对数据包进行排队。
Suricata IPS-NFQ模式中用到的判决有NF_DROP、NF_ACCEPT、NF_QUEUE、NF_REPEAT这四种。
2.2 IPS-NFQ模式里的判决模式
Suricata IPS-NFQ模式里有三种判决模式可选:accept、repeat、route。在suricata.yaml 配置文件里设置。accept模式下默认的判决是NF_ACCEPT。repeat模式下默认的判决是NF_REPEAT。route模式下默认的判决是NF_QUEUE。注意以上三种模式的判决结果都只存在两种:默认判决或者NF_DROP。
当采用accept模式时,只会给出DROP或者ACCEPT判决,只需在iptables的规则里写入类似如下规则:
iptables -I FORWARD -j NFQUEUE –-queue-num X
当采用repeat模式时, Suricata需要用到判决NF_REPEAT时,Suricata用NF_REPEAT判决会再次迭代相同的循环(再次调用该钩子函数),所以使用下面的iptables规则可以使得被suricata标记过的数据包不再发送到suricata,避免不必要的循环。
iptables -I FORWARD -m mark ! --mark $MARK/$MASK -j NFQUEUE
配置文件:
# repeat-mark: 1
# repeat-mask: 1
当采用route模式时,Suricata需要用到判决NF_QUEUE。这是最好要保证指定跳转的队列,不要是由Suricata监听的队列,以免造成死循环。从Suricata的代码来看,Suricata的repeat模式对数据包进行了防止循环的检验,但是route模式并没有做防止死循环的检验。(经测试,将suricata的route模式的目的队列设置为suricata监听的队列时,数据包会一直循环发送到suricata)
当在suricata命令行输入如下命令让suricata读取特定编号的nfqueue时:
sudo suricata -c /etc/suricata/suricata.yaml -q 0
在suricata.c文件的ParseCommandLine函数里会对这个命令做解析,将这个queue的编号作为参数调用source-nfq.c中的NFQRegisterQueue()函数。这个函数会把这个链编号注册到一个静态全局数组NFQQueueVars g_nfq_q[NFQ_MAX_QUEUE]里面。
建议在source-nfq.c的NFQInitConfig函数(被suricata.c的PostConfLoadedSetup函数调用)里读配置文件的route-queue时,在存储queue相关变量的数组g_nfq_q[NFQ_MAX_QUEUE]里比对一下,待跳转的链编号route-queue是不是在surcata接收的链编号里面。从而避免route模式下产生死循环。
2.3 suricata.yaml配置文件里与nfq相关的配置变量
suricata.yaml配置文件里与nfq相关的变量均在“nfq:”下面,所有可设置的配置变量如下:
nfq:
# mode: accept
# repeat-mark: 1
# repeat-mask: 1
# bypass-mark: 1
# bypass-mask: 1
# route-queue: 2
# batchcount: 20
# fail-open: yes
mode就是nfq模式下的判决模式,有三种判决模式:accept、repeat、route。已在上一节对这三种判决模式进行了介绍。
repeat-mark和repeat-mask是重复标记和标记掩码,iptables/netfilter框架下提供的数据包,有一个标记变量mark,可以对数据包做标记。repeat-mark和repeat-mask用于repeat模式下,对监听队列中的数据包做repeat标记,该模式下也会通过数据包的repeat标记对suricata监听队列的包做一个防止死循环的验证。
bypass-mark和bypass-mask是旁路标记和旁路掩码,但是这个bypass功能并不是运用的iptables/netfilter框架提供的bypass功能,而是由suricata提供的bypass功能。该功能的意义在于当一个数据包判定允许通过时,则直接允许该数据包所在流通过。bypass功能不仅限于NFQ模式。但是不明白为什么suricata要把这个bypass不设置enable或disable的形式而设置成标记和标记掩码并传到iptables/netfilter的mark变量里。
route-queue是在route模式下使用的,route模式默认的判定是NF_QUEUE,判决NF_QUEUE将数据包注入不同的队列(目标队列号位于判决的高16位)。route-queue就是目标队列号。
batchcount 是用于批量判决的一个参数,仅workers模式支持批量判决。所以batchcout仅在workers运行模式下可用,批量判决是libnetfilter_queue库提供的一种一次性提交对多个数据包的判决的方法。batchcount用于设定批量判决最多判定的数据包数量。
fail-open 是iptables/netfilter框架提供的一个选项,启用fail-open选项时,当suricata处理速度不够,内核队列数据包已满时,待排队的数据包不丢弃,而是允许接受。
2.4 Suricata IPS-NFQ模式下的解析流程
以Workers运行模式为例,Suricata IPS-NFQ模式下的解析流程如下图所示:
注:图片摘自其他博客
Workers运行模式
数据包处理线程中会依次经过如上图所示的几个模块,各模块功能如下
• Receive:从NFQUEUE中接收数据包,并将封装在Packet结构中,然后放入下一个缓冲区。(主要代码位于source-nfq.c)
• Decode:对数据包进行解码,主要是对数据包头部信息进行分析并保存在Packet结构中。
• StreamTCP:对数据包进行TCP流重组。
• Detect:检测数据包是否包含入侵行为。
• Verdict:对检测后的数据包进行判定,并将判定结果告诉内核,方便内核对数据包进行接收、丢弃等处理。
• RespondReject:通过libnet对那些要执行Reject操作的数据包进行相应的回应。
3. NFQ-Receive模块
3.1模块介绍
NFQ-Receive是NFQ模式下的Receive模块负责与NFQueue建立连接,并获取内核NFQueue上的排队的数据包,并将判决、标记、修改后的包(后两项如果有的话)传回iptables/netfilter框架。
3.2重要的变量介绍
1> NFQQueueVars
该结构体定义了一些与NFQueue相关的变量。在source-nfq.c中定义了一个静态全局数组static NFQQueueVars g_nfq_q[NFQ_MAX_QUEUE];每一条链对应维护一个NFQQueueVars变量。
typedef struct NFQQueueVars_
{struct nfq_handle *h; /*使用libnetfilter_queue库中的nfq_open()函数连接到数据包队列返回的handle变量*/struct nfnl_handle *nh;int fd;uint8_t use_mutex; /*标识是否会用到线程互斥变量*//* 2 threads deal with the queue handle, so add a mutex */struct nfq_q_handle *qh; /*libnetfilter_queue库中的nfq_create_queue函数创建一个新的队列句柄并返回指向新创建的队列的nfq_q_handle*/SCMutex mutex_qh; /*nfq_handle的互斥变量?*//* this one should be not changing after init */uint16_t queue_num; /*queue的数量*//* position into the NFQ queue var array */uint16_t nfq_index;#ifdef DBG_PERFint dbg_maxreadsize;
#endif /* DBG_PERF *//* counters 对已收到(处理)的数据包进行计数 */uint32_t pkts;uint64_t bytes;uint32_t errs;uint32_t accepted;uint32_t dropped;uint32_t replaced;struct {uint32_t packet_id; /* id of last processed packet */uint32_t verdict;uint32_t mark;uint8_t mark_valid:1;uint8_t len;uint8_t maxlen;} verdict_cache; //在workers模式下使用批量判决用到。} NFQQueueVars;
2> NFQPacketVars
该结构体是为了Packet变量所定义,其定义了来源为NFQ的数据包的一些参数。
typedef struct NFQPacketVars_
{int id; /* this nfq packets id */uint16_t nfq_index; /* index in NFQ array */uint8_t verdicted;uint32_t mark; /*要返回给iptables/netfilter框架的数据包标记*/uint32_t ifi; /*接收数据包的接口*/uint32_t ifo; /*数据包传出的接口*/uint16_t hw_protocol;
} NFQPacketVars;
3> NFQThreadVars
该结构体是为了NFQ模式下的数据包处理线程定义的,每一个链都有一个处理线程。在source-nfq.c中定义了一个静态全局数组static NFQThreadVars g_nfq_t[NFQ_MAX_QUEUE] ;每一条链的处理线程对应维护一个NFQThreadVars变量。
typedef struct NFQThreadVars_
{uint16_t nfq_index; //在g_nfq_t数组里的位置ThreadVars *tv; //Suricata线程通用的线程变量结构体,存储线程用到的一些变量TmSlot *slot; //线程槽,也是suricata线程里通用的一个概念char *data; /** Per function and thread data */int datalen; /** Length of per function and thread data */CaptureStats stats;
} NFQThreadVars;
4> NFQCnf
该结构体的变量存储着suricata.yaml配置文件里对应的nfq下的字段,即在2.3小节介绍的所有字段。
typedef struct NFQCnf_ {NFQMode mode;uint32_t mark; //repeat模式设置的,如果判定为nf_repeat,那么就给这个数据包做个标记,表明数据包回到netfilter框架后继续给下一条规则处理uint32_t mask;uint32_t bypass_mark; // 旁路标记,当数据包打上这个标记时, 如果没有软件正在监听队列,netfilter规则集将不会删除排队的数据包,而是忽略该条nfqueue规则,跳到下一条iptables规则uint32_t bypass_mask;uint32_t next_queue; //目标队列,route模式下使用uint32_t flags; //fail-open选项是否启用uint8_t batchcount; //批量判定的数据包个数
} NFQCnf;
5>Packet
在Suricata中,用来封装数据包的结构体为Packet(decode.h struct Packet_),核心字段如下:
字段 | 含义 |
---|---|
src/dst、sp/dp、proto | 五元组信息:源/目的地址,源/目的端口号,传输层协议 |
flow | 数据包所属的流指针(类型为Flow_ *)。 |
ip4h、ip6h | 网络层数据指针。 |
tcph、udph、sctph、icmpv4/6h | 传输层数据指针。 |
payload、payload_len | 应用层负载指针及长度。 |
uint32_t pktlen; uint8_t *ext_pkt | 数据包的长度,以及指向数据包数据的指针 |
next、prev | 前一个/后一个数据包指针,用于组成双向链表。 |
flags | 数据包的一些标识。如包是否被修改过、数据包的标记是否被修改过等。详见下表 |
Packet.flags二进制各个位上的掩码代表的含义:
宏定义 | 值 | 含义 |
---|---|---|
PKT_NOPACKET_INSPECTION | 1 | 指示不应检查数据包头或内容的标志 |
PKT_NOPAYLOAD_INSPECTION | (1<<2) | 注明不应检查数据包内容 |
PKT_ALLOC | (1<<3) | 此运行已分配数据包,需要释放 |
PKT_HAS_TAG | (1<<4) | 数据包与标记匹配 |
PKT_STREAM_ADD | (1<<5) | 已将数据包负载添加到重新组装的流中 |
PKT_STREAM_EST | (1<<6) | 数据包是已建立流的一部分 |
PKT_STREAM_EOF | (1<<7) | 流处于EOF状态 |
PKT_HAS_FLOW | (1<<8) | 有无流 |
PKT_PSEUDO_STREAM_END | (1<<9) | 结束流的伪包 |
PKT_STREAM_MODIFIED | (1<<10) | 数据包由流引擎修改,我们需要重新计算CSUM并重新注入/替换 |
PKT_MARK_MODIFIED | (1<<11) | 已修改包标记 |
PKT_STREAM_NOPCAPLOG | (1<<12) | 从PCAP日志中排除数据包,因为它是已达到重新组合深度的流的一部分。 |
PKT_TUNNEL | (1<<13) | 包在tunnel中 |
PKT_TUNNEL_VERDICTED | (1<<14) | 包在tunnel中并给出了判决 |
PKT_IGNORE_CHECKSUM | (1<<15) | 忽略校验和 |
PKT_ZERO_COPY | (1<<16) | 数据包来自零拷贝(ext_pkt不能释放) |
PKT_HOST_SRC_LOOKED_UP | (1<<17) | |
PKT_HOST_DST_LOOKED_UP | (1<<18) | |
PKT_IS_FRAGMENT | (1<<19) | 包是IP碎片?不是完整的数据 |
PKT_IS_INVALID | (1<<20) | 无效包 |
PKT_PROFILE | (1<<21) |
3.3重要的函数介绍
主要的函数在3.4节中基本都介绍到了,其余函数和更多细节的介绍可参看添加了注释的source-nfq.c源码。
3.4 代码逻辑
3.4.1数据包接收模块代码逻辑
1) 初始化
下图为数据包接收数据包初始化的代码逻辑。
图1 :
2) 数据包接收
图2和图3是数据包接收的逻辑。
图2:
图3:
3.4.2 数据包解码模块代码逻辑
1) 初始化
解码模块初始化的代码逻辑跟其他非NFQ模式基本一致,就是对解码线程变量进行初始化。
图4解码初始化函数:
2) 数据包解码
解码模块初始化的代码逻辑跟其他非NFQ模式基本一致,都是根据数据包的类型调用DecodeIPV4()或者DecodeIPV6()进行解码。
I.DecodeNFQ()函数是NFQ模式下数据包开始解析的地方,解码的一般流程如下:
DecodeNFQ() -> DecodeIPV4() ->DecodeTCP() -> II. tcp流重组
或者
DecodeNFQ() -> DecodeIPV4() ->DecodeUDP()-> AppLayerHandleUdp()-> AppLayerParserParse()
II. tcp流重组
StreamTcpReassembleAppLayer()(/src/stream-tcp-assemble.c)->
AppLayerHandleTCPData()( /src/app-layer.c )
将从tcp头中解析出来的应用层协议的相关信息传入 ->
AppLayerParserParse()函数,进行应用层解析。
图5解码函数:
3.4.3数据包判定模块代码逻辑
1) 初始化
数据包判定模块初始化对当前数据包所在NFQ的线程变量NFQThreadVars做了一些初始化。
图5判定初始化函数:
2) 数据包判定
图6是数据包判定的逻辑。
图6 数据包判定逻辑:
Suricata IPS-NFQ模式相关推荐
- 演示:思科IPS在线模式下Inline Interface Mode的响应行为(区别各个防御行为)
演示:思科IPS在线模式下Inline Interface Mode的响应行为 演示目标:科IPS在线模式下InlineInterface Mode的响应行为. 演示环境:仍然使用图5.16所示的网络 ...
- suricata的简介以及安装过程
Suricata介绍 Suricata是一款高性能的网络IDS.IPS和网络安全监控引擎.它是由the Open Information Security Foundation开发,是一款开源的系统. ...
- Surciata源码分析之IpsNFQ模式(1)
最近看了一下suricata-1.2.1的源代码,加之之前在网上没有搜到关于suricata的分析资料, 所以就把看源码时的一些笔记整理了一 下,发到网上,供其他对suricata感兴趣的网友参考.由 ...
- 网络入侵检测系统之Suricata(四)--初始化模块代码详解
initial Module 初始化流程 初始化Suricata instance 用来保存程序当前的一些状态.标志等上下文环境,通常是用来作为参数传递给各个模块的子函数 memset(suri, 0 ...
- suricata学习
<suricata简介> Suricata是一个高性能的网络IDS,IPS和网络安全监控引擎. IPS:入侵预防系统(IPS: Intrusion Prevention System)是电 ...
- tcp段重组--suricata实现
tcp协议上的应用层协议检测时,需要做段重组,这里分析下suricata的TCP 段重组源码. 1. 数据结构 1.1 TcpSession结构体 TcpSession结构体,为flow结构体成员pr ...
- 安全产品的核心逻辑-IPS/IDS
概述 在<软件定义安全>中介绍了,所有的安全产品本质上就是对安全业务的软件开发,在<安全产品的核心逻辑-防火墙>中介绍了防火墙的核心点和核心逻辑.本文介绍ips/ids的核心内 ...
- snort 联动iptables 配置为IPS,NIDS
一.环境准备 Ubuntu16.10,snort2.9.9,iptables1.6.0,daq-2.0.6 二.snort安装 首先关掉网卡的"Large Receive Offload&q ...
- Ubuntu下安装suricata
因为现在的工作主要是基于suricata这个开源软件来建立具有协议解析功能的防火墙,所以本篇简单介绍一下如何在Ubuntu16.04下安装suricata,之后会写一些关于suricata开发的文章. ...
最新文章
- 19 条 MySQL 技巧,效率至少提高 3倍!
- Linux 下安装matlab2014a
- 根据当前日期算前一年、前一月、前一天(java基础)
- 计算机组成与系统原理中的节拍是什么意思?
- C++11新特性选讲 语言部分 侯捷
- 圆与平面的接触面积_视频:5.3RJ六年级上册圆的面积例题+习题讲解
- unity, 不要用TextMesh,用图片代替
- GoogleTest测试框架介绍(二)
- vue-cli设置proxy代理
- 图像的手绘效果(PIL的应用实例)
- 每天花半小时给孩子讲故事,把他培养成依赖书的人种
- windows的cmd命令检测ip或者端口是否连通
- 区块链巨头2018:几家欢喜几家愁 |链捕手
- 曲率(Curvature)
- 对Lab颜色空间的学习(转)
- bi工程师和java哪个好_每个好架构师都是一位出色的程序员
- 编译isl和cloog库出现:fatal error: gmp.h: No such file or directory
- 雷锋网专访食神摇摇:为吃货而建的个性化推荐引擎
- Excel表格快速生成LaTeX
- 9月14日云栖精选夜读:揭秘IPHONE X刷脸认证的技术奥秘
热门文章
- 基于ssm的志愿者管理系统(idea+spring+springmvc+mybatis+jsp)
- 百分数转bigd_[转]BigDecimal使用(整理)
- 一文读懂区块链技术,史上最全,最通俗
- 微软应用商店Microsoft Store错误代码: 0xC002001B官方解决方法和Windows计算器替代品Qalculate
- 学习笔记-混凝土损伤检测的深度学习方法
- 基于Python的结构力学位移法编程求解
- c语言单播,OSPF单播、多播及网络类型
- 西门子s300编程实例,【西门子S7-300PLC 编程 】该死的 FC+Temp
- Java制作二级导航菜单_纯CSS实现超简单的二级下拉导航菜单代码
- 迅捷CAD编辑器如何将图片转换为CAD