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

Feature: support setSocketOptions on windows #192

Merged
merged 10 commits into from
Nov 26, 2022

Conversation

nange
Copy link
Contributor

@nange nange commented Nov 19, 2022

Hi, I have made a PR for supporting setSocketOptions on windows, since it is not supported now.

The idea is taken from wireguard-go

@nange nange changed the title Feature: support setSocketOptions on windows [WIP] Feature: support setSocketOptions on windows Nov 19, 2022
@nange
Copy link
Contributor Author

nange commented Nov 19, 2022

It seems mistake that base on address parameter to exec bindSocketToInterface4 or bindSocketToInterface6 func.
I think we should base on RawConn which is IPv4 or IPv6, but How can I know that? Do you have any suggestion? @xjasonlyu

@xjasonlyu
Copy link
Owner

Hi, thanks for the PR!

We don’t need RowConn to know whether is IPv4 or not. The network param already indicates that.

For example, in Darwin:

switch network {
case "tcp4", "udp4":
innerErr = unix.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_BOUND_IF, opts.InterfaceIndex)
case "tcp6", "udp6":
innerErr = unix.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_BOUND_IF, opts.InterfaceIndex)
}

@nange
Copy link
Contributor Author

nange commented Nov 22, 2022

Hi, @xjasonlyu
I have updated the code based on your suggestion. But I found it can't avoid routing loop with the code below:

pc, err := dialer.ListenPacketWithOptions("udp", "", &dialer.Options{
        InterfaceName:  mylocalName,
        InterfaceIndex: mylocalIndex,
})
if err != nil {
        // to do err
}

uAddr, _ := net.ResolveUDPAddr("udp", "114.114.114.114:53")
pc.WriteTo(mydata, uAddr)

Based on the code above, I found the network param in setSocketOptions will be udp6(actually my internet is "IPv4").

There are two ways to fix the routing loop issue that I found:

  1. use "udp4" in ListenPacketWithOptions func, eg.
        pc, err := dialer.ListenPacketWithOptions("udp4", "", &dialer.Options{
                InterfaceName:  mylocalName,
                InterfaceIndex: mylocalIndex,
        })
  2. only exec bindSocketToInterface4 in setSocketOptions func, eg.
    // ....
    if opts.InterfaceIndex != 0 {
        innerErr = bindSocketToInterface4(windows.Handle(fd), uint32(opts.InterfaceIndex))
    }
    // ....

I'm confused with the result. Any thought??

PS:
I want to proxy all the system traffic except the target ip in China, so I need use dialer.ListenPacketWithOptions interface.
And I also tested on MacOS, there is no routing loop issue although it has the similar code logic.
My local env is: OS: Windows11, IP Net: IPv4

@xjasonlyu
Copy link
Owner

I guess it might have something to do with Windows' long ipv6 representing ipv4 addresses.

@nange nange changed the title [WIP] Feature: support setSocketOptions on windows Feature: support setSocketOptions on windows Nov 24, 2022
@nange
Copy link
Contributor Author

nange commented Nov 24, 2022

Hi, @xjasonlyu please review the code again.
I found a workaround to fix the routing loop issue on windows.
I tested both on IPv4 and IPv6 network on windows11, it works correctly. But It's up to you to merge this PR, It's all ok for me.

@xjasonlyu
Copy link
Owner

I like this PR since I wondered how to solve routing loop issue on Windows before.

Copy link
Owner

@xjasonlyu xjasonlyu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, please see the reviews.

component/dialer/sockopt_windows.go Outdated Show resolved Hide resolved
component/dialer/sockopt_windows.go Outdated Show resolved Hide resolved
@nange
Copy link
Contributor Author

nange commented Nov 25, 2022

@xjasonlyu Hi, replied to you. Please take a look.

Copy link
Owner

@xjasonlyu xjasonlyu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts here.

component/dialer/sockopt_windows.go Outdated Show resolved Hide resolved
@nange nange requested a review from xjasonlyu November 25, 2022 12:19
component/dialer/sockopt_windows.go Outdated Show resolved Hide resolved
@xjasonlyu
Copy link
Owner

Also, this feature is only supported on Windows 11, right?

@nange
Copy link
Contributor Author

nange commented Nov 26, 2022

Also, this feature is only supported on Windows 11, right?

From the doc(https://learn.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options#windows-support-for-ip_proto-options), it doesn't support windows10 and lower. But I want to do a test on windows10 in Virtual Machine, I'll give you the result later.

}

func bindSocketToInterface4(handle windows.Handle, interfaceIndex uint32) error {
const IP_UNICAST_IF = 31
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we should put the const within the func or declare it globally?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems more reasonable that move IP_UNICAST_IF to global.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And how about IP_UNICAST_IF6? They're different param, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can use IP_UNICAST_IF for all of them, since the value is the same. I've done some research, I found the IP_UNICAST_IF param is unique. Here is the windows.rs example: https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Networking/WinSock/constant.IP_UNICAST_IF.html I didn't find IP_UNICAST_IF6 or similar.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a param for IPv6: IPV6_UNICAST_IF, see https://learn.microsoft.com/en-us/windows/win32/winsock/ipproto-ipv6-socket-options, nut I wonder if they have the same value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh sorry, It is true that IPV6_UNICAST_IF is for IPV6. I also find it in windows.rs and the value is 31 as well. I've updated the code.

component/dialer/sockopt_windows.go Outdated Show resolved Hide resolved
@xjasonlyu
Copy link
Owner

Also, this feature is only supported on Windows 11, right?

From the doc(https://learn.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options#windows-support-for-ip_proto-options), it doesn't support windows10 and lower. But I want to do a test on windows10 in Virtual Machine, I'll give you the result later.

Ok, thanks. And let me know what happens actually, so we can discuss how to handle those circumstances.

@nange
Copy link
Contributor Author

nange commented Nov 26, 2022

Also, this feature is only supported on Windows 11, right?

From the doc(https://learn.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options#windows-support-for-ip_proto-options), it doesn't support windows10 and lower. But I want to do a test on windows10 in Virtual Machine, I'll give you the result later.

I did the test on windows10 in Virtual Machine. I found it works fine on windows10 as well.

@xjasonlyu
Copy link
Owner

That's weird but OK.

component/dialer/sockopt_windows.go Outdated Show resolved Hide resolved
Copy link
Owner

@xjasonlyu xjasonlyu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, making last style changes, and I think it's good for merge.

component/dialer/sockopt_windows.go Outdated Show resolved Hide resolved
component/dialer/sockopt_windows.go Outdated Show resolved Hide resolved
component/dialer/sockopt_windows.go Outdated Show resolved Hide resolved
component/dialer/sockopt_windows.go Outdated Show resolved Hide resolved
component/dialer/sockopt_windows.go Outdated Show resolved Hide resolved
@nange
Copy link
Contributor Author

nange commented Nov 26, 2022

Hi, the style changes have been done.

@xjasonlyu xjasonlyu merged commit 35f6888 into xjasonlyu:main Nov 26, 2022
@xjasonlyu
Copy link
Owner

Great, thank you for the PR and all the hard effort!

@xjasonlyu
Copy link
Owner

Hi, just to mention, someone has tested this feature under Windows 10 21H2 or below, but it doesn't seem to work.

@nange
Copy link
Contributor Author

nange commented Dec 2, 2022

Hi, just to mention, someone has tested this feature under Windows 10 21H2 or below, but it doesn't seem to work.

That's weird. Could him open a issue? I want to know how to reproduce it.

@xjasonlyu
Copy link
Owner

xjasonlyu commented Dec 2, 2022

I've had it tested, there's no problem. It turns out he misconfigured the interface name. 🤦‍♂️

@nange nange deleted the windows-set-sockopt branch December 7, 2022 03:54
@e1732a364fed
Copy link

很重要的功能 以及 对于 ipv4部分的 endian转换的发现!感谢

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

Successfully merging this pull request may close these issues.

None yet

3 participants