Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

FakeUDP Function In Submit c41a1a5 Cannot Handle IPv6 Address Correctly #137

Closed
changyp6 opened this issue Jan 5, 2021 · 9 comments
Closed

Comments

@changyp6
Copy link

changyp6 commented Jan 5, 2021

in fakeudp_linux.go file

  78         zoneID, err := strconv.ParseUint(addr.Zone, 10, 32)
  79         if err != nil {
  80           return nil, 0, err
  81         }

zoneID is translated from a String type, however, the FakeUDP caller, never set addr.Zone, which leads to error.

Caller of FakeUDP function is from dokoemo.go

 271                 conn, err = FakeUDP(
 272                     &net.UDPAddr{
 273                         IP:   b.UDP.Address.IP(),
 274                         Port: int(b.UDP.Port),
 275                     },
 276                     w.mark,
 277                 )
@RPRX
Copy link
Member

RPRX commented Jan 5, 2021

十分感谢

@changyp6
Copy link
Author

changyp6 commented Jan 5, 2021

#135 is probably related to this issue.

If I comment out the zoneID error check, and do not set zone
in fakeudp_linux.go

  49     if err = syscall.Bind(fileDescriptor, localSocketAddress); err != nil {
  50         syscall.Close(fileDescriptor)
  51         return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("socket bind: %s", err)}
  52     }

this error always occur.

It seems that IPv6 needs to be handled carefully.

@changyp6
Copy link
Author

changyp6 commented Jan 5, 2021

@RPRX
zoneID is a string representing the device name, for example, "lo", "eth0", etc...
It cannot be translated from the string directly to a number.

@RPRX
Copy link
Member

RPRX commented Jan 5, 2021

试下写死 eth0 ?

@changyp6
Copy link
Author

changyp6 commented Jan 5, 2021

I did the following change

diff --git a/proxy/dokodemo/fakeudp_linux.go b/proxy/dokodemo/fakeudp_linux.go
index f793661..05fb77a 100644
--- a/proxy/dokodemo/fakeudp_linux.go
+++ b/proxy/dokodemo/fakeudp_linux.go
@@ -63,6 +63,21 @@ func FakeUDP(addr *net.UDPAddr, mark int) (net.PacketConn, error) {
        return packetConn, nil
 }
 
+func zoneToUint32(zone string) (uint32, error) {
+       if zone == "" {
+               return 0, nil
+       }
+       if ifi, err := net.InterfaceByName(zone); err == nil {
+               return uint32(ifi.Index), nil
+       }
+       n, err := strconv.Atoi(zone)
+       if err != nil {
+               return 0, err
+       }
+       return uint32(n), nil
+}
+
+
 func udpAddrToSocketAddr(addr *net.UDPAddr) (syscall.Sockaddr, int, error) {
        switch {
        case addr.IP.To4() != nil:
@@ -75,7 +90,7 @@ func udpAddrToSocketAddr(addr *net.UDPAddr) (syscall.Sockaddr, int, error) {
                ip := [16]byte{}
                copy(ip[:], addr.IP.To16())
 
-               zoneID, err := strconv.ParseUint(addr.Zone, 10, 32)
+               zoneID, err := zoneToUint32(addr.Zone)
                if err != nil {
                        return nil, 0, err
                }
diff --git a/proxy/dokodemo/dokodemo.go b/proxy/dokodemo/dokodemo.go
index bae3b10..1d59aa5 100644
--- a/proxy/dokodemo/dokodemo.go
+++ b/proxy/dokodemo/dokodemo.go
@@ -169,6 +169,7 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn in
                                addr = &net.UDPAddr{
                                        IP:   dest.Address.IP(),
                                        Port: int(dest.Port),
+                                       Zone: "eth3",
                                }
                        }
                        if d.sockopt != nil {
@@ -272,6 +273,7 @@ func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
                                        &net.UDPAddr{
                                                IP:   b.UDP.Address.IP(),
                                                Port: int(b.UDP.Port),
+                                               Zone: "eth3",
                                        },
                                        w.mark,
                                )

The following error message is always reported

app/proxyman/inbound: connection ends > fake: socket bind: address already in use

@changyp6
Copy link
Author

changyp6 commented Jan 5, 2021

@RPRX
Copy link
Member

RPRX commented Jan 7, 2021

感谢探索,根据我目前的理解,IPv6 中 scope/zone 的作用是指明非全局地址属于哪个网口,多网口时这应该是必要的
所以我进行了一些尝试性修改,并在 #135 中进行了一些测试,下面说明相关原理

透明代理 UDP,返回数据时需要在本地伪造一个来源 socket,并 Write/WriteTo 回发起请求的 socket,即被代理的程序
目前的代码的确存在问题,因为目前的代码伪造 IPv6 socket 时 100% 会触发 error,所以我分别尝试了三个方案:

  1. 伪造 IPv6 socket 时 zone 写死 0,WriteTo 回的 UDPAddr 不填 zone,经测试没有问题(但我猜多网口时会出错)
  2. 伪造 IPv6 socket 时 zone 与 conn.RemoteAddr 的一致,WriteTo 回的 UDPAddr 不填 zone,经测试也没有问题
  3. 伪造 IPv6 socket 时 zone 写死 0,WriteTo 回的 UDPAddr 直接用 conn.RemoteAddr 的值,经测试同样没有问题

事实上第三个方案是最合理且简洁的,即伪造 IPv6 socket 时 zone 写死 0,但 WriteTo 回的 UDPAddr 有正确的 zone

因为被伪造的地址显然属于全局地址(至少一般不属于本地的非全局地址),同时数据需要被发到正确的网口。

RPRX added a commit that referenced this issue Jan 7, 2021
@RPRX
Copy link
Member

RPRX commented Jan 7, 2021

代码已提交 161e182 ,再次感谢

@RPRX RPRX closed this as completed Jan 7, 2021
@changyp6
Copy link
Author

changyp6 commented Jan 8, 2021

Thanks!
I'll try this and get back to you the results.

mwhorse46 added a commit to mwhorse46/Xray-core that referenced this issue Feb 19, 2023
rampagekiller0725 added a commit to rampagekiller0725/wox that referenced this issue Jun 29, 2023
Autumn216 added a commit to Autumn216/wox that referenced this issue Oct 31, 2023
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants