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模式相关推荐

  1. 演示:思科IPS在线模式下Inline Interface Mode的响应行为(区别各个防御行为)

    演示:思科IPS在线模式下Inline Interface Mode的响应行为 演示目标:科IPS在线模式下InlineInterface Mode的响应行为. 演示环境:仍然使用图5.16所示的网络 ...

  2. suricata的简介以及安装过程

    Suricata介绍 Suricata是一款高性能的网络IDS.IPS和网络安全监控引擎.它是由the Open Information Security Foundation开发,是一款开源的系统. ...

  3. Surciata源码分析之IpsNFQ模式(1)

    最近看了一下suricata-1.2.1的源代码,加之之前在网上没有搜到关于suricata的分析资料, 所以就把看源码时的一些笔记整理了一 下,发到网上,供其他对suricata感兴趣的网友参考.由 ...

  4. 网络入侵检测系统之Suricata(四)--初始化模块代码详解

    initial Module 初始化流程 初始化Suricata instance 用来保存程序当前的一些状态.标志等上下文环境,通常是用来作为参数传递给各个模块的子函数 memset(suri, 0 ...

  5. suricata学习

    <suricata简介> Suricata是一个高性能的网络IDS,IPS和网络安全监控引擎. IPS:入侵预防系统(IPS: Intrusion Prevention System)是电 ...

  6. tcp段重组--suricata实现

    tcp协议上的应用层协议检测时,需要做段重组,这里分析下suricata的TCP 段重组源码. 1. 数据结构 1.1 TcpSession结构体 TcpSession结构体,为flow结构体成员pr ...

  7. 安全产品的核心逻辑-IPS/IDS

    概述 在<软件定义安全>中介绍了,所有的安全产品本质上就是对安全业务的软件开发,在<安全产品的核心逻辑-防火墙>中介绍了防火墙的核心点和核心逻辑.本文介绍ips/ids的核心内 ...

  8. snort 联动iptables 配置为IPS,NIDS

    一.环境准备 Ubuntu16.10,snort2.9.9,iptables1.6.0,daq-2.0.6 二.snort安装 首先关掉网卡的"Large Receive Offload&q ...

  9. Ubuntu下安装suricata

    因为现在的工作主要是基于suricata这个开源软件来建立具有协议解析功能的防火墙,所以本篇简单介绍一下如何在Ubuntu16.04下安装suricata,之后会写一些关于suricata开发的文章. ...

最新文章

  1. 19 条 MySQL 技巧,效率至少提高 3倍!
  2. Linux 下安装matlab2014a
  3. 根据当前日期算前一年、前一月、前一天(java基础)
  4. 计算机组成与系统原理中的节拍是什么意思?
  5. C++11新特性选讲 语言部分 侯捷
  6. 圆与平面的接触面积_视频:5.3RJ六年级上册圆的面积例题+习题讲解
  7. unity, 不要用TextMesh,用图片代替
  8. GoogleTest测试框架介绍(二)
  9. vue-cli设置proxy代理
  10. 图像的手绘效果(PIL的应用实例)
  11. 每天花半小时给孩子讲故事,把他培养成依赖书的人种
  12. windows的cmd命令检测ip或者端口是否连通
  13. 区块链巨头2018:几家欢喜几家愁 |链捕手
  14. 曲率(Curvature)
  15. 对Lab颜色空间的学习(转)
  16. bi工程师和java哪个好_每个好架构师都是一位出色的程序员
  17. 编译isl和cloog库出现:fatal error: gmp.h: No such file or directory
  18. 雷锋网专访食神摇摇:为吃货而建的个性化推荐引擎
  19. Excel表格快速生成LaTeX
  20. 9月14日云栖精选夜读:揭秘IPHONE X刷脸认证的技术奥秘

热门文章

  1. 基于ssm的志愿者管理系统(idea+spring+springmvc+mybatis+jsp)
  2. 百分数转bigd_[转]BigDecimal使用(整理)
  3. 一文读懂区块链技术,史上最全,最通俗
  4. 微软应用商店Microsoft Store错误代码: 0xC002001B官方解决方法和Windows计算器替代品Qalculate
  5. 学习笔记-混凝土损伤检测的深度学习方法
  6. 基于Python的结构力学位移法编程求解
  7. c语言单播,OSPF单播、多播及网络类型
  8. 西门子s300编程实例,【西门子S7-300PLC 编程 】该死的 FC+Temp
  9. Java制作二级导航菜单_纯CSS实现超简单的二级下拉导航菜单代码
  10. 迅捷CAD编辑器如何将图片转换为CAD