summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorLinus Färnstrand <linus@mullvad.net>2019-05-08 15:48:20 +0200
committerLinus Färnstrand <linus@mullvad.net>2019-05-08 15:48:20 +0200
commit304695494a8e4a3f34fd9882db43b3305ca88503 (patch)
tree4d1fdb28a373a3ab9c491300d89edd6aad26aea9
parent583f0c493cb5dc103ce28f13bea116bcde7bfa3c (diff)
parente365175bc7822982fb819d0d05043aadaeb3ef2d (diff)
downloadmullvadvpn-304695494a8e4a3f34fd9882db43b3305ca88503.tar.xz
mullvadvpn-304695494a8e4a3f34fd9882db43b3305ca88503.zip
Merge branch 'fix-ipv6-firewall-rules-ndp-multicast'
-rw-r--r--CHANGELOG.md9
-rw-r--r--Cargo.lock17
-rw-r--r--talpid-core/Cargo.toml2
-rw-r--r--talpid-core/src/firewall/linux.rs144
-rw-r--r--talpid-core/src/firewall/macos.rs128
-rw-r--r--talpid-core/src/firewall/mod.rs72
6 files changed, 251 insertions, 121 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f2127ff8cb..a1b5a83339 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -26,6 +26,15 @@ Line wrap the file at 100 chars. Th
### Added
#### Linux
- Add standard window decorations to the application window.
+- Allow a subset of NDP (Router solicitation, router advertisement and redirects) in the firewall.
+
+### Changed
+- Relax the allow local network rules slightly. only checking either source or destination IP field
+ instead of both. They are still unroutable
+
+### Fixed
+- Stop allowing the wrong IPv6 net fe02::/16 in the firewall when allow local network was enabled.
+ Instead allow the correct multicast nets ff02::/16 and ff05::/16.
## [2019.4-beta1] - 2019-05-02
diff --git a/Cargo.lock b/Cargo.lock
index 94e2e8b782..99dcef5a24 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1254,21 +1254,22 @@ dependencies = [
[[package]]
name = "nftnl"
-version = "0.1.0"
-source = "git+https://github.com/mullvad/nftnl-rs?rev=29651f4370fdf22cc2e3abf5097a51f8ff17e3a3#29651f4370fdf22cc2e3abf5097a51f8ff17e3a3"
+version = "0.2.0"
+source = "git+https://github.com/mullvad/nftnl-rs?rev=86b30cdc38a6d4b30a900c21f7c644857d6f7401#86b30cdc38a6d4b30a900c21f7c644857d6f7401"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"err-derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "nftnl-sys 0.1.0 (git+https://github.com/mullvad/nftnl-rs?rev=29651f4370fdf22cc2e3abf5097a51f8ff17e3a3)",
+ "nftnl-sys 0.2.0 (git+https://github.com/mullvad/nftnl-rs?rev=86b30cdc38a6d4b30a900c21f7c644857d6f7401)",
]
[[package]]
name = "nftnl-sys"
-version = "0.1.0"
-source = "git+https://github.com/mullvad/nftnl-rs?rev=29651f4370fdf22cc2e3abf5097a51f8ff17e3a3#29651f4370fdf22cc2e3abf5097a51f8ff17e3a3"
+version = "0.2.0"
+source = "git+https://github.com/mullvad/nftnl-rs?rev=86b30cdc38a6d4b30a900c21f7c644857d6f7401#86b30cdc38a6d4b30a900c21f7c644857d6f7401"
dependencies = [
+ "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1996,7 +1997,7 @@ dependencies = [
"netlink-proto 0.1.1 (git+https://github.com/mullvad/netlink?branch=hack-older-kernel-compat)",
"netlink-socket 0.0.2 (git+https://github.com/mullvad/netlink?branch=ignore-hw-address)",
"netlink-sys 0.1.0 (git+https://github.com/mullvad/netlink?branch=hack-older-kernel-compat)",
- "nftnl 0.1.0 (git+https://github.com/mullvad/nftnl-rs?rev=29651f4370fdf22cc2e3abf5097a51f8ff17e3a3)",
+ "nftnl 0.2.0 (git+https://github.com/mullvad/nftnl-rs?rev=86b30cdc38a6d4b30a900c21f7c644857d6f7401)",
"nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
"notify 4.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
"openvpn-plugin 0.3.0 (git+https://github.com/mullvad/openvpn-plugin-rs?branch=auth-failed-event)",
@@ -2726,8 +2727,8 @@ dependencies = [
"checksum netlink-proto 0.1.1 (git+https://github.com/mullvad/netlink?branch=hack-older-kernel-compat)" = "<none>"
"checksum netlink-socket 0.0.2 (git+https://github.com/mullvad/netlink?branch=ignore-hw-address)" = "<none>"
"checksum netlink-sys 0.1.0 (git+https://github.com/mullvad/netlink?branch=hack-older-kernel-compat)" = "<none>"
-"checksum nftnl 0.1.0 (git+https://github.com/mullvad/nftnl-rs?rev=29651f4370fdf22cc2e3abf5097a51f8ff17e3a3)" = "<none>"
-"checksum nftnl-sys 0.1.0 (git+https://github.com/mullvad/nftnl-rs?rev=29651f4370fdf22cc2e3abf5097a51f8ff17e3a3)" = "<none>"
+"checksum nftnl 0.2.0 (git+https://github.com/mullvad/nftnl-rs?rev=86b30cdc38a6d4b30a900c21f7c644857d6f7401)" = "<none>"
+"checksum nftnl-sys 0.2.0 (git+https://github.com/mullvad/nftnl-rs?rev=86b30cdc38a6d4b30a900c21f7c644857d6f7401)" = "<none>"
"checksum nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d37e713a259ff641624b6cb20e3b12b2952313ba36b6823c0f16e6cfd9e5de17"
"checksum nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46f0f3210768d796e8fa79ec70ee6af172dacbe7147f5e69be5240a47778302b"
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml
index c2fd7b90bc..982b1d500d 100644
--- a/talpid-core/Cargo.toml
+++ b/talpid-core/Cargo.toml
@@ -44,7 +44,7 @@ rtnetlink = { git = "https://github.com/mullvad/netlink", branch = "hack-older-k
netlink-proto = { git = "https://github.com/mullvad/netlink", branch = "hack-older-kernel-compat" }
netlink-packet = { git = "https://github.com/mullvad/netlink", branch = "hack-older-kernel-compat" }
netlink-sys = { git = "https://github.com/mullvad/netlink", branch = "hack-older-kernel-compat" }
-nftnl = { git = "https://github.com/mullvad/nftnl-rs", rev = "29651f4370fdf22cc2e3abf5097a51f8ff17e3a3", features = ["nftnl-1-1-0"] }
+nftnl = { git = "https://github.com/mullvad/nftnl-rs", rev = "86b30cdc38a6d4b30a900c21f7c644857d6f7401", features = ["nftnl-1-1-0"] }
mnl = { git = "https://github.com/mullvad/mnl-rs", rev = "f0d19501b9b85be9a1ffaec8317a378bcbdf4fa6", features = ["mnl-1-0-4"] }
which = "2.0"
err-derive = "0.1.5"
diff --git a/talpid-core/src/firewall/linux.rs b/talpid-core/src/firewall/linux.rs
index 540bdff1ee..244b320098 100644
--- a/talpid-core/src/firewall/linux.rs
+++ b/talpid-core/src/firewall/linux.rs
@@ -4,7 +4,8 @@ use ipnetwork::IpNetwork;
use lazy_static::lazy_static;
use libc;
use nftnl::{
- expr::{self, Verdict},
+ self,
+ expr::{self, Payload, Verdict},
nft_expr, table, Batch, Chain, FinalizedBatch, ProtoFamily, Rule, Table,
};
use std::{
@@ -199,6 +200,7 @@ impl<'a> PolicyBatch<'a> {
out_chain.set_policy(nftnl::Policy::Drop);
in_chain.set_policy(nftnl::Policy::Drop);
+ // A little dance that will make sure the table exists, but is cleared.
batch.add(table, nftnl::MsgType::Add);
batch.add(table, nftnl::MsgType::Del);
batch.add(table, nftnl::MsgType::Add);
@@ -237,43 +239,103 @@ impl<'a> PolicyBatch<'a> {
fn add_dhcp_rules(&mut self) {
use self::TransportProtocol::Udp;
- const SERVER_PORT_V4: u16 = 67;
- const CLIENT_PORT_V4: u16 = 68;
- const SERVER_PORT_V6: u16 = 547;
- const CLIENT_PORT_V6: u16 = 546;
{
let mut out_v4 = Rule::new(&self.out_chain);
- check_port(&mut out_v4, Udp, End::Src, CLIENT_PORT_V4);
+ check_port(&mut out_v4, Udp, End::Src, super::DHCPV4_CLIENT_PORT);
check_ip(&mut out_v4, End::Dst, IpAddr::V4(Ipv4Addr::BROADCAST));
- check_port(&mut out_v4, Udp, End::Dst, SERVER_PORT_V4);
+ check_port(&mut out_v4, Udp, End::Dst, super::DHCPV4_SERVER_PORT);
add_verdict(&mut out_v4, &Verdict::Accept);
self.batch.add(&out_v4, nftnl::MsgType::Add);
}
{
let mut in_v4 = Rule::new(&self.in_chain);
- check_port(&mut in_v4, Udp, End::Src, SERVER_PORT_V4);
- check_port(&mut in_v4, Udp, End::Dst, CLIENT_PORT_V4);
+ check_port(&mut in_v4, Udp, End::Src, super::DHCPV4_SERVER_PORT);
+ check_port(&mut in_v4, Udp, End::Dst, super::DHCPV4_CLIENT_PORT);
add_verdict(&mut in_v4, &Verdict::Accept);
self.batch.add(&in_v4, nftnl::MsgType::Add);
}
for dhcpv6_server in &*super::DHCPV6_SERVER_ADDRS {
let mut out_v6 = Rule::new(&self.out_chain);
- check_net(&mut out_v6, End::Src, *super::LOCAL_INET6_NET);
- check_port(&mut out_v6, Udp, End::Src, CLIENT_PORT_V6);
+ check_net(&mut out_v6, End::Src, *super::IPV6_LINK_LOCAL);
+ check_port(&mut out_v6, Udp, End::Src, super::DHCPV6_CLIENT_PORT);
check_ip(&mut out_v6, End::Dst, *dhcpv6_server);
- check_port(&mut out_v6, Udp, End::Dst, SERVER_PORT_V6);
+ check_port(&mut out_v6, Udp, End::Dst, super::DHCPV6_SERVER_PORT);
add_verdict(&mut out_v6, &Verdict::Accept);
self.batch.add(&out_v6, nftnl::MsgType::Add);
}
{
let mut in_v6 = Rule::new(&self.in_chain);
- check_net(&mut in_v6, End::Src, *super::LOCAL_INET6_NET);
- check_port(&mut in_v6, Udp, End::Src, SERVER_PORT_V6);
- check_net(&mut in_v6, End::Dst, *super::LOCAL_INET6_NET);
- check_port(&mut in_v6, Udp, End::Dst, CLIENT_PORT_V6);
+ check_net(&mut in_v6, End::Src, *super::IPV6_LINK_LOCAL);
+ check_port(&mut in_v6, Udp, End::Src, super::DHCPV6_SERVER_PORT);
+ check_net(&mut in_v6, End::Dst, *super::IPV6_LINK_LOCAL);
+ check_port(&mut in_v6, Udp, End::Dst, super::DHCPV6_CLIENT_PORT);
add_verdict(&mut in_v6, &Verdict::Accept);
self.batch.add(&in_v6, nftnl::MsgType::Add);
}
+ // Outgoing Router solicitation (part of NDP)
+ {
+ let mut rule = Rule::new(&self.out_chain);
+ check_ip(
+ &mut rule,
+ End::Dst,
+ *super::ROUTER_SOLICITATION_OUT_DST_ADDR,
+ );
+
+ rule.add_expr(&nft_expr!(meta l4proto));
+ rule.add_expr(&nft_expr!(cmp == libc::IPPROTO_ICMPV6 as u8));
+
+ rule.add_expr(&Payload::Transport(
+ nftnl::expr::TransportHeaderField::Icmpv6(nftnl::expr::Icmpv6HeaderField::Type),
+ ));
+ rule.add_expr(&nft_expr!(cmp == 133u8));
+ rule.add_expr(&nftnl::expr::Payload::Transport(
+ nftnl::expr::TransportHeaderField::Icmpv6(nftnl::expr::Icmpv6HeaderField::Code),
+ ));
+ rule.add_expr(&nft_expr!(cmp == 0u8));
+
+ add_verdict(&mut rule, &Verdict::Accept);
+ self.batch.add(&rule, nftnl::MsgType::Add);
+ }
+ // Incoming Router advertisement (part of NDP)
+ {
+ let mut rule = Rule::new(&self.in_chain);
+ check_net(&mut rule, End::Src, *super::IPV6_LINK_LOCAL);
+
+ rule.add_expr(&nft_expr!(meta l4proto));
+ rule.add_expr(&nft_expr!(cmp == libc::IPPROTO_ICMPV6 as u8));
+
+ rule.add_expr(&Payload::Transport(
+ nftnl::expr::TransportHeaderField::Icmpv6(nftnl::expr::Icmpv6HeaderField::Type),
+ ));
+ rule.add_expr(&nft_expr!(cmp == 134u8));
+ rule.add_expr(&nftnl::expr::Payload::Transport(
+ nftnl::expr::TransportHeaderField::Icmpv6(nftnl::expr::Icmpv6HeaderField::Code),
+ ));
+ rule.add_expr(&nft_expr!(cmp == 0u8));
+
+ add_verdict(&mut rule, &Verdict::Accept);
+ self.batch.add(&rule, nftnl::MsgType::Add);
+ }
+ // Incoming Redirect (part of NDP)
+ {
+ let mut rule = Rule::new(&self.in_chain);
+ check_net(&mut rule, End::Src, *super::IPV6_LINK_LOCAL);
+
+ rule.add_expr(&nft_expr!(meta l4proto));
+ rule.add_expr(&nft_expr!(cmp == libc::IPPROTO_ICMPV6 as u8));
+
+ rule.add_expr(&Payload::Transport(
+ nftnl::expr::TransportHeaderField::Icmpv6(nftnl::expr::Icmpv6HeaderField::Type),
+ ));
+ rule.add_expr(&nft_expr!(cmp == 137u8));
+ rule.add_expr(&nftnl::expr::Payload::Transport(
+ nftnl::expr::TransportHeaderField::Icmpv6(nftnl::expr::Icmpv6HeaderField::Code),
+ ));
+ rule.add_expr(&nft_expr!(cmp == 0u8));
+
+ add_verdict(&mut rule, &Verdict::Accept);
+ self.batch.add(&rule, nftnl::MsgType::Add);
+ }
}
fn add_policy_specific_rules(&mut self, policy: &FirewallPolicy) -> Result<()> {
@@ -406,42 +468,24 @@ impl<'a> PolicyBatch<'a> {
fn add_allow_lan_rules(&mut self) {
// LAN -> LAN
- for chain in &[&self.in_chain, &self.out_chain] {
- for net in &*super::PRIVATE_NETS {
- let mut rule = Rule::new(chain);
- check_net(&mut rule, End::Src, *net);
- check_net(&mut rule, End::Dst, *net);
- add_verdict(&mut rule, &Verdict::Accept);
- self.batch.add(&rule, nftnl::MsgType::Add);
- }
- let mut rule = Rule::new(chain);
- check_net(&mut rule, End::Src, *super::LOCAL_INET6_NET);
- check_net(&mut rule, End::Dst, *super::LOCAL_INET6_NET);
- add_verdict(&mut rule, &Verdict::Accept);
- self.batch.add(&rule, nftnl::MsgType::Add);
- }
- // LAN -> multicast
- for net in &*super::PRIVATE_NETS {
- let mut rule = Rule::new(&self.out_chain);
- check_net(&mut rule, End::Src, *net);
- check_net(&mut rule, End::Dst, *super::MULTICAST_NET);
- add_verdict(&mut rule, &Verdict::Accept);
-
- self.batch.add(&rule, nftnl::MsgType::Add);
+ for net in &*super::ALLOWED_LAN_NETS {
+ let mut out_rule = Rule::new(&self.out_chain);
+ check_net(&mut out_rule, End::Dst, *net);
+ add_verdict(&mut out_rule, &Verdict::Accept);
+ self.batch.add(&out_rule, nftnl::MsgType::Add);
- // LAN -> SSDP + WS-Discovery protocols
+ let mut in_rule = Rule::new(&self.in_chain);
+ check_net(&mut in_rule, End::Src, *net);
+ add_verdict(&mut in_rule, &Verdict::Accept);
+ self.batch.add(&in_rule, nftnl::MsgType::Add);
+ }
+ // LAN -> Multicast
+ for net in &*super::ALLOWED_LAN_MULTICAST_NETS {
let mut rule = Rule::new(&self.out_chain);
- check_net(&mut rule, End::Src, *net);
- check_ip(&mut rule, End::Dst, *super::SSDP_IP);
+ check_net(&mut rule, End::Dst, *net);
add_verdict(&mut rule, &Verdict::Accept);
-
self.batch.add(&rule, nftnl::MsgType::Add);
}
- let mut rule = Rule::new(&self.out_chain);
- check_net(&mut rule, End::Src, *super::LOCAL_INET6_NET);
- check_net(&mut rule, End::Dst, *super::MULTICAST_INET6_NET);
- add_verdict(&mut rule, &Verdict::Accept);
- self.batch.add(&rule, nftnl::MsgType::Add);
}
}
@@ -468,7 +512,8 @@ fn check_iface(rule: &mut Rule<'_>, direction: Direction, iface: &str) -> Result
Ok(())
}
-fn check_net(rule: &mut Rule<'_>, end: End, net: IpNetwork) {
+fn check_net(rule: &mut Rule<'_>, end: End, net: impl Into<IpNetwork>) {
+ let net = net.into();
// Must check network layer protocol before loading network layer payload
check_l3proto(rule, net.ip());
@@ -490,7 +535,8 @@ fn check_endpoint(rule: &mut Rule<'_>, end: End, endpoint: &Endpoint) {
check_port(rule, endpoint.protocol, end, endpoint.address.port());
}
-fn check_ip(rule: &mut Rule<'_>, end: End, ip: IpAddr) {
+fn check_ip(rule: &mut Rule<'_>, end: End, ip: impl Into<IpAddr>) {
+ let ip = ip.into();
// Must check network layer protocol before loading network layer payload
check_l3proto(rule, ip);
diff --git a/talpid-core/src/firewall/macos.rs b/talpid-core/src/firewall/macos.rs
index ba920d4e79..04dfa7c8d2 100644
--- a/talpid-core/src/firewall/macos.rs
+++ b/talpid-core/src/firewall/macos.rs
@@ -1,4 +1,5 @@
use super::{FirewallArguments, FirewallPolicy, FirewallT};
+use ipnetwork::IpNetwork;
use pfctl::FilterRuleAction;
use std::{
env,
@@ -206,21 +207,21 @@ impl Firewall {
let out_rule = self
.create_rule_builder(FilterRuleAction::Pass)
+ .quick(true)
.direction(pfctl::Direction::Out)
- .to(pfctl::Endpoint::new(*host, 0))
.proto(icmp_proto)
+ .to(pfctl::Endpoint::new(*host, 0))
.keep_state(pfctl::StatePolicy::Keep)
- .quick(true)
.build()?;
rules.push(out_rule);
let in_rule = self
.create_rule_builder(FilterRuleAction::Pass)
+ .quick(true)
.direction(pfctl::Direction::In)
- .from(pfctl::Endpoint::new(*host, 0))
.proto(icmp_proto)
+ .from(pfctl::Endpoint::new(*host, 0))
.keep_state(pfctl::StatePolicy::Keep)
- .quick(true)
.build()?;
rules.push(in_rule);
}
@@ -230,108 +231,131 @@ impl Firewall {
fn get_allow_tunnel_rule(&self, tunnel_interface: &str) -> Result<pfctl::FilterRule> {
Ok(self
.create_rule_builder(FilterRuleAction::Pass)
+ .quick(true)
.interface(tunnel_interface)
.keep_state(pfctl::StatePolicy::Keep)
.tcp_flags(Self::get_tcp_flags())
- .quick(true)
.build()?)
}
fn get_allow_loopback_rules(&self) -> Result<Vec<pfctl::FilterRule>> {
let lo0_rule = self
.create_rule_builder(FilterRuleAction::Pass)
+ .quick(true)
.interface("lo0")
.keep_state(pfctl::StatePolicy::Keep)
- .quick(true)
.build()?;
Ok(vec![lo0_rule])
}
fn get_allow_lan_rules(&self) -> Result<Vec<pfctl::FilterRule>> {
let mut rules = vec![];
- // IPv4
- for net in &*super::PRIVATE_NETS {
+ for net in &*super::ALLOWED_LAN_NETS {
let mut rule_builder = self.create_rule_builder(FilterRuleAction::Pass);
- rule_builder
+ rule_builder.quick(true).af(match net {
+ IpNetwork::V4(_) => pfctl::AddrFamily::Ipv4,
+ IpNetwork::V6(_) => pfctl::AddrFamily::Ipv6,
+ });
+ let allow_out = rule_builder
+ .direction(pfctl::Direction::Out)
+ .from(pfctl::Ip::Any)
+ .to(pfctl::Ip::from(*net))
+ .build()?;
+ let allow_in = rule_builder
+ .direction(pfctl::Direction::In)
+ .from(pfctl::Ip::from(*net))
+ .to(pfctl::Ip::Any)
+ .build()?;
+ rules.push(allow_out);
+ rules.push(allow_in);
+ }
+ for multicast_net in &*super::ALLOWED_LAN_MULTICAST_NETS {
+ let allow_multicast_out = self
+ .create_rule_builder(FilterRuleAction::Pass)
.quick(true)
- .af(pfctl::AddrFamily::Ipv4)
- .from(pfctl::Ip::from(*net));
- let allow_net = rule_builder.to(pfctl::Ip::from(*net)).build()?;
- let allow_multicast = rule_builder
- .to(pfctl::Ip::from(*super::MULTICAST_NET))
+ .direction(pfctl::Direction::Out)
+ .af(match multicast_net {
+ IpNetwork::V4(_) => pfctl::AddrFamily::Ipv4,
+ IpNetwork::V6(_) => pfctl::AddrFamily::Ipv6,
+ })
+ .to(pfctl::Ip::from(*multicast_net))
.build()?;
- let allow_ssdp = rule_builder.to(pfctl::Ip::from(*super::SSDP_IP)).build()?;
- rules.push(allow_net);
- rules.push(allow_multicast);
- rules.push(allow_ssdp);
+ rules.push(allow_multicast_out);
}
- // IPv6
- let mut rule_builder = self.create_rule_builder(FilterRuleAction::Pass);
- rule_builder
- .quick(true)
- .af(pfctl::AddrFamily::Ipv6)
- .from(pfctl::Ip::from(*super::LOCAL_INET6_NET));
- let allow_net_v6 = rule_builder
- .to(pfctl::Ip::from(*super::LOCAL_INET6_NET))
- .build()?;
- let allow_multicast_v6 = rule_builder
- .to(pfctl::Ip::from(*super::MULTICAST_INET6_NET))
- .build()?;
- rules.push(allow_net_v6);
- rules.push(allow_multicast_v6);
-
Ok(rules)
}
fn get_allow_dhcp_rules(&self) -> Result<Vec<pfctl::FilterRule>> {
- let server_port_v4 = pfctl::Port::from(67);
- let client_port_v4 = pfctl::Port::from(68);
- let server_port_v6 = pfctl::Port::from(547);
- let client_port_v6 = pfctl::Port::from(546);
let mut dhcp_rule_builder = self.create_rule_builder(FilterRuleAction::Pass);
dhcp_rule_builder.quick(true).proto(pfctl::Proto::Udp);
let mut rules = Vec::new();
+
+ // DHCPv4
+ dhcp_rule_builder.af(pfctl::AddrFamily::Ipv4);
let allow_outgoing_dhcp_v4 = dhcp_rule_builder
.direction(pfctl::Direction::Out)
- .from(client_port_v4)
- .to(pfctl::Endpoint::new(Ipv4Addr::BROADCAST, server_port_v4))
+ .from(pfctl::Port::from(super::DHCPV4_CLIENT_PORT))
+ .to(pfctl::Endpoint::new(
+ Ipv4Addr::BROADCAST,
+ pfctl::Port::from(super::DHCPV4_SERVER_PORT),
+ ))
.build()?;
- rules.push(allow_outgoing_dhcp_v4);
let allow_incoming_dhcp_v4 = dhcp_rule_builder
- .af(pfctl::AddrFamily::Ipv4)
.direction(pfctl::Direction::In)
- .from(server_port_v4)
- .to(client_port_v4)
+ .from(pfctl::Port::from(super::DHCPV4_SERVER_PORT))
+ .to(pfctl::Port::from(super::DHCPV4_CLIENT_PORT))
.build()?;
+ rules.push(allow_outgoing_dhcp_v4);
rules.push(allow_incoming_dhcp_v4);
+ // DHCPv6
+ dhcp_rule_builder.af(pfctl::AddrFamily::Ipv6);
for dhcpv6_server in &*super::DHCPV6_SERVER_ADDRS {
let allow_outgoing_dhcp_v6 = dhcp_rule_builder
- .af(pfctl::AddrFamily::Ipv6)
.direction(pfctl::Direction::Out)
.from(pfctl::Endpoint::new(
- *super::LOCAL_INET6_NET,
- client_port_v6,
+ IpNetwork::V6(*super::IPV6_LINK_LOCAL),
+ pfctl::Port::from(super::DHCPV6_CLIENT_PORT),
+ ))
+ .to(pfctl::Endpoint::new(
+ *dhcpv6_server,
+ pfctl::Port::from(super::DHCPV6_SERVER_PORT),
))
- .to(pfctl::Endpoint::new(*dhcpv6_server, server_port_v6))
.build()?;
rules.push(allow_outgoing_dhcp_v6);
}
let allow_incoming_dhcp_v6 = dhcp_rule_builder
- .af(pfctl::AddrFamily::Ipv6)
.direction(pfctl::Direction::In)
.from(pfctl::Endpoint::new(
- *super::LOCAL_INET6_NET,
- server_port_v6,
+ pfctl::Ip::from(IpNetwork::V6(*super::IPV6_LINK_LOCAL)),
+ pfctl::Port::from(super::DHCPV6_SERVER_PORT),
))
.to(pfctl::Endpoint::new(
- *super::LOCAL_INET6_NET,
- client_port_v6,
+ pfctl::Ip::from(IpNetwork::V6(*super::IPV6_LINK_LOCAL)),
+ pfctl::Port::from(super::DHCPV6_CLIENT_PORT),
))
.build()?;
rules.push(allow_incoming_dhcp_v6);
+ // NDP (router solicitation, advertisement and redirect)
+ let allow_router_solicitation = self
+ .create_rule_builder(FilterRuleAction::Pass)
+ .quick(true)
+ .proto(pfctl::Proto::IcmpV6)
+ .direction(pfctl::Direction::Out)
+ .to(*super::ROUTER_SOLICITATION_OUT_DST_ADDR)
+ .build()?;
+ let allow_router_advertisement_and_redirect = self
+ .create_rule_builder(FilterRuleAction::Pass)
+ .quick(true)
+ .proto(pfctl::Proto::IcmpV6)
+ .direction(pfctl::Direction::In)
+ .from(pfctl::Ip::from(IpNetwork::V6(*super::IPV6_LINK_LOCAL)))
+ .build()?;
+ rules.push(allow_router_solicitation);
+ rules.push(allow_router_advertisement_and_redirect);
+
Ok(rules)
}
diff --git a/talpid-core/src/firewall/mod.rs b/talpid-core/src/firewall/mod.rs
index c86ac65b68..6561b3298b 100644
--- a/talpid-core/src/firewall/mod.rs
+++ b/talpid-core/src/firewall/mod.rs
@@ -30,26 +30,76 @@ pub use self::imp::Error;
#[cfg(unix)]
lazy_static! {
- static ref PRIVATE_NETS: [IpNetwork; 4] = [
+ /// When "allow local network" is enabled the app will allow traffic to and from these networks.
+ static ref ALLOWED_LAN_NETS: [IpNetwork; 5] = [
IpNetwork::V4(Ipv4Network::new(Ipv4Addr::new(10, 0, 0, 0), 8).unwrap()),
IpNetwork::V4(Ipv4Network::new(Ipv4Addr::new(172, 16, 0, 0), 12).unwrap()),
IpNetwork::V4(Ipv4Network::new(Ipv4Addr::new(192, 168, 0, 0), 16).unwrap()),
IpNetwork::V4(Ipv4Network::new(Ipv4Addr::new(169, 254, 0, 0), 16).unwrap()),
+ IpNetwork::V6(Ipv6Network::new(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0), 10).unwrap()),
];
- static ref LOCAL_INET6_NET: IpNetwork =
- IpNetwork::V6(Ipv6Network::new(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0), 10).unwrap());
- static ref MULTICAST_NET: IpNetwork =
- IpNetwork::V4(Ipv4Network::new(Ipv4Addr::new(224, 0, 0, 0), 24).unwrap());
- static ref MULTICAST_INET6_NET: IpNetwork =
- IpNetwork::V6(Ipv6Network::new(Ipv6Addr::new(0xfe02, 0, 0, 0, 0, 0, 0, 0), 16).unwrap());
- static ref SSDP_IP: IpAddr = IpAddr::V4(Ipv4Addr::new(239, 255, 255, 250));
- static ref DHCPV6_SERVER_ADDRS: [IpAddr; 2] = [
- IpAddr::V6(Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 1, 2)),
- IpAddr::V6(Ipv6Addr::new(0xff05, 0, 0, 0, 0, 0, 1, 3)),
+ /// When "allow local network" is enabled the app will allow traffic to these networks.
+ static ref ALLOWED_LAN_MULTICAST_NETS: [IpNetwork; 4] = [
+ // Local subnetwork multicast. Not routable
+ IpNetwork::V4(Ipv4Network::new(Ipv4Addr::new(224, 0, 0, 0), 24).unwrap()),
+ // Simple Service Discovery Protocol (SSDP) address
+ IpNetwork::V4(Ipv4Network::new(Ipv4Addr::new(239, 255, 255, 250), 32).unwrap()),
+ // Link-local IPv6 multicast. IPv6 equivalent of 224.0.0.0/24
+ IpNetwork::V6(Ipv6Network::new(Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 0), 16).unwrap()),
+ // Site-local IPv6 multicast.
+ IpNetwork::V6(Ipv6Network::new(Ipv6Addr::new(0xff05, 0, 0, 0, 0, 0, 0, 0), 16).unwrap()),
];
+ static ref IPV6_LINK_LOCAL: Ipv6Network = Ipv6Network::new(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0), 10).unwrap();
+ /// The allowed target addresses of outbound DHCPv6 requests
+ static ref DHCPV6_SERVER_ADDRS: [Ipv6Addr; 2] = [
+ Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 1, 2),
+ Ipv6Addr::new(0xff05, 0, 0, 0, 0, 0, 1, 3),
+ ];
+ static ref ROUTER_SOLICITATION_OUT_DST_ADDR: Ipv6Addr = Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 2);
}
+#[cfg(all(unix, not(target_os = "android")))]
+const DHCPV4_SERVER_PORT: u16 = 67;
+#[cfg(all(unix, not(target_os = "android")))]
+const DHCPV4_CLIENT_PORT: u16 = 68;
+#[cfg(all(unix, not(target_os = "android")))]
+const DHCPV6_SERVER_PORT: u16 = 547;
+#[cfg(all(unix, not(target_os = "android")))]
+const DHCPV6_CLIENT_PORT: u16 = 546;
+
/// A enum that describes network security strategy
+///
+/// # Firewall block/allow specification.
+///
+/// Except what's described as allowed below, all network packets should be blocked.
+///
+/// ## In all policies the firewall should always allow the following traffic
+///
+/// 1. All traffic on loopback adapters
+/// 2. DHCPv4 and DHCPv6 requests to go out and responses to come in:
+/// * Outgoing from *:DHCPV4_CLIENT_PORT to 255.255.255.255:DHCPV4_SERVER_PORT
+/// * Incoming *:DHCPV4_SERVER_PORT to *:DHCPV4_CLIENT_PORT
+/// * Outgoing from IPV6_LINK_LOCAL:DHCPV6_CLIENT_PORT to DHCPV6_SERVER_ADDRS:DHCPV6_SERVER_PORT
+/// * Incoming from IPV6_LINK_LOCAL:DHCPV6_SERVER_PORT to IPV6_LINK_LOCAL:DHCPV6_CLIENT_PORT
+/// 3. Router solicitation, advertisement and redirects (subset of NDP):
+/// * Outgoing to ROUTER_SOLICITATION_OUT_DST_ADDR, but only ICMPv6 with type 133 and code 0.
+/// * Incoming from IPV6_LINK_LOCAL, but only ICMPv6 type 134 or 137 and code 0.
+/// 4. If `allow_lan` is enabled, all policies should allow the following traffic:
+/// * Outgoing to, and incoming from, any IP in the networks listed in ALLOWED_LAN_NETS
+/// * Outgoing to any IP in the networks listed in ALLOWED_LAN_MULTICAST_NETS
+///
+/// ## Policy specific rules
+///
+/// 1. In the `Connecting` and `Connected` policies traffic should be allowed to and from the IP and
+/// port in `peer_endpoint`
+/// 2. In the `Connecting` policy, ICMP packets should be allowed to and from all IPs in
+/// `pingable_hosts`.
+/// 3. In the `Connected` policy, DNS requests (destination port 53 on both UDP and TCP) should be
+/// allowed over the tunnel interface in `tunnel.interface` and to the IPs `tunnel.ipv4_gateway`
+/// and `tunnel.ipv6_gateway`. But blocked to all other destinations and over all other
+/// interfaces.
+/// 4. In the `Connected` policy, all traffic should be allowed over the tunnel interface in
+/// `tunnel.interface`, minus the DNS packets described above.
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum FirewallPolicy {
/// Allow traffic only to server