本文档的Copyleft归wwwlkk所有,使用GPL发布,可以自由拷贝、转载,转载时请保持文档的完整性,严禁用于任何商业用途。
E-mail: [email protected]
来源: http://blog.csdn.net/wwwlkk
1.都是使用netlink进行通信,ULOG使用NETLINK_NFLOG通信协议,而NFLOG使用NETLINK_NETFILTER通信协议。
2. ULOG只能从内核发送消息到用户空间,而NFLOG用户空间可以发送消息到内核。
3. NFLOG用户空间有libnfnetlink库的支持,而ULOG没有
4. ULOG消息编码固定,NFLOG消息编码使用属性,相对比较灵活。
5. ULOG的消息池的个数和属性都是固定,而NFLOG消息池是动态添加和撤销,并且属性可以设置。
6. 两个都是使用netlink进行通信,从性能方面看,两者应该差不多。
7. 但是NFLOG有一个特点,就是,消息池是动态建立和撤销的,如果nflog target指定的消息池不存在,则不会去创建消息,也不会去写入消息,避免做无用功。
8. NFLOG消息池是用户空间通过发送配置消息创建,用户空间如果关闭netlink socket,对应的消息池也会随之撤销。
9.NFLOG数据包是通过单播来发送数据包,也就是,只有创建消息池的进程才可以接收到数据包,
而ULOG是通过广播来发送数据包,一个消息池对应一个广播组,所有加入到某个广播组的接收进程都可以接收到这个广播中的数据包。
总的来说:ULOG通信是单向的,用户空间不能发送一些配置命令到内核,而NFLOG弥补了ULOG的不足,使得通信是双向的。
1. 可以使用libnfnetlink提供的接口来发送和接收数据
2. 一个分组对应一个消息池,可以建立多个消息池,并可以设置消息池的属性,比较有用的属性有3个:
NFULA_CFG_NLBUFSIZ 可以设置消息池的大小
NFULA_CFG_TIMEOUT 设置消息池的刷新定时器时间间隔
NFULA_CFG_QTHRESH 设置消息池允许存储的最大消息个数
说明:内核向用户空间发送数据有3个时机
(1) 消息池已满,先发送所有旧的消息,再写入新消息。
(2) 刷新定时器到期,发送所有消息。
(3) 消息池的消息个数大于允许的最大消息个数,发送所有旧消息,再写入新消息。
3. 消息的创建和撤销是由用户空间程序控制,用户空间通过发送配置消息到netlink子系统,创建消息池,并设置消息池的属性。
4. 用户空间关闭NFLOG子系统,对应的消息池也随之撤销,内核将不再写消息。
也就是说,用户空间的读取程序控制消息池的创建,撤销以及消息池的属性。
iptables中的NFLOG有以下4个属性:
--nflog-group NUM 标识消息写入哪个消息池,如果消息池不存在将不会写消息
--nflog-range NUM 消息中包含网络层数据最大长度,0表示将写入所有网络层数据
--nflog-threshold NUM 消息池消息个数等于大于NUM,刷新消息池。
--nflog-prefix STRING 本条消息的前缀
说明:消息池也有一个threshold属性,target中也有一个threshold属性,每次写入消息前,取两者最小的,然后判断是否刷新消息池。
注意:这里的group是选择消息池,nflog netlink消息是发送给创建这个消息池的进程,不是用广播包来发送,而ULOG是用广播包来发送消息。
iptables的默认属性: -j NFLOG –nflog-group 0 –nflog-threshold 1
图 1 nflog消息编码
图1说明:nflog是使用属性的名值对应来传递消息,可以通过添加属性来传递更多的消息,下图是2.6.33.2内核中nflog已有的属性:
图2 nflog属性列表
图2说明:其中nfulnl_attr_type是数据包的信息属性列表
nfulnl_attr_config是用于配置nflog消息池的属性列表
NFLOG消息发送流程:
当一个数据包满足了匹配条件,并开始执行NFLOG target时,需要根据nflog target的私有数据struct xt_nflog_info来构造要发送的消息,并使用xt_nflog_info中消息池编号指定的消息池发送消息,使用单播把数据包发送给创建本消息池的进程。
注意:如果消息池还未建立,将不会写入消息,也不会构造消息。
图3是消息池和xt_nflog_info结构
图3 消息池 和 NFLOG target私有数据结构
图3说明:如果指定的消息池不存在(使用分组编号定位一个消息池),将不写入消息,也不构造消息。消息池的建立和配置不是通过iptables进行,而是通过netlink传递配置消息到内核的nfnetlink子系统,nfnetlink子系统会根据消息的信息,来初始化一个消息池,图2中的nfulnl_attr_config属性列表就是用来建立和配置一个消息池。
这里就要涉及到nfnetlink子系统如何根据消息的类型,定位相应的处理函数,如图4《内核nfnetlink子系统结构》所示,是根据struct nlmsghdr {__u16 nlmsg_type;//前8位为子系统id号 //后8位为消息的类型};,找到相应的消息处理函数,最后根据属性列表执行相应的动作。
图4 内核nfnetlink子系统结构
图4说明的是内核nfnetlink子系统根据消息类型定位相应的处理函数。
内核发送消息到用户空间,用户空间可以通过libnfnetlink库函数来接收消息,和内核类似libnfnetlink库也是根据相同的字段来定位处理函数,如图5《用户空间libnfnetlink结构》所示。
图5 用户空间libnfnetlink结构
图5说明:一个回调函数对应一个消息类型。
这里说明的是nflog实现机制,事实上conntrack也是nfnetlink子系统的一部分,实现机制是一样的,libnetfilter_conntrack库就是在nfnetlink库的基础上进行扩展的,是否也有nflog对应的扩展库呢?如果没有也可以仿照libnetfilter_conntrack建立一个扩展库。
接下来说明ulog的实现机制,最后通过具体的例子程序来做最后的说明。
1.内核也是通过消息池来发送消息,但是不能接收消息,消息池是静态的,也就是说,消息池的个数和相应的属性是固定的,用户空间没法进行配置,
2.由于消息池总是存在的,只要执行ULOG目标,总会创建消息,并将消息写入消息池。
3.消息的格式是固定的,传出的是一个结构体,如图6所示。
4.用户空间没有对应的库函数支持。
说明:内核向用户空间发送数据有3个时机
(1)消息池已满,先发送所有旧的消息,再写入新消息。
(2)刷新定时器到期,发送所有消息。
(3)消息池的消息个数大于ipt_ulog_info中的qthreshold,发送所有旧消息,再写入新消息。
iptables中的ULOG有以下4个属性
--ulog-nlgroup nlgroup 选择哪个消息池发送消息,发送给编号和消息池编号相同的分组
--ulog-cprange size 截取网络层数据的最大长度
--ulog-qthreshold 当消息池中消息个数大于等于qthreshold,刷新消息池,然后再写入新消息。
--ulog-prefix prefix 消息的前缀
注意:nlgroup是消息池的编号,同样也是netlink分组编号,接收进城只有加入到这个分组才可以接收这个分组的广播包,具体的可以看ULOG例子程序中的说明。
iptables的默认属性: -j ULOG –ulog-group 1 –ulog-threshold 1
图6 ulog消息结构
ULOG消息发送流程:
当一个数据包满足了匹配条件,并开始执行ULOG target时,需要根据uflog target的私有数据struct ipt_uflog_info来构造要发送的消息,并使用ipt_uflog_info中消息池编号指定的消息池发送消息,使用和消息池编号相同的广播组发送广播包。
注意:消息池总是存在,也总会写入消息。
消息池的结构如图7《ulog消息池结构》所示。
图7 ulog消息池结构
1. 首先加载NFLOG target并建立NFLOG规则,如下:
iptables -A OUTPUT -p ICMP -d 192.168.190.2 -j NFLOG --nflog-group 112 --nflog-prefix "nflog-test."
规则说明:使用编号是112的消息池发送消息,消息的前缀是“nflog-test.”
现在消息池还未建立,内核还不会写入消息。
2. 使用libnfnetlink库函数提供的接口向内核发送配置命令,程序段如下:
cmd.command = NFULNL_CFG_CMD_BIND;
nfnl_fill_hdr(subh, &u.req.nlh, 0, AF_INET, 112, NFULNL_MSG_CONFIG, NLM_F_REQUEST);
nfnl_addattr_l(&u.req.nlh, sizeof(u), NFULA_CFG_CMD, &cmd, sizeof(cmd));
nfnl_addattr32(&u.req.nlh, sizeof(u), NFULA_CFG_QTHRESH, 10);
nfnl_addattr32(&u.req.nlh, sizeof(u), NFULA_CFG_TIMEOUT, htonl(400));
命令说明:
NFULNL_CFG_CMD_BIND:创建一个消息池
112是创建的消息池的编号
NFULA_CFG_QTHRESH:消息池最大消息个数 10。
NFULA_CFG_TIMEOUT:消息池定时器刷新时间,400*(1/100)=4秒。
运行截图如下:
注意:这里虽然设置消息的QTHRESH属性是10,但是NFLOG target的QTHRESH属性是1,
取其中最小的,所以QTHRESH属性是1.
1. 首先加载ULOG target并建立ULOG规则,如下:
iptables -A OUTPUT -p ICMP -d 192.168.190.2 -j ULOG --ulog-nlgroup 6 --ulog-prefix "ulog-test."
说明:使用编号是6的消息池广播消息,所有属于广播6的接收进程都可以接收到消息,消息前缀是"ulog-test.",使用默认的最大消息个数1
现在只要有规则匹配,内核就会向消息池写入消息,并发送消息。
2. 其中用户空间消息接收程序,程序段如下:
group = 6;
sd = socket(AF_NETLINK, SOCK_RAW,NETLINK_NFLOG);
memset(&saddr, 0, sizeof(saddr));
memset(&daddr, 0, sizeof(daddr));
saddr.nl_family = AF_NETLINK;
saddr.nl_pid = getpid();
saddr.nl_groups = group;
bind(sd, (struct sockaddr*)&saddr, sizeof(saddr));
setsockopt(sd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group));
说明:
AF_NETLINK和NETLINK_NFLOG是说接收netlink协议族中协议类型是NETLINK_NFLOG
的数据包。
saddr.nl_pid = getpid()是说,接收发给本进程的数据包。
saddr.nl_groups = 6是说,也接收广播组6的广播包。
Setsockopt是由于内核 2.6.14 对 netlink 套接字有新的实现,它缺省情况下不允许用户态应用发送给组号非 1 的netlink 组,因此用户态应用要想使用非1的组,必须先加入到该组,使用Setsockopt实现。
程序运行截图: