iptables -t mangle -A PREROUTING -d x.x.x.x/a -i eth2 -m conntrack --ctdir ORIGINAL -j MARK --set-xmark 100 iptables -t mangle -A PREROUTING -s y.y.y.y/b -i eth2 -m conntrack --ctdir REPLY -j MARK --set-xmark 100我这么写有什么问题吗?绝对没有!懂iptables的人都知道我要做什么,我要做的就是截获两类流,一个是主动访问x.x.x.x/a的流,另一类是y.y.y.y/b被访问的流。OK,一切看起来很好。然而当我将上述规则实际部署的时候,出问题了,截取竟然是相反的流,也就是说,截取了两类流,一类是x.x.x.x/a被访问的流,另一类是主动访问y.y.y.y/b的流...我该怎么办?很长一段时间,我一直认为我错了,google过,百度过,毫无结果。作为一个成熟的用了这么久的iptables,不会犯错,一定是我错了。因此我的下一个计划就是找到:我哪里错了?
static bool conntrack_mt(const struct sk_buff *skb, const struct xt_match_param *par, u16 state_mask, u16 status_mask) { const struct xt_conntrack_mtinfo2 *info = par->matchinfo; enum ip_conntrack_info ctinfo; const struct nf_conn *ct; unsigned int statebit; ct = nf_ct_get(skb, &ctinfo); /* 这些都不是当前所关注的,当前只关注ctdir */ ... if ((info->match_flags & XT_CONNTRACK_DIRECTION) && (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) ^ !!(info->invert_flags & XT_CONNTRACK_DIRECTION)) return false; /* 这些都不是当前所关注的,当前只关注ctdir */ ... return true; }为了思路更清晰,将帮助函数/宏也列举出来:
enum ip_conntrack_dir { IP_CT_DIR_ORIGINAL, IP_CT_DIR_REPLY, IP_CT_DIR_MAX }; enum ip_conntrack_info { /* Part of an established connection (either direction). */ IP_CT_ESTABLISHED, /* Like NEW, but related to an existing connection, or ICMP error (in either direction). */ IP_CT_RELATED, /* Started a new connection to track (only IP_CT_DIR_ORIGINAL); may be a retransmission. */ IP_CT_NEW, /* >= this indicates reply direction */ IP_CT_IS_REPLY, /* Number of distinct IP_CT types (no NEW in reply dirn). */ IP_CT_NUMBER = IP_CT_IS_REPLY * 2 - 1 }; #define CTINFO2DIR(ctinfo) ((ctinfo) >= IP_CT_IS_REPLY ? IP_CT_DIR_REPLY : IP_CT_DIR_ORIGINAL) static inline struct nf_conn * nf_ct_get(const struct sk_buff *skb, enum ip_conntrack_info *ctinfo) { *ctinfo = skb->nfctinfo; return (struct nf_conn *)skb->nfct; }然后我们看一下skb->nfctinfo到底在哪里初始化的,实际上不用找也知道是nf_conntrack_in里面的resolve_normal_ct中:
if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) { *ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY; /* Please set reply bit if this packet OK */ *set_reply = 1; } else { //只要不是返回方向的,都是正向包。 /* Once we've had two way comms, always ESTABLISHED. */ if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { *ctinfo = IP_CT_ESTABLISHED; } else if (test_bit(IPS_EXPECTED_BIT, &ct->status)) { *ctinfo = IP_CT_RELATED; } else { *ctinfo = IP_CT_NEW; } *set_reply = 0; } skb->nfct = &ct->ct_general; skb->nfctinfo = *ctinfo;可见,正方向的数据包的nfctinfo被初始化成了IP_CT_ESTABLISHED,IP_CT_RELATED或者IP_CT_NEW,而这些都比IP_CT_IS_REPLY要小,因此正方向的数据包的CTINFO2DIR肯定是IP_CT_DIR_ORIGINAL(详见CTINFO2DIR宏定义)。另一个证据在init_conntrack函数,它是一个初始化ct的函数,这种情况下ct肯定被认为是正方向的,该函数在最后返回的是&ct->tuplehash[IP_CT_DIR_ORIGINAL],一个IP_CT_DIR_ORIGINAL方向的ct-part。
(CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) ^ !!(info->invert_flags & XT_CONNTRACK_DIRECTION))改为:
(CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) ^ !(info->invert_flags & XT_CONNTRACK_DIRECTION))
从“!!(info->invert_flags & XT_CONNTRACK_DIRECTION)”这一行代码来看,笔误的可能性比较大,否则实在想不通为何要用双重否定句。
令人气愤的是,如此一个问题怎么竟然很少有人提出来啊,难道真的是能用就行吗?或者说真的是我错了?唉,反正不管怎么说,我配反了就能用,配正了就错误!搞了一下午,真TM fuXX the conntrack,怎么就没人管呢?这么成熟的iptables竟然都不可信,茫茫尘世,我还能信谁?