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

WIP: disorder #1

Closed
wants to merge 5 commits into from
Closed

WIP: disorder #1

wants to merge 5 commits into from

Conversation

derlaft
Copy link
Collaborator

@derlaft derlaft commented Nov 2, 2024

No description provided.

Copy link

@fortuna fortuna left a comment

Choose a reason for hiding this comment

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

Thanks for the contribution. This is exciting!

Copy link

Choose a reason for hiding this comment

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

Same comments as in the other PRs: let's keep all the new stuff in the x module until they have comprehensive tests and are validated. That also simplifies development.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

data = data[written:]
}
w.resetTTL.Do(func() {
err = syscall.SetsockoptInt(w.fd, syscall.IPPROTO_IP, syscall.IP_TTL, defaultTTL)
Copy link

Choose a reason for hiding this comment

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

Ideally this should work on Windows as well, and both IPv4 and IPv6, so providers can easily specify strategies that work everywhere. It would be great if you can validate that.

By the way, I don't know if it helps, but there's

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Awesome, didn't know about this package

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ideally this should work on Windows as well

I think for windows, mac and ios we would need a completely different implementation. I personally don't have any ways to test on those platforms, so I am thinking about disabling this code anywhere but on linux

Copy link

Choose a reason for hiding this comment

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

Here is an example of a prototype I was working some time ago: https://github.com/Jigsaw-Code/outline-sdk/edit/fortuna-disorder/transport/split/writer.go

Copy link
Collaborator Author

@derlaft derlaft Nov 2, 2024

Choose a reason for hiding this comment

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

https://pkg.go.dev/golang.org/x/net/ipv4#Conn.SetTTL

this will only work on Linux btw (but will at least compile on other platforms, which is maybe a bit better)

Copy link

Choose a reason for hiding this comment

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

Why do you think it will only work on Linux? I don't see any documentation saying that. They usually say when it doesn't work on other platforms.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

hmm, I might have mis-read the code when I checked it, looks like the implementations are there

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

in any case, some testing is required on each platform, even if API is there there's no guarantee it works the same way

return nil, fmt.Errorf("setting tcp NO_DELAY failed: %w", err)
}

file, err := tcpConn.File()
Copy link

Choose a reason for hiding this comment

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

changed to use x/net

// this strategy only works when we set TCP as a strategy
tcpConn, ok := innerConn.(*net.TCPConn)
if !ok {
return nil, fmt.Errorf("split strategy only works with direct TCP connections")
Copy link

Choose a reason for hiding this comment

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

Suggested change
return nil, fmt.Errorf("split strategy only works with direct TCP connections")
return nil, fmt.Errorf("disorder strategy only works with direct TCP connections")

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed

return nil, fmt.Errorf("split strategy only works with direct TCP connections")
}

err = tcpConn.SetNoDelay(true)
Copy link

Choose a reason for hiding this comment

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

Isn't this the default? Also, I'd rather not silently change settings. The base dialer provider should be responsible for that.

Perhaps we check if it's set and error if not. Though I don't see an easy way to check.

Add to the comments that the TCPConn must have NoDelay set.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Isn't this the default?

Honestly no idea. In Linux it's default now (but only for recent kernels, most android likely will need years to adopt the change), but in golang no idea.

I've added this in an attempt to debug something, but the issue was somewhere else, so this is no longer needed

Copy link

Choose a reason for hiding this comment

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

It's enabled by default in Go, so no need to set it: https://pkg.go.dev/net#TCPConn.SetNoDelay

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

awesome

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed

@derlaft derlaft force-pushed the disorder branch 2 times, most recently from 161f18f to db99e8a Compare November 2, 2024 16:33
alim-shapiev pushed a commit that referenced this pull request Nov 3, 2024
conn := ipv6.NewConn(conn)
oldTTL, _ = conn.HopLimit()
err = conn.SetHopLimit(ttl)
}
Copy link

Choose a reason for hiding this comment

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

This should fail if we can't set the TTL

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

error check for both SetTTL and SetHopLimit is on lines 88-90

}

if oldTTL == 0 {
oldTTL = defaultTTL
Copy link

Choose a reason for hiding this comment

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

What's the rationale of returning a default if it's zero? Why not return zero if it's zero?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I was not sure what happens by default (what getsockopt returns if ttl was not changed), also need to check what happens when we set ttl to 0

return nil, err
}

oldTTL, err := setTtl(innerConn, 1)
Copy link

Choose a reason for hiding this comment

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

Please move this to the Writer, so the Writer can be used independent of the Dialer.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

the idea was to fail early if something (like setting socket options) is not supported or lacks permissions


// setTtl changes the socket TTL and returns the old value
// socket must be `*net.TCPConn`
func setTtl(conn net.Conn, ttl int) (oldTTL int, err error) {
Copy link

Choose a reason for hiding this comment

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

Let's rename this to setHopLimit, which is a preferred term. TTL is actually confusing, since it is not related to time, hop limit is clearer.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

renamed

prefixBytesStr := config.URL.Opaque
prefixBytes, err := strconv.Atoi(prefixBytesStr)
if err != nil {
return nil, fmt.Errorf("prefixBytes is not a number: %v. Split config should be in split:<number> format", prefixBytesStr)
Copy link

Choose a reason for hiding this comment

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

Suggested change
return nil, fmt.Errorf("prefixBytes is not a number: %v. Split config should be in split:<number> format", prefixBytesStr)
return nil, fmt.Errorf("Disorder config should take a number, got %v: %w, prefixBytesStr, err)

I'd move away from specifying the format scheme, since that can be changed in registration.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

data = data[written:]
}
w.resetTTL.Do(func() {
err = syscall.SetsockoptInt(w.fd, syscall.IPPROTO_IP, syscall.IP_TTL, defaultTTL)
Copy link

Choose a reason for hiding this comment

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

Why do you think it will only work on Linux? I don't see any documentation saying that. They usually say when it doesn't work on other platforms.

Comment on lines 55 to 57
w.resetTTL.Do(func() {
_, err = setTtl(w.conn, w.oldTTL)
})
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

before resetting ttl back to the "normal" value we might need to make sure send buffers are empty (= the first packet already departed). if golang is too fast this might change ttl back to normal too fast

other methods using setsocksopt also suffer from the same

@derlaft derlaft force-pushed the disorder branch 2 times, most recently from d35a4da to a910d10 Compare November 3, 2024 17:20
// var _ io.ReaderFrom = (*splitWriterReaderFrom)(nil)

// TODO
func NewWriter(conn net.Conn, prefixBytes int64, oldTTL int) io.Writer {
Copy link

Choose a reason for hiding this comment

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

oldTTL is not needed. You can read it from the connection.

Copy link

@fortuna fortuna left a comment

Choose a reason for hiding this comment

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

Could you send this code to the official repository?
Thanks!


// setHopLimit changes the socket TTL for IPv4 (or HopLimit for IPv6) and returns the old value
// socket must be `*net.TCPConn`
func setHopLimit(conn net.Conn, ttl int) (oldTTL int, err error) {
Copy link

Choose a reason for hiding this comment

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

Can you use Jigsaw-Code#316 instead?

// var _ io.ReaderFrom = (*splitWriterReaderFrom)(nil)

// TODO
func NewWriter(conn net.Conn, prefixBytes int64, oldTTL int) io.Writer {
Copy link

Choose a reason for hiding this comment

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

Please pass an io.Writer and the new sockopt.TCPOptions instead of net.Conn.

data = data[written:]
}
w.resetTTL.Do(func() {
_, err = setHopLimit(w.conn, w.oldTTL)
Copy link

Choose a reason for hiding this comment

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

@PeterZhizhin PeterZhizhin force-pushed the disorder branch 2 times, most recently from 4cb2ac5 to e5480ac Compare November 9, 2024 22:43
@derlaft
Copy link
Collaborator Author

derlaft commented Nov 13, 2024

Superseded by Jigsaw-Code#323

@PeterZhizhin please check the last commits were pushed where you expected them to be pushed

@derlaft derlaft closed this Nov 13, 2024
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.

3 participants