初始化
iptable_mangle_table_init函数通过调用ipt_register_table完成mangle表注册和钩子函数注册的功能;该流程与iptable_filter的函数调用的函数一致,此处不再重复分析,详情请移步<iptable_filter分析>;
1 static int __net_init iptable_mangle_table_init(struct net *net) 2 { 3 struct ipt_replace *repl; 4 int ret; 5 6 /* 已经初始化 */ 7 if (net->ipv4.iptable_mangle) 8 return 0; 9 10 /* 分配初始化用于下面注册的结构 */ 11 repl = ipt_alloc_initial_table(&packet_mangler); 12 if (repl == NULL) 13 return -ENOMEM; 14 /* 注册表和钩子函数 */ 15 ret = ipt_register_table(net, &packet_mangler, repl, mangle_ops, 16 &net->ipv4.iptable_mangle); 17 kfree(repl); 18 return ret; 19 }
钩子函数
从下面的钩子函数可以看到其分布于全部5个钩子点;
1 #define MANGLE_VALID_HOOKS ((1 << NF_INET_PRE_ROUTING) | 2 (1 << NF_INET_LOCAL_IN) | 3 (1 << NF_INET_FORWARD) | 4 (1 << NF_INET_LOCAL_OUT) | 5 (1 << NF_INET_POST_ROUTING))
1 static const struct xt_table packet_mangler = { 2 .name = "mangle", 3 .valid_hooks = MANGLE_VALID_HOOKS, 4 .me = THIS_MODULE, 5 .af = NFPROTO_IPV4, 6 .priority = NF_IP_PRI_MANGLE, 7 .table_init = iptable_mangle_table_init, 8 };
iptable_mangle_hook为mangle钩子函数,如果当前是处于LOCAL_OUT钩子点,则需要调用ip_mangle_out函数,其他店则调用ipt_do_table进行规则匹配;ipt_do_table函数此处不再重复分析,详情请移步<iptable_filter分析>;
1 static unsigned int 2 iptable_mangle_hook(void *priv, 3 struct sk_buff *skb, 4 const struct nf_hook_state *state) 5 { 6 /* LOCAL_OUT钩子点,调用mangle_out */ 7 if (state->hook == NF_INET_LOCAL_OUT) 8 return ipt_mangle_out(skb, state); 9 /* 规则匹配 */ 10 return ipt_do_table(skb, state, state->net->ipv4.iptable_mangle); 11 }
ipt_mangle_out首先保存ip头部的一些信息,然后调用ipt_do_table进行规则匹配,规则之后检查ip头中的保存字段是否发生变化,如果发生变化,则需要重新查路由;
1 static unsigned int 2 ipt_mangle_out(struct sk_buff *skb, const struct nf_hook_state *state) 3 { 4 unsigned int ret; 5 const struct iphdr *iph; 6 u_int8_t tos; 7 __be32 saddr, daddr; 8 u_int32_t mark; 9 int err; 10 11 /* root is playing with raw sockets. */ 12 /* 原始套接字 */ 13 if (skb->len < sizeof(struct iphdr) || 14 ip_hdrlen(skb) < sizeof(struct iphdr)) 15 return NF_ACCEPT; 16 17 /* Save things which could affect route */ 18 19 mark = skb->mark; 20 iph = ip_hdr(skb); 21 saddr = iph->saddr; 22 daddr = iph->daddr; 23 tos = iph->tos; 24 25 /* 进行规则匹配 */ 26 ret = ipt_do_table(skb, state, state->net->ipv4.iptable_mangle); 27 /* Reroute for ANY change. */ 28 /* 经过规则后 */ 29 if (ret != NF_DROP && ret != NF_STOLEN) { 30 iph = ip_hdr(skb); 31 32 /* 判断ip头中的字段是否有改变 */ 33 if (iph->saddr != saddr || 34 iph->daddr != daddr || 35 skb->mark != mark || 36 iph->tos != tos) { 37 /* 重新查路由 */ 38 err = ip_route_me_harder(state->net, skb, RTN_UNSPEC); 39 if (err < 0) 40 ret = NF_DROP_ERR(err); 41 } 42 } 43 44 return ret; 45 }