Skip to content
New issue

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

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

Already on GitHub? Sign in 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants