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

feat(client/linux): revamp the Linux VPN routing logic #2291

Open
wants to merge 32 commits into
base: master
Choose a base branch
from

Conversation

jyyi1
Copy link
Contributor

@jyyi1 jyyi1 commented Nov 22, 2024

This PR simplifies the Linux VPN routing architecture by:

  • Removing the daemon service: All routing is now handled by the app process and its backend library.
  • Leveraging NetworkManager for routing and DNS: Provides more robust DNS configuration and avoids conflicts with other resolver services; and prevents pollution of the user's default routing table.

This approach minimizes changes to the user's network configuration and improves overall stability.

Next steps: See #2291 (comment)

Tested on Ubuntu 22 and Ubuntu 24

Known Issue: The server connection status is not refreshed when network changes.

TODO: #2312

@jyyi1 jyyi1 changed the title feat(client/linux): revamp the Linux routing logic feat(client/linux): revamp the Linux VPN routing logic Nov 23, 2024
@github-actions github-actions bot added size/L and removed size/M labels Nov 23, 2024
@github-actions github-actions bot added size/XL and removed size/L labels Nov 26, 2024
@github-actions github-actions bot added size/XXL and removed size/XL labels Dec 3, 2024
@jyyi1

This comment was marked as outdated.

@jyyi1 jyyi1 added the os/linux label Dec 4, 2024
@jyyi1 jyyi1 marked this pull request as ready for review December 4, 2024 23:45
@jyyi1 jyyi1 requested review from a team as code owners December 4, 2024 23:45
@jyyi1 jyyi1 requested review from fortuna and sbruens December 4, 2024 23:45
@jyyi1 jyyi1 added the needs test Pull requests that require tests label Dec 5, 2024
@@ -20,7 +20,7 @@
"deb": {
"depends": [
"gconf2", "gconf-service", "libnotify4", "libappindicator1", "libxtst6", "libnss3",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

"gconf2", "gconf-service", "libappindicator1" do not exist on Ubuntu 24's apt repositories any more. After removal these dependencies, the app still runs. Maybe we can remove these? (even though electron declares they are required)

Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe they are required on newer versions?
I'd say that if it works, let's remove, especially if it blocks support on Ubuntu 24

Copy link
Contributor

Choose a reason for hiding this comment

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

Are they needed for earlier Ubuntu versions?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

From my experiment, nope. App installs and runs.

Copy link
Contributor Author

@jyyi1 jyyi1 Dec 7, 2024

Choose a reason for hiding this comment

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

Gemini told me:

gconf2 and gconf-service: These were core parts of GNOME 2. GNOME 3, with its dconf configuration system, became the default in Ubuntu 11.10 (Oneiric Ocelot) released in October 2011. While gconf2 stuck around for backward compatibility, this marked the start of its decline.

libappindicator1: This was tied to the older system tray design. Ubuntu's switch to the Ayatana indicator system began around Ubuntu 10.04 LTS (Lucid Lynx) in April 2010 and continued to evolve in subsequent releases.

Key takeaway: While these packages might still be available in later Ubuntu versions, their core functionalities were effectively superseded around Ubuntu 11.10. You'll mainly encounter them if you're dealing with older applications designed for GNOME 2 or early implementations of the system tray.

@jyyi1 jyyi1 requested review from fortuna and sbruens December 7, 2024 00:10
Copy link
Collaborator

@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.

This is looking solid. I like that you are using the NM more.

I think there are a few places that is a bit complex and could be cleaned up.

canHandleUDP = true
}

if d.pkt == nil {
Copy link
Collaborator

Choose a reason for hiding this comment

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

The truncate approach requires an additional exchange, though it's all local, so it shouldn't matter much

})
}

tcp := defaultBaseTCPDialer()
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is modifying the base dialer. Imutability > Mutability. They should remain unmodified after creation, this kind of side-effect is hard to track.

I think it will be cleaner if you have a newClientWithFwmark(transport, fwmark), and create the base dialers there.

Copy link
Collaborator

Choose a reason for hiding this comment

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

This also avoids tying the interface with LinuxOpts. I'd say delete DeviceOpts for now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed defaultBaseTCPDialer and defaultBaseUDPDialer.

client/go/outline/vpn/vpn_linux.go Outdated Show resolved Hide resolved
client/go/outline/vpn/vpn.go Outdated Show resolved Hide resolved
return nil, errIllegalConfig("must provide a valid TUN interface IP(v4)")
}
for _, dns := range conf.DNSServers {
dnsIP := net.ParseIP(dns).To4()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why convert to IPv4? I'd keep them as they are.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See the comment below.

client/go/outline/vpn/tun_linux.go Outdated Show resolved Hide resolved
gonm "github.com/Wifx/gonetworkmanager/v2"
)

type linuxVPNConn struct {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This type is doing a lot. See the other comments. We need to clarify side-effects and some method are better off being standalone.

Copy link
Collaborator

Choose a reason for hiding this comment

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

It's also not super clear what this type represents, perhaps some comments will help.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added some comments. This is the Linux implementation of VPNConnection. We used Linux-only APIs (which is not compilable on when targeting Windows).

But I may move some common logic (for example, the WaitGroups, and the traffic copying goroutines) to the vpn.go file, because these can be shared between Windows and Linux, but still keep the Linux specific calls in this vpn_linux.go file. Though this move might be done in the next PR (when introducing the callback, because the callback of status change logic is shared).

client/go/outline/method_channel.go Outdated Show resolved Hide resolved
client/go/outline/vpn/method_channel.go Outdated Show resolved Hide resolved
client/go/outline/electron/go_plugin.go Outdated Show resolved Hide resolved
@jyyi1 jyyi1 requested a review from fortuna December 10, 2024 01:23
@jyyi1
Copy link
Contributor Author

jyyi1 commented Dec 11, 2024

@fortuna @sbruens Any thoughts?

client/go/outline/method_channel.go Outdated Show resolved Hide resolved
@jyyi1 jyyi1 requested a review from fortuna December 16, 2024 22:22
@jyyi1
Copy link
Contributor Author

jyyi1 commented Dec 16, 2024

@fortuna I cleaned up some the code in the vpn package, and also refactored the architecture according to the review comment. Please take another look.

@jyyi1 jyyi1 removed the needs test Pull requests that require tests label Dec 16, 2024
client/go/outline/vpn.go Outdated Show resolved Hide resolved
client/go/outline/vpn/dialer_others.go Outdated Show resolved Hide resolved
client/go/outline/vpn/dialer_linux.go Outdated Show resolved Hide resolved
client/go/outline/vpn/dialer.go Outdated Show resolved Hide resolved
client/go/outline/vpn.go Outdated Show resolved Hide resolved
client/go/outline/device.go Outdated Show resolved Hide resolved
client/go/outline/vpn/vpn.go Outdated Show resolved Hide resolved
client/go/outline/vpn/vpn.go Outdated Show resolved Hide resolved
client/go/outline/vpn/vpn.go Outdated Show resolved Hide resolved
client/go/outline/vpn.go Outdated Show resolved Hide resolved
@jyyi1 jyyi1 requested a review from fortuna December 19, 2024 21:05
)

// newTCPDialer creates a default base TCP dialer for [Client].
func newTCPDialer() net.Dialer {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's just inline these two functions in the caller.


// RemoteDevice is an IPDevice that connects to a remote Outline server.
type RemoteDevice struct {
network.IPDevice
Copy link
Collaborator

Choose a reason for hiding this comment

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

I wonder if we should just use io.ReadWriterCloser for now and rethink if we need the IPDevice abstraction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants