summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorLinus Färnstrand <linus@mullvad.net>2017-05-23 15:40:48 +0200
committerLinus Färnstrand <linus@mullvad.net>2017-05-23 15:40:48 +0200
commitc2aaa4eb26af015ef949785668751cded783675d (patch)
tree63c1c854c8349400d6c50b01784053de00381e38
parent54754e78fa41244f661bf76d921b35edeb6b9be8 (diff)
parentb642c63dc281746cef24a59aa01f9d30ce441ca9 (diff)
downloadmullvadvpn-c2aaa4eb26af015ef949785668751cded783675d.tar.xz
mullvadvpn-c2aaa4eb26af015ef949785668751cded783675d.zip
Merge branch 'tunnel-monitor'
-rw-r--r--talpid_cli/src/main.rs45
-rw-r--r--talpid_core/src/tunnel/mod.rs104
-rw-r--r--talpid_core/src/tunnel/openvpn.rs1
3 files changed, 119 insertions, 31 deletions
diff --git a/talpid_cli/src/main.rs b/talpid_cli/src/main.rs
index 8433fa03e8..2f1e5c36dc 100644
--- a/talpid_cli/src/main.rs
+++ b/talpid_cli/src/main.rs
@@ -9,17 +9,14 @@ extern crate error_chain;
extern crate log;
extern crate env_logger;
-use std::path::Path;
use std::sync::Mutex;
use std::sync::mpsc::{self, Receiver};
+use talpid_core::net::RemoteAddr;
-use talpid_core::process::openvpn::OpenVpnCommand;
-use talpid_core::tunnel::openvpn::{OpenVpnEvent, OpenVpnMonitor};
+use talpid_core::tunnel::{TunnelEvent, TunnelMonitor};
mod cli;
-use cli::Args;
-
error_chain!{}
@@ -28,52 +25,38 @@ quick_main!(run);
fn run() -> Result<()> {
init_logger()?;
let args = cli::parse_args_or_exit();
- let command = create_openvpn_command(&args);
- main_loop(&command, args.plugin_path.as_path())
+ main_loop(&args.remotes)
}
pub fn init_logger() -> Result<()> {
env_logger::init().chain_err(|| "Failed to bootstrap logging system")
}
-fn create_openvpn_command(args: &Args) -> OpenVpnCommand {
- let mut command = OpenVpnCommand::new(&args.binary);
- command
- .config(&args.config)
- .remotes(&args.remotes[..])
- .unwrap();
- command
-}
-
-fn main_loop(command: &OpenVpnCommand, plugin_path: &Path) -> Result<()> {
- let (monitor, rx) = create_openvpn_monitor(plugin_path)?;
+fn main_loop(remotes: &[RemoteAddr]) -> Result<()> {
+ let mut remotes_iter = remotes.iter().cloned().cycle();
+ let (monitor, rx) = create_tunnel_monitor()?;
loop {
- monitor.start(command.clone()).chain_err(|| "Unable to start OpenVPN")?;
+ monitor.start(remotes_iter.next().unwrap()).chain_err(|| "Unable to start OpenVPN")?;
while let Ok(msg) = rx.recv() {
match msg {
- OpenVpnEvent::Shutdown(result) => {
- println!(
- "Monitored process exited. clean: {}",
- result.map(|s| s.success()).unwrap_or(false)
- );
+ TunnelEvent::Shutdown => {
+ println!("Monitored process exited");
break;
}
- OpenVpnEvent::PluginEvent(event, env) => {
- println!("OpenVPN event:\nEvent: {:?}\nENV: {:?}", event, env);
- }
+ TunnelEvent::Up => println!("Tunnel UP"),
+ TunnelEvent::Down => println!("Tunnel DOWN"),
}
}
std::thread::sleep(std::time::Duration::from_millis(500));
}
}
-fn create_openvpn_monitor(plugin_path: &Path) -> Result<(OpenVpnMonitor, Receiver<OpenVpnEvent>)> {
+fn create_tunnel_monitor() -> Result<(TunnelMonitor, Receiver<TunnelEvent>)> {
let (event_tx, event_rx) = mpsc::channel();
let event_tx_mutex = Mutex::new(event_tx);
- let on_event = move |event: OpenVpnEvent| {
+ let on_event = move |event: TunnelEvent| {
event_tx_mutex.lock().unwrap().send(event).expect("Unable to send on tx_lock");
};
- let monitor = OpenVpnMonitor::new(on_event, plugin_path)
- .chain_err(|| "Unable to start OpenVPN monitor")?;
+ let monitor = TunnelMonitor::new(on_event).chain_err(|| "Unable to start OpenVPN monitor")?;
Ok((monitor, event_rx))
}
diff --git a/talpid_core/src/tunnel/mod.rs b/talpid_core/src/tunnel/mod.rs
index 0b477ca1c4..c453c52056 100644
--- a/talpid_core/src/tunnel/mod.rs
+++ b/talpid_core/src/tunnel/mod.rs
@@ -1,2 +1,106 @@
+use net;
+use openvpn_ffi::OpenVpnPluginEvent;
+use process::openvpn::OpenVpnCommand;
+
/// A module for all OpenVPN related tunnel management.
pub mod openvpn;
+
+use self::openvpn::{OpenVpnEvent, OpenVpnMonitor};
+
+mod errors {
+ error_chain!{
+ errors {
+ /// An error indicating there was an error listening for events from the VPN tunnel.
+ TunnelMonitoringError {
+ description("Error while setting up or processing events from the VPN tunnel")
+ }
+ /// An error indicating that there was an error when trying to start up a VPN tunnel.
+ TunnelStartError {
+ description("Error while trying to start the tunnel")
+ }
+ }
+ }
+}
+pub use self::errors::*;
+
+
+/// Possible events from the VPN tunnel and the child process managing it.
+pub enum TunnelEvent {
+ /// Sent when the tunnel comes up and is ready for traffic.
+ Up,
+ /// Sent when the tunnel goes down.
+ Down,
+ /// Sent when the process managing the tunnel exits.
+ Shutdown,
+}
+
+impl TunnelEvent {
+ /// Converts an `OpenVpnEvent` to a `TunnelEvent`.
+ /// Returns `None` if there is no corresponding `TunnelEvent`.
+ pub fn from_openvpn_event(event: &OpenVpnEvent) -> Option<TunnelEvent> {
+ match *event {
+ OpenVpnEvent::PluginEvent(ref event, _) => Self::from_openvpn_plugin_event(event),
+ OpenVpnEvent::Shutdown(_) => Some(TunnelEvent::Shutdown),
+ }
+ }
+
+ fn from_openvpn_plugin_event(event: &OpenVpnPluginEvent) -> Option<TunnelEvent> {
+ match *event {
+ OpenVpnPluginEvent::Up => Some(TunnelEvent::Up),
+ OpenVpnPluginEvent::RoutePredown => Some(TunnelEvent::Down),
+ _ => None,
+ }
+ }
+}
+
+
+/// Abstraction for monitoring a generic VPN tunnel.
+pub struct TunnelMonitor {
+ monitor: OpenVpnMonitor,
+}
+
+impl TunnelMonitor {
+ /// Creates a new `TunnelMonitor` with the given event callback.
+ pub fn new<L>(on_event: L) -> Result<Self>
+ where L: Fn(TunnelEvent) + Send + Sync + 'static
+ {
+ let on_openvpn_event = move |openvpn_event| {
+ // FIXME: This comment must be here to make rustfmt 0.8.3 not screw up.
+ match TunnelEvent::from_openvpn_event(&openvpn_event) {
+ Some(tunnel_event) => on_event(tunnel_event),
+ None => debug!("Ignoring OpenVpnEvent {:?}", openvpn_event),
+ }
+ };
+ let monitor = openvpn::OpenVpnMonitor::new(on_openvpn_event, get_plugin_path())
+ .chain_err(|| ErrorKind::TunnelMonitoringError)?;
+ Ok(TunnelMonitor { monitor })
+ }
+
+ /// Tries to start a VPN tunnel towards the given address. Will fail if there is a tunnel
+ /// running already.
+ pub fn start(&self, remote: net::RemoteAddr) -> Result<()> {
+ let mut cmd = OpenVpnCommand::new("openvpn");
+ cmd.config(get_config_path()).remotes(remote).unwrap();
+ self.monitor.start(cmd).chain_err(|| ErrorKind::TunnelStartError)
+ }
+}
+
+
+// TODO(linus): Temporary implementation for getting plugin path during development.
+fn get_plugin_path() -> &'static str {
+ if cfg!(all(unix, not(target_os = "macos"))) {
+ "./target/debug/libtalpid_openvpn_plugin.so"
+ } else if cfg!(target_os = "macos") {
+ "./target/debug/libtalpid_openvpn_plugin.dylib"
+ } else if cfg!(windows) {
+ "./target/debug/libtalpid_openvpn_plugin.dll"
+ } else {
+ panic!("Unsupported platform");
+ }
+}
+
+// TODO(linus): Temporary implementation for getting hold of a config location.
+// Manually place a working config here or change this string in order to test
+fn get_config_path() -> &'static str {
+ "./openvpn.conf"
+}
diff --git a/talpid_core/src/tunnel/openvpn.rs b/talpid_core/src/tunnel/openvpn.rs
index bcf89e3eb0..9d366ebb4a 100644
--- a/talpid_core/src/tunnel/openvpn.rs
+++ b/talpid_core/src/tunnel/openvpn.rs
@@ -33,6 +33,7 @@ pub use self::errors::*;
/// Possible events from OpenVPN
+#[derive(Debug)]
pub enum OpenVpnEvent {
/// An event from the plugin loaded into OpenVPN.
PluginEvent(openvpn_ffi::OpenVpnPluginEvent, openvpn_ffi::OpenVpnEnv),