概述
源进源出,也叫记录上一跳,即请求报文从某条路径进入,回应报文依然沿着同样的路径返回,而不是通过查找路由表来确定接口,保证了报文从一个接口进出。
在我们正常的网络环境下,如果要保证业务正常,就是对整网的路由进行规划,确保回应报文能够回到请求发送者,这也是源进源出的效果。但是有两个场景通过路由规划来实现源进源出不现实:Linux作为互联网接入设备、应用负载均衡记录源。下面以互联网接入设备为场景介绍Linux的源进源出,应用负载均衡记录源将在后面的文章中介绍。
URPF
在介绍场景之前,先介绍一下URPF。
URPF(Unicast Reverse Path Forwarding)单播逆向路径转发,网络设备收到一个报文时首先获取包的源地址和入接口,然后以源地址为目的地址,在路由转发表中查找相对应的转发接口是否与入接口匹配,如果不匹配就认为这个源IP是伪装的,并将该报文丢失。
URPF检查分严格型和松散型,严格型要求报文的入接口与报文源IP路由的出接口一致,松散型仅要求路由器的路由表中存在报文源IP的路由即可。
严格型URPF
如下图,Router上有四个接口,且仅有四条路由表,当Eth0收到报文时,首先以源IP为目的地址查找路由,第一个报文的源IP和入接口与路由表匹配,接收进入一步转发;第二个和第三个报文的源IP和入接口与路由表不匹配,将报文丢弃:
松散型URPF
如下图,Router上有四个接口,且仅有四条路由表,当Eth0收到报文时,首先以源IP为目的地址查找路由,第一个报文的源IP和入接口与路由表匹配,接收进入一步转发;第二个报文的源IP和入接口与路由表不匹配,但是路由表存在到源IP的路由,所以接收进入一步转发;第三个报文的源IP不存在于路由表中,将报文行弃:
互联网接入设备
如下图,Linux作为互联网接入设备,并连接了两个联通和电信两个运营商,每个链路都配置了公网IP,并在Linux上配置DNAT,使用户可以从互联网上访问内网的服务器。
当一个用户通过互联网访问内网服务器时,从外到内的请求报文肯定可以到达内网服务器,但是服务器的回应报文往外转发到Linux时,将通过哪条链路转发出去,就不太好判断。可以在Linux上面导入到联通和电信的路由,通过查找路由来确定回包链路。但是对于移动,以及其它运营商的路由就有问题。首先,虽然运营商的地址大体上确定,但是也会有小范围的变化,在Linux上无法做到实时更新路由;其次,对于移动或者其它运营商的用户访问内网,从哪条链路进入Linux不能确定,这要根据各运营商之间路由通行来确定。如果一个用户的请求报文通过联通链路进入,但是回报文通过电信转发出去,就可能导致非常慢,甚至丢包的情况。
针对这个场景,就要通过源进源出来实现,针对每一个请求报文,Linux记录好报文从哪个接口进行,对应的回应报文就从哪个接口转发出去。
源进源出实现
回顾前斯的策略路由实验,每个报文在查找路由表的时候,都可以通过匹配不同的条件来确定使用哪个路由表,以现实指定从哪个接口转发出去的目的,等于针对每一个回应报文,都要判断他对应的请求报文是从哪个接口进来的。回顾IP报文的格式,在报文中并没有哪个字段携带对应请求报文的信息,更不会记录是从哪个接口接收的,所以想通过查找报文中的信息以实现指定出接口无法实现。
Linux在转发报文的时候,除了查路由表,查每个链每个表的策略,也会对请求和回应报文构成的会话进行记录,如下图所示,Linux记录了请求和回应两个方向报文的五元组:
针对有DNAT和SNAT的场景,对比请求和回应报文的五元组,就可以确定是否做了NAT。
在Linux系统中,在转发第一个请求报文时,就是会产生一条会话,后续的请求和回应报文都会匹配之前产生的会话。同时在会话中有一个mark标记,叫做ct mark,这个属性只记录在会话表中,而meta mark是在报文转发过程中携带,通过ct mark和meta mark的相互转换,实现源进源出的需求。
结合Linux的转发流程图来分析源进源出的实现方式:
请求报文
当请求报文进入prerouting链时,先匹配从哪个接口进入,为不同的接口添加不同的ct mark:
针对请求方向的报文设置完ct mark后,将进入其它流程(如dnat, routing等),这些流程正常处理,请求报文最终会转发到内网服务器。通过设置ct mark后,系统内的会话表就是记录mark值:
如上图所示,会话表中的mak被设置成了0x1000(十进制为4096)和0x1001,ct mark的设置,不会影响任何转发机制,只是一个标记。
回应报文
针对回应报文进入prerouting链时,要根据会话中的ct mark设置meta mark。默认情况下,全部会话的ct mark都为0,所以只要处理非0的ct mark即可:
报文出prerouting链后进入Routing模块,Routing模块首先查找ip rule规则,根据不同的meta mark使用不同的路由表,以实现从不同的接口往外转发:
修改rt_tables 文件,在里面添加路由表table_eth0和table_eth1,每个路由表要有一个唯一的ID,修改之后的文件如下:
报文在转发过程中通过ip rule规则进入路由表查找路由,所以我们需要在对应路由表中添加对应的路由:
关闭URPF
Linux默认是开启URPF,因为在连接运营商的链路上并没有配置路由,即可以导致从运营商链路进来的流量会全部被丢弃,所以要关闭接口的urpf检查,关闭脚本如下:
至此,Linux作为互联网接入设备,实现源进源出的功能已全部现实。
总结
Linux的源进源出,主要有几个动作:
1。在请求报文的入接口针对会话设置ct mark;
2。在回应报文的入接口根据会话的ct mark设备报文的meta mark;
3。根据报文的meta mark指定不同的路由表;
4。由对应路由表中的缺省路由指定出接口为请求报文的入接口。