summaryrefslogtreecommitdiffhomepage
path: root/gui/src
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2020-03-09 20:33:19 +0100
committerOskar Nyberg <oskar@mullvad.net>2020-03-10 17:20:29 +0100
commitf9aa642563c6c1bc0d6a8db8f0f780348f4edcbd (patch)
treec0f97b377b15d8096c8f6ee3b35c37aa3d0c81d8 /gui/src
parentc2018a0c960990bfdc428dd9c2c3eb3c7e21e1ba (diff)
downloadmullvadvpn-f9aa642563c6c1bc0d6a8db8f0f780348f4edcbd.tar.xz
mullvadvpn-f9aa642563c6c1bc0d6a8db8f0f780348f4edcbd.zip
Add WireGuard MTU setting to gui
Diffstat (limited to 'gui/src')
-rw-r--r--gui/src/renderer/components/AdvancedSettings.tsx131
-rw-r--r--gui/src/renderer/components/AdvancedSettingsStyles.tsx11
-rw-r--r--gui/src/renderer/components/Cell.tsx230
-rw-r--r--gui/src/renderer/components/CellStyles.tsx130
-rw-r--r--gui/src/renderer/containers/AdvancedSettingsPage.tsx9
5 files changed, 311 insertions, 200 deletions
diff --git a/gui/src/renderer/components/AdvancedSettings.tsx b/gui/src/renderer/components/AdvancedSettings.tsx
index 10757e4d8d..27d4043d7c 100644
--- a/gui/src/renderer/components/AdvancedSettings.tsx
+++ b/gui/src/renderer/components/AdvancedSettings.tsx
@@ -20,6 +20,8 @@ import SettingsHeader, { HeaderTitle } from './SettingsHeader';
const MIN_MSSFIX_VALUE = 1000;
const MAX_MSSFIX_VALUE = 1450;
+const MIN_WIREGUARD_MTU_VALUE = 1280;
+const MAX_WIREGUARD_MTU_VALUE = 1420;
const UDP_PORTS = [1194, 1195, 1196, 1197, 1300, 1301, 1302];
const TCP_PORTS = [80, 443];
const WIREUGARD_UDP_PORTS = [53];
@@ -44,25 +46,21 @@ interface IProps {
wireguardKeyState: WgKeyState;
wireguard: { port?: number };
mssfix?: number;
+ wireguardMtu?: number;
bridgeState: BridgeState;
setBridgeState: (value: BridgeState) => void;
setEnableIpv6: (value: boolean) => void;
setBlockWhenDisconnected: (value: boolean) => void;
setTunnelProtocol: (value: OptionalTunnelProtocol) => void;
setOpenVpnMssfix: (value: number | undefined) => void;
+ setWireguardMtu: (value: number | undefined) => void;
setOpenVpnRelayProtocolAndPort: (protocol?: RelayProtocol, port?: number) => void;
setWireguardRelayPort: (port?: number) => void;
onViewWireguardKeys: () => void;
onClose: () => void;
}
-interface IState {
- persistedMssfix?: number;
- editedMssfix?: number;
- focusOnMssfix: boolean;
-}
-
-export default class AdvancedSettings extends Component<IProps, IState> {
+export default class AdvancedSettings extends Component<IProps> {
private portItems: { [key in RelayProtocol]: Array<ISelectorItem<OptionalPort>> };
private protocolItems: Array<ISelectorItem<OptionalRelayProtocol>>;
private bridgeStateItems: Array<ISelectorItem<BridgeState>>;
@@ -118,30 +116,9 @@ export default class AdvancedSettings extends Component<IProps, IState> {
value: 'off',
},
];
-
- this.state = {
- persistedMssfix: props.mssfix,
- editedMssfix: props.mssfix,
- focusOnMssfix: false,
- };
- }
-
- public componentDidUpdate(_prevProps: IProps, _prevState: IState) {
- if (this.props.mssfix !== this.state.persistedMssfix) {
- this.setState((state, props) => ({
- ...state,
- persistedMssfix: props.mssfix,
- editedMssfix: state.focusOnMssfix ? state.editedMssfix : props.mssfix,
- }));
- }
}
public render() {
- const mssfixStyle = this.mssfixIsValid()
- ? styles.advanced_settings__mssfix_valid_value
- : styles.advanced_settings__mssfix_invalid_value;
- const mssfixValue = this.state.editedMssfix;
-
const hasWireguardKey = this.props.wireguardKeyState.type === 'key-set';
return (
@@ -298,18 +275,20 @@ export default class AdvancedSettings extends Component<IProps, IState> {
/>
<Cell.Container>
- <Cell.Label>{messages.pgettext('advanced-settings-view', 'Mssfix')}</Cell.Label>
- <Cell.InputFrame style={styles.advanced_settings__mssfix_frame}>
+ <Cell.Label>
+ {messages.pgettext('advanced-settings-view', 'OpenVPN Mssfix')}
+ </Cell.Label>
+ <Cell.InputFrame style={styles.advanced_settings__input_frame}>
<Cell.AutoSizingTextInputContainer>
<Cell.Input
+ value={this.props.mssfix ? this.props.mssfix.toString() : ''}
keyboardType={'numeric'}
maxLength={4}
placeholder={messages.pgettext('advanced-settings-view', 'Default')}
- value={mssfixValue ? mssfixValue.toString() : ''}
- style={[styles.advanced_settings__mssfix_input, mssfixStyle]}
- onChangeText={this.onMssfixChange}
- onFocus={this.onMssfixFocus}
- onBlur={this.onMssfixBlur}
+ onSubmit={this.onMssfixSubmit}
+ validateValue={AdvancedSettings.mssfixIsValid}
+ submitOnBlur={true}
+ modifyValue={AdvancedSettings.removeNonNumericCharacters}
/>
</Cell.AutoSizingTextInputContainer>
</Cell.InputFrame>
@@ -332,6 +311,45 @@ export default class AdvancedSettings extends Component<IProps, IState> {
)}
</Cell.FooterText>
</Cell.Footer>
+
+ <Cell.Container>
+ <Cell.Label>
+ {messages.pgettext('advanced-settings-view', 'WireGuard MTU')}
+ </Cell.Label>
+ <Cell.InputFrame style={styles.advanced_settings__input_frame}>
+ <Cell.AutoSizingTextInputContainer>
+ <Cell.Input
+ value={this.props.wireguardMtu ? this.props.wireguardMtu.toString() : ''}
+ keyboardType={'numeric'}
+ maxLength={4}
+ placeholder={messages.pgettext('advanced-settings-view', 'Default')}
+ onSubmit={this.onWireguardMtuSubmit}
+ validateValue={AdvancedSettings.wireguarMtuIsValid}
+ submitOnBlur={true}
+ modifyValue={AdvancedSettings.removeNonNumericCharacters}
+ />
+ </Cell.AutoSizingTextInputContainer>
+ </Cell.InputFrame>
+ </Cell.Container>
+ <Cell.Footer>
+ <Cell.FooterText>
+ {sprintf(
+ // TRANSLATORS: The hint displayed below the WireGuard MTU input field.
+ // TRANSLATORS: Available placeholders:
+ // TRANSLATORS: %(max)d - the maximum possible wireguard mtu value
+ // TRANSLATORS: %(min)d - the minimum possible wireguard mtu value
+ messages.pgettext(
+ 'advanced-settings-view',
+ 'Set WireGuard MTU value. Valid range: %(min)d - %(max)d.',
+ ),
+ {
+ min: MIN_WIREGUARD_MTU_VALUE,
+ max: MAX_WIREGUARD_MTU_VALUE,
+ },
+ )}
+ </Cell.FooterText>
+ </Cell.Footer>
+
<View style={styles.advanced_settings__wgkeys_cell}>
<Cell.CellButton onPress={this.props.onViewWireguardKeys}>
<Cell.Label>
@@ -394,32 +412,37 @@ export default class AdvancedSettings extends Component<IProps, IState> {
this.props.setBridgeState(bridgeState);
};
- private onMssfixChange = (mssfixString: string) => {
- const mssfix = mssfixString.replace(/[^0-9]/g, '');
-
- if (mssfix === '') {
- this.setState({ editedMssfix: undefined });
- } else {
- this.setState({ editedMssfix: parseInt(mssfix, 10) });
+ private onMssfixSubmit = (value: string) => {
+ const parsedValue = value === '' ? undefined : parseInt(value, 10);
+ if (AdvancedSettings.mssfixIsValid(value)) {
+ this.props.setOpenVpnMssfix(parsedValue);
}
};
- private onMssfixFocus = () => {
- this.setState({ focusOnMssfix: true });
- };
+ private static removeNonNumericCharacters(value: string) {
+ return value.replace(/[^0-9]/g, '');
+ }
- private onMssfixBlur = () => {
- this.setState({ focusOnMssfix: false });
+ private static mssfixIsValid(mssfix: string): boolean {
+ const parsedMssFix = mssfix ? parseInt(mssfix) : undefined;
+ return (
+ parsedMssFix === undefined ||
+ (parsedMssFix >= MIN_MSSFIX_VALUE && parsedMssFix <= MAX_MSSFIX_VALUE)
+ );
+ }
- if (this.mssfixIsValid()) {
- this.props.setOpenVpnMssfix(this.state.editedMssfix);
- this.setState((state, _props) => ({ persistedMssfix: state.editedMssfix }));
+ private onWireguardMtuSubmit = (value: string) => {
+ const parsedValue = value === '' ? undefined : parseInt(value, 10);
+ if (AdvancedSettings.wireguarMtuIsValid(value)) {
+ this.props.setWireguardMtu(parsedValue);
}
};
- private mssfixIsValid(): boolean {
- const mssfix = this.state.editedMssfix;
-
- return mssfix === undefined || (mssfix >= MIN_MSSFIX_VALUE && mssfix <= MAX_MSSFIX_VALUE);
+ private static wireguarMtuIsValid(mtu: string): boolean {
+ const parsedMtu = mtu ? parseInt(mtu) : undefined;
+ return (
+ parsedMtu === undefined ||
+ (parsedMtu >= MIN_WIREGUARD_MTU_VALUE && parsedMtu <= MAX_WIREGUARD_MTU_VALUE)
+ );
}
}
diff --git a/gui/src/renderer/components/AdvancedSettingsStyles.tsx b/gui/src/renderer/components/AdvancedSettingsStyles.tsx
index acdc9e5d8e..737a3e1acd 100644
--- a/gui/src/renderer/components/AdvancedSettingsStyles.tsx
+++ b/gui/src/renderer/components/AdvancedSettingsStyles.tsx
@@ -55,18 +55,9 @@ export default {
advanced_settings__cell_footer_internet_warning_label: Styles.createTextStyle({
marginTop: 4,
}),
- advanced_settings__mssfix_input: Styles.createTextInputStyle({
- minWidth: 80,
- }),
- advanced_settings__mssfix_frame: Styles.createViewStyle({
+ advanced_settings__input_frame: Styles.createViewStyle({
flex: 0,
}),
- advanced_settings__mssfix_valid_value: Styles.createTextStyle({
- color: colors.white,
- }),
- advanced_settings__mssfix_invalid_value: Styles.createTextStyle({
- color: colors.red,
- }),
advanced_settings__block_when_disconnected_label: Styles.createTextStyle({
letterSpacing: -0.5,
}),
diff --git a/gui/src/renderer/components/Cell.tsx b/gui/src/renderer/components/Cell.tsx
index ddd5d0ea53..8d884db307 100644
--- a/gui/src/renderer/components/Cell.tsx
+++ b/gui/src/renderer/components/Cell.tsx
@@ -1,130 +1,10 @@
import * as React from 'react';
import { Button, Component, Styles, Text, TextInput, Types, View } from 'reactxp';
import { colors } from '../../config.json';
+import styles from './CellStyles';
import ImageView from './ImageView';
import { default as SwitchControl } from './Switch';
-const styles = {
- cellButton: {
- base: Styles.createButtonStyle({
- backgroundColor: colors.blue,
- paddingVertical: 0,
- paddingHorizontal: 16,
- marginBottom: 1,
- flex: 1,
- flexDirection: 'row',
- alignItems: 'center',
- alignContent: 'center',
- cursor: 'default',
- }),
- section: Styles.createButtonStyle({
- backgroundColor: colors.blue40,
- }),
- hover: Styles.createButtonStyle({
- backgroundColor: colors.blue80,
- }),
- selected: Styles.createViewStyle({
- backgroundColor: colors.green,
- }),
- },
- cellContainer: Styles.createViewStyle({
- backgroundColor: colors.blue,
- flexDirection: 'row',
- alignItems: 'center',
- paddingLeft: 16,
- paddingRight: 12,
- }),
- footer: {
- container: Styles.createViewStyle({
- paddingTop: 8,
- paddingRight: 24,
- paddingBottom: 24,
- paddingLeft: 24,
- }),
- text: Styles.createTextStyle({
- fontFamily: 'Open Sans',
- fontSize: 13,
- fontWeight: '600',
- lineHeight: 20,
- letterSpacing: -0.2,
- color: colors.white80,
- }),
- boldText: Styles.createTextStyle({
- fontWeight: '900',
- }),
- },
- label: {
- container: Styles.createViewStyle({
- marginLeft: 8,
- marginTop: 14,
- marginBottom: 14,
- flex: 1,
- }),
- text: Styles.createTextStyle({
- fontFamily: 'DINPro',
- fontSize: 20,
- fontWeight: '900',
- lineHeight: 26,
- letterSpacing: -0.2,
- color: colors.white,
- }),
- },
- switch: Styles.createViewStyle({
- flex: 0,
- }),
- input: {
- frame: Styles.createViewStyle({
- flexGrow: 0,
- backgroundColor: 'rgba(255,255,255,0.1)',
- borderRadius: 4,
- padding: 4,
- }),
- text: Styles.createTextInputStyle({
- color: colors.white,
- backgroundColor: 'transparent',
- fontFamily: 'Open Sans',
- fontSize: 20,
- fontWeight: '600',
- lineHeight: 26,
- textAlign: 'right',
- padding: 0,
- }),
- },
- autoSizingInputContainer: {
- measuringView: Styles.createViewStyle({
- position: 'absolute',
- opacity: 0,
- }),
- measureText: Styles.createTextStyle({
- width: undefined,
- flexBasis: undefined,
- }),
- },
- icon: Styles.createViewStyle({
- marginLeft: 8,
- }),
- subtext: Styles.createTextStyle({
- color: colors.white60,
- fontFamily: 'Open Sans',
- fontSize: 13,
- fontWeight: '800',
- flex: -1,
- textAlign: 'right',
- marginLeft: 8,
- }),
- sectionTitle: Styles.createTextStyle({
- backgroundColor: colors.blue,
- paddingVertical: 14,
- paddingHorizontal: 24,
- marginBottom: 1,
- fontFamily: 'DINPro',
- fontSize: 20,
- fontWeight: '900',
- lineHeight: 26,
- color: colors.white,
- }),
-};
-
interface ICellButtonProps {
children?: React.ReactNode;
disabled?: boolean;
@@ -272,22 +152,100 @@ export const InputFrame = function CellInputFrame(props: IInputFrameProps) {
return <View style={[styles.input.frame, style]}>{children}</View>;
};
-export const Input = React.forwardRef(function CellInput(
- props: Types.TextInputProps,
- ref?: React.Ref<TextInput>,
-) {
- const { style, ...otherProps } = props;
+interface IInputProps extends Types.TextInputProps {
+ validateValue?: (value: string) => boolean;
+ modifyValue?: (value: string) => string;
+ submitOnBlur?: boolean;
+ onSubmit?: (value: string) => void;
+}
- return (
- <TextInput
- ref={ref}
- placeholderTextColor={colors.white60}
- autoCorrect={false}
- style={[styles.input.text, style]}
- {...otherProps}
- />
- );
-});
+interface IInputState {
+ value?: string;
+ focused: boolean;
+}
+
+export class Input extends Component<IInputProps, IInputState> {
+ public state = {
+ value: this.props.value || '',
+ focused: false,
+ };
+
+ public componentDidUpdate(prevProps: IInputProps, _prevState: IInputState) {
+ if (
+ !this.state.focused &&
+ prevProps.value !== this.props.value &&
+ this.props.value !== this.state.value
+ ) {
+ this.setState((_state, props) => ({
+ value: props.value,
+ }));
+ }
+ }
+
+ public render() {
+ const {
+ style,
+ value: _value,
+ onChangeText: _onChangeText,
+ onFocus: _onFocus,
+ onBlur: _onBlur,
+ onSubmitEditing: _onSubmitEditing,
+ ...otherProps
+ } = this.props;
+
+ const validityStyle =
+ this.props.validateValue && this.props.validateValue(this.state.value)
+ ? styles.input.validValue
+ : styles.input.invalidValue;
+
+ return (
+ <TextInput
+ placeholderTextColor={colors.white60}
+ autoCorrect={false}
+ style={[styles.input.text, validityStyle, style]}
+ onChangeText={this.onChangeText}
+ onFocus={this.onFocus}
+ onBlur={this.onBlur}
+ onSubmitEditing={this.onSubmitEditing}
+ value={this.state.value}
+ {...otherProps}
+ />
+ );
+ }
+
+ private onChangeText = (value: string) => {
+ this.setState({ value });
+ if (this.props.onChangeText) {
+ this.props.onChangeText(value);
+ }
+ };
+
+ private onFocus = (e: Types.FocusEvent) => {
+ this.setState({ focused: true });
+ if (this.props.onFocus) {
+ this.props.onFocus(e);
+ }
+ };
+
+ private onBlur = (e: Types.FocusEvent) => {
+ this.setState({ focused: false });
+ if (this.props.onBlur) {
+ this.props.onBlur(e);
+ }
+ if (this.props.submitOnBlur && this.props.onSubmit) {
+ this.props.onSubmit(this.state.value);
+ }
+ };
+
+ private onSubmitEditing = () => {
+ if (this.props.onSubmit) {
+ this.props.onSubmit(this.state.value);
+ }
+ if (this.props.onSubmitEditing) {
+ this.props.onSubmitEditing();
+ }
+ };
+}
interface IAutoSizingTextInputContainerProps {
style?: Types.StyleRuleSetRecursive<Types.ViewStyleRuleSet>;
diff --git a/gui/src/renderer/components/CellStyles.tsx b/gui/src/renderer/components/CellStyles.tsx
new file mode 100644
index 0000000000..453a8d3cca
--- /dev/null
+++ b/gui/src/renderer/components/CellStyles.tsx
@@ -0,0 +1,130 @@
+import { Styles } from 'reactxp';
+import { colors } from '../../config.json';
+
+export default {
+ cellButton: {
+ base: Styles.createButtonStyle({
+ backgroundColor: colors.blue,
+ paddingVertical: 0,
+ paddingHorizontal: 16,
+ marginBottom: 1,
+ flex: 1,
+ flexDirection: 'row',
+ alignItems: 'center',
+ alignContent: 'center',
+ cursor: 'default',
+ }),
+ section: Styles.createButtonStyle({
+ backgroundColor: colors.blue40,
+ }),
+ hover: Styles.createButtonStyle({
+ backgroundColor: colors.blue80,
+ }),
+ selected: Styles.createViewStyle({
+ backgroundColor: colors.green,
+ }),
+ },
+ cellContainer: Styles.createViewStyle({
+ backgroundColor: colors.blue,
+ flexDirection: 'row',
+ alignItems: 'center',
+ paddingLeft: 16,
+ paddingRight: 12,
+ }),
+ footer: {
+ container: Styles.createViewStyle({
+ paddingTop: 8,
+ paddingRight: 24,
+ paddingBottom: 24,
+ paddingLeft: 24,
+ }),
+ text: Styles.createTextStyle({
+ fontFamily: 'Open Sans',
+ fontSize: 13,
+ fontWeight: '600',
+ lineHeight: 20,
+ letterSpacing: -0.2,
+ color: colors.white80,
+ }),
+ boldText: Styles.createTextStyle({
+ fontWeight: '900',
+ }),
+ },
+ label: {
+ container: Styles.createViewStyle({
+ marginLeft: 8,
+ marginTop: 14,
+ marginBottom: 14,
+ flex: 1,
+ }),
+ text: Styles.createTextStyle({
+ fontFamily: 'DINPro',
+ fontSize: 20,
+ fontWeight: '900',
+ lineHeight: 26,
+ letterSpacing: -0.2,
+ color: colors.white,
+ }),
+ },
+ switch: Styles.createViewStyle({
+ flex: 0,
+ }),
+ input: {
+ frame: Styles.createViewStyle({
+ flexGrow: 0,
+ backgroundColor: 'rgba(255,255,255,0.1)',
+ borderRadius: 4,
+ padding: 4,
+ }),
+ text: Styles.createTextInputStyle({
+ color: colors.white,
+ backgroundColor: 'transparent',
+ fontFamily: 'Open Sans',
+ fontSize: 20,
+ fontWeight: '600',
+ lineHeight: 26,
+ textAlign: 'right',
+ padding: 0,
+ minWidth: 80,
+ }),
+ validValue: Styles.createTextStyle({
+ color: colors.white,
+ }),
+ invalidValue: Styles.createTextStyle({
+ color: colors.red,
+ }),
+ },
+ autoSizingInputContainer: {
+ measuringView: Styles.createViewStyle({
+ position: 'absolute',
+ opacity: 0,
+ }),
+ measureText: Styles.createTextStyle({
+ width: undefined,
+ flexBasis: undefined,
+ }),
+ },
+ icon: Styles.createViewStyle({
+ marginLeft: 8,
+ }),
+ subtext: Styles.createTextStyle({
+ color: colors.white60,
+ fontFamily: 'Open Sans',
+ fontSize: 13,
+ fontWeight: '800',
+ flex: -1,
+ textAlign: 'right',
+ marginLeft: 8,
+ }),
+ sectionTitle: Styles.createTextStyle({
+ backgroundColor: colors.blue,
+ paddingVertical: 14,
+ paddingHorizontal: 24,
+ marginBottom: 1,
+ fontFamily: 'DINPro',
+ fontSize: 20,
+ fontWeight: '900',
+ lineHeight: 26,
+ color: colors.white,
+ }),
+};
diff --git a/gui/src/renderer/containers/AdvancedSettingsPage.tsx b/gui/src/renderer/containers/AdvancedSettingsPage.tsx
index b61003bc8f..c9f33f3d63 100644
--- a/gui/src/renderer/containers/AdvancedSettingsPage.tsx
+++ b/gui/src/renderer/containers/AdvancedSettingsPage.tsx
@@ -18,6 +18,7 @@ const mapStateToProps = (state: IReduxState) => {
blockWhenDisconnected: state.settings.blockWhenDisconnected,
wireguardKeyState: state.settings.wireguardKeyState,
mssfix: state.settings.openVpn.mssfix,
+ wireguardMtu: state.settings.wireguard.mtu,
bridgeState: state.settings.bridgeState,
...protocolAndPort,
};
@@ -145,6 +146,14 @@ const mapDispatchToProps = (dispatch: ReduxDispatch, props: IAppContext) => {
log.error('Failed to update mssfix value', e.message);
}
},
+
+ setWireguardMtu: async (mtu?: number) => {
+ try {
+ await props.app.setWireguardMtu(mtu);
+ } catch (e) {
+ log.error('Failed to update mtu value', e.message);
+ }
+ },
onViewWireguardKeys: () => history.push('/settings/advanced/wireguard-keys'),
};
};