diff options
| author | David Lönnhager <david.l@mullvad.net> | 2025-09-24 14:47:01 +0200 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2025-09-30 16:09:04 +0200 |
| commit | 514b4a8d15c4e3fd8da884cefcb395588d341623 (patch) | |
| tree | 4a5704083a7760615be6028e02248bac7d2be090 /desktop/packages | |
| parent | ffa9bc85f446b21cf6017b979bb6602684033b04 (diff) | |
| download | mullvadvpn-514b4a8d15c4e3fd8da884cefcb395588d341623.tar.xz mullvadvpn-514b4a8d15c4e3fd8da884cefcb395588d341623.zip | |
Add LWO to UI
Diffstat (limited to 'desktop/packages')
11 files changed, 115 insertions, 7 deletions
diff --git a/desktop/packages/mullvad-vpn/src/main/daemon-rpc.ts b/desktop/packages/mullvad-vpn/src/main/daemon-rpc.ts index 8db5d1b5f2..b7871f4c76 100644 --- a/desktop/packages/mullvad-vpn/src/main/daemon-rpc.ts +++ b/desktop/packages/mullvad-vpn/src/main/daemon-rpc.ts @@ -364,6 +364,11 @@ export class DaemonRpc extends GrpcClient { grpcTypes.ObfuscationSettings.SelectedObfuscation.QUIC, ); break; + case ObfuscationType.lwo: + grpcObfuscationSettings.setSelectedObfuscation( + grpcTypes.ObfuscationSettings.SelectedObfuscation.LWO, + ); + break; } if (obfuscationSettings.udp2tcpSettings) { diff --git a/desktop/packages/mullvad-vpn/src/main/grpc-type-convertions.ts b/desktop/packages/mullvad-vpn/src/main/grpc-type-convertions.ts index f4a8945bc6..f29979c520 100644 --- a/desktop/packages/mullvad-vpn/src/main/grpc-type-convertions.ts +++ b/desktop/packages/mullvad-vpn/src/main/grpc-type-convertions.ts @@ -133,12 +133,14 @@ function convertFromRelayListRelay(relay: grpcTypes.Relay): IRelayListHostname { const daita = wireguard ? wireguard.daita : false; const quic = wireguard?.quic ? quicFromRelayType(wireguard.quic) : undefined; + const lwo = wireguard ? wireguard.lwo : false; return { ...relayObject, endpointType, daita, quic, + lwo, }; } @@ -727,6 +729,9 @@ function convertFromObfuscationSettings( case grpcTypes.ObfuscationSettings.SelectedObfuscation.QUIC: selectedObfuscationType = ObfuscationType.quic; break; + case grpcTypes.ObfuscationSettings.SelectedObfuscation.LWO: + selectedObfuscationType = ObfuscationType.lwo; + break; } return { diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/select-location/RelayListContext.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/select-location/RelayListContext.tsx index 59a48c4130..643b8a6dbb 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/components/select-location/RelayListContext.tsx +++ b/desktop/packages/mullvad-vpn/src/renderer/components/select-location/RelayListContext.tsx @@ -10,6 +10,7 @@ import { filterLocations, filterLocationsByDaita, filterLocationsByEndPointType, + filterLocationsByLwo, filterLocationsByQuic, getLocationsExpandedBySearch, searchForLocations, @@ -76,6 +77,9 @@ export function RelayListContextProvider(props: RelayListContextProviderProps) { const quic = useSelector( (state) => state.settings.obfuscationSettings.selectedObfuscation === ObfuscationType.quic, ); + const lwo = useSelector( + (state) => state.settings.obfuscationSettings.selectedObfuscation === ObfuscationType.lwo, + ); const fullRelayList = useSelector((state) => state.settings.relayLocations); const relaySettings = useNormalRelaySettings(); @@ -113,12 +117,16 @@ export function RelayListContextProvider(props: RelayListContextProviderProps) { ipVersion, ); }, [quic, relayListForDaita, locationType, tunnelProtocol, multihop, ipVersion]); + // Only show relays that have LWO endpoints when LWO is enabled. + const relayListForLwo = useMemo(() => { + return filterLocationsByLwo(relayListForQuic, lwo, tunnelProtocol, locationType, multihop); + }, [lwo, relayListForQuic, locationType, tunnelProtocol, multihop]); // Filters the relays to only keep the relays matching the currently selected filters, e.g. // ownership and providers const relayListForFilters = useMemo(() => { - return filterLocations(relayListForQuic, relaySettings?.ownership, relaySettings?.providers); - }, [relaySettings?.ownership, relaySettings?.providers, relayListForQuic]); + return filterLocations(relayListForLwo, relaySettings?.ownership, relaySettings?.providers); + }, [relaySettings?.ownership, relaySettings?.providers, relayListForLwo]); // Filters the relays based on the provided search term const relayListForSearch = useMemo(() => { diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/select-location/SelectLocation.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/select-location/SelectLocation.tsx index 2bcae3428b..5c75cefdaf 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/components/select-location/SelectLocation.tsx +++ b/desktop/packages/mullvad-vpn/src/renderer/components/select-location/SelectLocation.tsx @@ -11,6 +11,7 @@ import { useRelaySettingsUpdater } from '../../lib/constraint-updater'; import { daitaFilterActive, filterSpecialLocations, + lwoFilterActive, quicFilterActive, } from '../../lib/filter-locations'; import { useHistory } from '../../lib/history'; @@ -70,7 +71,11 @@ export default function SelectLocation() { const quic = useSelector( (state) => state.settings.obfuscationSettings.selectedObfuscation === ObfuscationType.quic, ); + const lwo = useSelector( + (state) => state.settings.obfuscationSettings.selectedObfuscation === ObfuscationType.lwo, + ); const showQuicFilter = quicFilterActive(quic, locationType, tunnelProtocol, multihop); + const showLwoFilter = lwoFilterActive(lwo, locationType, tunnelProtocol, multihop); const showDaitaFilter = daitaFilterActive( daita, directOnly, @@ -129,7 +134,11 @@ export default function SelectLocation() { const showOwnershipFilter = ownership !== Ownership.any; const showProvidersFilter = providers.length > 0; const showFilters = - showOwnershipFilter || showProvidersFilter || showDaitaFilter || showQuicFilter; + showOwnershipFilter || + showProvidersFilter || + showDaitaFilter || + showQuicFilter || + showLwoFilter; return ( <BackAction action={onClose}> <Layout> @@ -226,6 +235,23 @@ export default function SelectLocation() { </FilterChip.Text> </FilterChip> )} + + {showLwoFilter && ( + <FilterChip as="div"> + <FilterChip.Text> + {sprintf( + // TRANSLATORS: Label for indicator that shows that obfuscation is being used as a filter. + // TRANSLATORS: Available placeholders: + // TRANSLATORS: %(obfuscation)s - type of obfuscation in use + messages.pgettext( + 'select-location-view', + 'Obfuscation: %(obfuscation)s', + ), + { obfuscation: 'LWO' }, + )} + </FilterChip.Text> + </FilterChip> + )} </Flex> )} diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/wireguard-settings/components/obfuscation-settings/ObfuscationSettings.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/wireguard-settings/components/obfuscation-settings/ObfuscationSettings.tsx index aa9b1e7835..7058548c36 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/components/views/wireguard-settings/components/obfuscation-settings/ObfuscationSettings.tsx +++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/wireguard-settings/components/obfuscation-settings/ObfuscationSettings.tsx @@ -107,6 +107,9 @@ export function ObfuscationSettings() { <SettingsListbox.BaseOption value={ObfuscationType.quic}> {messages.pgettext('wireguard-settings-view', 'QUIC')} </SettingsListbox.BaseOption> + <SettingsListbox.BaseOption value={ObfuscationType.lwo}> + {messages.pgettext('wireguard-settings-view', 'LWO')} + </SettingsListbox.BaseOption> <SettingsListbox.BaseOption value={ObfuscationType.off}> {messages.gettext('Off')} </SettingsListbox.BaseOption> diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/filter-locations.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/filter-locations.ts index 2c125cdb9a..0149f57752 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/lib/filter-locations.ts +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/filter-locations.ts @@ -50,6 +50,19 @@ export function filterLocationsByQuic( : locations; } +export function filterLocationsByLwo( + locations: IRelayLocationCountryRedux[], + lwo: boolean, + tunnelProtocol: TunnelProtocol, + locationType: LocationType, + multihop: boolean, +): IRelayLocationCountryRedux[] { + const lwoOnRelay = (relay: IRelayLocationRelayRedux) => relay.lwo; + return lwoFilterActive(lwo, locationType, tunnelProtocol, multihop) + ? filterLocationsImpl(locations, lwoOnRelay) + : locations; +} + export function quicFilterActive( quic: boolean, locationType: LocationType, @@ -62,6 +75,18 @@ export function quicFilterActive( return quic && isEntry && tunnelProtocol !== 'openvpn'; } +export function lwoFilterActive( + lwo: boolean, + locationType: LocationType, + tunnelProtocol: TunnelProtocol, + multihop: boolean, +) { + const isEntry = multihop + ? locationType === LocationType.entry + : locationType === LocationType.exit; + return lwo && isEntry && tunnelProtocol !== 'openvpn'; +} + export function filterLocationsByDaita( locations: IRelayLocationCountryRedux[], daita: boolean, diff --git a/desktop/packages/mullvad-vpn/src/renderer/redux/settings/reducers.ts b/desktop/packages/mullvad-vpn/src/renderer/redux/settings/reducers.ts index 285a0500e2..0def2b2d98 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/redux/settings/reducers.ts +++ b/desktop/packages/mullvad-vpn/src/renderer/redux/settings/reducers.ts @@ -79,6 +79,7 @@ export interface IRelayLocationRelayRedux { endpointType: RelayEndpointType; daita: boolean; quic?: Quic; + lwo: boolean; } export interface IRelayLocationCityRedux { diff --git a/desktop/packages/mullvad-vpn/src/shared/daemon-rpc-types.ts b/desktop/packages/mullvad-vpn/src/shared/daemon-rpc-types.ts index ab7773d1a3..f42a9292e9 100644 --- a/desktop/packages/mullvad-vpn/src/shared/daemon-rpc-types.ts +++ b/desktop/packages/mullvad-vpn/src/shared/daemon-rpc-types.ts @@ -399,6 +399,7 @@ export interface IRelayListHostname { daita: boolean; // The absence of this value signals that the relay does not deploy QUIC. quic?: Quic; + lwo: boolean; } export type Quic = { @@ -527,6 +528,7 @@ export enum ObfuscationType { udp2tcp, shadowsocks, quic, + lwo, } export type ObfuscationSettings = { diff --git a/desktop/packages/mullvad-vpn/test/e2e/mock-data.ts b/desktop/packages/mullvad-vpn/test/e2e/mock-data.ts index 4bd4436772..0c21bab06b 100644 --- a/desktop/packages/mullvad-vpn/test/e2e/mock-data.ts +++ b/desktop/packages/mullvad-vpn/test/e2e/mock-data.ts @@ -22,6 +22,7 @@ const relayList: IRelayList = { owned: true, endpointType: 'wireguard', daita: true, + lwo: true, }, { hostname: 'mullvad-wireguard-23', @@ -33,6 +34,7 @@ const relayList: IRelayList = { owned: true, endpointType: 'wireguard', daita: true, + lwo: false, }, { hostname: 'another-provider-wireguard-1', @@ -44,6 +46,7 @@ const relayList: IRelayList = { owned: false, endpointType: 'wireguard', daita: true, + lwo: false, }, { hostname: 'mullvad-wireguard-quic-1', @@ -60,6 +63,7 @@ const relayList: IRelayList = { domain: '', token: '', }, + lwo: false, }, { hostname: 'mullvad-openvpn-1', @@ -70,7 +74,8 @@ const relayList: IRelayList = { weight: 0, owned: true, endpointType: 'openvpn', - daita: true, + daita: false, + lwo: false, }, ], }, diff --git a/desktop/packages/mullvad-vpn/test/e2e/mocked/select-location/helpers.ts b/desktop/packages/mullvad-vpn/test/e2e/mocked/select-location/helpers.ts index ea3e033c30..1a30729ac8 100644 --- a/desktop/packages/mullvad-vpn/test/e2e/mocked/select-location/helpers.ts +++ b/desktop/packages/mullvad-vpn/test/e2e/mocked/select-location/helpers.ts @@ -62,10 +62,15 @@ export const createHelpers = (page: Page, routes: RoutesObjectModel, utils: Mock ), ); - const locateRelaysByObfuscation = (relayList: IRelayList): LocatedRelay[] => + const locateRelaysByObfuscation = ( + relayList: IRelayList, + relayCondition: (relay: IRelayListHostname) => boolean, + ): LocatedRelay[] => relayList.countries.flatMap((country) => country.cities.flatMap((city) => - city.relays.filter((relay) => relay.quic).map((relay) => ({ country, city, relay })), + city.relays + .filter((relay) => relayCondition(relay)) + .map((relay) => ({ country, city, relay })), ), ); diff --git a/desktop/packages/mullvad-vpn/test/e2e/mocked/select-location/select-location.spec.ts b/desktop/packages/mullvad-vpn/test/e2e/mocked/select-location/select-location.spec.ts index d4ea5343f7..3826d9e27e 100644 --- a/desktop/packages/mullvad-vpn/test/e2e/mocked/select-location/select-location.spec.ts +++ b/desktop/packages/mullvad-vpn/test/e2e/mocked/select-location/select-location.spec.ts @@ -289,7 +289,30 @@ test.describe('Select location', () => { } await util.ipc.settings[''].notify(settings); - const locatedRelays = helpers.locateRelaysByObfuscation(relayList); + const locatedRelays = helpers.locateRelaysByObfuscation( + relayList, + (relay) => 'quic' in relay, + ); + const relays = locatedRelays.map((locatedRelay) => locatedRelay.relay); + const relayNames = relays.map((relay) => relay.hostname); + + await helpers.expandLocatedRelays(locatedRelays); + + const buttons = routes.selectLocation.getRelaysMatching(relayNames); + + // Expect all filtered relays to have a button + await expect(buttons).toHaveCount(relays.length); + }); + }); + test.describe('Filter by LWO', () => { + test('Should apply filter when LWO obfuscation is selected', async () => { + const settings = getDefaultSettings(); + if ('normal' in settings.relaySettings) { + settings.obfuscationSettings.selectedObfuscation = ObfuscationType.lwo; + } + await util.ipc.settings[''].notify(settings); + + const locatedRelays = helpers.locateRelaysByObfuscation(relayList, (relay) => relay.lwo); const relays = locatedRelays.map((locatedRelay) => locatedRelay.relay); const relayNames = relays.map((relay) => relay.hostname); |
