Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2034898
  • 博文数量: 369
  • 博客积分: 10093
  • 博客等级: 上将
  • 技术积分: 4271
  • 用 户 组: 普通用户
  • 注册时间: 2005-03-21 00:59
文章分类

全部博文(369)

文章存档

2013年(1)

2011年(2)

2010年(10)

2009年(16)

2008年(33)

2007年(146)

2006年(160)

2005年(1)

分类: LINUX

2006-06-13 23:30:51

问题描述,当配置http服务器的透明代理以实现web加速时,我们常常会发现即使做了端口重定向,我们在web服务器本机连接其80端口时仍然报告端口不可达,这是为什么呢?有一种解决方法是在LOCAL_OUT链中加上同样的规则,原理又何在呢?
本文将通过对netfilter nat部分的源码分析,找到问题的本源。
File:net/ipv4/netfilter/ip_nat_standalone.c
Function:ip_nat_fn()
118         case IP_CT_NEW:
119                 info = &ct->nat.info;
120
121                 /* Seen it before?  This can happen for loopback, retrans,
122                    or local packets.. */
123                 if (!ip_nat_initialized(ct, maniptype)) {
124                         unsigned int ret;
125
126                         if (unlikely(is_confirmed(ct)))
127                                 /* NAT module was loaded late */
128                                 ret = alloc_null_binding_confirmed(ct, info,
129                                                                    hooknum);
130                         else if (hooknum == NF_IP_LOCAL_IN)
131                                 /* LOCAL_IN hook doesn't have a chain!  */
132                                 ret = alloc_null_binding(ct, info, hooknum);
133                         else
134                                 ret = ip_nat_rule_find(pskb, hooknum,
135                                                        in, out, ct,
136                                                        info);
137
138                         if (ret != NF_ACCEPT) {
139                                 return ret;
140                         }
141                 } else
142                         DEBUGP("Already setup manip %s for ct %p\n",
143                                maniptype == IP_NAT_MANIP_SRC ? "SRC" : "DST",
144                                ct);
145                 break;
146
我们不难发现只有包状态为NEW,并且没有做过NAT转化的包才会通过ip_nat_rule_find()查找并生成NAT规则信息。
File:include/linux/netfilter_ipv4/ip_conntrack.h
380 static inline int ip_nat_initialized(struct ip_conntrack *conntrack,
381                                      enum ip_nat_manip_type manip)
382 {
383         if (manip == IP_NAT_MANIP_SRC)
384                 return test_bit(IPS_SRC_NAT_DONE_BIT, &conntrack->status);
385         return test_bit(IPS_DST_NAT_DONE_BIT, &conntrack->status);
386 }
ip_nat_initialnized()监测是否做过此类NAT(这里的NAT分为两种,一种为DNAT,即改变目的地址的NAT,与之相对的是SNAT)转换,如果做过返回真值。具体是通过判断一个标志位是否置。
Netfiler的NAT的HOOK点,有四处,即PREROUTING, LOCAL_IN, LOCAL_OUT, POSTROUTING。
File:net/ipv4/netfilter/ip_nat_standalone.c
261 static struct nf_hook_ops ip_nat_in_ops = {
262         .hook           = ip_nat_in,
263         .owner          = THIS_MODULE,
264         .pf             = PF_INET,
265         .hooknum        = NF_IP_PRE_ROUTING,
266         .priority       = NF_IP_PRI_NAT_DST,
267 };
268
269 /* After packet filtering, change source */
270 static struct nf_hook_ops ip_nat_out_ops = {
271         .hook           = ip_nat_out,
272         .owner          = THIS_MODULE,
273         .pf             = PF_INET,
274         .hooknum        = NF_IP_POST_ROUTING,
275         .priority       = NF_IP_PRI_NAT_SRC,
276 };
... ...
287 /* Before packet filtering, change destination */
288 static struct nf_hook_ops ip_nat_local_out_ops = {
289         .hook           = ip_nat_local_fn,
290         .owner          = THIS_MODULE,
291         .pf             = PF_INET,
292         .hooknum        = NF_IP_LOCAL_OUT,
293         .priority       = NF_IP_PRI_NAT_DST,
294 };
295
296 /* After packet filtering, change source for reply packets of LOCAL_OUT DNAT */
297 static struct nf_hook_ops ip_nat_local_in_ops = {
298         .hook           = ip_nat_fn,
299         .owner          = THIS_MODULE,
300         .pf             = PF_INET,
301         .hooknum        = NF_IP_LOCAL_IN,
302         .priority       = NF_IP_PRI_NAT_SRC,
303 };
304
其中PREROUTING和LOCAL_OUT做DNAT,POSTROUTING和LOCAL_INP做SNAT,实际上LOCAL_IN不做任何NAT操作,只是置SNAT已经做过的位,具体看上面的蓝色标记部分的代码。因为DNAT的操作标志位已经在LOCAL_OUT中置过,
File:net/ipv4/netfilter/ip_nat_rule.c
279 int ip_nat_rule_find(struct sk_buff **pskb,
280                      unsigned int hooknum,
281                      const struct net_device *in,
282                      const struct net_device *out,
283                      struct ip_conntrack *ct,
284                      struct ip_nat_info *info)
285 {
286         int ret;
287
288         ret = ipt_do_table(pskb, hooknum, in, out, &nat_table, NULL);
289
290         if (ret == NF_ACCEPT) {
291                 if (!ip_nat_initialized(ct, HOOK2MANIP(hooknum)))
292                         /* NUL mapping */
293                         ret = alloc_null_binding(ct, info, hooknum);
294         }
295         return ret;
296 }
File:net/ipv4/netfilter/ip_nat_rule.c
237 inline unsigned int
238 alloc_null_binding(struct ip_conntrack *conntrack,
239                    struct ip_nat_info *info,
240                    unsigned int hooknum)
241 {
242         /* Force range to this IP; let proto decide mapping for
243            per-proto parts (hence not IP_NAT_RANGE_PROTO_SPECIFIED).
244            Use reply in case it's already been mangled (eg local packet).
245         */
246         u_int32_t ip
247                 = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC
248                    ? conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip
249                    : conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip);
250         struct ip_nat_range range
251                 = { IP_NAT_RANGE_MAP_IPS, ip, ip, { 0 }, { 0 } };
252
253         DEBUGP("Allocating NULL binding for %p (%u.%u.%u.%u)\n", conntrack,
254                NIPQUAD(ip));
255         return ip_nat_setup_info(conntrack, &range, hooknum);
256 }
File:net/ipv4/netfilter/ip_nat_core.c
Function:ip_nat_setup_info()
344         /* It's done. */
345         if (maniptype == IP_NAT_MANIP_DST)
346                 set_bit(IPS_DST_NAT_DONE_BIT, &conntrack->status);
347         else
348                 set_bit(IPS_SRC_NAT_DONE_BIT, &conntrack->status);
349
350         return NF_ACCEPT;
351 }
所以在PREROUTING中将不再过任何DNAT操作。所以,如果想支持本地连接的话需要在LOCAL_OUTPUT中加上同样的规则。
阅读(3425) | 评论(2) | 转发(0) |
1

上一篇:世界杯

下一篇:

给主人留下些什么吧!~~