-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(client/linux): revamp the Linux VPN routing logic (#2291)
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.
- Loading branch information
Showing
23 changed files
with
1,091 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
// Copyright 2024 The Outline Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
import {invokeMethod} from './go_plugin'; | ||
import { | ||
StartRequestJson, | ||
TunnelStatus, | ||
} from '../src/www/app/outline_server_repository/vpn'; | ||
|
||
// TODO: Separate this config into LinuxVpnConfig and WindowsVpnConfig. Some fields may share. | ||
interface VpnConfig { | ||
id: string; | ||
interfaceName: string; | ||
connectionName: string; | ||
ipAddress: string; | ||
dnsServers: string[]; | ||
routingTableId: number; | ||
routingPriority: number; | ||
protectionMark: number; | ||
} | ||
|
||
interface EstablishVpnRequest { | ||
vpn: VpnConfig; | ||
transport: string; | ||
} | ||
|
||
let currentRequestId: string | undefined = undefined; | ||
|
||
export async function establishVpn(request: StartRequestJson) { | ||
currentRequestId = request.id; | ||
statusCb?.(currentRequestId, TunnelStatus.RECONNECTING); | ||
|
||
const config: EstablishVpnRequest = { | ||
vpn: { | ||
id: currentRequestId, | ||
|
||
// TUN device name, being compatible with old code: | ||
// https://github.com/Jigsaw-Code/outline-apps/blob/client/linux/v1.14.0/client/electron/linux_proxy_controller/outline_proxy_controller.h#L203 | ||
interfaceName: 'outline-tun0', | ||
|
||
// Network Manager connection name, Use "TUN Connection" instead of "VPN Connection" | ||
// because Network Manager has a dedicated "VPN Connection" concept that we did not implement | ||
connectionName: 'Outline TUN Connection', | ||
|
||
// TUN IP, being compatible with old code: | ||
// https://github.com/Jigsaw-Code/outline-apps/blob/client/linux/v1.14.0/client/electron/linux_proxy_controller/outline_proxy_controller.h#L204 | ||
ipAddress: '10.0.85.1', | ||
|
||
// DNS server list, being compatible with old code: | ||
// https://github.com/Jigsaw-Code/outline-apps/blob/client/linux/v1.14.0/client/electron/linux_proxy_controller/outline_proxy_controller.h#L207 | ||
dnsServers: ['9.9.9.9'], | ||
|
||
// Outline magic numbers, 7113 and 0x711E visually resembles "T L I E" in "ouTLInE" | ||
routingTableId: 7113, | ||
routingPriority: 0x711e, | ||
protectionMark: 0x711e, | ||
}, | ||
|
||
// The actual transport config | ||
transport: JSON.stringify(request.config.transport), | ||
}; | ||
|
||
await invokeMethod('EstablishVPN', JSON.stringify(config)); | ||
statusCb?.(currentRequestId, TunnelStatus.CONNECTED); | ||
} | ||
|
||
export async function closeVpn(): Promise<void> { | ||
statusCb?.(currentRequestId!, TunnelStatus.DISCONNECTING); | ||
await invokeMethod('CloseVPN', ''); | ||
statusCb?.(currentRequestId!, TunnelStatus.DISCONNECTED); | ||
} | ||
|
||
export type VpnStatusCallback = (id: string, status: TunnelStatus) => void; | ||
|
||
let statusCb: VpnStatusCallback | undefined = undefined; | ||
|
||
export function onVpnStatusChanged(cb: VpnStatusCallback): void { | ||
statusCb = cb; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// Copyright 2024 The Outline Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package outline | ||
|
||
import ( | ||
"net" | ||
"syscall" | ||
) | ||
|
||
// newFWMarkProtectedTCPDialer creates a base TCP dialer for [Client] | ||
// protected by the specified firewall mark. | ||
func newFWMarkProtectedTCPDialer(fwmark uint32) net.Dialer { | ||
return net.Dialer{ | ||
KeepAlive: -1, | ||
Control: func(network, address string, c syscall.RawConn) error { | ||
return c.Control(func(fd uintptr) { | ||
syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, int(fwmark)) | ||
}) | ||
}, | ||
} | ||
} | ||
|
||
// newFWMarkProtectedUDPDialer creates a new UDP dialer for [Client] | ||
// protected by the specified firewall mark. | ||
func newFWMarkProtectedUDPDialer(fwmark uint32) net.Dialer { | ||
return net.Dialer{ | ||
Control: func(network, address string, c syscall.RawConn) error { | ||
return c.Control(func(fd uintptr) { | ||
syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, int(fwmark)) | ||
}) | ||
}, | ||
} | ||
} |
Oops, something went wrong.