好久不灌水,来水一贴。
最近在用ulogd时,遇到一个小.问题,就是经常用户空间收到的数据没有二层帧。.不知是何原因,于是来看了一个Netfilter中ulogd模块(不是应用程序)的实现,虽然仍旧没有找到原因,也顺.便把代码分析的笔记放上来共享一下。.
注意:
1、看的是很老的ulogd 1.2.4的内核实现,.不是最新的2.0,最新的好像用的libnetlink接口,偶还没有时间玩这.个东东;服务器
2、内核版本2.6.12
首先是初始化全局变量,创建netlink套接字,注册netli.nk targe.t:虚拟主机
static int __init init(..void)教育
{
int i;
DEBU.GP(".ipt_ULOG: init module\n"); 鲜花
if (nlbu.fsiz >= .128*1024) { 汽车
printk("Netlink buffer has to be <.= 128.kB\n"); 婚庆
retu.rn. -EINVAL;虚拟主机
}
. /* ulog_buffers 这个数据很重要,它的下标对.应的ulogd使用的netlink group。ulog_b.uffers 用来缓存数据,可以存放一个或多个记录的skb*/健康
for (i = 0;. i < ULOG_MAXNLGROU.PS; i++) {域名
. init._timer(&ulog_buffers.timer);.
ulog_buffers.timer.function = ulog_timer; /./这个定时器后.面会分析到健康
. ulog_buffers.t.imer.data = i;服务器
}
. nflognl = netlink_kernel_create(NETLINK_NFLOG., NULL); 乙肝
if (.!nflognl) 外汇
. return -ENOME.M; 美容
if (ipt_regist.er_target(&.ipt_ulog_reg) != 0) {电脑
sock_release(.nflognl->sk_socke.t);投资
. ret.urn -EINVAL; 婚庆
}
. if (nflog) 汽车
. nf_log_register(PF_INE.T, &ipt_logfn);.
return 0.; 汽车
}
复制代码
target函数ipt_ulog_target是个包裹.函数,转向至i.pt_ulog_packet:外贸
static unsig.ned int ipt_.ulog_target(struct sk_buff **pskb,投资
const struct net_dev.ice *i.n,.
.const str.uct net_device *out,服务器
. . unsigned int hooknum,--- 印刷
const .void *targinfo, void. *userinfo).
{
s.truct ipt_ulog_info *loginfo = (struct ipt_ulog_info *) .targinfo;.
.ipt_ulog_packet(hooknum, *pskb, in, out, loginfo, .NULL);.
. retu.rn IPT_CONTINUE; 杀毒
}
复制代码
ipt_ulog_packet实现了数据的记录,需要.两点准.备知识:电影
1、用户态各个参数的作.用,看一下帮助:--- 印刷
--ulog-nlgroup nlgroup. NET.LINK group used for logging 外汇
--ulog-cpra.nge si.ze Bytes of each packet to be passed( 游戏 )
--ulog-qt.hreshol.d Threshold of in-kernel queue投资
-.-ulog-prefix prefix Prefix log messag.es with this prefix.电脑
复制代码
这样才能理解对应的结构:
struct ipt_ulog_in.fo {外贸
. unsigned int. nl_group;.
. si.ze_t copy_range;.
s.ize_t qth.reshold;.
char prefix[.ULOG_P.REFIX_LEN];.
};
复制代码
各个成员的含义
2、Linux Ne.tlink的一些基本知识,以前偶在论坛.上写过一贴关于此的,可以找一下,仅供参考; 女人
来看ipt_ulog._packet函数:投资
static void ipt_ulog_pac.ket(unsigned int ho.oknum,域名
const struct .sk_buff *.skb, 鲜花
. const struct net._device *in,投资
const struct net_devi..ce *out,健康
. const struct ipt_ulog_.info *loginfo,虚拟主机
. const char *.prefix) 外汇
{
. ulog_buff_t *ub;服务器
. . ulog_packet_msg_t *pm;.
size_.t s.ize, copy_len;域名
struct .nlmsghdr *nl.h;电脑
. /* ffs ==. find first bit set, necessary because userspace服务器
* is already. shifti.ng groupnumber, but we need unshifted. 女人
* ffs() returns [1..32], we need [0..31].. */域名
un.signed int g.roupnum = ffs(loginfo->nl_group) - 1; 外汇
/*
* 计算需要拷贝的skb长度,如果未设置或者设置的值大.于包的长.度,则len为包长,否则取用户设置值.
*/
if ((loginf.o-.>copy_range == 0) || 婚庆
(.loginfo->.copy_range > skb->len)) {电脑
copy._len = sk.b->len;.
} .else {投资
. copy_len = loginfo->copy_range;.(广告)
}
. ./* 计算总长度:Netlink报头长度+ulog报头长度+数据包长度 */ 美容
size = NLMSG.._SPACE(sizeof(*pm) + copy_len);--------------彩票
. /* 取得对应Netlink grou.p号的ulog数据包缓存 */[成人用品]
. . ub = &ulog_buffers[groupnum];--- 印刷
. /* 暂时还没有理会,为什么非要这么大一个锁? .*/--- 印刷
. LOCK_B.H(&ulog_lock);.
.if (!ub->skb) {<性病>
/.* 如果没.有,则分配之 */(广告)
. if (!(ub->sk.b = ulog_alloc_skb(size)))服务器
. goto al.loc_failure; 外汇
} else if .(ub->qle.n >= loginfo->qthreshold || 女人
. size > skb_tailroo.m(ub->skb)) {.
. /* 外汇
. * 如果队列已满,或者当前缓存的剩余空间已经不足已装下当.前包,投资
. * 则把原来的发.送至用户空间,然后再为当前包重新分配.
. */.
. ulog_se.nd(groupnum);.
. if (!(ub->skb = ulog_alloc_skb(size).))--- 印刷
. goto alloc_.failure;.
}
. DEBUGP("ipt_ULOG.: qlen %d, qthreshold %d\n", ub->qlen, 电子
loginfo->.q.threshold); 女人
./* 初始化Ne.tlink 消息首部 */ 美容
nlh = NLMS.G_PUT(ub->skb, 0, ub->ql.en, ULOG_NL_EVENT, 电脑
. sizeof(*.pm)+copy_len);投资
ub->qlen.++; 鲜花
/* 跳过.消息首部,指向数据区. */ 婚庆
pm .= NLMSG_D.ATA(nlh);.
. /* 设置时间戳,如.果没有的话 */ 鲜花
i.f (skb->stamp.t.v_sec == 0)教育
.. do_gettimeofday((struct timeval *)&skb->stamp);.
. /* 填充Netlink数据区中.的ulog数据首部 */学习
. pm->data_len = co.py_len;教育
pm->timestamp._sec = skb->st.amp.tv_sec;外贸
pm->timestamp_usec = skb.->stamp.tv_u.sec; 杀毒
. pm.->mark = skb->nfmark; 汽车
. pm->hook = hookn.um; 婚庆
/* 如果用户.指定了一.个前缀,设置之 */.
. i.f (prefix != NULL).
. strn.cpy(pm->prefix, prefix, sizeof(pm->prefix));.
else if (logi.nfo-.>prefix[0] != '\0')(广告)
strncpy.(pm.->prefix, loginfo->prefix, sizeof(pm->prefix));--------------彩票
else
. *(pm->prefix) = '\0';.--- 印刷
/*
* 要记录二层的帧,需要满.足以下几个条件.:
. * 1、存在一个输.出设备(从本机发出的包,还没有二层帧);虚拟主机
*.. 2、设备对应的二层的帧头部长度应大于0; 汽车
* 3、………………………………………….………………………………小地ulog的规.则;域名
*/
if (in &.&. in->hard_header_len > 0 外汇
&& skb->mac.raw != (v.oid *) skb->.nh.iph电脑
. && in->hard_header_len <= ULOG._MAC_LEN) { 建材
.memcpy(pm->mac,. skb->mac.raw, in->hard_header_len);投资
. pm->mac_len. = in->hard_header_len;.
} else
. pm->ma.c_len = 0; 婚庆
. . /* 拷贝进入接口,如果有的话 */ 汽车
if .(in) 乙肝
. strncpy(pm->indev_name, i.n->name, sizeof(pm->indev_name)); 鲜花
else
. pm->ind.ev_name[0] = '\0'; 外汇
/* 拷贝流出接口,如.果有的.话 */电脑
. if (out) 乙肝
strncpy(pm->outdev_name, out->.name, sizeof(pm->ou.tdev_name));..
else
. . pm->outdev_name[0] = '\0';.
. /* 拷贝skb中的数据. */外贸
. if (skb_copy_bits(skb, 0, pm-.>payload, copy_len) < 0)投资
BUG(.); 鲜花
/* 如果队列大于1,即缓存了多个包,设置一个多重标.志位,这个标志位在.发送时清除 */[成人用品]
. if (ub.->qlen > 1) {.
. ub->lastnlh->nlmsg_flags |= NLM._F_MULTI;.
}
/.* lastnlh指向当前队列中.的最后一个包,即当前包 */[成人用品]
ub.->lastnlh = nlh;.[成人用品]
/*
*. 定时器期的功能函数在ul.og模块初始化时指向了ulog_timer(),它用于在指定的时间内,清空缓存, 外汇
. * 即除了队列长度达到指定要求、或者缓存空间不足之.外,还有一个定时向用户空间发送的机制。投资
*/
if (!timer._pending(&ub->ti.mer)) {[成人用品]
ub->timer.expi.res = jiffies + flus.htimeout * HZ / 100; 美容
. add_time.r(&ub->timer);.
}
/*
* ub->qlen是当前ne.tlink group下的已经收聚的队列的长度,qthresh.old是用户指定的,需要收集的阀值,当达到这个值.时,将其发送至用户空间 健康
*/
if (ub->qlen >= lo.ginfo->qthr.eshold) {健康
. if (loginfo.->qthreshold > 1).
. nlh->nlmsg_type = .NLMSG_DONE;电影
. ulog_send(g.roupnum);.
}
U.NLOCK_BH(&ulog_lock).;学习
return;..
nlmsg_failure:
PRI.NTR("ipt._ULOG: error during NLMSG_PUT\n"); 建材
alloc_failure:
PRINT.R("ipt_ULOG: Error building netlink mes.sage\n");电脑
. UNLOCK_BH(&ulog_l.ock);外贸
}
复制代码
这个.函数中,涉及两个重要的函数,一个是分配ulog的skb,用于存储记录的数据,另一个是发送函数.。先来看发送。(广告)
netlink的发送数.据包,是通过调用API netlink_broadcast函数实现的,这里还要完成其它一些工作,比如删除定时器,因为数据已经发出去了,不需要它了,还有就是清除标志变量,修改一.些起控制作用的成员的.值,等等:投资
/* send one ulog_.buff_t to. userspace */外贸
stati.c void ulog_send(unsigned .int nlgroupnum)投资
{
ulog_buff_t *ub .= &ulog_buf.fers[nlgroupnum];.
if (timer_pen.ding(&ub->t.imer)) { 外汇
. DEBU.GP("ipt_ULOG: ulog_send: timer was pending, de.leting\n");.
. del_timer(&ub->timer).;[成人用品]
}
/*. last nlmsg needs NLMSG_DONE *./ 外汇
if (ub-.>qlen > 1) 美容
. ub->l.astnlh->nlmsg_type = NLMSG_DONE;.
NETLIN.K_CB(ub->skb).dst_gro.ups = (1 << nlgroupnum);.
DEBUGP("ipt_ULOG.: throwin.g %d packets to netlink mask %u\n",--------------彩票
. ub->qlen, nlgroupn.um);.
netlink_broadcas.t(nflogn.l, ub->skb., 0, (1 << nlgroupnum), GFP_ATOMIC);电脑
ub->qlen = 0.; 婚庆
ub->skb .= NULL;电脑
ub->lastnlh =. NULL.;[成人用品]
}
复制代码
另一个是分配函数:
static struct sk_b.uff *ulog_alloc_sk.b(unsigned int size).
{
struct sk_.buff *skb.;教育
/* al.loc skb which should be big. enough for a whole 汽车
. *. multipart message. WARNING: has to be <= 131000投资
. * due to slab allocator restrictions */.( 游戏 )
sk.b = a.lloc_skb(nlbufsiz, GFP_ATOMIC);.
. if (!skb) {( 游戏 )
. PRINTR("i.pt_ULOG: can't alloc whole buffer %ub!\n", 杀毒
nl.bufs.iz);--- 印刷
/.* t.ry to allocate only as much as we need for 美容
* .curren.t packet */外贸
.skb .= alloc_skb(size, GFP_ATOMIC);.
i.f (!skb)虚拟主机
. PRINTR("ipt_ULOG: can't ev.en al.locate %ub\n", size);--- 印刷
}
. return skb;--------------彩票
}
复制代码分配skb,就是调用alloc_sk.b函数,不过这个分配函数需要注意的一点就是分配的包的大小的问题.,它是先尝试分配(广告)
static unsigned. int .nlbufsiz = 4096; 美容
这么大的空间,如果失败了,才按需分配。这样做的原因还是在于,一个缓存可能要.存放多.个数据包。.
忽忽浏览了一下,错误之.处,大家帮偶指正。
OK,收工,继续..去看为什么偶的系统记录下来的包,应该有而没有二层帧了…… 鲜花