Linux Netfilter的filter表注册在以下几个链的位置:LOCAL_IN、FORWORD、LOCAL_OUT。
注册函数如下:
static int __init iptable_filter_init(void)
{
int ret;
if (forward < 0 || forward > NF_MAX_VERDICT) {
printk("iptables forward must be 0 or 1\n");
return -EINVAL;
}
/* Entry 1 is the FORWARD hook */
initial_table.entries[1].target.verdict = -forward - 1;
/* 通过iptable_filter_net_init函数调用ipt_register_table函数注册filter表net->ipv4.iptable_filter */
ret = register_pernet_subsys(&iptable_filter_net_ops);
if (ret < 0)
return ret;
/* Register hooks */
/* 注册filter表 */
ret = nf_register_hooks(ipt_ops, ARRAY_SIZE(ipt_ops));
if (ret < 0)
goto cleanup_table;
return ret;
cleanup_table:
unregister_pernet_subsys(&iptable_filter_net_ops);
return ret;
}
filter表的hook定义,注册到三个链:
static struct nf_hook_ops ipt_ops[] __read_mostly = {
{
.hook = ipt_local_in_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP_PRI_FILTER,
},
{
.hook = ipt_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_INET_FORWARD,
.priority = NF_IP_PRI_FILTER,
},
{
.hook = ipt_local_out_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_FILTER,
},
};
LOCAL_IN的hook函数:
/* The work comes in here from netfilter.c. */
static unsigned int ipt_local_in_hook(unsigned int hook,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
/* 实现对报文的过滤,遍历filter表的所有规则,
* 对数据包进行检查,然后根据返回值作后续动作 */
return ipt_do_table(skb, hook, in, out,
dev_net(in)->ipv4.iptable_filter);
}
FORWORD的hook函数:
static unsigned int ipt_hook(unsigned int hook,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
/* 实现对报文的过滤 */
return ipt_do_table(skb, hook, in, out,
dev_net(in)->ipv4.iptable_filter);
}
LOCAL_OUT的hook函数:
增加对数据包长度的判断,如果ip报头不完整,则返回NF_ACCEPT,不进行filter过滤。
static unsigned int ipt_local_out_hook(unsigned int hook,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
/* root is playing with raw sockets. */
if (skb->len < sizeof(struct iphdr) ||
ip_hdrlen(skb) < sizeof(struct iphdr))
return NF_ACCEPT;
return ipt_do_table(skb, hook, in, out,
dev_net(out)->ipv4.iptable_filter);
}