summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorEmīls Piņķis <emils@mullvad.net>2019-02-27 20:23:10 +0000
committerEmīls Piņķis <emils@mullvad.net>2019-02-27 21:13:31 +0000
commit5a2c5815dcf856e1c0c3e5d1e09ea52d57bff94a (patch)
treeacd843379bf08860cce15da09c9cd67c9f406d97
parent502bf6fa28820fcfe9f1a16bf208d09191792cd7 (diff)
downloadmullvadvpn-5a2c5815dcf856e1c0c3e5d1e09ea52d57bff94a.tar.xz
mullvadvpn-5a2c5815dcf856e1c0c3e5d1e09ea52d57bff94a.zip
Adjust firewall to allow ICMP traffic to selected hosts when connecting
-rw-r--r--talpid-core/src/firewall/linux.rs27
-rw-r--r--talpid-core/src/firewall/macos.rs41
-rw-r--r--talpid-core/src/firewall/mod.rs12
-rw-r--r--talpid-core/src/firewall/windows.rs2
-rw-r--r--talpid-core/src/tunnel_state_machine/connecting_state.rs32
5 files changed, 97 insertions, 17 deletions
diff --git a/talpid-core/src/firewall/linux.rs b/talpid-core/src/firewall/linux.rs
index 2743b9d318..ffec0bc1fd 100644
--- a/talpid-core/src/firewall/linux.rs
+++ b/talpid-core/src/firewall/linux.rs
@@ -273,8 +273,10 @@ impl<'a> PolicyBatch<'a> {
let allow_lan = match policy {
FirewallPolicy::Connecting {
peer_endpoint,
+ pingable_hosts,
allow_lan,
} => {
+ self.add_allow_icmp_pingable_hosts(&pingable_hosts)?;
self.add_allow_endpoint_rules(peer_endpoint)?;
*allow_lan
}
@@ -320,6 +322,31 @@ impl<'a> PolicyBatch<'a> {
Ok(())
}
+ fn add_allow_icmp_pingable_hosts(&mut self, pingable_hosts: &[IpAddr]) -> Result<()> {
+ for host in pingable_hosts {
+ let icmp_proto = match &host {
+ &IpAddr::V4(_) => libc::IPPROTO_ICMP as u8,
+ &IpAddr::V6(_) => libc::IPPROTO_ICMPV6 as u8,
+ };
+
+ let mut out_rule = Rule::new(&self.out_chain)?;
+ check_ip(&mut out_rule, End::Dst, *host)?;
+ out_rule.add_expr(&nft_expr!(meta l4proto))?;
+ out_rule.add_expr(&nft_expr!(cmp == icmp_proto))?;
+ add_verdict(&mut out_rule, &Verdict::Accept)?;
+ self.batch.add(&out_rule, nftnl::MsgType::Add)?;
+
+ let mut in_rule = Rule::new(&self.in_chain)?;
+ check_ip(&mut in_rule, End::Src, *host)?;
+ in_rule.add_expr(&nft_expr!(meta l4proto))?;
+ in_rule.add_expr(&nft_expr!(cmp == icmp_proto))?;
+ add_verdict(&mut in_rule, &Verdict::Accept)?;
+ self.batch.add(&in_rule, nftnl::MsgType::Add)?;
+ }
+
+ Ok(())
+ }
+
fn add_dns_rule(
&mut self,
tunnel: &tunnel::TunnelMetadata,
diff --git a/talpid-core/src/firewall/macos.rs b/talpid-core/src/firewall/macos.rs
index 6662539dc9..b411c58a9a 100644
--- a/talpid-core/src/firewall/macos.rs
+++ b/talpid-core/src/firewall/macos.rs
@@ -1,6 +1,9 @@
use super::{FirewallPolicy, FirewallT};
use pfctl::FilterRuleAction;
-use std::{env, net::Ipv4Addr};
+use std::{
+ env,
+ net::{IpAddr, Ipv4Addr},
+};
use talpid_types::net;
pub use pfctl::Error;
@@ -85,8 +88,10 @@ impl Firewall {
FirewallPolicy::Connecting {
peer_endpoint,
allow_lan,
+ pingable_hosts,
} => {
let mut rules = vec![self.get_allow_relay_rule(peer_endpoint)?];
+ rules.extend(self.get_allow_pingable_hosts(&pingable_hosts)?);
if allow_lan {
rules.append(&mut self.get_allow_lan_rules()?);
}
@@ -166,6 +171,40 @@ impl Firewall {
.build()?)
}
+ fn get_allow_pingable_hosts(
+ &self,
+ pingable_hosts: &[IpAddr],
+ ) -> Result<Vec<pfctl::FilterRule>> {
+ let mut rules = vec![];
+ for host in pingable_hosts.iter() {
+ let icmp_proto = match &host {
+ IpAddr::V4(_) => pfctl::Proto::Icmp,
+ IpAddr::V6(_) => pfctl::Proto::IcmpV6,
+ };
+
+ let out_rule = self
+ .create_rule_builder(FilterRuleAction::Pass)
+ .direction(pfctl::Direction::Out)
+ .to(pfctl::Endpoint::new(*host, 0))
+ .proto(icmp_proto)
+ .keep_state(pfctl::StatePolicy::Keep)
+ .quick(true)
+ .build()?;
+ rules.push(out_rule);
+
+ let in_rule = self
+ .create_rule_builder(FilterRuleAction::Pass)
+ .direction(pfctl::Direction::In)
+ .from(pfctl::Endpoint::new(*host, 0))
+ .proto(icmp_proto)
+ .keep_state(pfctl::StatePolicy::Keep)
+ .quick(true)
+ .build()?;
+ rules.push(in_rule);
+ }
+ Ok(rules)
+ }
+
fn get_allow_tunnel_rule(&self, tunnel_interface: &str) -> Result<pfctl::FilterRule> {
Ok(self
.create_rule_builder(FilterRuleAction::Pass)
diff --git a/talpid-core/src/firewall/mod.rs b/talpid-core/src/firewall/mod.rs
index b7977bc2cd..059a49e326 100644
--- a/talpid-core/src/firewall/mod.rs
+++ b/talpid-core/src/firewall/mod.rs
@@ -3,6 +3,8 @@ use ipnetwork::{IpNetwork, Ipv4Network, Ipv6Network};
#[cfg(unix)]
use lazy_static::lazy_static;
use std::fmt;
+#[cfg(windows)]
+use std::net::IpAddr;
#[cfg(unix)]
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use talpid_types::net::Endpoint;
@@ -51,6 +53,8 @@ pub enum FirewallPolicy {
Connecting {
/// The peer endpoint that should be allowed.
peer_endpoint: Endpoint,
+ /// Hosts that should be pingable whilst connecting.
+ pingable_hosts: Vec<IpAddr>,
/// Flag setting if communication with LAN networks should be possible.
allow_lan: bool,
},
@@ -77,11 +81,17 @@ impl fmt::Display for FirewallPolicy {
match self {
FirewallPolicy::Connecting {
peer_endpoint,
+ pingable_hosts,
allow_lan,
} => write!(
f,
- "Connecting to {}, {} LAN",
+ "Connecting to {} with gateways {}, {} LAN",
peer_endpoint,
+ pingable_hosts
+ .iter()
+ .map(ToString::to_string)
+ .collect::<Vec<String>>()
+ .join(","),
if *allow_lan { "Allowing" } else { "Blocking" }
),
FirewallPolicy::Connected {
diff --git a/talpid-core/src/firewall/windows.rs b/talpid-core/src/firewall/windows.rs
index d77a9a3dbd..16702b62f9 100644
--- a/talpid-core/src/firewall/windows.rs
+++ b/talpid-core/src/firewall/windows.rs
@@ -71,6 +71,8 @@ impl FirewallT for Firewall {
match policy {
FirewallPolicy::Connecting {
peer_endpoint,
+ // TODO: Allow ICMP traffic to a list of hosts for wireguard
+ pingable_hosts: _,
allow_lan,
} => {
let cfg = &WinFwSettings::new(allow_lan);
diff --git a/talpid-core/src/tunnel_state_machine/connecting_state.rs b/talpid-core/src/tunnel_state_machine/connecting_state.rs
index 305fb24383..10c693f5ef 100644
--- a/talpid-core/src/tunnel_state_machine/connecting_state.rs
+++ b/talpid-core/src/tunnel_state_machine/connecting_state.rs
@@ -1,5 +1,6 @@
use std::{
ffi::OsString,
+ net::IpAddr,
path::{Path, PathBuf},
thread,
time::{Duration, Instant},
@@ -12,7 +13,7 @@ use futures::{
};
use log::{debug, error, info, trace, warn};
use talpid_types::{
- net::{openvpn, Endpoint, TunnelParameters},
+ net::{openvpn, TunnelParameters},
tunnel::BlockReason,
};
@@ -48,10 +49,11 @@ pub struct ConnectingState {
impl ConnectingState {
fn set_firewall_policy(
shared_values: &mut SharedTunnelStateValues,
- proxy: &Option<openvpn::ProxySettings>,
- endpoint: Endpoint,
+ params: &TunnelParameters,
) -> Result<()> {
- // If a proxy is specified we need to pass it on as the peer endpoint.
+ let proxy = &get_openvpn_proxy_settings(&params);
+ let endpoint = params.get_tunnel_endpoint().endpoint;
+
let peer_endpoint = match proxy {
Some(proxy_settings) => proxy_settings.get_endpoint(),
None => endpoint,
@@ -59,6 +61,7 @@ impl ConnectingState {
let policy = FirewallPolicy::Connecting {
peer_endpoint,
+ pingable_hosts: gateway_list_from_params(params),
allow_lan: shared_values.allow_lan,
};
shared_values
@@ -175,11 +178,7 @@ impl ConnectingState {
match try_handle_event!(self, commands.poll()) {
Ok(TunnelCommand::AllowLan(allow_lan)) => {
shared_values.allow_lan = allow_lan;
- match Self::set_firewall_policy(
- shared_values,
- &get_openvpn_proxy_settings(&self.tunnel_parameters),
- self.tunnel_parameters.get_tunnel_endpoint().endpoint,
- ) {
+ match Self::set_firewall_policy(shared_values, &self.tunnel_parameters) {
Ok(()) => SameState(self),
Err(error) => {
error!("{}", error.display_chain());
@@ -326,12 +325,7 @@ impl TunnelState for ConnectingState {
{
None => BlockedState::enter(shared_values, BlockReason::NoMatchingRelay),
Some(tunnel_parameters) => {
- let endpoint = tunnel_parameters.get_tunnel_endpoint().endpoint;
- if let Err(error) = Self::set_firewall_policy(
- shared_values,
- &get_openvpn_proxy_settings(&tunnel_parameters),
- endpoint,
- ) {
+ if let Err(error) = Self::set_firewall_policy(shared_values, &tunnel_parameters) {
error!("{}", error.display_chain());
BlockedState::enter(shared_values, BlockReason::StartTunnelError)
} else {
@@ -375,3 +369,11 @@ impl TunnelState for ConnectingState {
.or_else(Self::handle_tunnel_close_event, shared_values)
}
}
+
+fn gateway_list_from_params(params: &TunnelParameters) -> Vec<IpAddr> {
+ match params {
+ TunnelParameters::Wireguard(params) => vec![params.connection.gateway],
+ // No gateway list required when connecting to openvpn
+ TunnelParameters::OpenVpn(_) => vec![],
+ }
+}