本文档的Copyleft归wwwlkk全部,使用GPL发布,能够自由拷贝、转载,转载时请保持文档的完整性,严禁用于任何商业用途。数据结构
E-mail: wwwlkk@126.com并发
来源: http://blog.csdn.net/wwwlkksocket
1.都是使用netlink进行通讯,ULOG使用NETLINK_NFLOG通讯协议,而NFLOG使用NETLINK_NETFILTER通讯协议。函数
2. ULOG只能从内核发送消息到用户空间,而NFLOG用户空间能够发送消息到内核。post
3. NFLOG用户空间有libnfnetlink库的支持,而ULOG没有性能
4. ULOG消息编码固定,NFLOG消息编码使用属性,相对比较灵活。编码
5. ULOG的消息池的个数和属性都是固定,而NFLOG消息池是动态添加和撤销,而且属性能够设置。spa
6. 两个都是使用netlink进行通讯,从性能方面看,二者应该差很少。.net
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实现。
程序运行截图: