Add AllowedIps
setting to CLI
This commit is contained in:
parent
e2d1514cf2
commit
e91d35854c
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2926,6 +2926,7 @@ dependencies = [
|
||||
"fern",
|
||||
"futures",
|
||||
"hickory-resolver",
|
||||
"ipnetwork",
|
||||
"libc",
|
||||
"log",
|
||||
"log-panics",
|
||||
|
@ -6,9 +6,9 @@ use mullvad_types::{
|
||||
constraints::{Constraint, Match},
|
||||
location::CountryCode,
|
||||
relay_constraints::{
|
||||
GeographicLocationConstraint, LocationConstraint, LocationConstraintFormatter,
|
||||
OpenVpnConstraints, Ownership, Provider, Providers, RelayConstraints, RelayOverride,
|
||||
RelaySettings, TransportPort, WireguardConstraints,
|
||||
allowed_ip::AllowedIps, GeographicLocationConstraint, LocationConstraint,
|
||||
LocationConstraintFormatter, OpenVpnConstraints, Ownership, Provider, Providers,
|
||||
RelayConstraints, RelayOverride, RelaySettings, TransportPort, WireguardConstraints,
|
||||
},
|
||||
relay_list::{RelayEndpointData, RelayListCountry},
|
||||
ConnectionConfig, CustomTunnelEndpoint,
|
||||
@ -18,9 +18,7 @@ use std::{
|
||||
io::BufRead,
|
||||
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
|
||||
};
|
||||
use talpid_types::net::{
|
||||
all_of_the_internet, openvpn, wireguard, Endpoint, IpVersion, TransportProtocol, TunnelType,
|
||||
};
|
||||
use talpid_types::net::{openvpn, wireguard, Endpoint, IpVersion, TransportProtocol, TunnelType};
|
||||
|
||||
use super::{relay_constraints::LocationArgs, BooleanOption};
|
||||
use crate::{cmds::receive_confirmation, print_option};
|
||||
@ -538,7 +536,7 @@ impl Relay {
|
||||
},
|
||||
peer: wireguard::PeerConfig {
|
||||
public_key: peer_pubkey,
|
||||
allowed_ips: all_of_the_internet(),
|
||||
allowed_ips: AllowedIps::allow_all().resolve(Some(ipv4_gateway), ipv6_gateway),
|
||||
endpoint: SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), port),
|
||||
psk: None,
|
||||
constant_packet_size: false,
|
||||
|
@ -3,6 +3,7 @@ use clap::Subcommand;
|
||||
use mullvad_management_interface::MullvadProxyClient;
|
||||
use mullvad_types::{
|
||||
constraints::Constraint,
|
||||
relay_constraints::{AllowedIps, RelaySettings, WireguardConstraints},
|
||||
wireguard::{QuantumResistantState, RotationInterval, DEFAULT_ROTATION_INTERVAL},
|
||||
};
|
||||
|
||||
@ -44,6 +45,15 @@ pub enum TunnelOptions {
|
||||
/// Configure whether to enable DAITA direct only
|
||||
#[arg(long)]
|
||||
daita_direct_only: Option<BooleanOption>,
|
||||
/// Specify custom allowed IPs for WireGuard tunnels. Use comma-separated values of IPs and IP ranges in CIDR notation.
|
||||
/// A empty string resets to the default value, where all traffic is allowed, i.e. (0.0.0.0/0,::/0).
|
||||
/// For CIDR ranges, host bits must be zero (e.g., "10.0.0.0/24" is valid, "10.0.0.1/24" is not).
|
||||
///
|
||||
/// Example: "10.0.0.0/24,192.168.1.1,fd00::/8"
|
||||
///
|
||||
/// WARNING: Setting this value incorrectly may cause internet access to be blocked or the app to not work properly.
|
||||
#[arg(long)]
|
||||
allowed_ips: Option<String>,
|
||||
/// The key rotation interval. Number of hours, or 'any'
|
||||
#[arg(long)]
|
||||
rotation_interval: Option<Constraint<RotationInterval>>,
|
||||
@ -117,6 +127,22 @@ impl Tunnel {
|
||||
},
|
||||
);
|
||||
|
||||
// Get the WireGuard allowed IPs
|
||||
let wireguard_constraints = match rpc.get_settings().await?.relay_settings {
|
||||
RelaySettings::Normal(settings) => settings.wireguard_constraints,
|
||||
RelaySettings::CustomTunnelEndpoint(_) => WireguardConstraints::default(),
|
||||
};
|
||||
|
||||
print_option!(
|
||||
"Allowed IPs",
|
||||
match wireguard_constraints.allowed_ips {
|
||||
mullvad_types::constraints::Constraint::Any => "all traffic (default)".to_string(),
|
||||
mullvad_types::constraints::Constraint::Only(ips) => {
|
||||
ips.to_string()
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
println!("Generic options");
|
||||
|
||||
print_option!(
|
||||
@ -139,6 +165,7 @@ impl Tunnel {
|
||||
quantum_resistant,
|
||||
daita,
|
||||
daita_direct_only,
|
||||
allowed_ips,
|
||||
rotation_interval,
|
||||
rotate_key,
|
||||
} => {
|
||||
@ -147,6 +174,7 @@ impl Tunnel {
|
||||
quantum_resistant,
|
||||
daita,
|
||||
daita_direct_only,
|
||||
allowed_ips,
|
||||
rotation_interval,
|
||||
rotate_key,
|
||||
)
|
||||
@ -179,6 +207,7 @@ impl Tunnel {
|
||||
quantum_resistant: Option<QuantumResistantState>,
|
||||
daita: Option<BooleanOption>,
|
||||
daita_direct_only: Option<BooleanOption>,
|
||||
allowed_ips: Option<String>,
|
||||
rotation_interval: Option<Constraint<RotationInterval>>,
|
||||
rotate_key: Option<RotateKey>,
|
||||
) -> Result<()> {
|
||||
@ -194,6 +223,13 @@ impl Tunnel {
|
||||
println!("Quantum resistant setting has been updated");
|
||||
}
|
||||
|
||||
if let Some(allowed_ips_str) = allowed_ips {
|
||||
let ips = AllowedIps::parse(allowed_ips_str.split(','))?;
|
||||
|
||||
rpc.set_wireguard_allowed_ips(ips).await?;
|
||||
println!("WireGuard allowed IPs have been updated")
|
||||
}
|
||||
|
||||
if let Some(enable_daita) = daita {
|
||||
rpc.set_enable_daita(*enable_daita).await?;
|
||||
println!("DAITA setting has been updated");
|
||||
|
@ -31,6 +31,7 @@ serde_json = { workspace = true }
|
||||
tokio = { workspace = true, features = ["fs", "io-util", "rt-multi-thread", "sync", "time"] }
|
||||
tokio-stream = { version = "0.1", features = ["sync"]}
|
||||
socket2 = { workspace = true }
|
||||
ipnetwork = { workspace = true }
|
||||
|
||||
mullvad-relay-selector = { path = "../mullvad-relay-selector" }
|
||||
mullvad-types = { path = "../mullvad-types" }
|
||||
|
@ -53,12 +53,14 @@ use mullvad_types::{
|
||||
access_method::{AccessMethod, AccessMethodSetting},
|
||||
account::{AccountData, AccountNumber, VoucherSubmission},
|
||||
auth_failed::AuthFailed,
|
||||
constraints::Constraint,
|
||||
custom_list::CustomList,
|
||||
device::{Device, DeviceEvent, DeviceEventCause, DeviceId, DeviceState, RemoveDeviceEvent},
|
||||
features::{compute_feature_indicators, FeatureIndicator, FeatureIndicators},
|
||||
location::{GeoIpLocation, LocationEventData},
|
||||
relay_constraints::{
|
||||
BridgeSettings, BridgeState, BridgeType, ObfuscationSettings, RelayOverride, RelaySettings,
|
||||
allowed_ip::AllowedIps, BridgeSettings, BridgeState, BridgeType, ObfuscationSettings,
|
||||
RelayOverride, RelaySettings,
|
||||
},
|
||||
relay_list::RelayList,
|
||||
settings::{DnsOptions, Settings},
|
||||
@ -289,6 +291,8 @@ pub enum DaemonCommand {
|
||||
/// Toggle macOS network check leak
|
||||
/// Set MTU for wireguard tunnels
|
||||
SetWireguardMtu(ResponseTx<(), settings::Error>, Option<u16>),
|
||||
/// Set allowed IPs for wireguard tunnels
|
||||
SetWireguardAllowedIps(ResponseTx<(), settings::Error>, Constraint<AllowedIps>),
|
||||
/// Set automatic key rotation interval for wireguard tunnels
|
||||
SetWireguardRotationInterval(ResponseTx<(), settings::Error>, Option<RotationInterval>),
|
||||
/// Get the daemon settings
|
||||
@ -1419,6 +1423,9 @@ impl Daemon {
|
||||
}
|
||||
ClearAllRelayOverrides(tx) => self.on_clear_all_relay_overrides(tx).await,
|
||||
SetWireguardMtu(tx, mtu) => self.on_set_wireguard_mtu(tx, mtu).await,
|
||||
SetWireguardAllowedIps(tx, allowed_ips) => {
|
||||
self.on_set_wireguard_allowed_ips(tx, allowed_ips).await
|
||||
}
|
||||
SetWireguardRotationInterval(tx, interval) => {
|
||||
self.on_set_wireguard_rotation_interval(tx, interval).await
|
||||
}
|
||||
@ -2785,6 +2792,38 @@ impl Daemon {
|
||||
}
|
||||
}
|
||||
|
||||
async fn on_set_wireguard_allowed_ips(
|
||||
&mut self,
|
||||
tx: ResponseTx<(), settings::Error>,
|
||||
allowed_ips: Constraint<AllowedIps>,
|
||||
) {
|
||||
match self
|
||||
.settings
|
||||
.update(move |settings| {
|
||||
if let RelaySettings::Normal(ref mut relay_settings) = settings.relay_settings {
|
||||
relay_settings.wireguard_constraints.allowed_ips = allowed_ips;
|
||||
}
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(settings_changed) => {
|
||||
Self::oneshot_send(tx, Ok(()), "set_wireguard_allowed_ips response");
|
||||
if settings_changed {
|
||||
if let Some(TunnelType::Wireguard) = self.get_connected_tunnel_type() {
|
||||
log::info!(
|
||||
"Initiating tunnel restart because the WireGuard allowed IPs setting changed"
|
||||
);
|
||||
self.reconnect_tunnel();
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{}", e.display_chain_with_msg("Unable to save settings"));
|
||||
Self::oneshot_send(tx, Err(e), "set_wireguard_allowed_ips response");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_rotate_wireguard_key(&self, tx: ResponseTx<(), Error>) {
|
||||
let manager = self.account_manager.clone();
|
||||
tokio::spawn(async move {
|
||||
|
@ -11,7 +11,8 @@ use mullvad_management_interface::{
|
||||
use mullvad_types::{
|
||||
account::AccountNumber,
|
||||
relay_constraints::{
|
||||
BridgeSettings, BridgeState, ObfuscationSettings, RelayOverride, RelaySettings,
|
||||
allowed_ip::AllowedIps, BridgeSettings, BridgeState, ObfuscationSettings, RelayOverride,
|
||||
RelaySettings,
|
||||
},
|
||||
relay_list::RelayList,
|
||||
settings::{DnsOptions, Settings},
|
||||
@ -639,6 +640,26 @@ impl ManagementService for ManagementServiceImpl {
|
||||
}
|
||||
}
|
||||
|
||||
async fn set_wireguard_allowed_ips(
|
||||
&self,
|
||||
request: Request<types::AllowedIpsList>,
|
||||
) -> ServiceResult<()> {
|
||||
let allowed_ips_str = request.into_inner().values;
|
||||
log::debug!("set_wireguard_allowed_ips({:?})", allowed_ips_str);
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let allowed_ips = AllowedIps::parse(&allowed_ips_str)
|
||||
.map_err(|e| {
|
||||
log::error!("{e}");
|
||||
Status::invalid_argument(format!("Invalid allowed IPs: {}", e))
|
||||
})?
|
||||
.to_constraint();
|
||||
|
||||
self.send_command_to_daemon(DaemonCommand::SetWireguardAllowedIps(tx, allowed_ips))?;
|
||||
self.wait_for_result(rx).await??;
|
||||
Ok(Response::new(()))
|
||||
}
|
||||
|
||||
// Custom lists
|
||||
//
|
||||
|
||||
|
@ -39,6 +39,9 @@ pub enum Error {
|
||||
|
||||
#[error("Failed to apply settings update")]
|
||||
UpdateFailed(Box<dyn std::error::Error + Send + Sync>),
|
||||
|
||||
#[error("Failed to parse IP network from string: {0}")]
|
||||
ParseIp(String),
|
||||
}
|
||||
|
||||
/// Converts an [Error] to a management interface status
|
||||
@ -57,9 +60,10 @@ impl From<Error> for mullvad_management_interface::Status {
|
||||
let custom_list_err = *err.downcast::<CustomListError>().unwrap();
|
||||
handle_custom_list_error(custom_list_err)
|
||||
}
|
||||
Error::SerializeError(..) | Error::ParseError(..) | Error::UpdateFailed(..) => {
|
||||
Status::new(Code::Internal, error.to_string())
|
||||
}
|
||||
Error::SerializeError(..)
|
||||
| Error::ParseError(..)
|
||||
| Error::UpdateFailed(..)
|
||||
| Error::ParseIp(..) => Status::new(Code::Internal, error.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -48,6 +48,7 @@ service ManagementService {
|
||||
rpc SetAutoConnect(google.protobuf.BoolValue) returns (google.protobuf.Empty) {}
|
||||
rpc SetOpenvpnMssfix(google.protobuf.UInt32Value) returns (google.protobuf.Empty) {}
|
||||
rpc SetWireguardMtu(google.protobuf.UInt32Value) returns (google.protobuf.Empty) {}
|
||||
rpc SetWireguardAllowedIps(AllowedIpsList) returns (google.protobuf.Empty) {}
|
||||
rpc SetEnableIpv6(google.protobuf.BoolValue) returns (google.protobuf.Empty) {}
|
||||
rpc SetQuantumResistantTunnel(QuantumResistantState) returns (google.protobuf.Empty) {}
|
||||
rpc SetEnableDaita(google.protobuf.BoolValue) returns (google.protobuf.Empty) {}
|
||||
@ -562,8 +563,9 @@ enum IpVersion {
|
||||
message WireguardConstraints {
|
||||
optional uint32 port = 1;
|
||||
optional IpVersion ip_version = 2;
|
||||
bool use_multihop = 3;
|
||||
LocationConstraint entry_location = 4;
|
||||
repeated string allowed_ips = 3;
|
||||
bool use_multihop = 4;
|
||||
LocationConstraint entry_location = 5;
|
||||
}
|
||||
|
||||
message CustomRelaySettings {
|
||||
@ -831,3 +833,5 @@ message PlayPurchase {
|
||||
}
|
||||
|
||||
message PlayPurchasePaymentToken { string token = 1; }
|
||||
|
||||
message AllowedIpsList { repeated string values = 1; }
|
||||
|
@ -22,7 +22,7 @@ use mullvad_types::{
|
||||
device::{Device, DeviceId, DeviceState},
|
||||
features::FeatureIndicators,
|
||||
relay_constraints::{
|
||||
BridgeSettings, BridgeState, ObfuscationSettings, RelayOverride, RelaySettings,
|
||||
AllowedIps, BridgeSettings, BridgeState, ObfuscationSettings, RelayOverride, RelaySettings,
|
||||
},
|
||||
settings::DnsOptions,
|
||||
wireguard::{PublicKey, QuantumResistantState, RotationInterval},
|
||||
@ -779,6 +779,16 @@ impl MullvadProxyClient {
|
||||
self.0.enable_relay(relay).await.map_err(Error::Rpc)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn set_wireguard_allowed_ips(&mut self, allowed_ips: AllowedIps) -> Result<()> {
|
||||
self.0
|
||||
.set_wireguard_allowed_ips(types::AllowedIpsList {
|
||||
values: allowed_ips.0.iter().map(ToString::to_string).collect(),
|
||||
})
|
||||
.await
|
||||
.map_err(Error::Rpc)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
|
@ -1,6 +1,11 @@
|
||||
use crate::types::{conversions::net::try_tunnel_type_from_i32, proto, FromProtobufTypeError};
|
||||
use mullvad_types::{
|
||||
constraints::Constraint, custom_list::Id, relay_constraints::GeographicLocationConstraint,
|
||||
constraints::Constraint,
|
||||
custom_list::Id,
|
||||
relay_constraints::{
|
||||
allowed_ip::{self, AllowedIps},
|
||||
GeographicLocationConstraint,
|
||||
},
|
||||
};
|
||||
use std::str::FromStr;
|
||||
use talpid_types::net::proxy::CustomProxy;
|
||||
@ -24,10 +29,17 @@ impl TryFrom<&proto::WireguardConstraints>
|
||||
)),
|
||||
None => None,
|
||||
};
|
||||
let allowed_ips = AllowedIps::parse(&constraints.allowed_ips)
|
||||
.map_err(|e| {
|
||||
log::error!("Failed to parse allowed IPs: {}", e);
|
||||
FromProtobufTypeError::InvalidArgument("invalid allowed IPs")
|
||||
})?
|
||||
.to_constraint();
|
||||
|
||||
Ok(mullvad_constraints::WireguardConstraints {
|
||||
port: Constraint::from(constraints.port.map(|port| port as u16)),
|
||||
ip_version: Constraint::from(ip_version),
|
||||
allowed_ips,
|
||||
use_multihop: constraints.use_multihop,
|
||||
entry_location: constraints
|
||||
.entry_location
|
||||
@ -252,6 +264,14 @@ impl From<mullvad_types::relay_constraints::RelaySettings> for proto::RelaySetti
|
||||
.ip_version
|
||||
.option()
|
||||
.map(|ipv| i32::from(proto::IpVersion::from(ipv))),
|
||||
allowed_ips: allowed_ip::resolve_from_constraint(
|
||||
&constraints.wireguard_constraints.allowed_ips,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.into_iter()
|
||||
.map(|ip| ip.to_string())
|
||||
.collect(),
|
||||
use_multihop: constraints.wireguard_constraints.multihop(),
|
||||
entry_location: constraints
|
||||
.wireguard_constraints
|
||||
|
@ -13,14 +13,13 @@ use ipnetwork::IpNetwork;
|
||||
use mullvad_types::{
|
||||
constraints::Constraint,
|
||||
endpoint::MullvadWireguardEndpoint,
|
||||
relay_constraints::TransportPort,
|
||||
relay_constraints::{allowed_ip::resolve_from_constraint, TransportPort},
|
||||
relay_list::{
|
||||
BridgeEndpointData, OpenVpnEndpoint, OpenVpnEndpointData, Relay, RelayEndpointData,
|
||||
WireguardEndpointData,
|
||||
},
|
||||
};
|
||||
use talpid_types::net::{
|
||||
all_of_the_internet,
|
||||
proxy::Shadowsocks,
|
||||
wireguard::{PeerConfig, PublicKey},
|
||||
Endpoint, IpVersion, TransportProtocol,
|
||||
@ -79,7 +78,13 @@ fn wireguard_singlehop_endpoint(
|
||||
let peer_config = PeerConfig {
|
||||
public_key: get_public_key(exit)?.clone(),
|
||||
endpoint,
|
||||
allowed_ips: all_of_the_internet(),
|
||||
// The peer should be able to route incoming VPN traffic to the given user given IP
|
||||
// ranges, if any, else the rest of the internet.
|
||||
allowed_ips: resolve_from_constraint(
|
||||
&query.allowed_ips,
|
||||
Some(data.ipv4_gateway),
|
||||
Some(data.ipv6_gateway),
|
||||
),
|
||||
// This will be filled in later, not the relay selector's problem
|
||||
psk: None,
|
||||
// This will be filled in later
|
||||
@ -118,9 +123,13 @@ fn wireguard_multihop_endpoint(
|
||||
let exit = PeerConfig {
|
||||
public_key: get_public_key(exit)?.clone(),
|
||||
endpoint: exit_endpoint,
|
||||
// The exit peer should be able to route incoming VPN traffic to the rest of
|
||||
// the internet.
|
||||
allowed_ips: all_of_the_internet(),
|
||||
// The exit peer should be able to route incoming VPN traffic to the given user given IP
|
||||
// ranges, if any, else the rest of the internet.
|
||||
allowed_ips: resolve_from_constraint(
|
||||
&query.allowed_ips,
|
||||
Some(data.ipv4_gateway),
|
||||
Some(data.ipv6_gateway),
|
||||
),
|
||||
// This will be filled in later, not the relay selector's problem
|
||||
psk: None,
|
||||
// This will be filled in later
|
||||
|
@ -330,6 +330,7 @@ impl<'a> TryFrom<NormalSelectorConfig<'a>> for RelayQuery {
|
||||
let WireguardConstraints {
|
||||
port,
|
||||
ip_version,
|
||||
allowed_ips,
|
||||
use_multihop,
|
||||
entry_location,
|
||||
} = wireguard_constraints;
|
||||
@ -341,6 +342,7 @@ impl<'a> TryFrom<NormalSelectorConfig<'a>> for RelayQuery {
|
||||
WireguardRelayQuery {
|
||||
port,
|
||||
ip_version,
|
||||
allowed_ips,
|
||||
use_multihop: Constraint::Only(use_multihop),
|
||||
entry_location,
|
||||
obfuscation: ObfuscationQuery::from(obfuscation_settings),
|
||||
|
@ -32,9 +32,9 @@ use crate::Error;
|
||||
use mullvad_types::{
|
||||
constraints::Constraint,
|
||||
relay_constraints::{
|
||||
BridgeConstraints, BridgeSettings, BridgeState, BridgeType, LocationConstraint,
|
||||
ObfuscationSettings, OpenVpnConstraints, Ownership, Providers, RelayConstraints,
|
||||
RelaySettings, SelectedObfuscation, ShadowsocksSettings, TransportPort,
|
||||
allowed_ip::AllowedIps, BridgeConstraints, BridgeSettings, BridgeState, BridgeType,
|
||||
LocationConstraint, ObfuscationSettings, OpenVpnConstraints, Ownership, Providers,
|
||||
RelayConstraints, RelaySettings, SelectedObfuscation, ShadowsocksSettings, TransportPort,
|
||||
Udp2TcpObfuscationSettings, WireguardConstraints,
|
||||
},
|
||||
wireguard::QuantumResistantState,
|
||||
@ -268,6 +268,7 @@ impl From<RelayQuery> for RelaySettings {
|
||||
pub struct WireguardRelayQuery {
|
||||
pub port: Constraint<u16>,
|
||||
pub ip_version: Constraint<IpVersion>,
|
||||
pub allowed_ips: Constraint<AllowedIps>,
|
||||
pub use_multihop: Constraint<bool>,
|
||||
pub entry_location: Constraint<LocationConstraint>,
|
||||
pub obfuscation: ObfuscationQuery,
|
||||
@ -360,6 +361,7 @@ impl WireguardRelayQuery {
|
||||
WireguardRelayQuery {
|
||||
port: Constraint::Any,
|
||||
ip_version: Constraint::Any,
|
||||
allowed_ips: Constraint::Any,
|
||||
use_multihop: Constraint::Any,
|
||||
entry_location: Constraint::Any,
|
||||
obfuscation: ObfuscationQuery::Auto,
|
||||
@ -374,6 +376,7 @@ impl WireguardRelayQuery {
|
||||
WireguardConstraints {
|
||||
port: self.port,
|
||||
ip_version: self.ip_version,
|
||||
allowed_ips: self.allowed_ips,
|
||||
entry_location: self.entry_location,
|
||||
use_multihop: self.use_multihop.unwrap_or(false),
|
||||
}
|
||||
@ -392,6 +395,7 @@ impl From<WireguardRelayQuery> for WireguardConstraints {
|
||||
WireguardConstraints {
|
||||
port: value.port,
|
||||
ip_version: value.ip_version,
|
||||
allowed_ips: value.allowed_ips,
|
||||
entry_location: value.entry_location,
|
||||
use_multihop: value.use_multihop.unwrap_or(false),
|
||||
}
|
||||
|
@ -130,15 +130,16 @@ macro_rules! impl_intersection_partialeq {
|
||||
};
|
||||
}
|
||||
|
||||
// Note that deriving `Intersection` for using `impl_intersection_partialeq`
|
||||
// may not do what you expect for data structures that represent/wrap sets, such as
|
||||
// `Vec<T>` or `HashSet<T>`. `Constraint::Only` will only match if the
|
||||
// `Vec<T>` or `HashSet<T>` is exactly the same, not if they contain overlapping elements.
|
||||
impl_intersection_partialeq!(u16);
|
||||
impl_intersection_partialeq!(bool);
|
||||
|
||||
// NOTE: this implementation does not do what you may expect of an intersection
|
||||
impl_intersection_partialeq!(relay_constraints::Providers);
|
||||
// NOTE: should take actual intersection
|
||||
impl_intersection_partialeq!(relay_constraints::LocationConstraint);
|
||||
impl_intersection_partialeq!(relay_constraints::Ownership);
|
||||
// NOTE: it contains an inner constraint
|
||||
impl_intersection_partialeq!(talpid_types::net::TransportProtocol);
|
||||
impl_intersection_partialeq!(talpid_types::net::TunnelType);
|
||||
impl_intersection_partialeq!(talpid_types::net::IpVersion);
|
||||
impl_intersection_partialeq!(relay_constraints::AllowedIps);
|
||||
|
@ -407,10 +407,140 @@ impl fmt::Display for OpenVpnConstraints {
|
||||
pub struct WireguardConstraints {
|
||||
pub port: Constraint<u16>,
|
||||
pub ip_version: Constraint<IpVersion>,
|
||||
pub allowed_ips: Constraint<AllowedIps>,
|
||||
pub use_multihop: bool,
|
||||
pub entry_location: Constraint<LocationConstraint>,
|
||||
}
|
||||
|
||||
pub use allowed_ip::AllowedIps;
|
||||
pub mod allowed_ip {
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
|
||||
use crate::constraints::Constraint;
|
||||
use ipnetwork::IpNetwork;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct AllowedIps(pub Vec<IpNetwork>);
|
||||
|
||||
impl Default for AllowedIps {
|
||||
fn default() -> Self {
|
||||
AllowedIps::allow_all()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for AllowedIps {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(
|
||||
&self
|
||||
.0
|
||||
.iter()
|
||||
.map(|net| net.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum AllowedIpParseError {
|
||||
#[error("Failed to parse IP network: {0}")]
|
||||
Parse(#[from] ipnetwork::IpNetworkError),
|
||||
#[error("IP network {0} has non-zero host bits (should be {1})")]
|
||||
NonZeroHostBits(IpNetwork, std::net::IpAddr),
|
||||
}
|
||||
|
||||
/// Represents a collection of allowed IP networks.
|
||||
///
|
||||
/// Provides utility methods to construct `AllowedIps` from various sources,
|
||||
/// including allowing all IPs, parsing from string representations, and
|
||||
/// converting into a constraint.
|
||||
impl AllowedIps {
|
||||
/// Creates an `AllowedIps` instance that allows all IP addresses.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// An `AllowedIps` containing all possible IP networks.
|
||||
pub fn allow_all() -> Self {
|
||||
AllowedIps(vec![
|
||||
"0.0.0.0/0".parse().expect("Failed to parse ipv4 network"),
|
||||
"::0/0".parse().expect("Failed to parse ipv6 network"),
|
||||
])
|
||||
}
|
||||
|
||||
/// Constructs an `AllowedIps` from an iterator of string representations of IP networks.
|
||||
///
|
||||
/// Each string should be a valid CIDR notation (e.g., "192.168.1.0/24").
|
||||
/// Ignores empty strings. Returns an error if any string is not a valid network or if it contains non-zero host bits.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `AllowedIpParseError::Parse` if parsing fails, or
|
||||
/// `AllowedIpParseError::NonZeroHostBits` if the network contains non-zero host bits.
|
||||
pub fn parse<I, S>(allowed_ips: I) -> Result<AllowedIps, AllowedIpParseError>
|
||||
where
|
||||
I: IntoIterator<Item = S>,
|
||||
S: AsRef<str>,
|
||||
{
|
||||
let mut networks = vec![];
|
||||
for s in allowed_ips {
|
||||
let s = s.as_ref().trim();
|
||||
if !s.is_empty() {
|
||||
let net: IpNetwork = s.parse().map_err(AllowedIpParseError::Parse)?;
|
||||
if net.network() != net.ip() {
|
||||
return Err(AllowedIpParseError::NonZeroHostBits(net, net.network()));
|
||||
}
|
||||
networks.push(net);
|
||||
}
|
||||
}
|
||||
Ok(AllowedIps(networks))
|
||||
}
|
||||
|
||||
/// Converts the `AllowedIps` into a `Constraint<AllowedIps>`.
|
||||
/// If the list of ip ranges is empty, it returns `Constraint::Any`, otherwise it returns `Constraint::Only(self)`.
|
||||
pub fn to_constraint(self) -> Constraint<AllowedIps> {
|
||||
if self.0.is_empty() {
|
||||
Constraint::Any
|
||||
} else {
|
||||
Constraint::Only(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolves the allowed IPs to a `Vec<IpNetwork>`, adding the host IPv4 and IPv6 addresses if provided.
|
||||
pub fn resolve(
|
||||
self,
|
||||
host_ipv4: Option<Ipv4Addr>,
|
||||
host_ipv6: Option<Ipv6Addr>,
|
||||
) -> Vec<IpNetwork> {
|
||||
let mut networks = self.0;
|
||||
if let Some(host_ipv6) = host_ipv6 {
|
||||
networks.push(IpNetwork::V6(host_ipv6.into()));
|
||||
}
|
||||
if let Some(host_ipv4) = host_ipv4 {
|
||||
networks.push(IpNetwork::V4(host_ipv4.into()));
|
||||
}
|
||||
log::trace!("Resolved allowed IPs: {networks:?}");
|
||||
networks
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolves the allowed IPs from a `Constraint<AllowedIps>`, adding the host IPv4 and IPv6 addresses if provided.
|
||||
/// If the constraint is `Constraint::Any` or `Constraint::Only` with an empty list, it allows all IPs.
|
||||
/// Returns a vector of `IpNetwork` containing the resolved allowed IPs.
|
||||
pub fn resolve_from_constraint(
|
||||
allowed_ips: &Constraint<AllowedIps>,
|
||||
host_ipv4: Option<Ipv4Addr>,
|
||||
host_ipv6: Option<Ipv6Addr>,
|
||||
) -> Vec<IpNetwork> {
|
||||
match allowed_ips {
|
||||
Constraint::Any => AllowedIps::allow_all(),
|
||||
Constraint::Only(ips) if ips.0.is_empty() => AllowedIps::allow_all(),
|
||||
Constraint::Only(ips) => ips.clone(),
|
||||
}
|
||||
.resolve(host_ipv4, host_ipv6)
|
||||
}
|
||||
}
|
||||
|
||||
impl WireguardConstraints {
|
||||
/// Enable or disable multihop.
|
||||
pub fn use_multihop(&mut self, multihop: bool) {
|
||||
|
@ -544,16 +544,6 @@ pub struct GenericTunnelOptions {
|
||||
pub enable_ipv6: bool,
|
||||
}
|
||||
|
||||
/// Returns a vector of IP networks representing all of the internet, 0.0.0.0/0.
|
||||
/// This may be used in [`crate::net::wireguard::PeerConfig`] to route all traffic
|
||||
/// to the tunnel interface.
|
||||
pub fn all_of_the_internet() -> Vec<ipnetwork::IpNetwork> {
|
||||
vec![
|
||||
"0.0.0.0/0".parse().expect("Failed to parse ipv6 network"),
|
||||
"::0/0".parse().expect("Failed to parse ipv6 network"),
|
||||
]
|
||||
}
|
||||
|
||||
/// Details about the hosts's connectivity.
|
||||
///
|
||||
/// Information about the host's connectivity, such as the preesence of
|
||||
|
Loading…
x
Reference in New Issue
Block a user