diff options
| author | Emīls <emils@mullvad.net> | 2020-10-28 12:21:31 +0000 |
|---|---|---|
| committer | Emīls <emils@mullvad.net> | 2020-10-28 12:21:31 +0000 |
| commit | 3dac78afdbe264a8ea88f4f26cfe1f07991c4418 (patch) | |
| tree | 332da1d9cc365b8ccef4459114986ee777dcb81e | |
| parent | 673eea790b9807d55ee3f2dac6c563a20932cbeb (diff) | |
| parent | 56b4c36ed814f4ca7b22512a1313819c02d75562 (diff) | |
| download | mullvadvpn-3dac78afdbe264a8ea88f4f26cfe1f07991c4418.tar.xz mullvadvpn-3dac78afdbe264a8ea88f4f26cfe1f07991c4418.zip | |
Merge branch 'linux-nm-wg-use-older-api' into master
| -rw-r--r-- | talpid-core/src/dns/linux/network_manager.rs | 6 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/wireguard/mod.rs | 33 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/wireguard/network_manager.rs | 120 |
3 files changed, 124 insertions, 35 deletions
diff --git a/talpid-core/src/dns/linux/network_manager.rs b/talpid-core/src/dns/linux/network_manager.rs index ed5944cd41..36ef2c56f3 100644 --- a/talpid-core/src/dns/linux/network_manager.rs +++ b/talpid-core/src/dns/linux/network_manager.rs @@ -222,7 +222,11 @@ impl NetworkManager { // if the link contains link local addresses, addresses shouldn't be reset if ipv6_settings .get("method") - .map(|method| method.as_str() != Some("link-local")) + .map(|method| { + // if IPv6 isn't enabled, IPv6 method will be set to "ignore", in which case we + // shouldn't reapply any config for ipv6 + method.as_str() != Some("link-local") && method.as_str() != Some("ignore") + }) .unwrap_or(true) { ipv6_settings.insert("addresses", Variant(Box::new(device_addresses6))); diff --git a/talpid-core/src/tunnel/wireguard/mod.rs b/talpid-core/src/tunnel/wireguard/mod.rs index 8d60db885d..4edcc93e94 100644 --- a/talpid-core/src/tunnel/wireguard/mod.rs +++ b/talpid-core/src/tunnel/wireguard/mod.rs @@ -158,6 +158,25 @@ impl WireguardMonitor { return Ok(Box::new(tunnel)); } Err(err) => { + if !err.should_use_userspace() { + match wireguard_kernel::KernelTunnel::new( + route_manager.runtime_handle(), + config, + ) { + Ok(tunnel) => { + log::debug!("Using kernel WireGuard implementation"); + return Ok(Box::new(tunnel)); + } + Err(error) => { + log::error!( + "{}", + error.display_chain_with_msg( + "Failed to setup kernel WireGuard device, falling back to userspace" + ) + ); + } + }; + } log::debug!( "{}", err.display_chain_with_msg( @@ -166,20 +185,6 @@ impl WireguardMonitor { ); } }; - match wireguard_kernel::KernelTunnel::new(route_manager.runtime_handle(), config) { - Ok(tunnel) => { - log::debug!("Using kernel WireGuard implementation"); - return Ok(Box::new(tunnel)); - } - Err(error) => { - log::error!( - "{}", - error.display_chain_with_msg( - "Failed to setup kernel WireGuard device, falling back to userspace" - ) - ); - } - }; } else { log::debug!("Using userspace WireGuard implementation"); } diff --git a/talpid-core/src/tunnel/wireguard/network_manager.rs b/talpid-core/src/tunnel/wireguard/network_manager.rs index 56e36f7181..978128b1c6 100644 --- a/talpid-core/src/tunnel/wireguard/network_manager.rs +++ b/talpid-core/src/tunnel/wireguard/network_manager.rs @@ -22,6 +22,12 @@ const NM_ADD_CONNECTION_VOLATILE: u32 = 0x2; const TRAFFIC_STATS_REFRESH_RATE_MS: u32 = 1000; const RPC_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(3); +const DBUS_UNKNOWN_METHOD: &str = "org.freedesktop.DBus.Error.UnknownMethod"; + +const MINIMUM_SUPPORTED_MAJOR_VERSION: u32 = 1; +const MINIMUM_SUPPORTED_MINOR_VERSION: u32 = 16; + + pub type Result<T> = std::result::Result<T, Error>; #[derive(err_derive::Error, Debug)] @@ -44,6 +50,18 @@ pub enum Error { #[error(display = "Configuration has no device associated to it")] NoDevice, + + #[error(display = "NetworkManager is too old - {}", _0)] + NMTooOld(String), +} + +impl Error { + pub fn should_use_userspace(&self) -> bool { + match self { + Error::NMTooOld(_) => true, + _ => false, + } + } } pub struct NetworkManager { @@ -60,6 +78,7 @@ type DbusSettings = HashMap<String, VariantMap>; impl NetworkManager { pub fn new(config: &Config) -> Result<Self> { let mut dbus_connection = Connection::new_system().map_err(Error::Dbus)?; + Self::ensure_nm_is_new_enough(&dbus_connection)?; let tunnel = Some(Self::create_wg_tunnel(&mut dbus_connection, config)?); let manager = NetworkManager { dbus_connection, @@ -70,27 +89,47 @@ impl NetworkManager { Ok(manager) } + fn ensure_nm_is_new_enough(connection: &Connection) -> Result<()> { + let manager = Self::nm_proxy(connection); + let version_string: String = manager.get(NM_MANAGER, "Version").map_err(Error::Dbus)?; + let version_too_old = || Error::NMTooOld(version_string.clone()); + let mut parts = version_string + .split(".") + .map(|part| part.parse().map_err(|_| version_too_old())); + + let major_version: u32 = parts.next().ok_or_else(|| version_too_old())??; + let minor_version: u32 = parts.next().ok_or_else(|| version_too_old())??; + + if major_version < MINIMUM_SUPPORTED_MAJOR_VERSION + || minor_version < MINIMUM_SUPPORTED_MINOR_VERSION + { + Err(version_too_old()) + } else { + Ok(()) + } + } + + fn nm_proxy<'a>(connection: &'a Connection) -> Proxy<'a, &Connection> { + Proxy::new(NM_BUS, NM_MANAGER_PATH, RPC_TIMEOUT, connection) + } + fn create_wg_tunnel(dbus_connection: &Connection, config: &Config) -> Result<WireguardTunnel> { let settings_map = Self::convert_config_to_dbus(config); - let args: VariantMap = HashMap::new(); - let new_device = Message::new_method_call( - NM_BUS, - NM_SETTINGS_PATH, - NM_INTERFACE_SETTINGS, - "AddConnection2", - ) - .map_err(Error::DbusMethodCall)? - .append3(settings_map, NM_ADD_CONNECTION_VOLATILE, args); - - let application = dbus_connection - .send_with_reply_and_block(new_device, RPC_TIMEOUT) - .map_err(Error::Dbus)?; - let (config_path, _result): (Path<'static>, DbusSettings) = - application.read2().map_err(Error::MatchDBusTypeError)?; + let config_path: Path<'static> = Self::add_connection_2(dbus_connection, &settings_map) + .map(|(path, _result)| path) + .or_else(|err| { + log::error!("Failed to create a new interface via NM - {}", err); + match err { + Error::Dbus(dbus_error) if dbus_error.name() == Some(DBUS_UNKNOWN_METHOD) => { + Self::add_connection_unsaved(dbus_connection, &settings_map) + } + err => Err(err), + } + })?; - let manager = Proxy::new(NM_BUS, NM_MANAGER_PATH, RPC_TIMEOUT, dbus_connection); + let manager = Self::nm_proxy(dbus_connection); let (connection_path,): (Path<'static>,) = manager .method_call( NM_MANAGER, @@ -116,6 +155,47 @@ impl NetworkManager { }) } + fn add_connection_2( + connection: &Connection, + settings_map: &DbusSettings, + ) -> Result<(Path<'static>, DbusSettings)> { + let args: VariantMap = HashMap::new(); + let new_device = Message::new_method_call( + NM_BUS, + NM_SETTINGS_PATH, + NM_INTERFACE_SETTINGS, + "AddConnection2", + ) + .map_err(Error::DbusMethodCall)? + .append3(settings_map, NM_ADD_CONNECTION_VOLATILE, args); + + connection + .send_with_reply_and_block(new_device, RPC_TIMEOUT) + .map_err(Error::Dbus)? + .read2() + .map_err(Error::MatchDBusTypeError) + } + + fn add_connection_unsaved( + connection: &Connection, + settings_map: &DbusSettings, + ) -> Result<Path<'static>> { + let new_connection = Message::new_method_call( + NM_BUS, + NM_SETTINGS_PATH, + NM_INTERFACE_SETTINGS, + "AddConnectionUnsaved", + ) + .map_err(Error::DbusMethodCall)? + .append1(settings_map); + + connection + .send_with_reply_and_block(new_connection, RPC_TIMEOUT) + .map_err(Error::Dbus)? + .read1() + .map_err(Error::MatchDBusTypeError) + } + fn convert_config_to_dbus(config: &Config) -> DbusSettings { let mut ipv6_config: VariantMap = HashMap::new(); let mut ipv4_config: VariantMap = HashMap::new(); @@ -130,8 +210,6 @@ impl NetworkManager { Variant(Box::new(config.tunnel.private_key.to_base64())), ); wireguard_config.insert("private-key-flags".into(), Variant(Box::new(0x0u32))); - wireguard_config.insert("ip4-auto-default-route".into(), Variant(Box::new(0u32))); - wireguard_config.insert("ip6-auto-default-route".into(), Variant(Box::new(0u32))); for peer in config.peers.iter() { let mut peer_config: VariantMap = HashMap::new(); @@ -188,17 +266,19 @@ impl NetworkManager { ipv4_config.insert("method".into(), Variant(Box::new("manual".to_string()))); if !ipv6_addrs.is_empty() { + ipv6_config.insert("method".into(), Variant(Box::new("manual".to_string()))); ipv6_config.insert("address-data".into(), Variant(Box::new(ipv6_addrs))); ipv6_config.insert("ignore-auto-routes".into(), Variant(Box::new(true))); ipv6_config.insert("ignore-auto-dns".into(), Variant(Box::new(true))); ipv6_config.insert("may-fail".into(), Variant(Box::new(true))); - ipv6_config.insert("method".into(), Variant(Box::new("manual".to_string()))); } let mut settings = HashMap::new(); settings.insert("ipv4".into(), ipv4_config); - settings.insert("ipv6".into(), ipv6_config); + if !ipv6_config.is_empty() { + settings.insert("ipv6".into(), ipv6_config); + } settings.insert("wireguard".into(), wireguard_config); settings.insert("connection".into(), connection_config); |
