Linux Netfilter的mangle表的主要功能是根據規則修改資料包的一些标志位,以便其他規則或程式可以利用這種标志對資料包進行過濾或政策路由。
mangle表在五個鍊的位置都有注冊。
注冊函數如下:
static int __init iptable_mangle_init(void)
{
int ret;
/* 通過iptable_mangle_net_init函數調用ipt_register_table函數注冊mangle表net->ipv4.iptable_mangle */
ret = register_pernet_subsys(&iptable_mangle_net_ops);
if (ret < 0)
return ret;
/* Register hooks */
/* 向netfilter注冊mangle表 */
ret = nf_register_hooks(ipt_ops, ARRAY_SIZE(ipt_ops));
if (ret < 0)
goto cleanup_table;
return ret;
cleanup_table:
unregister_pernet_subsys(&iptable_mangle_net_ops);
return ret;
}
mangle表的hook定義,注冊到五個鍊:
static struct nf_hook_ops ipt_ops[] __read_mostly = {
{
.hook = ipt_pre_routing_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP_PRI_MANGLE,
},
{
.hook = ipt_local_in_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP_PRI_MANGLE,
},
{
.hook = ipt_forward_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_INET_FORWARD,
.priority = NF_IP_PRI_MANGLE,
},
{
.hook = ipt_local_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_MANGLE,
},
{
.hook = ipt_post_routing_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP_PRI_MANGLE,
},
};
關于mangle表的hook函數,PREROUTING、LOCAL_IN、FORWORD、POSTROUTING的實作一樣,
主要是實作對封包的比對,周遊mangle表的所有規則,對資料包進行檢查及修改封包内容,然後根據傳回值作後續動作。
/* The work comes in here from netfilter.c. */
static unsigned int ipt_pre_routing_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_mangle);
}
mangle表的LOCAL_OUT鍊的hook函數:
static unsigned int ipt_local_hook(unsigned int hook,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
unsigned int ret;
const struct iphdr *iph;
u_int8_t tos;
__be32 saddr, daddr;
u_int32_t mark;
/* 增加對資料包長度的判斷,如果ip報頭不完整,
* 則傳回NF_ACCEPT,不進行mangle處理 */
/* root is playing with raw sockets. */
if (skb->len < sizeof(struct iphdr)
|| ip_hdrlen(skb) < sizeof(struct iphdr))
return NF_ACCEPT;
/* 在mangle之前,儲存封包中原有的頭資訊 */
/* Save things which could affect route */
mark = skb->mark;
iph = ip_hdr(skb);
saddr = iph->saddr;
daddr = iph->daddr;
tos = iph->tos;
/* 對mangle表的處理 */
ret = ipt_do_table(skb, hook, in, out,
dev_net(out)->ipv4.iptable_mangle);
/* Reroute for ANY change. */
if (ret != NF_DROP && ret != NF_STOLEN && ret != NF_QUEUE) {
iph = ip_hdr(skb);
/* 注意,如果ip頭發生改變(比如經過nat轉換),則需要調用ip_rout_me_header重新計算路由 */
if (iph->saddr != saddr ||
iph->daddr != daddr ||
skb->mark != mark ||
iph->tos != tos)
if (ip_route_me_harder(skb, RTN_UNSPEC))
ret = NF_DROP;
}
return ret;
}