diff options
| author | David Lönnhager <david.l@mullvad.net> | 2024-01-08 10:04:21 +0100 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2024-01-08 16:48:46 +0100 |
| commit | 24c56f3ff7cf790a807b4d803fd7564d7f724c57 (patch) | |
| tree | 8f72f6f45e9669f648eff6096e630399560f19ea | |
| parent | df0cd903463fcf1c7004b72fee20beb3f2c231d0 (diff) | |
| download | mullvadvpn-24c56f3ff7cf790a807b4d803fd7564d7f724c57.tar.xz mullvadvpn-24c56f3ff7cf790a807b4d803fd7564d7f724c57.zip | |
Set scope ID when restoring default route whose gateway is a link-local address
| -rw-r--r-- | talpid-routing/src/unix/macos/data.rs | 3 | ||||
| -rw-r--r-- | talpid-routing/src/unix/macos/interface.rs | 38 |
2 files changed, 32 insertions, 9 deletions
diff --git a/talpid-routing/src/unix/macos/data.rs b/talpid-routing/src/unix/macos/data.rs index f0be4186bc..1952578860 100644 --- a/talpid-routing/src/unix/macos/data.rs +++ b/talpid-routing/src/unix/macos/data.rs @@ -179,8 +179,7 @@ impl RouteMessage { self } - pub fn set_gateway_addr(mut self, addr: IpAddr) -> Self { - let gateway: SocketAddr = (addr, 0).into(); + pub fn set_gateway_addr(mut self, gateway: impl Into<SockaddrStorage>) -> Self { self.insert_sockaddr(RouteSocketAddress::Gateway(Some(gateway.into()))); self.route_flags |= RouteFlag::RTF_GATEWAY; diff --git a/talpid-routing/src/unix/macos/interface.rs b/talpid-routing/src/unix/macos/interface.rs index 5d7f24d9f5..f2b8383d94 100644 --- a/talpid-routing/src/unix/macos/interface.rs +++ b/talpid-routing/src/unix/macos/interface.rs @@ -7,7 +7,7 @@ use nix::{ use std::{ collections::BTreeMap, io, - net::{IpAddr, Ipv4Addr, Ipv6Addr}, + net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, }; use super::data::{Destination, RouteMessage}; @@ -155,9 +155,32 @@ impl PrimaryInterfaceMonitor { }) .next()?; - // Synthesize a scoped route for the interface + let router_addr = (iface.router_ip, 0); + let mut router_addr = SocketAddr::from(router_addr); + + // If the gateway is a link-local address, scope ID must be specified + if let SocketAddr::V6(ref mut v6_addr) = router_addr { + let v6ip = v6_addr.ip(); + + if is_link_local_v6(v6ip) { + // The second pair of octets should be set to the scope id + // See getaddr() in route.c: + // https://opensource.apple.com/source/network_cmds/network_cmds-396.6/route.tproj/route.c.auto.html + + let second_octet = u16::try_from(index).unwrap().to_be_bytes(); + + let mut octets = v6ip.octets(); + octets[2] = second_octet[0]; + octets[3] = second_octet[1]; + + let new_ip = Ipv6Addr::from(octets); + + v6_addr.set_ip(new_ip); + } + } + let msg = RouteMessage::new_route(Destination::Network(family.default_network())) - .set_gateway_addr(iface.router_ip) + .set_gateway_addr(router_addr) .set_interface_index(u16::try_from(index).unwrap()); Some(msg) } @@ -301,8 +324,9 @@ fn is_routable_v4(addr: &Ipv4Addr) -> bool { } fn is_routable_v6(addr: &Ipv6Addr) -> bool { - !addr.is_unspecified() - && !addr.is_loopback() - // !(link local) - && (addr.segments()[0] & 0xffc0) != 0xfe80 + !addr.is_unspecified() && !addr.is_loopback() && !is_link_local_v6(addr) +} + +fn is_link_local_v6(addr: &Ipv6Addr) -> bool { + (addr.segments()[0] & 0xffc0) == 0xfe80 } |
