1.首先指出,NF_HOOK系列宏的outdev参数的传递方式(直接传递一个net_device结构体指针)是不正确的
正确的方式要么是不传递,要么是传递指针的地址,即地址的地址。
2.接下来指出,仅仅传递一个地址为何不对
因 为在该HOOK点可能存在多个HOOK函数,每一个函数都有可能改变skb的路由,即调用reroute,比如NAT,比如IP Mark等,这样后续的HOOK函数看到的依然是旧的outdev参数,而不是reroute之后的skb_dst(skb)->dev。
3.然后看一个实际出错例子
设置默认路由
0.0.0.0/0 via 192.168.1.1 eth0
设置策略路由
iptables -t mangle -A OUTPUT -d 1.1.1.1 -j MARK --set-mark 100
ip rule fwmark 100 table my
ip route add 0.0.0.0/0 via 192.168.2.1 dev eth1 table my
防止mark 100的数据包从eth0出去(这实际上只是为了展现问题而添加的多余的一条规则)
iptables -A OUTPUT -d 1.1.1.1 -o eth0 -j DROP
效 果是到达1.1.1.1的数据包被DROP掉了。是的,策略路由确实生效了,问题在于进入OUTPUT的filter HOOK函数的时候,outdev还是旧的outdev。因为OUTPUT处在路由之后,如果其中的mangle表改变了skb的mark,那么会 reroute,不幸的是,reroute并无法改变OUTPUT点上NF_HOOK的outdev参数值!
4.怎么修正
办法很多,依次介绍:
a.使用setsockopt打mark而不是iptables打mark,绕开OUTPUT和路由的暧昧关系;
b. 修改NF_HOOK的dev参数为struct net_device **类型,然后在reroute中重路由成功后执行*out = (struct dst_entry*)skb_dst(skb)->dev;从而改变NF_HOOK中的outdev的值;
c.去掉NF_HOOK宏的outdev参数,需要时从skb_dst(skb)->dev中实时获取;
很简单,在ipt_do_table的开头位置,即变量声明的完结处,加入下面的代码:
struct xt_target_param tgpar; #if 1 struct dst_entry *dst_e = skb_dst(skb); if (dst_e) { out = dst_e->dev; } #endif
实时替换掉参数定义的out值。
d.使用非传值机制!C语言是传值的啊!使用连续的******可以为了寻址一个字节遍历整个内存,即整个内存只存储一个字节的值,其它的都被填满为它的直接或者间接的地址,地址,地址...
5.彻底仿真世界
一个实体只能同时存在于一个位置!就像人一样,进入了一个房间,外面就没有这个人了,可惜编程语言不是这样的。