目录
- 1. 缘起
 - 2. 源码分析
 - 3. 让ICMP也走源进源出
 
1. 缘起
  在网络通信中,当一个请求报文从源主机到达目标主机,并经过中间路由器或交换机进行转发时,请求报文进入主机A的路径和响应报文离开主机A的路径可能不同。这种情况下,就会出现所谓的三角路径问题。如下图:
 
具体来说,当一个请求报文进入主机A的路径与响应报文离开主机A的路径不同,可能导致以下问题:
-  
网络性能不稳定:如果进入主机A的路径与离开主机A的路径存在较大差异,可能会导致网络性能不稳定。例如,进入主机A的路径可能经过高负载的网络链路,而离开主机A的路径可能经过较空闲的链路。这样的差异可能导致请求和响应之间的延迟不一致,影响网络性能和用户体验。
 -  
安全性问题:在某些情况下,网络中的安全策略可能只应用于特定的路径。如果请求报文进入主机A的路径与响应报文离开主机A的路径不同,可能导致安全策略无法完全覆盖所有的路径,从而增加了网络的安全风险。
 -  
问题排查困难:当出现网络故障或问题时,如果请求报文进入主机A的路径与响应报文离开主机A的路径不同,可能增加了故障排查的难度。因为网络管理员需要同时考虑请求和响应的不同路径,以确定问题的根本原因。
 
为了解决这个问题,可以采取以下措施:
-  
路由优化:网络管理员可以优化网络的路由配置,使得请求报文进入主机A的路径和响应报文离开主机A的路径尽可能一致。这可以通过路由策略调整、链路负载均衡等方式实现。
 -  
建立对称路径:尽可能建立对称路径,即让请求报文和响应报文经过相同的网络路径。这可以通过协议配置、网络设备设置等手段实现。
 -  
网络监控和故障排查:网络管理员应该使用适当的网络监控工具来监测网络性能和路径信息。当出现问题时,可以通过监控数据和故障排查工具来分析请求和响应的路径差异,并找出问题所在。
 
  最近,我们在用dpvs的fnat模式的部署测试中,就碰到了三角路径的问题。其原因是由于dpvs设置的默认路由和进来的路由不一致导致的,当时抓包发现tcp和udp报文走的路径和来源路径是一致的,就是所谓的源进源出,而ping报文则不是,dpvs上的出向流量走的是dpvs上面配置的默认路由。我们希望ping报文能够像tcp/udp一样也能够走源进源出的路径。
   下面就以fnat模式来进行分析。
2. 源码分析
于是对tcp/udp的出向报文发送逻辑进行了分析。经过分析,tcp/udp出向报文是通过xmit_outbound转发到客户端的,由__dp_vs_in函数调用,源码片段如下:
    /* holding the conn, need a "put" later. */
    if (dir == DPVS_CONN_DIR_INBOUND)
        return xmit_inbound(mbuf, prot, conn);
    else
        return xmit_outbound(mbuf, prot, conn);
 
如果dir != DPVS_CONN_DIR_INBOUND就表示当前需要转发的数据包是出向流量,于是调用xmit_outbound函数进行数据包的转发。
再看xmit_outbound的逻辑:
/* return verdict INET_XXX */
static int xmit_outbound(struct rte_mbuf *mbuf,
                         struct dp_vs_proto *prot,
                         struct dp_vs_conn *conn)
{
   
    int err;
    assert(mbuf && prot && conn);
    if (dp_vs_stats_out(conn, mbuf)) {
   
        dp_vs_conn_put(conn);
        return INET_DROP;
    }
    if (!conn->packet_out_xmit) {
   
        RTE_LOG(WARNING, IPVS, "%s: missing out_xmit\n", __func__);
        dp_vs_conn_put(conn);
        return INET_ACCEPT;
    }
    err = conn->packet_out_xmit(prot, conn, mbuf);
    if (err != EDPVS_OK)
        RTE_LOG(DEBUG, IPVS, "%s: fail to out xmit: %d\n", __func__, err);
    dp_vs_conn_put(conn);
    /* always stolen the packet */
    return INET_STOLEN;
}
 
这里关键的就是调用了conn->packet_out_xmit进行数据包的转发。而packet_out_xmit函数指针在fnat模式下面对应的是dp_vs_out_xmit_fnat函数,对于ipv4,dp_vs_out_xmit_fnat函数最终调用的是__dp_vs_out_xmit_fnat4来进行数据包的转发。所以我们来看看__dp_vs_out_xmit_fnat4的实现,源码如下:
static int __dp_vs_out_xmit_fnat4(struct dp_vs_proto *proto,
                                  struct dp_vs_conn *conn,
                                  struct rte_mbuf *mbuf)
{
   
    struct flow4 fl4;
    struct ipv4_hdr *iph = ip4_hdr(mbuf);
    struct route_entry *rt;
    int err, mtu;
    if (!fast_xmit_close && !(conn->flags & DPVS_CONN_F_NOFASTXMIT)) {
   
        /* 通过来源端口转发 */
		dp_vs_save_outxmit_info(mbuf, proto, conn);
        if (!dp_vs_fast_outxmit_fnat(AF_INET, proto, conn, mbuf)) {
   
            return EDPVS_OK;
        }
    }
	/* 通过路由转发 */
    /*
     * drop old route. just for safe, because
     * FNAT is PRE_ROUTING, should not have route.
     */
    if (unlikely(mbuf->userdata != NULL))
        route4_put((struct route_entry *)mbuf-&g
                

















