static struct ipt_match ipaddr_match
= {
'name' 是你的模块的文件名字符串(也就是说 ipt_ipaddr)。
.name = "ipaddr",
下面的字段是框架将要使用的回调函数.'match'是当一个包传送给你的模块的时候要调用的函数.
.match = match,
.checkentry = checkentry,
.me = THIS_MODULE,
};
你的内核模块的 init 函数需要通过指向一个 'struct ipt_match' 的指针调用 'ipt_register_match()' 来向 netfilter 框架注册.这个函数在模块被加载的时候调用.
static int __init init(void)
{
printk(KERN_INFO "ipt_ipaddr: init!\n");
return ipt_register_match(&ipaddr_match);
}
当把模块从内核中移出的时候这个函数会被调用.这里我们进行的工作是注销匹配器。
static void __exit fini(void)
{
printk(KERN_INFO "ipt_ipaddr: exit!\n");
ipt_unregister_match(&ipaddr_match);
}
设置让这两个函数在模块装入和移出的时候被调用。
module_init(init);
module_exit(fini);
2.2.2 match 函数
Linux 的 TCP/IP 协议栈包括5个 netfilter 钩子。这样,一个包近来之后,协议栈把包送到相应的钩子,依次进入每个表,再依次叠带每条规则。当你的模块得到包的时候,你的模块就可以进行它的工作了。
static int match(const struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
const void *matchinfo,
int offset,
const void *hdr,
u_int16_t datalen,
int *hotdrop)
{
希望你还记着我们在用户态程序里面做了些什么! :)。现在把用户态程序拷贝过来的数据结构映射到我们这里
const struct ipt_skeleton_info *info = matchinfo;
'skb' 包含了我们想要处理的包。想要得到关于这个在 linux 的 TCP/IP 协议栈中到处都是功能强大的数据结构的信息,可以看看 Harald Welte 写的一出色的文章
article (ftp://ftp.gnumonks.org/pub/doc/skb-doc.html) 。
struct iphdr *iph = skb->nh.iph;
这里,我们就是打印一些有趣的东西来看看他们长成什么样子。宏 'NIPQUAD' 用于以可读的方式显示一个 IP 地址,它是在 <
linux/include/linux/kernel.h> 中定义的。
printk(KERN_INFO "ipt_ipaddr: IN=%s OUT=%s TOS=0x%02X "
"TTL=%x SRC=%u.%u.%u.%u DST=%u.%u.%u.%u "
"ID=%u IPSRC=%u.%u.%u.%u IPDST=%u.%u.%u.%u\n",
in ? (char *)in : "", out ? (char *)out : "", iph->tos,
iph->ttl, NIPQUAD(iph->saddr), NIPQUAD(iph->daddr),
ntohs(iph->id), NIPQUAD(info->ipaddr.src), NIPQUAD(info->ipaddr.dst)
);
如果输入了 '--ipsrc' 参数,我们察看源地址是否和规则指定的地址相匹配。别忘了考虑反标志 '!'。如果没有匹配,我们返回 0.
if (info->flags & IPADDR_SRC) {
if ( (ntohl(iph->saddr) != ntohl(info->ipaddr.src)) ^ !!(info->flags & IPADDR_SRC_INV) ) {
printk(KERN_NOTICE "src IP %u.%u.%u.%u is not matching %s.\n",
NIPQUAD(info->ipaddr.src),
info->flags & IPADDR_SRC_INV ? " (INV)" : "");
return 0;
}
}
这里,我们进行完全相同的工作,只是察看 '--ipdst' 参数。
if (info->flags & IPADDR_DST) {
if ( (ntohl(iph->daddr) != ntohl(info->ipaddr.dst)) ^ !!(info->flags & IPADDR_DST_INV) ) {
printk(KERN_NOTICE "dst IP %u.%u.%u.%u is not matching%s.\n",
NIPQUAD(info->ipaddr.dst),
info->flags & IPADDR_DST_INV ? " (INV)" : "");
return 0;
}
}
如果都不成功,返回 1,表明我们匹配了这个包。
return 1;
}
2.2.3 checkentry 函数
checkentry 通常是最后一次合法性检查的机会。关于它何时被调用有些难以理解。看看
post (http://www.mail-archive.com/netfilter-devel@lists.samba.org/msg00625.html) 作为一个解释吧。这篇文章也是一篇 netfilter hacking howto。
static int checkentry(const char *tablename,
const struct ipt_ip *ip,
void *matchinfo,
unsigned int matchsize,
unsigned int hook_mask)
{
const struct ipt_skeleton_info *info = matchinfo;
if (matchsize != IPT_ALIGN(sizeof(struct ipt_skeleton_info))) {
printk(KERN_ERR "ipt_skeleton: matchsize differ, you may have forgotten to recompile me.\n");
return 0;
}
printk(KERN_INFO "ipt_skeleton: Registered in the %s table, hook=%x, proto=%u\n",
tablename, hook_mask, ip->proto);
return 1;
}
2.3 第二章小结
在第二部分,我们讲了 netfilter 模块以及如何使用特定结构注册它。另外我们还讨论了如何根据用户空间