golang – iptables REDIRECT获取原始目标地址

之前我利用redsocks项目,在网关上实现了透明SOCKS5代理。

redsocks要求在网关上利用iptables将TCP流量改写给redsocks端口,这样redsocks收到流量后就会按照SOCKS5协议向代理服务器转发数据。

实现redsocks的唯一难点就是redsocks如何知道原始的TCP目标地址,因为经过iptables做REDIRECT改写后,数据包的目标地址已经被修改为redsocks自身的监听地址了。

既然redsocks能做到,我们也一定有办法做到,答案就是linux的getsockopt系统调用,做过linux网络编程的同学一定都非常熟悉了。getsockopt支持一个选项,可以获取到socket改写前的原始目标地址,包括ip和port。

我用Golang实现了一个demo:https://github.com/owenliang/go-orig-dst/blob/master/main.go,下面我简单讲解一下。

体验demo

这个demo类似于redsocks,只不过它收到TCP连接后只是打印了一下原始目标IP地址,然后就关闭了连接。

首先运行程序,它监听在8080端口:

然后telnet直连它,将打印出目标IP地址就是demo的监听地址:

连接被立即断开,这符合demo程序的逻辑。

在demo服务端,打印出了目标地址:

接下来,我们要测试利用iptables做REDIRECT拦截TCP流量到Demo服务端,再看一下能否获取到原始的目标地址。

因此,先后添加如下2条iptables规则:

  • 从本机发往本机8080端口的TCP流量直接放行,也就是telnet localhost 8080将直连到Demo。
  • 而从本机发往本机非8080端口的流量,将做REDIRECT改写给8080。

因此,Demo能收到2种流量:

  • telnet localhost 8080的直连流量。
  • telnet localhost 7000这样的流量,将被改写给localhost 8080。

这时候,我们尝试尝试telnet localhost 7000:

会发现Demo打印了如下内容:

也就是说,Demo成功获取了REDIRECT之前的原始目标地址,而不是127.0.0.1:8080这个改写后的地址。

实现Demo

首先就是监听TCP连接,一旦得到TCP连接就获取socket文件描述符(fd),因为后续要调用getsockopt获取fd的原始目标地址:

你必须付费加入我的知识星球,为有效知识付费是对作者最好的回报。

二维码见下方 或者 右侧边栏。