Linux 服务器 存在多网卡多IP UDP 监听 INADDR ANY(0.0.0.0)出现收包源IP不一致问题解决方案!



在多IP服务器上,通过策略路由如何保证 回复给客户端的数据不从从原来的网卡出去导致访问不通的问题?


比如在一台双线上,电信 IP 是182.x.x.119 ,网通 IP 是 119.x.x.107 

可以通过以下策略路由保证:

ip route flush table ct
ip route add default via 182.x.x.97 dev eth0 src 182.x.x.119 table ct
ip rule add from 182.x.x.119 table ct
ip route flush table cu
ip route add default via 119.x.x.97 dev eth1 src 119.x.x.107 table cu
ip rule add from 119.x.x.107 table cu

对这段策略路由简单解释下。

ip rule add from 182.x.x.119 table ct 表示源地址是 182.x.x.119 的包,通过ct路由表选路,

ip route add default via 182.x.x.97 dev eth0 src 182.x.x.119 table ct 表示ct路由表的默认路由是从 eth0 的 182.x.x.97 这个网关路由,源地址设置为 182.x.x.119 。

有了这层保证后,我们在实现Server 的时候,监听 socket 依然 bind 到 0.0.0.0 ,在回 数据 包的时候,只要设置该数据包从本机发出去的 源地址 配合策略路由,就可以保证 数据包 正确返回。

现在的关键问题是: 如何获取 数据 包的目的地址? 

对于无连接状态的 UDP socket 获取目的地址就很难了,木有现成的系统调用,获取方法还是有点点麻烦的。通过 man ip 可以获取详细的步骤和原理:

IP_PKTINFO (since Linux 2.2)
    Pass an IP_PKTINFO ancillary message that contains a pktinfo 
    structure that supplies some information about the incoming packet. 
    This only works for datagram oriented sockets. 
    The argument is a flag that tells the socket 
    whether the IP_PKTINFO message should be passed or not. 
    The message itself can only be sent/retrieved as control message 
    with a packet using recvmsg(2) or sendmsg(2).
    
    struct in_pktinfo {
        unsigned int   ipi_ifindex;  /* Interface index */
        struct in_addr ipi_spec_dst; /* Local address */
        struct in_addr ipi_addr;     /* Header Destination
                                        address */
    };
    
    ipi_ifindex is the unique index of the interface the packet was received on. 
    ipi_spec_dst is the local address of the packet and 
    ipi_addr is the destination address in the packet header. 
    If IP_PKTINFO is passed to sendmsg(2) and ipi_spec_dst is not zero, 
    then it is used as the local source address for the routing table lookup 
    and for setting up IP source route options. When ipi_ifindex is not zero, 
    the primary local address of the interface specified 
    by the index overwrites ipi_spec_dst for the routing table lookup.

这里的说明说的相当清楚

先决条件就是要 setsockopt 设置 IP_PKTINFO ,通过系统调用 recvmsg 便可获取 in_pktinfo 结构数据,其中 ipi_spec_dst 便是我们想要的目的地址,

然后 sendmsg 的时候传入 ipi_spec_dst ,这样就会使用这个地址来做策略路由。

有了这个做指导,编码起来就很简单的啦,有 C版本 的实现,这里的 python 版本 通熟易懂些。

上面的方法固然好,但是很多语言目前稳定版本,socket 都不支持 sendmsg,recvmsg,更不要说 setsockopt 设置 IP_PKTINFO 了。在 java 中没有,在 python 2.7 版本也没有,只有 python 3.3 版本支持。所以这种方法还不具有普适性,但是如果用的是 C 或者 Go 语言,实现起来倒是很方便的。


原文来自:再论UDP SERVER绑定IP到INADDR ANY - On the road (qiusuo.im)

Comments

Popular posts from this blog

IOS小火箭/Shadowsocks无需AppleID即可在线安装!

苹果手机/IOS/IPAD如何离线安装小火箭Shadowrocket.IPA文件?

利用Cloudflare等免费CDN翻墙的科学上网方法 — V2Ray Cloudflare