Unix Like, 操作系统, 网络

使用策略路由实现涉及getsockopt(…, SO_ORIGINAL_DST, …)程序的远程调用(Redsocks, ss-redir之类的远程调用)

Redsocks/ss-redir的实现

DNAT与REDIRECT模块在iptables target extension之列。

这两个模块都能根据参数,修改数据包的header中的目的IP和端口。

既然修改,那就不是附加,毕竟网络层传输层header的内容怎么可能随随便便增删呢,这意味着,数据包的真实目的地会被完完全全地抹掉

Redsocks/ss-redir收到这样的数据包后,不做特殊处理,何以知道这数据包原来想发给谁呢?

这些程序之所以能正常工作,得益于NAT的透明性,被NAT处理的数据包,在内核中均有所记录,NAT后的正常通讯由NAT发起者维持,Redsocks/ss-redir这类程序便是通过内核中的记录得知真实目的地的。

这个原地址的获取可以通过系统调用实现:

optname需要传值SO_ORIGINAL_DST。

Redsocks的调用见base.c的223行

问题所在

假定有以下的网络架构:

正常情况下,NAT与getsockopt(…, SO_ORIGINAL_DST, …)调用程序都运行在Router上,工作正常。

现在把getsockopt(…, SO_ORIGINAL_DST, …)调用程序迁移到Server 1上,很容易想到的,就是把原来的NAT规则改成DNAT的,并指定目的地IP与端口:

NAT的记录由内核管理,这隐含的意思是,只有在发起NAT处理的内核上才能获取到NAT前的信息。

若调用远程的getsockopt(…, SO_ORIGINAL_DST, …)调用程序,即发起NAT的与运行getsockopt(…, SO_ORIGINAL_DST, …)调用程序的不是同一个内核,很明显,这些程序对getsockopt()的调用不可能实现他们的目的。

问题已很明显,这类程序无法通过getsockopt()调用取得真实目的地。

实现getsockopt()的远程调用成本有点高,不太现实。

不过,我们把方式稍作改变,让“远程”变为“本地”。

实现

假定有以下的网络架构:

Server 1上运行着getsockopt(…, SO_ORIGINAL_DST, …)调用程序。

既然处理NAT要与调用getsockopt()的使用同一个内核,那么把需要NAT的那部分也交给Server 1处理好了。

要实现这种需求,可以利用策略路由,正常的数据包按正常处理,原本需要NAT到Server 1的数据包,使用路由规则转发到Server 1。这样就不需要网内其余联网设备作任何改变,只需要对Router与Server 1进行操作。

准备

Router 1上首先把针对getsockopt(…, SO_ORIGINAL_DST, …)调用程序的NAT规则删掉。

路由表

修改/etc/iproute2/rt_tables增加一个路由表,如12行,增加一个ID为250,名为server1的路由表:

往server1路由表中增加一条默认路由规则,把所有数据包都转发到Server 1:

数据包分类

接下来需要把数据包分类,即决定哪些数据包应使用路由表server1。

我这里选择iptables的MARK模块,把所有需要给getsockopt(…, SO_ORIGINAL_DST, …)调用程序处理的数据包,都加一个固定标记,然后让路由让有该特定标记的数据包都根据路由表server1处理。

iptables MARK模块的–set-mark操作仅能在mangle表完成。

假设MARK为150(把…改成你需要的参数):

这里需要注意,Server 1访问国际互联网,也要通过Router,如果getsockopt(…, SO_ORIGINAL_DST, …)调用程序访问互联网会匹配到上面的规则,必须排除,否则就有环路了,最简单的,就是直接排除掉Server 1的MAC地址:

这条排除Server 1的规则需要插入在MARK之前。

最后修改路由策略,让带150 MARK的数据包都使用路由表server1:

Server 1的处理

数据包已在Router处分类,并不经网络层和传输层Header的修改转发到Server 1处。

Server 1要区分这些数据包是要交给getsockopt(…, SO_ORIGINAL_DST, …)调用程序,还是交给自己的程序处理,很简单,看看数据包目的IP是不是自己就行了,要交给getsockopt(…, SO_ORIGINAL_DST, …)调用程序的数据包,目的IP肯定不是Server 1的IP:

总结

这个方法仅适用于Router与Server 1处于同一局域网的情况,因为路由转发仅能在链路层上进行,若需要跨网域调用,可能要考虑使用隧道封装,但这样开销有点大。

Router一般都在芯片上实现了NAT,NAT效率高,Server 1虽然没芯片级的NAT,但我相信,你处理被NAT的数据包的程序,开销比NAT大得多。