Skip to content

Commit

Permalink
Merge branch 'linux-lower-entry-multihop-mtu' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
dlon committed Dec 4, 2023
2 parents e119d9b + 88a04da commit d4ed3bf
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 94 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ Line wrap the file at 100 chars. Th
#### Linux
- Rename interface name from `wg-mullvad` to `wg0-mullvad`.

### Fixed
#### Linux
- Prevent fragmentation when multihop is enabled by setting a default route MTU.


## [2023.6-beta1] - 2023-11-23
### Added
Expand Down
2 changes: 1 addition & 1 deletion talpid-routing/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ talpid-types = { path = "../talpid-types" }
libc = "0.2"
once_cell = { workspace = true }
rtnetlink = "0.11"
netlink-packet-route = "0.13"
netlink-packet-route = { version = "0.13", features = ["rich_nlas"] }
netlink-sys = "0.8.3"

[target.'cfg(target_os = "macos")'.dependencies]
Expand Down
20 changes: 20 additions & 0 deletions talpid-routing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ pub struct Route {
metric: Option<u32>,
#[cfg(target_os = "linux")]
table_id: u32,
#[cfg(target_os = "linux")]
mtu: Option<u32>,
}

impl Route {
Expand All @@ -49,6 +51,8 @@ impl Route {
metric: None,
#[cfg(target_os = "linux")]
table_id: u32::from(RT_TABLE_MAIN),
#[cfg(target_os = "linux")]
mtu: None,
}
}

Expand All @@ -72,6 +76,10 @@ impl fmt::Display for Route {
}
#[cfg(target_os = "linux")]
write!(f, " table {}", self.table_id)?;
#[cfg(target_os = "linux")]
if let Some(mtu) = self.mtu {
write!(f, " mtu {mtu}")?;
}
Ok(())
}
}
Expand All @@ -87,6 +95,9 @@ pub struct RequiredRoute {
/// Specifies whether the route should be added to the main routing table or not.
#[cfg(target_os = "linux")]
main_table: bool,
/// Specifies route MTU
#[cfg(target_os = "linux")]
mtu: Option<u16>,
}

impl RequiredRoute {
Expand All @@ -97,6 +108,8 @@ impl RequiredRoute {
prefix,
#[cfg(target_os = "linux")]
main_table: true,
#[cfg(target_os = "linux")]
mtu: None,
}
}

Expand All @@ -106,6 +119,13 @@ impl RequiredRoute {
self.main_table = main_table;
self
}

/// Set route MTU to the given value.
#[cfg(target_os = "linux")]
pub fn mtu(mut self, mtu: u16) -> Self {
self.mtu = Some(mtu);
self
}
}

/// A NetNode represents a network node - either a real one or a symbolic default one.
Expand Down
20 changes: 17 additions & 3 deletions talpid-routing/src/unix/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use libc::{AF_INET, AF_INET6};
use netlink_packet_route::{
constants::{ARPHRD_LOOPBACK, FIB_RULE_INVERT, FR_ACT_TO_TBL, NLM_F_REQUEST},
link::{nlas::Nla as LinkNla, LinkMessage},
route::{nlas::Nla as RouteNla, RouteHeader, RouteMessage},
route::{nlas::Nla as RouteNla, Metrics, RouteHeader, RouteMessage},
rtnl::{
constants::{
RTN_UNSPEC, RTPROT_UNSPEC, RT_SCOPE_LINK, RT_SCOPE_UNIVERSE, RT_TABLE_COMPAT,
Expand Down Expand Up @@ -293,7 +293,9 @@ impl RouteManagerImpl {
} else {
self.table_id
};
required_normal_routes.insert(Route::new(node, route.prefix).table(table));
let mut new_route = Route::new(node, route.prefix).table(table);
new_route.mtu = route.mtu.map(u32::from);
required_normal_routes.insert(new_route);
}
}
}
Expand Down Expand Up @@ -450,12 +452,13 @@ impl RouteManagerImpl {
destination_length,
)
.map_err(Error::InvalidNetworkPrefix)?;

let mut node_addr = None;
let mut device = None;
let mut metric = None;
let mut gateway: Option<IpAddr> = None;

let mut table_id = u32::from(msg.header.table);
let mut route_mtu = None;

for nla in msg.nlas.iter() {
match nla {
Expand Down Expand Up @@ -501,6 +504,10 @@ impl RouteManagerImpl {
RouteNla::Table(id) => {
table_id = *id;
}

RouteNla::Metrics(Metrics::Mtu(mtu)) => {
route_mtu = Some(*mtu);
}
_ => continue,
}
}
Expand All @@ -519,6 +526,7 @@ impl RouteManagerImpl {
prefix,
metric,
table_id,
mtu: route_mtu,
}))
}

Expand Down Expand Up @@ -700,6 +708,11 @@ impl RouteManagerImpl {
add_message.nlas.push(RouteNla::Priority(metric));
}

// Set route MTU
if let Some(mtu) = route.mtu {
add_message.nlas.push(RouteNla::Metrics(Metrics::Mtu(mtu)));
}

// Need to modify the request in place to set the correct flags to be able to replace any
// existing routes - self.handle.route().add_v4().execute() sets the NLM_F_EXCL flag which
// will make the request fail if a route with the same destination already exists.
Expand Down Expand Up @@ -743,6 +756,7 @@ impl RouteManagerImpl {
async fn get_mtu_for_route(&self, ip: IpAddr) -> Result<u16> {
// RECURSION_LIMIT controls how many times we recurse to find the device name by looking up
// an IP with `get_destination_route`.
// TODO: Check route MTU first
const RECURSION_LIMIT: usize = 10;
const STANDARD_MTU: u16 = 1500;
let mut attempted_ip = ip;
Expand Down
104 changes: 59 additions & 45 deletions talpid-wireguard/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ use talpid_types::net::{obfuscation::ObfuscatorConfig, wireguard, GenericTunnelO
pub struct Config {
/// Contains tunnel endpoint specific config
pub tunnel: wireguard::TunnelConfig,
/// List of peer configurations
pub peers: Vec<wireguard::PeerConfig>,
/// Entry peer
pub entry_peer: wireguard::PeerConfig,
/// Multihop exit peer
pub exit_peer: Option<wireguard::PeerConfig>,
/// IPv4 gateway
pub ipv4_gateway: Ipv4Addr,
/// IPv6 gateway
Expand Down Expand Up @@ -46,54 +48,28 @@ pub enum Error {
/// Peer has no valid IPs
#[error(display = "Supplied peer has no valid IPs")]
InvalidPeerIpError,

/// Parameters don't contain any peers
#[error(display = "No peers supplied")]
NoPeersSuppliedError,
}

impl Config {
/// Constructs a Config from parameters
pub fn from_parameters(params: &wireguard::TunnelParameters) -> Result<Config, Error> {
let tunnel = params.connection.tunnel.clone();
let mut peers = vec![params.connection.peer.clone()];
if let Some(exit_peer) = &params.connection.exit_peer {
peers.push(exit_peer.clone());
}
Self::new(
tunnel,
peers,
&params.connection,
&params.options,
&params.generic_options,
params.obfuscation.clone(),
&params.obfuscation,
)
}

/// Constructs a new Config struct
pub fn new(
mut tunnel: wireguard::TunnelConfig,
mut peers: Vec<wireguard::PeerConfig>,
connection_config: &wireguard::ConnectionConfig,
fn new(
connection: &wireguard::ConnectionConfig,
wg_options: &wireguard::TunnelOptions,
generic_options: &GenericTunnelOptions,
obfuscator_config: Option<ObfuscatorConfig>,
obfuscator_config: &Option<ObfuscatorConfig>,
) -> Result<Config, Error> {
if peers.is_empty() {
return Err(Error::NoPeersSuppliedError);
}
let mut tunnel = connection.tunnel.clone();
let mtu = wg_options.mtu.unwrap_or(DEFAULT_MTU);
for peer in &mut peers {
peer.allowed_ips = peer
.allowed_ips
.iter()
.cloned()
.filter(|ip| ip.is_ipv4() || generic_options.enable_ipv6)
.collect();
if peer.allowed_ips.is_empty() {
return Err(Error::InvalidPeerIpError);
}
}

if tunnel.addresses.is_empty() {
return Err(Error::InvalidTunnelIpError);
Expand All @@ -102,24 +78,33 @@ impl Config {
.addresses
.retain(|ip| ip.is_ipv4() || generic_options.enable_ipv6);

let ipv6_gateway = if generic_options.enable_ipv6 {
connection_config.ipv6_gateway
} else {
None
};
let ipv6_gateway = connection
.ipv6_gateway
.filter(|_opt| generic_options.enable_ipv6);

Ok(Config {
let mut config = Config {
tunnel,
peers,
ipv4_gateway: connection_config.ipv4_gateway,
entry_peer: connection.peer.clone(),
exit_peer: connection.exit_peer.clone(),
ipv4_gateway: connection.ipv4_gateway,
ipv6_gateway,
mtu,
#[cfg(target_os = "linux")]
fwmark: connection_config.fwmark,
fwmark: connection.fwmark,
#[cfg(target_os = "linux")]
enable_ipv6: generic_options.enable_ipv6,
obfuscator_config,
})
obfuscator_config: obfuscator_config.to_owned(),
};

for peer in config.peers_mut() {
peer.allowed_ips
.retain(|ip| ip.is_ipv4() || generic_options.enable_ipv6);
if peer.allowed_ips.is_empty() {
return Err(Error::InvalidPeerIpError);
}
}

Ok(config)
}

/// Returns a CString with the appropriate config for WireGuard-go
Expand All @@ -138,7 +123,7 @@ impl Config {

wg_conf.add("replace_peers", "true");

for peer in &self.peers {
for peer in self.peers() {
wg_conf
.add("public_key", peer.public_key.as_bytes().as_ref())
.add("endpoint", peer.endpoint.to_string().as_str())
Expand All @@ -154,6 +139,35 @@ impl Config {
let bytes = wg_conf.into_config();
CString::new(bytes).expect("null bytes inside config")
}

/// Return whether the config connects to an exit peer from another remote peer.
pub fn is_multihop(&self) -> bool {
self.exit_peer.is_some()
}

/// Return the exit peer. `exit_peer` if it is set, otherwise `entry_peer`.
pub fn exit_peer_mut(&mut self) -> &mut wireguard::PeerConfig {
if let Some(ref mut peer) = self.exit_peer {
return peer;
}
&mut self.entry_peer
}

/// Return an iterator over all peers.
pub fn peers(&self) -> impl Iterator<Item = &wireguard::PeerConfig> {
self.exit_peer
.as_ref()
.into_iter()
.chain(std::iter::once(&self.entry_peer))
}

/// Return a mutable iterator over all peers.
pub fn peers_mut(&mut self) -> impl Iterator<Item = &mut wireguard::PeerConfig> {
self.exit_peer
.as_mut()
.into_iter()
.chain(std::iter::once(&mut self.entry_peer))
}
}

enum ConfValue<'a> {
Expand Down
Loading

0 comments on commit d4ed3bf

Please sign in to comment.