diff options
| author | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2018-08-14 09:29:59 -0300 |
|---|---|---|
| committer | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2018-08-14 09:29:59 -0300 |
| commit | 78eab08b73e8d98f9db700509951e780f7c1f212 (patch) | |
| tree | cd54f6b068318c43ff59afbbd9ac5a7a9aaee931 | |
| parent | 53e904f5c36b5b43ec5d9389f99d19a781625081 (diff) | |
| parent | 42ca10de518f81d7d86194ddbd858bd875424b86 (diff) | |
| download | mullvadvpn-78eab08b73e8d98f9db700509951e780f7c1f212.tar.xz mullvadvpn-78eab08b73e8d98f9db700509951e780f7c1f212.zip | |
Merge branch 'ipv6-toggle'
| -rw-r--r-- | CHANGELOG.md | 3 | ||||
| -rw-r--r-- | app/app.js | 13 | ||||
| -rw-r--r-- | app/components/AdvancedSettings.js | 17 | ||||
| -rw-r--r-- | app/components/AdvancedSettingsStyles.js | 8 | ||||
| -rw-r--r-- | app/containers/AdvancedSettingsPage.js | 16 | ||||
| -rw-r--r-- | app/lib/daemon-rpc.js | 48 | ||||
| -rw-r--r-- | app/redux/settings/actions.js | 23 | ||||
| -rw-r--r-- | app/redux/settings/reducers.js | 8 | ||||
| -rw-r--r-- | mullvad-cli/src/cmds/tunnel.rs | 50 | ||||
| -rw-r--r-- | mullvad-daemon/src/main.rs | 28 | ||||
| -rw-r--r-- | mullvad-daemon/src/management_interface.rs | 23 | ||||
| -rw-r--r-- | mullvad-daemon/src/settings.rs | 9 | ||||
| -rw-r--r-- | mullvad-ipc-client/src/lib.rs | 4 | ||||
| -rw-r--r-- | talpid-core/src/process/openvpn.rs | 10 | ||||
| -rw-r--r-- | talpid-types/src/net.rs | 15 | ||||
| -rw-r--r-- | test/components/SelectLocation.spec.js | 1 |
16 files changed, 252 insertions, 24 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index dbdbe5f87f..808639b6a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,9 @@ Line wrap the file at 100 chars. Th ## [Unreleased] +### Added +- Add option to enable or disable IPv6 on the tunnel interface. + ### Changed - The "Buy more credit" button is changed to open a dedicated account login page instead of one having a create account form first. diff --git a/app/app.js b/app/app.js index 369c51be2d..87d0c10186 100644 --- a/app/app.js +++ b/app/app.js @@ -369,6 +369,12 @@ export default class AppRenderer { actions.settings.updateAllowLan(allowLan); } + async setEnableIpv6(enableIpv6: boolean) { + const actions = this._reduxActions; + await this._daemonRpc.setOpenVpnEnableIpv6(enableIpv6); + actions.settings.updateEnableIpv6(enableIpv6); + } + async setAutoConnect(autoConnect: boolean) { const actions = this._reduxActions; await this._daemonRpc.setAutoConnect(autoConnect); @@ -393,6 +399,12 @@ export default class AppRenderer { this._updateConnectionState(connectionState); } + async _fetchTunnelOptions() { + const actions = this._reduxActions; + const tunnelOptions = await this._daemonRpc.getTunnelOptions(); + actions.settings.updateEnableIpv6(tunnelOptions.openvpn.enableIpv6); + } + async _connectToDaemon(): Promise<void> { let credentials; try { @@ -523,6 +535,7 @@ export default class AppRenderer { this._fetchAutoConnect(), this._fetchLocation(), this._fetchAccountHistory(), + this._fetchTunnelOptions(), ]); } diff --git a/app/components/AdvancedSettings.js b/app/components/AdvancedSettings.js index fa1c61152f..3763e9687d 100644 --- a/app/components/AdvancedSettings.js +++ b/app/components/AdvancedSettings.js @@ -6,12 +6,15 @@ import { Layout, Container } from './Layout'; import NavigationBar, { BackBarItem } from './NavigationBar'; import SettingsHeader, { HeaderTitle } from './SettingsHeader'; import CustomScrollbars from './CustomScrollbars'; +import Switch from './Switch'; import styles from './AdvancedSettingsStyles'; import Img from './Img'; type AdvancedSettingsProps = { + enableIpv6: boolean, protocol: string, port: string | number, + setEnableIpv6: (boolean) => void, onUpdate: (protocol: string, port: string | number) => void, onClose: () => void, }; @@ -40,6 +43,20 @@ export class AdvancedSettings extends Component<AdvancedSettingsProps> { <HeaderTitle>Advanced</HeaderTitle> </SettingsHeader> <CustomScrollbars style={styles.advanced_settings__scrollview} autoHide={true}> + <View style={styles.advanced_settings__ipv6}> + <View style={styles.advanced_settings__cell_label_container}> + <Text style={styles.advanced_settings__cell_label}>Enable IPv6</Text> + </View> + <View style={styles.advanced_settings__ipv6_accessory}> + <Switch isOn={this.props.enableIpv6} onChange={this.props.setEnableIpv6} /> + </View> + </View> + <View style={styles.advanced_settings__cell_footer}> + <Text style={styles.advanced_settings__cell_footer_label}> + {'Enable IPv6 communication through the tunnel.'} + </Text> + </View> + <View style={styles.advanced_settings__content}> <Selector title={'Network protocols'} diff --git a/app/components/AdvancedSettingsStyles.js b/app/components/AdvancedSettingsStyles.js index fc2f4a8587..cb3923e282 100644 --- a/app/components/AdvancedSettingsStyles.js +++ b/app/components/AdvancedSettingsStyles.js @@ -24,6 +24,14 @@ export default { flexBasis: 'auto', overflow: 'visible', }), + advanced_settings__ipv6: Styles.createViewStyle({ + backgroundColor: colors.blue, + flexDirection: 'row', + alignItems: 'center', + }), + advanced_settings__ipv6_accessory: Styles.createViewStyle({ + marginRight: 12, + }), advanced_settings__cell: Styles.createViewStyle({ cursor: 'default', backgroundColor: colors.green, diff --git a/app/containers/AdvancedSettingsPage.js b/app/containers/AdvancedSettingsPage.js index bf584096b9..ce935a89e4 100644 --- a/app/containers/AdvancedSettingsPage.js +++ b/app/containers/AdvancedSettingsPage.js @@ -8,10 +8,16 @@ import RelaySettingsBuilder from '../lib/relay-settings-builder'; import { log } from '../lib/platform'; import type { ReduxState, ReduxDispatch } from '../redux/store'; +import type { RelaySettingsRedux } from '../redux/settings/reducers'; import type { SharedRouteProps } from '../routes'; const mapStateToProps = (state: ReduxState) => { - const relaySettings = state.settings.relaySettings; + const protocolAndPort = mapRelaySettingsToProtocolAndPort(state.settings.relaySettings); + + return { enableIpv6: state.settings.enableIpv6, ...protocolAndPort }; +}; + +const mapRelaySettingsToProtocolAndPort = (relaySettings: RelaySettingsRedux) => { if (relaySettings.normal) { const { protocol, port } = relaySettings.normal; return { @@ -55,6 +61,14 @@ const mapDispatchToProps = (dispatch: ReduxDispatch, props: SharedRouteProps) => log.error('Failed to update relay settings', e.message); } }, + + setEnableIpv6: async (enableIpv6) => { + try { + await props.app.setEnableIpv6(enableIpv6); + } catch (e) { + log.error('Failed to update enable IPv6', e.message); + } + }, }; }; diff --git a/app/lib/daemon-rpc.js b/app/lib/daemon-rpc.js index 163ea170d0..8e2925f83e 100644 --- a/app/lib/daemon-rpc.js +++ b/app/lib/daemon-rpc.js @@ -49,16 +49,16 @@ export type BackendState = { export type RelayProtocol = 'tcp' | 'udp'; export type RelayLocation = {| city: [string, string] |} | {| country: string |}; -type OpenVpnParameters = { +type OpenVpnConstraints = { port: 'any' | { only: number }, protocol: 'any' | { only: RelayProtocol }, }; -type TunnelOptions<TOpenVpnParameters> = { - openvpn: TOpenVpnParameters, +type TunnelConstraints<TOpenVpnConstraints> = { + openvpn: TOpenVpnConstraints, }; -type RelaySettingsNormal<TTunnelOptions> = { +type RelaySettingsNormal<TTunnelConstraints> = { location: | 'any' | { @@ -67,7 +67,7 @@ type RelaySettingsNormal<TTunnelOptions> = { tunnel: | 'any' | { - only: TTunnelOptions, + only: TTunnelConstraints, }, }; @@ -83,7 +83,7 @@ export type RelaySettingsCustom = { }; export type RelaySettings = | {| - normal: RelaySettingsNormal<TunnelOptions<OpenVpnParameters>>, + normal: RelaySettingsNormal<TunnelConstraints<OpenVpnConstraints>>, |} | {| custom_tunnel_endpoint: RelaySettingsCustom, @@ -91,7 +91,7 @@ export type RelaySettings = // types describing the partial update of RelaySettings export type RelaySettingsNormalUpdate = $Shape< - RelaySettingsNormal<TunnelOptions<$Shape<OpenVpnParameters>>>, + RelaySettingsNormal<TunnelConstraints<$Shape<OpenVpnConstraints>>>, >; export type RelaySettingsUpdate = | {| @@ -182,6 +182,19 @@ const RelayListSchema = object({ ), }); +export type TunnelOptions = { + openvpn: { + enableIpv6: boolean, + }, +}; + +const TunnelOptionsSchema = object({ + openvpn: object({ + enable_ipv6: boolean, + mssfix: maybe(number), + }), +}); + const AccountDataSchema = object({ expiry: string, }); @@ -203,6 +216,8 @@ export interface DaemonRpcProtocol { getRelaySettings(): Promise<RelaySettings>; setAllowLan(boolean): Promise<void>; getAllowLan(): Promise<boolean>; + setOpenVpnEnableIpv6(boolean): Promise<void>; + getTunnelOptions(): Promise<TunnelOptions>; setAutoConnect(boolean): Promise<void>; getAutoConnect(): Promise<boolean>; connectTunnel(): Promise<void>; @@ -356,6 +371,25 @@ export class DaemonRpc implements DaemonRpcProtocol { } } + async setOpenVpnEnableIpv6(enableIpv6: boolean): Promise<void> { + await this._transport.send('set_openvpn_enable_ipv6', [enableIpv6]); + } + + async getTunnelOptions(): Promise<TunnelOptions> { + const response = await this._transport.send('get_tunnel_options'); + try { + const validatedObject = validate(TunnelOptionsSchema, response); + + return { + openvpn: { + enableIpv6: validatedObject.openvpn.enable_ipv6, + }, + }; + } catch (error) { + throw new ResponseParseError('Invalid response from get_tunnel_options', error); + } + } + async setAutoConnect(autoConnect: boolean): Promise<void> { await this._transport.send('set_auto_connect', [autoConnect]); } diff --git a/app/redux/settings/actions.js b/app/redux/settings/actions.js index fd3ad651d9..2c7efdf554 100644 --- a/app/redux/settings/actions.js +++ b/app/redux/settings/actions.js @@ -22,11 +22,17 @@ export type UpdateAllowLanAction = { allowLan: boolean, }; +export type UpdateEnableIpv6Action = { + type: 'UPDATE_ENABLE_IPV6', + enableIpv6: boolean, +}; + export type SettingsAction = | UpdateRelayAction | UpdateRelayLocationsAction | UpdateAutoConnectAction - | UpdateAllowLanAction; + | UpdateAllowLanAction + | UpdateEnableIpv6Action; function updateRelay(relay: RelaySettingsRedux): UpdateRelayAction { return { @@ -58,4 +64,17 @@ function updateAllowLan(allowLan: boolean): UpdateAllowLanAction { }; } -export default { updateRelay, updateRelayLocations, updateAutoConnect, updateAllowLan }; +function updateEnableIpv6(enableIpv6: boolean): UpdateEnableIpv6Action { + return { + type: 'UPDATE_ENABLE_IPV6', + enableIpv6, + }; +} + +export default { + updateRelay, + updateRelayLocations, + updateAutoConnect, + updateAllowLan, + updateEnableIpv6, +}; diff --git a/app/redux/settings/reducers.js b/app/redux/settings/reducers.js index 2bd5798aa5..9870066edc 100644 --- a/app/redux/settings/reducers.js +++ b/app/redux/settings/reducers.js @@ -39,6 +39,7 @@ export type SettingsReduxState = { relayLocations: Array<RelayLocationRedux>, autoConnect: boolean, allowLan: boolean, + enableIpv6: boolean, }; const initialState: SettingsReduxState = { @@ -52,6 +53,7 @@ const initialState: SettingsReduxState = { relayLocations: [], autoConnect: false, allowLan: false, + enableIpv6: true, }; export default function( @@ -83,6 +85,12 @@ export default function( autoConnect: action.autoConnect, }; + case 'UPDATE_ENABLE_IPV6': + return { + ...state, + enableIpv6: action.enableIpv6, + }; + default: return state; } diff --git a/mullvad-cli/src/cmds/tunnel.rs b/mullvad-cli/src/cmds/tunnel.rs index bf7172c655..a0c86fe1f0 100644 --- a/mullvad-cli/src/cmds/tunnel.rs +++ b/mullvad-cli/src/cmds/tunnel.rs @@ -22,6 +22,13 @@ impl Command for Tunnel { .subcommand( clap::SubCommand::with_name("set") .subcommand( + clap::SubCommand::with_name("ipv6").arg( + clap::Arg::with_name("enable") + .required(true) + .takes_value(true) + .possible_values(&["on", "off"]), + ), + ).subcommand( clap::SubCommand::with_name("mssfix").arg( clap::Arg::with_name("mssfix") .help( @@ -60,23 +67,38 @@ impl Tunnel { } fn set_openvpn_option(matches: &clap::ArgMatches) -> Result<()> { - if let Some(mssfix_args) = matches.subcommand_matches("mssfix") { - let mssfix_str = mssfix_args.value_of("mssfix").unwrap(); - let mssfix: Option<u16> = if mssfix_str == "" { - None - } else { - Some(mssfix_str.parse()?) - }; - - let mut rpc = DaemonRpcClient::new()?; - rpc.set_openvpn_mssfix(mssfix)?; - println!("mssfix parameter updated"); - Ok(()) + if let Some(ipv6_args) = matches.subcommand_matches("ipv6") { + Self::set_openvpn_enable_ipv6_option(ipv6_args) + } else if let Some(mssfix_args) = matches.subcommand_matches("mssfix") { + Self::set_openvpn_mssfix_option(mssfix_args) } else { unreachable!("Invalid option passed to 'openvpn set'"); } } + fn set_openvpn_enable_ipv6_option(args: &clap::ArgMatches) -> Result<()> { + let enabled = args.value_of("enable").unwrap() == "on"; + + let mut rpc = DaemonRpcClient::new()?; + rpc.set_openvpn_enable_ipv6(enabled)?; + println!("enable_ipv6 parameter updated"); + Ok(()) + } + + fn set_openvpn_mssfix_option(args: &clap::ArgMatches) -> Result<()> { + let mssfix_str = args.value_of("mssfix").unwrap(); + let mssfix: Option<u16> = if mssfix_str == "" { + None + } else { + Some(mssfix_str.parse()?) + }; + + let mut rpc = DaemonRpcClient::new()?; + rpc.set_openvpn_mssfix(mssfix)?; + println!("mssfix parameter updated"); + Ok(()) + } + fn get_tunnel_options() -> Result<TunnelOptions> { let mut rpc = DaemonRpcClient::new()?; Ok(rpc.get_tunnel_options()?) @@ -90,5 +112,9 @@ impl Tunnel { .mssfix .map_or_else(|| "UNSET".to_string(), |v| v.to_string()) ); + println!( + "\tIPv6: {}", + if options.enable_ipv6 { "on" } else { "off" } + ); } } diff --git a/mullvad-daemon/src/main.rs b/mullvad-daemon/src/main.rs index db322d8155..9c87bdef74 100644 --- a/mullvad-daemon/src/main.rs +++ b/mullvad-daemon/src/main.rs @@ -389,6 +389,9 @@ impl Daemon { SetAutoConnect(tx, auto_connect) => self.on_set_auto_connect(tx, auto_connect), GetAutoConnect(tx) => Ok(self.on_get_auto_connect(tx)), SetOpenVpnMssfix(tx, mssfix_arg) => self.on_set_openvpn_mssfix(tx, mssfix_arg), + SetOpenVpnEnableIpv6(tx, enable_ipv6) => { + self.on_set_openvpn_enable_ipv6(tx, enable_ipv6) + } GetTunnelOptions(tx) => self.on_get_tunnel_options(tx), GetRelaySettings(tx) => Ok(self.on_get_relay_settings(tx)), GetVersionInfo(tx) => Ok(self.on_get_version_info(tx)), @@ -577,6 +580,31 @@ impl Daemon { Ok(()) } + fn on_set_openvpn_enable_ipv6( + &mut self, + tx: OneshotSender<()>, + enable_ipv6: bool, + ) -> Result<()> { + let save_result = self.settings.set_openvpn_enable_ipv6(enable_ipv6); + + match save_result.chain_err(|| "Unable to save settings") { + Ok(settings_changed) => { + Self::oneshot_send(tx, (), "set_openvpn_enable_ipv6 response"); + + let tunnel_needs_restart = + self.state == TunnelState::Connecting || self.state == TunnelState::Connected; + + if settings_changed && tunnel_needs_restart { + info!("Initiating tunnel restart because the enable IPv6 setting changed"); + self.kill_tunnel()?; + } + } + Err(e) => error!("{}", e.display_chain()), + }; + + Ok(()) + } + fn on_get_tunnel_options(&self, tx: OneshotSender<TunnelOptions>) -> Result<()> { let tunnel_options = self.settings.get_tunnel_options().clone(); Self::oneshot_send(tx, tunnel_options, "get_tunnel_options response"); diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs index 230e478afb..2a77650a52 100644 --- a/mullvad-daemon/src/management_interface.rs +++ b/mullvad-daemon/src/management_interface.rs @@ -127,6 +127,10 @@ build_rpc_trait! { #[rpc(meta, name = "set_openvpn_mssfix")] fn set_openvpn_mssfix(&self, Self::Metadata, Option<u16>) -> BoxFuture<(), Error>; + /// Set if IPv6 is enabled in the tunnel + #[rpc(meta, name = "set_openvpn_enable_ipv6")] + fn set_openvpn_enable_ipv6(&self, Self::Metadata, bool) -> BoxFuture<(), Error>; + /// Gets tunnel specific options #[rpc(meta, name = "get_tunnel_options")] fn get_tunnel_options(&self, Self::Metadata) -> BoxFuture<TunnelOptions, Error>; @@ -195,7 +199,9 @@ pub enum TunnelCommand { GetAutoConnect(OneshotSender<bool>), /// Set the mssfix argument for OpenVPN SetOpenVpnMssfix(OneshotSender<()>, Option<u16>), - /// Get the mssfix argument for OpenVPN + /// Set if IPv6 should be enabled in the tunnel + SetOpenVpnEnableIpv6(OneshotSender<()>, bool), + /// Get the tunnel options GetTunnelOptions(OneshotSender<TunnelOptions>), /// Get information about the currently running and latest app versions GetVersionInfo(OneshotSender<BoxFuture<version::AppVersionInfo, mullvad_rpc::Error>>), @@ -637,6 +643,21 @@ impl<T: From<TunnelCommand> + 'static + Send> ManagementInterfaceApi for Managem Box::new(future) } + fn set_openvpn_enable_ipv6( + &self, + meta: Self::Metadata, + enable_ipv6: bool, + ) -> BoxFuture<(), Error> { + trace!("set_openvpn_enable_ipv6"); + try_future!(self.check_auth(&meta)); + let (tx, rx) = sync::oneshot::channel(); + let future = self + .send_command_to_daemon(TunnelCommand::SetOpenVpnEnableIpv6(tx, enable_ipv6)) + .and_then(|_| rx.map_err(|_| Error::internal_error())); + + Box::new(future) + } + fn get_tunnel_options(&self, meta: Self::Metadata) -> BoxFuture<TunnelOptions, Error> { trace!("get_tunnel_options"); try_future!(self.check_auth(&meta)); diff --git a/mullvad-daemon/src/settings.rs b/mullvad-daemon/src/settings.rs index b11b80017a..4256735cdf 100644 --- a/mullvad-daemon/src/settings.rs +++ b/mullvad-daemon/src/settings.rs @@ -180,6 +180,15 @@ impl Settings { } } + pub fn set_openvpn_enable_ipv6(&mut self, enable_ipv6: bool) -> Result<bool> { + if self.tunnel_options.openvpn.enable_ipv6 != enable_ipv6 { + self.tunnel_options.openvpn.enable_ipv6 = enable_ipv6; + self.save().map(|_| true) + } else { + Ok(false) + } + } + pub fn get_tunnel_options(&self) -> &TunnelOptions { &self.tunnel_options } diff --git a/mullvad-ipc-client/src/lib.rs b/mullvad-ipc-client/src/lib.rs index c4b4ebdd3f..6c1734a4c9 100644 --- a/mullvad-ipc-client/src/lib.rs +++ b/mullvad-ipc-client/src/lib.rs @@ -203,6 +203,10 @@ impl DaemonRpcClient { self.call("set_account", &[account]) } + pub fn set_openvpn_enable_ipv6(&mut self, enabled: bool) -> Result<()> { + self.call("set_openvpn_enable_ipv6", &[enabled]) + } + pub fn set_openvpn_mssfix(&mut self, mssfix: Option<u16>) -> Result<()> { self.call("set_openvpn_mssfix", &[mssfix]) } diff --git a/talpid-core/src/process/openvpn.rs b/talpid-core/src/process/openvpn.rs index 409cc605a7..8c3fae8f6a 100644 --- a/talpid-core/src/process/openvpn.rs +++ b/talpid-core/src/process/openvpn.rs @@ -184,6 +184,16 @@ impl OpenVpnCommand { args.push(OsString::from(mssfix.to_string())); } + if !self.tunnel_options.enable_ipv6 { + args.push(OsString::from("--pull-filter")); + args.push(OsString::from("ignore")); + args.push(OsString::from("route-ipv6")); + + args.push(OsString::from("--pull-filter")); + args.push(OsString::from("ignore")); + args.push(OsString::from("ifconfig-ipv6")); + } + if let Some(ref tunnel_device) = self.tunnel_alias { args.push(OsString::from("--dev-node")); args.push(tunnel_device.clone()); diff --git a/talpid-types/src/net.rs b/talpid-types/src/net.rs index 53a2b30ab5..296045fd05 100644 --- a/talpid-types/src/net.rs +++ b/talpid-types/src/net.rs @@ -138,9 +138,22 @@ pub struct TunnelOptions { /// OpenVpnTunnelOptions contains options for an openvpn tunnel that should be applied irrespective /// of the relay parameters - i.e. have nothing to do with the particular OpenVPN server, but do /// affect the connection. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Default)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(default)] pub struct OpenVpnTunnelOptions { /// Optional argument for openvpn to try and limit TCP packet size, /// as discussed [here](https://openvpn.net/archive/openvpn-users/2003-11/msg00154.html) pub mssfix: Option<u16>, + /// Enable configuration of IPv6 on the tunnel interface, allowing IPv6 communication to be + /// forwarded through the tunnel. By default, this is set to `true`. + pub enable_ipv6: bool, +} + +impl Default for OpenVpnTunnelOptions { + fn default() -> Self { + OpenVpnTunnelOptions { + mssfix: None, + enable_ipv6: true, + } + } } diff --git a/test/components/SelectLocation.spec.js b/test/components/SelectLocation.spec.js index a8cd18cba3..8db9c99602 100644 --- a/test/components/SelectLocation.spec.js +++ b/test/components/SelectLocation.spec.js @@ -42,6 +42,7 @@ describe('components/SelectLocation', () => { ], autoConnect: false, allowLan: false, + enableIpv6: true, }; const makeProps = ( |
