semigodking / redsocks

transparent redirector of any TCP/UDP connection to proxy
Apache License 2.0
1.17k stars 247 forks source link

使用TPROXY,udp穿透失败 #195

Closed zglqaq closed 10 months ago

zglqaq commented 10 months ago

1705915385.219948 warning redudp.c:298 redudp_fwd_pkt_to_sender(...) [172.16.168.53:59739->114.114.114.114:53]: bound_udp_get failure 1705915387.149023 err redudp.c:151 bound_udp_get(...) bind: Address already in use 1705915387.149063 warning redudp.c:298 redudp_fwd_pkt_to_sender(...) [172.16.168.53:59740->114.114.114.114:53]: bound_udp_get failure 1705915389.161856 err redudp.c:151 bound_udp_get(...) bind: Address already in use 1705915389.161887 warning redudp.c:298 redudp_fwd_pkt_to_sender(...) [172.16.168.53:59741->114.114.114.114:53]: bound_udp_get failure 1705915391.165874 err redudp.c:151 bound_udp_get(...) bind: Address already in use 1705915391.165900 warning redudp.c:298 redudp_fwd_pkt_to_sender(...) [172.16.168.53:59742->114.114.114.114:53]: bound_udp_get failure 1705915393.181151 err redudp.c:151 bound_udp_get(...) bind: Address already in use 1705915393.181186 warning redudp.c:298 redudp_fwd_pkt_to_sender(...) [172.16.168.53:59743->114.114.114.114:53]: bound_udp_get failure

zglqaq commented 10 months ago

redudp { bind = "0.0.0.0:10053"; relay = "x.x.x.x:1080"; //login = username;// field 'login' is reused as encryption // method of shadowsocks //password = pazzw0rd;

// know types: socks5, shadowsocks
type = socks5;

// redsocks knows about two options while redirecting UDP packets at
// linux: TPROXY and REDIRECT.  TPROXY requires more complex routing
// configuration and fresh kernel (>= 2.6.37 according to squid
// developers[1]) but has hack-free way to get original destination
// address, REDIRECT is easier to configure, but requires `dest`
// to be set, limiting packet redirection to single destination.
// [1] http://wiki.squid-cache.org/Features/Tproxy4
//dest = "8.8.8.8:53";

udp_timeout = 30;
// udp_timeout_stream = 180;

}

zglqaq commented 10 months ago

我修改了redudp.c打印日志的代码 if (0 != bind(node->fd, (struct sockaddr*)addr, sizeof(*addr))) { const struct sockaddr_in *v4 = (const struct sockaddr_in*)addr; char str[1000]; char* ipAddressStr = inet_ntoa(v4->sin_addr); sprintf(str, "bind %s:%d", ipAddressStr,v4->sin_port); log_errno(LOG_ERR, str); goto fail; } 重新编译后的报错内容 1705994981.498481 err redudp.c:155 bound_udp_get(...) bind 192.168.11.53:37395: Address already in use 1705994981.498513 warning redudp.c:302 redudp_fwd_pkt_to_sender(...) [172.16.168.53:4780->192.168.11.53:5010]: bound_udp_get failure 发现redsocks把目标端口号5010=0x1392读取成了37395=0x9213,应该是大小端问题。 然后我使用抓包工具分析socks5代理服务器的回复数据包没有问题

f41e0a9687bc8c8f00a1296718b132c

我是用java实现的socks5服务器,下面是udp穿透中代理服务器给客户端回复部分的代码 `//从真实服务器发来的数据,封装后转发给客户端 ByteBuf originData = msg.content();

                                SocketAddress remoteServerAddress = msg.sender();
                                int remoteServerPort = msg.sender().getPort();

                                ByteBuf respBuff = Unpooled.buffer(originData.capacity() + 4 + ((InetSocketAddress) remoteServerAddress).getAddress().getAddress().length + 2);

// resp.put((byte)0x00); //保留 // resp.put((byte)0x00); //保留 // resp.put((byte)0x00); //分片 byte type = ((InetSocketAddress) remoteServerAddress).getAddress().getAddress().length == 4 ? TYPE_IPV4 : TYPE_IPV6; byte[] header = new byte[]{0x00, 0x00, 0x00, type}; respBuff.writeBytes(header); respBuff.writeBytes(((InetSocketAddress) remoteServerAddress).getAddress().getAddress()); //远程地址 0x08 0x08 0x08 0x08 Short remotePort = (short) (remoteServerPort & 0xFFFF); respBuff.writeShort(remotePort); // 远程地址端口 0x00 0x35 //真实数据 respBuff.writeBytes(originData);

                                ctx.channel().writeAndFlush(new DatagramPacket(respBuff,
                                        new InetSocketAddress(clientAddress, clientPort)));`

另外我发现同样的redsocks代码再ubuntu上可以正常运行,在centos7上会出现这样的问题

semigodking commented 10 months ago

感谢反馈和调试。最近比较忙,没时间修复。如果你能查出原因并修复最好。提供线索我抽时间看看。

On Tue, Jan 23, 2024, 16:18 zglqaq @.***> wrote:

我修改了redudp.c打印日志的代码 if (0 != bind(node->fd, (struct sockaddr)addr, sizeof(addr))) { const struct sockaddr_in v4 = (const struct sockaddr_in)addr; char str[1000]; char* ipAddressStr = inet_ntoa(v4->sin_addr); sprintf(str, "bind %s:%d", ipAddressStr,v4->sin_port); log_errno(LOG_ERR, str); goto fail; } 重新编译后的报错内容 1705994981.498481 err redudp.c:155 bound_udp_get(...) bind 192.168.11.53:37395: Address already in use 1705994981.498513 warning redudp.c:302 redudp_fwd_pkt_to_sender(...) [172.16.168.53:4780->192.168.11.53:5010]: bound_udp_get failure 发现redsocks把目标端口号5010=0x1392读取成了37395=0x9213,应该是大小端问题。 然后我使用抓包工具分析socks5代理服务器的回复数据包没有问题 f41e0a9687bc8c8f00a1296718b132c.png (view on web) https://github.com/semigodking/redsocks/assets/35891490/a7fa9a0c-193a-4817-ba84-52db26ccb730

我是用java实现的socks5服务器,下面是udp穿透中代理服务器给客户端回复部分的代码 `//从真实服务器发来的数据,封装后转发给客户端 ByteBuf originData = msg.content();

                            SocketAddress remoteServerAddress = msg.sender();
                            int remoteServerPort = msg.sender().getPort();

                            ByteBuf respBuff = Unpooled.buffer(originData.capacity() + 4 + ((InetSocketAddress) remoteServerAddress).getAddress().getAddress().length + 2);

// resp.put((byte)0x00); //保留 // resp.put((byte)0x00); //保留 // resp.put((byte)0x00); //分片 byte type = ((InetSocketAddress) remoteServerAddress).getAddress().getAddress().length == 4 ? TYPE_IPV4 : TYPE_IPV6; byte[] header = new byte[]{0x00, 0x00, 0x00, type}; respBuff.writeBytes(header); respBuff.writeBytes(((InetSocketAddress) remoteServerAddress).getAddress().getAddress()); //远程地址 0x08 0x08 0x08 0x08 Short remotePort = (short) (remoteServerPort & 0xFFFF); respBuff.writeShort(remotePort); // 远程地址端口 0x00 0x35 //真实数据 respBuff.writeBytes(originData);

                            ctx.channel().writeAndFlush(new DatagramPacket(respBuff,
                                    new InetSocketAddress(clientAddress, clientPort)));`

另外我发现同样的redsocks代码再ubuntu上可以正常运行,在centos7上会出现这样的问题

— Reply to this email directly, view it on GitHub https://github.com/semigodking/redsocks/issues/195#issuecomment-1905514642, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAP2XD3PZRVW7DHSBXLRM6LYP5W5ZAVCNFSM6AAAAABCE4F5LCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSMBVGUYTINRUGI . You are receiving this because you are subscribed to this thread.Message ID: @.***>

semigodking commented 10 months ago

刚才快速偷空看了几眼。问题可能出在socks5-udp.c的208和215行。这里没做ntoh转换!

On Tue, Jan 23, 2024, 16:30 Semigod King @.***> wrote:

感谢反馈和调试。最近比较忙,没时间修复。如果你能查出原因并修复最好。提供线索我抽时间看看。

On Tue, Jan 23, 2024, 16:18 zglqaq @.***> wrote:

我修改了redudp.c打印日志的代码 if (0 != bind(node->fd, (struct sockaddr)addr, sizeof(addr))) { const struct sockaddr_in v4 = (const struct sockaddr_in)addr; char str[1000]; char* ipAddressStr = inet_ntoa(v4->sin_addr); sprintf(str, "bind %s:%d", ipAddressStr,v4->sin_port); log_errno(LOG_ERR, str); goto fail; } 重新编译后的报错内容 1705994981.498481 err redudp.c:155 bound_udp_get(...) bind 192.168.11.53:37395: Address already in use 1705994981.498513 warning redudp.c:302 redudp_fwd_pkt_to_sender(...) [172.16.168.53:4780->192.168.11.53:5010]: bound_udp_get failure 发现redsocks把目标端口号5010=0x1392读取成了37395=0x9213,应该是大小端问题。 然后我使用抓包工具分析socks5代理服务器的回复数据包没有问题 f41e0a9687bc8c8f00a1296718b132c.png (view on web) https://github.com/semigodking/redsocks/assets/35891490/a7fa9a0c-193a-4817-ba84-52db26ccb730

我是用java实现的socks5服务器,下面是udp穿透中代理服务器给客户端回复部分的代码 `//从真实服务器发来的数据,封装后转发给客户端 ByteBuf originData = msg.content();

                            SocketAddress remoteServerAddress = msg.sender();
                            int remoteServerPort = msg.sender().getPort();

                            ByteBuf respBuff = Unpooled.buffer(originData.capacity() + 4 + ((InetSocketAddress) remoteServerAddress).getAddress().getAddress().length + 2);

// resp.put((byte)0x00); //保留 // resp.put((byte)0x00); //保留 // resp.put((byte)0x00); //分片 byte type = ((InetSocketAddress) remoteServerAddress).getAddress().getAddress().length == 4 ? TYPE_IPV4 : TYPE_IPV6; byte[] header = new byte[]{0x00, 0x00, 0x00, type}; respBuff.writeBytes(header); respBuff.writeBytes(((InetSocketAddress) remoteServerAddress).getAddress().getAddress()); //远程地址 0x08 0x08 0x08 0x08 Short remotePort = (short) (remoteServerPort & 0xFFFF); respBuff.writeShort(remotePort); // 远程地址端口 0x00 0x35 //真实数据 respBuff.writeBytes(originData);

                            ctx.channel().writeAndFlush(new DatagramPacket(respBuff,
                                    new InetSocketAddress(clientAddress, clientPort)));`

另外我发现同样的redsocks代码再ubuntu上可以正常运行,在centos7上会出现这样的问题

— Reply to this email directly, view it on GitHub https://github.com/semigodking/redsocks/issues/195#issuecomment-1905514642, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAP2XD3PZRVW7DHSBXLRM6LYP5W5ZAVCNFSM6AAAAABCE4F5LCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSMBVGUYTINRUGI . You are receiving this because you are subscribed to this thread.Message ID: @.***>

semigodking commented 10 months ago

花了点时间调查了一下。你这个问题我没办法确认。你看到的端口错误其实是因为你打印的时候没有做ntohs。#194 的问题没解决之前,兼容性是个问题。

zglqaq commented 10 months ago

的确是打印错误,但是我已经找到了原因。当udp穿透的目标端口是53时,如果允许redsocks的主机上已经运行了dns服务就会出现bound_udp_get(...) bind: Address already in use。同样的我上面提到的目标端口是5010的错误也是因为本机udp5010端口已经被占用了。当我把5010端口所在的服务监听的地址从绑定所有地址0.0.0.0:5010改成其中一个地址172.16.0.8:5010后错误就消失了。虽然错误解决了,但是我不理解为什么redsocks转发udp数据时要在本机上绑定目标端口,这是TPROXY机制吗?

semigodking commented 10 months ago

要绑定的是目标地址,以便以目标地址向客户端发响应数据。