+ -
当前位置:首页 → 问答吧 → linux多路路由保持长链接的问题

linux多路路由保持长链接的问题

时间:2010-06-19

来源:互联网

本帖最后由 Godbach 于 2010-06-21 09:47 编辑

由于linux默认每10分钟就清空一次路由缓存,所以在多路路由的情况下就不能保持长时间的链接。如下情形

1. 两条电信的PPPoE线路,用设备做负载均衡,均衡路由是这样的:
default
  nexthop via a.a.a.a dev ppp0 weight 1
  nexthop via b.b.b.b. dev ppp1 weight 1
2. telnet登录到公网的某个服务器,大概10几分钟后就可能断开;
3. Linux内核版本2.6.20
4. 路由缓冲默认每隔10分钟刷新(/proc/sys/net/ipv4/route/secret_interval)
5. telnet第一次登陆时,路由选路假设选择ppp1口;最长10分钟后,路由缓存被清除,此时telnet报文被重新路由,multipath可能选择另外的接口,即PPP0;从ppp0接口抓包,可以看到以ppp1接口地址为源地址的报文从ppp0接口发出,导致telnet断开;


在linux下有什么方法可以解决这个问题吗?

作者: 瀚海书香   发布时间: 2010-06-19

回复 瀚海书香
怎么没人回答啊?难道我理解的不对???

作者: 瀚海书香   发布时间: 2010-06-20

LZ你好。帮你把标题编辑了一下,希望不要介意。
欢迎大家参与讨论该问题。

作者: Godbach   发布时间: 2010-06-21

回复 Godbach
God兄没遇到过这个问题吗?
还望大虾指点

作者: 瀚海书香   发布时间: 2010-06-21

嗯,路由方面的问题遇到的不多。

作者: Godbach   发布时间: 2010-06-21

难道就没有哪位大虾做过负载均衡方面的东西吗??不解中。。。。。。

作者: 瀚海书香   发布时间: 2010-06-21

如果仅仅使用Linux标准的路由处理,确实会存在这个问题,而且似乎没什么好的解决办法。

不过,如果LZ是做内核开发的话,应该比较好解决,与conntrack关联就可以了。

作者: ShadowStar   发布时间: 2010-06-21

回复 ShadowStar
conntrack与路由关联??
在查找路由的时候先查找conntrack链表??

作者: 瀚海书香   发布时间: 2010-06-21



QUOTE:
回复  ShadowStar
conntrack与路由关联??
在查找路由的时候先查找conntrack链表??
瀚海书香 发表于 2010-06-21 14:51


是的。

作者: ShadowStar   发布时间: 2010-06-21



QUOTE:
如果仅仅使用Linux标准的路由处理,确实会存在这个问题,而且似乎没什么好的解决办法。

不过,如果LZ是做 ...
ShadowStar 发表于 2010-06-21 14:19



不错的办法,conntrack 往往可以帮我们解决很多问题

作者: platinum   发布时间: 2010-06-21

应该是利用conntrack可以加速路由

作者: Godbach   发布时间: 2010-06-21

回复 ShadowStar
难到没有人写过这个模块吗?我觉得这个应用挺广泛啊,应该早就有人写了这个模块才对啊???

作者: 瀚海书香   发布时间: 2010-06-21

恐怕没有那么多的应该吧,也许以后会有人提交的,但目前确实没有

作者: platinum   发布时间: 2010-06-21

再说即使有人写了,一定会open出来吗。

作者: Godbach   发布时间: 2010-06-21



QUOTE:
回复  ShadowStar
难到没有人写过这个模块吗?我觉得这个应用挺广泛啊,应该早就有人写了这个模块才对啊? ...
瀚海书香 发表于 2010-06-21 17:41



早就写完了。不过由于公司制度不能open出来。



QUOTE:
再说即使有人写了,一定会open出来吗。
Godbach 发表于 2010-06-21 18:02



网御也应该有这个功能吧?

作者: ShadowStar   发布时间: 2010-06-22

回复 ShadowStar
可以交流一下啊。
我研究了一下可以通过如下修改实现:
基于2.6.24.4内核


net/ipv4/route.c
< #include <linux/netfilter_ipv4.h>
< #include <net/netfilter/nf_conntrack.h>
< #include <net/netfilter/nf_conntrack_helper.h>
< #include <net/netfilter/nf_conntrack_l4proto.h>
< #include <net/netfilter/nf_conntrack_l3proto.h>
< #include <net/netfilter/nf_conntrack_core.h>
< #include <net/netfilter/ipv4/nf_conntrack_ipv4.h>
75d66
1724,1727d1714
<         /*if (res->fi && res->fi->fib_nhs > 1 && fl->oif == 0)
<                 fib_select_multipath(fl, res);*/
<         // change upper two lines
1729,1740c1716
<         {
<                 struct nf_conn *ct;
<                 enum ip_conntrack_info  ctinfo;
<                 ct=nf_ct_get(skb, &ctinfo);
<                 if(!ct||ctinfo==IP_CT_NEW||ctinfo==IP_CT_RELATED)
<                         fib_select_multipath(fl, res);
<                 else{
<                         if(fib_select_multipath_ct(fl,res,ct)==0)
<                                 fib_select_multipath(fl,res);
<                 }
<         }
---
>                 fib_select_multipath(fl, res);


net/ipv4/fib_semantics.c
< //return 0 means doesn't match
< int fib_select_multipath_ct(const struct flowi *flp, struct fib_result *res,struct nf_conn *ct)
< {
<         struct fib_info *fi = res->fi;
<         struct in_device *in_dev;
<         struct in_ifaddr *ifa;
<         spin_lock_bh(&fib_multipath_lock);
<         change_nexthops(fi) {
<                 in_dev=in_dev_get(nh->nh_dev);
<                 if(!in_dev)
<                         continue;
<                 rcu_read_lock();
<                 if (!(nh->nh_flags&RTNH_F_DEAD) &&in_dev->ifa_list){
<                         /*&&(in_dev->ifa_list==ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip||nh->nh_gw==ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip) {*/
<                         for(ifa=in_dev->ifa_list;ifa;ifa=ifa->ifa_next){
<                                 if(ifa->ifa_local==ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip&&inet_ifa_match(nh->nh_gw,ifa))
<                                         break;
<                         }
<                         rcu_read_unlock();
<                         in_dev_put(in_dev);
<                         if(ifa){
<                                 printk(KERN_INFO "Match %u.%u.%u.%u\n",NIPQUAD(ifa->ifa_local));
<                                 res->nh_sel=nhsel;
<                                 spin_unlock_bh(&fib_multipath_lock);
<                                 return 1;
<                         }
<                 }
<                 rcu_read_unlock();
<                 in_dev_put(in_dev);
<         } endfor_nexthops(fi);
<         spin_unlock_bh(&fib_multipath_lock);
<         return 0;
< }
---
>

include/net/ip_fib.h
< #include <linux/netfilter_ipv4.h>
< #include <net/netfilter/nf_conntrack.h>
< #include <net/netfilter/nf_conntrack_helper.h>
< #include <net/netfilter/nf_conntrack_l4proto.h>
< #include <net/netfilter/nf_conntrack_l3proto.h>
< #include <net/netfilter/nf_conntrack_core.h>
< #include <net/netfilter/ipv4/nf_conntrack_ipv4.h>
<
< extern int fib_select_multipath_ct(const struct flowi *flp, struct fib_result *res,struct nf_conn *ct);

作者: 瀚海书香   发布时间: 2010-06-24

实验结果怎么样?

作者: platinum   发布时间: 2010-06-24



QUOTE:
回复  ShadowStar
可以交流一下啊。
我研究了一下可以通过如下修改实现:
基于2.6.24.4内核


net/i ...
瀚海书香 发表于 2010-06-24 14:53



你做的太复杂了,完全没必要。

作者: ShadowStar   发布时间: 2010-06-24

回复 platinum
理论上应该是可以的,还没有具体测试呢。嘿嘿

作者: 瀚海书香   发布时间: 2010-06-24

回复 ShadowStar
愿闻其详。

作者: 瀚海书香   发布时间: 2010-06-24

我看不懂你的实现,但是我觉得好像有问题
正确的做法应该是直接获取 ip、port、protocol,通过 hash 函数计算 key,直接在 conntrack 里查表看是否存在

作者: platinum   发布时间: 2010-06-24

回复 platinum
因为有skb,所以ct=nf_ct_get(skb,&ctinfo)获取。而ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip则应该是应答包的目的ip地址,那么你的下一跳就应该是从这个ip地址所属的接口出去的。
例如:
内口:192.168.0.1
外口1:172.16.0.2
外口2:172.16.1.2
route default via nexthop 172.16.0.1
                           nexthop 172.16.1.1

如果有一个连接 192.168.0.8---x.x.x.x,使用路由172.16.0.1,那么应答连接应该是 x.x.x.x---172.16.0.2;所以我们如果知道应答连接的目的ip是172.16.0.2的话,那么它的原连接的路由就应该是172.16.0.1。也就是应答的目的ip跟路由应该是同一个网段的。

作者: 瀚海书香   发布时间: 2010-06-24

1、为什么一定是 REPLY 包的 IP
2、如果本身也做了 NAT,那么可能还比较麻烦,因为 MASQUERADE 和 SNAT 是在 POSTROUTING 里做的

作者: platinum   发布时间: 2010-06-24

本帖最后由 瀚海书香 于 2010-06-24 17:06 编辑

回复 platinum
就是因为做SNAT所以才用在reply的dip。因为如果做snat的话,reply的dip就应该是snat后的ip。
虽然snat是在postrouting,但是我们只对已建立的链接确保路由flush后再次查询的路由跟上次的不变,所以说我们处理的链接已经是做了snat的了。因为snat只对新建的链接和related的链接起作用。

作者: 瀚海书香   发布时间: 2010-06-24

我不太了解路由的选路方式,如果数据是从外面主动向内请求的呢?是不是也依然如此?
另外,如果是 FTP 的 DATA 信道,在开启 nf_nat_ftp 模块的情况下,状态不是 ESTABLISHED,而是 RELATED,这个你考虑了没有?

作者: platinum   发布时间: 2010-06-24

回复 platinum
如果从外向内请求的情况,因为向内的路由的单路的,所以应该不会做多路路由这一部分的,而对应的向外的时候应该是查询local表,不会走multipath,所以应该没问题。
本人接触的网络拓扑比较少,可能有理解不到的地方。

作者: 瀚海书香   发布时间: 2010-06-24

回复 ShadowStar

ShadowStar兄,不要在那偷着笑了
给点别的思路吧

作者: 瀚海书香   发布时间: 2010-06-24



QUOTE:
回复  platinum
就是因为做SNAT所以才用在reply的dip。因为如果做snat的话,reply的dip就应该是snat后的ip。
瀚海书香 发表于 2010-06-24 17:04




如果是从外网回来的包,是 REPLY,在 PREROUTING 阶段目的 IP 被还原,
在 ROUTING 阶段看到的应该是内网 IP,也就是 SNAT 之前的,如果是这样,那么和你说的恰好相反啊?

作者: platinum   发布时间: 2010-06-24

回复 platinum
如果是从外网回来的包,是 REPLY,在 PREROUTING 阶段目的 IP的确 被还原,但是在nf_conn的tuplehasn中记录的是没有被还原的ip。

作者: 瀚海书香   发布时间: 2010-06-25

回复 瀚海书香


我的理解是这样的你看对不对
1、如果是从内网出去的请求,REPLY 状态包是从外网回来的
2、如果是从外网主动进来的请求,那么无论是 ORIGIN 还是 REPLY 都不走 route.c 中的 ROUTE_MULTIPATH 流程,因此不用考虑如何处理

如果是这样的,那么我有一个疑问
从内网出去的包的回包,根据 NAT 要做 DNAT 转换,而这个转换是在 PREROUTING 阶段完成的,那么为什么在 ROUTE 阶段看到的 DIP 却是转换前的呢?

作者: platinum   发布时间: 2010-06-25

热门下载

更多