Compare commits

...

3 Commits

Author SHA1 Message Date
Dennis Schwerdel 35bdfafabf Fix compile with musl 2020-11-02 22:32:27 +01:00
Dennis Schwerdel 3797106f14 Fix test 2020-11-02 22:32:13 +01:00
Dennis Schwerdel cbc73d0c0e Some fixes 2020-11-02 20:44:05 +01:00
5 changed files with 71 additions and 40 deletions

View File

@ -5,6 +5,12 @@ This project follows [semantic versioning](http://semver.org).
### UNRELEASED ### UNRELEASED
- [changed] Changed documentation - [changed] Changed documentation
- [changed] Updated dependencies
- [changed] Retrying connections for 120 secs
- [fixed] Fixed corner case with lost init message
- [fixed] Do not reconnect to timed out pending connections
- [fixed] Most specific claims beat less specific claims
- [fixed] Count all invalid protocol traffic
### v2.0.0 (2020-10-30) ### v2.0.0 (2020-10-30)

View File

@ -55,12 +55,12 @@ struct PeerData {
#[derive(Clone)] #[derive(Clone)]
pub struct ReconnectEntry { pub struct ReconnectEntry {
address: String, address: Option<(String, Time)>,
resolved: Vec<SocketAddr>, resolved: Vec<SocketAddr>,
next_resolve: Time,
tries: u16, tries: u16,
timeout: u16, timeout: u16,
next: Time next: Time,
final_timeout: Option<Time>
} }
@ -250,12 +250,12 @@ impl<D: Device, P: Protocol, S: Socket, TS: TimeSource> GenericCloud<D, P, S, TS
} }
}; };
self.reconnect_peers.push(ReconnectEntry { self.reconnect_peers.push(ReconnectEntry {
address: add, address: Some((add, now)),
tries: 0, tries: 0,
timeout: 1, timeout: 1,
resolved, resolved,
next_resolve: now, next: now,
next: now final_timeout: None
}) })
} }
@ -398,11 +398,13 @@ impl<D: Device, P: Protocol, S: Socket, TS: TimeSource> GenericCloud<D, P, S, TS
} }
} }
// Resolve entries anew // Resolve entries anew
if entry.next_resolve <= now { if let Some((ref address, ref mut next_resolve)) = entry.address {
if let Ok(addrs) = resolve(&entry.address as &str) { if *next_resolve <= now {
if let Ok(addrs) = resolve(address as &str) {
entry.resolved = addrs; entry.resolved = addrs;
} }
entry.next_resolve = now + RESOLVE_INTERVAL; *next_resolve = now + RESOLVE_INTERVAL;
}
} }
// Ignore if next attempt is already in the future // Ignore if next attempt is already in the future
if entry.next > now { if entry.next > now {
@ -421,6 +423,7 @@ impl<D: Device, P: Protocol, S: Socket, TS: TimeSource> GenericCloud<D, P, S, TS
// Schedule next connection attempt // Schedule next connection attempt
entry.next = now + Time::from(entry.timeout); entry.next = now + Time::from(entry.timeout);
} }
self.reconnect_peers.retain(|e| e.final_timeout.unwrap_or(now) >= now);
if self.next_stats_out < now { if self.next_stats_out < now {
// Write out the statistics // Write out the statistics
self.write_out_stats().map_err(|err| Error::FileIo("Failed to write stats file", err))?; self.write_out_stats().map_err(|err| Error::FileIo("Failed to write stats file", err))?;
@ -697,7 +700,10 @@ impl<D: Device, P: Protocol, S: Socket, TS: TimeSource> GenericCloud<D, P, S, TS
} }
MESSAGE_TYPE_KEEPALIVE => self.update_peer_info(src, None)?, MESSAGE_TYPE_KEEPALIVE => self.update_peer_info(src, None)?,
MESSAGE_TYPE_CLOSE => self.remove_peer(src), MESSAGE_TYPE_CLOSE => self.remove_peer(src),
_ => return Err(Error::Message("Unknown message type")) _ => {
self.traffic.count_invalid_protocol(data.len());
return Err(Error::Message("Unknown message type"))
}
} }
} }
MessageResult::Initialized(info) => self.add_new_peer(src, info)?, MessageResult::Initialized(info) => self.add_new_peer(src, info)?,
@ -733,13 +739,17 @@ impl<D: Device, P: Protocol, S: Socket, TS: TimeSource> GenericCloud<D, P, S, TS
self.pending_inits.insert(src, init); self.pending_inits.insert(src, init);
Ok(res) Ok(res)
} }
Err(err) => return Err(err) Err(err) => {
self.traffic.count_invalid_protocol(data.len());
return Err(err)
}
} }
} }
} else if let Some(peer) = self.peers.get_mut(&src) { } else if let Some(peer) = self.peers.get_mut(&src) {
peer.crypto.handle_message(data) peer.crypto.handle_message(data)
} else { } else {
info!("Ignoring non-init message from unknown peer {}", addr_nice(src)); info!("Ignoring non-init message from unknown peer {}", addr_nice(src));
self.traffic.count_invalid_protocol(data.len());
return Ok(()) return Ok(())
}; };
match msg_result { match msg_result {

View File

@ -79,6 +79,8 @@ pub const STAGE_PENG: u8 = 3;
pub const WAITING_TO_CLOSE: u8 = 4; pub const WAITING_TO_CLOSE: u8 = 4;
pub const CLOSING: u8 = 5; pub const CLOSING: u8 = 5;
pub const MAX_FAILED_RETRIES: usize = 120;
pub const SALTED_NODE_ID_HASH_LEN: usize = 20; pub const SALTED_NODE_ID_HASH_LEN: usize = 20;
pub type SaltedNodeIdHash = [u8; SALTED_NODE_ID_HASH_LEN]; pub type SaltedNodeIdHash = [u8; SALTED_NODE_ID_HASH_LEN];
@ -447,7 +449,7 @@ impl<P: Payload> InitState<P> {
Ok(()) Ok(())
} else if self.next_stage == CLOSING { } else if self.next_stage == CLOSING {
Ok(()) Ok(())
} else if self.failed_retries < 5 { } else if self.failed_retries < MAX_FAILED_RETRIES {
self.failed_retries += 1; self.failed_retries += 1;
self.repeat_last_message(out); self.repeat_last_message(out);
Ok(()) Ok(())
@ -632,8 +634,9 @@ impl<P: Payload> InitState<P> {
} }
// decrypt the payload // decrypt the payload
let peer_payload = let peer_payload = self
self.decrypt(&mut encrypted_payload).map_err(|_| Error::CryptoInitFatal("Failed to decrypt payload"))?; .decrypt(&mut encrypted_payload)
.map_err(|_| Error::CryptoInitFatal("Failed to decrypt payload"))?;
// create and send stage 3 reply // create and send stage 3 reply
self.send_message(STAGE_PENG, None, out); self.send_message(STAGE_PENG, None, out);
@ -644,8 +647,9 @@ impl<P: Payload> InitState<P> {
} }
InitMsg::Peng { mut encrypted_payload, .. } => { InitMsg::Peng { mut encrypted_payload, .. } => {
// decrypt the payload // decrypt the payload
let peer_payload = let peer_payload = self
self.decrypt(&mut encrypted_payload).map_err(|_| Error::CryptoInitFatal("Failed to decrypt payload"))?; .decrypt(&mut encrypted_payload)
.map_err(|_| Error::CryptoInitFatal("Failed to decrypt payload"))?;
self.next_stage = CLOSING; // force resend when receiving any message self.next_stage = CLOSING; // force resend when receiving any message
Ok(InitResult::Success { peer_payload, is_initiator: false }) Ok(InitResult::Success { peer_payload, is_initiator: false })
@ -790,7 +794,7 @@ mod tests {
let mut out = MsgBuffer::new(8); let mut out = MsgBuffer::new(8);
sender.send_ping(&mut out); sender.send_ping(&mut out);
assert_eq!(sender.stage(), STAGE_PONG); assert_eq!(sender.stage(), STAGE_PONG);
for _ in 0..5 { for _ in 0..120 {
out.clear(); out.clear();
sender.every_second(&mut out).unwrap(); sender.every_second(&mut out).unwrap();
} }
@ -921,10 +925,7 @@ mod tests {
// Fail if no match // Fail if no match
test_algorithm_negotiation( test_algorithm_negotiation(
Algorithms { Algorithms { algorithm_speeds: smallvec![(&AES_128_GCM, 600.0)], allow_unencrypted: true },
algorithm_speeds: smallvec![(&AES_128_GCM, 600.0)],
allow_unencrypted: true
},
Algorithms { Algorithms {
algorithm_speeds: smallvec![(&AES_256_GCM, 500.0), (&CHACHA20_POLY1305, 400.0)], algorithm_speeds: smallvec![(&AES_256_GCM, 500.0), (&CHACHA20_POLY1305, 400.0)],
allow_unencrypted: false allow_unencrypted: false

View File

@ -10,6 +10,7 @@ use std::{
io::{self, BufRead, BufReader, Cursor, Error as IoError, Read, Write}, io::{self, BufRead, BufReader, Cursor, Error as IoError, Read, Write},
net::{Ipv4Addr, UdpSocket}, net::{Ipv4Addr, UdpSocket},
os::unix::io::{AsRawFd, RawFd}, os::unix::io::{AsRawFd, RawFd},
convert::TryInto,
str, str,
str::FromStr str::FromStr
}; };
@ -18,7 +19,6 @@ use crate::{crypto, error::Error, util::MsgBuffer};
static TUNSETIFF: libc::c_ulong = 1074025674; static TUNSETIFF: libc::c_ulong = 1074025674;
#[repr(C)] #[repr(C)]
union IfReqData { union IfReqData {
flags: libc::c_short, flags: libc::c_short,
@ -141,6 +141,7 @@ impl TunTapDevice {
/// ///
/// # Panics /// # Panics
/// This method panics if the interface name is longer than 31 bytes. /// This method panics if the interface name is longer than 31 bytes.
#[allow(clippy::useless_conversion)]
pub fn new(ifname: &str, type_: Type, path: Option<&str>) -> io::Result<Self> { pub fn new(ifname: &str, type_: Type, path: Option<&str>) -> io::Result<Self> {
let path = path.unwrap_or_else(|| Self::default_path(type_)); let path = path.unwrap_or_else(|| Self::default_path(type_));
if type_ == Type::Dummy { if type_ == Type::Dummy {
@ -154,7 +155,7 @@ impl TunTapDevice {
}; };
let mut ifreq = IfReq::new(ifname); let mut ifreq = IfReq::new(ifname);
ifreq.data.flags = flags as libc::c_short; ifreq.data.flags = flags as libc::c_short;
let res = unsafe { libc::ioctl(fd.as_raw_fd(), TUNSETIFF, &mut ifreq) }; let res = unsafe { libc::ioctl(fd.as_raw_fd(), TUNSETIFF.try_into().unwrap(), &mut ifreq) };
match res { match res {
0 => { 0 => {
let mut ifname = String::with_capacity(32); let mut ifname = String::with_capacity(32);
@ -398,31 +399,34 @@ impl AsRawFd for MockDevice {
} }
#[allow(clippy::useless_conversion)]
fn set_device_mtu(ifname: &str, mtu: usize) -> io::Result<()> { fn set_device_mtu(ifname: &str, mtu: usize) -> io::Result<()> {
let sock = UdpSocket::bind("0.0.0.0:0")?; let sock = UdpSocket::bind("0.0.0.0:0")?;
let mut ifreq = IfReq::new(ifname); let mut ifreq = IfReq::new(ifname);
ifreq.data.value = mtu as libc::c_int; ifreq.data.value = mtu as libc::c_int;
let res = unsafe { libc::ioctl(sock.as_raw_fd(), libc::SIOCSIFMTU, &mut ifreq) }; let res = unsafe { libc::ioctl(sock.as_raw_fd(), libc::SIOCSIFMTU.try_into().unwrap(), &mut ifreq) };
match res { match res {
0 => Ok(()), 0 => Ok(()),
_ => Err(IoError::last_os_error()) _ => Err(IoError::last_os_error())
} }
} }
#[allow(clippy::useless_conversion)]
fn get_device_mtu(ifname: &str) -> io::Result<usize> { fn get_device_mtu(ifname: &str) -> io::Result<usize> {
let sock = UdpSocket::bind("0.0.0.0:0")?; let sock = UdpSocket::bind("0.0.0.0:0")?;
let mut ifreq = IfReq::new(ifname); let mut ifreq = IfReq::new(ifname);
let res = unsafe { libc::ioctl(sock.as_raw_fd(), libc::SIOCGIFMTU, &mut ifreq) }; let res = unsafe { libc::ioctl(sock.as_raw_fd(), libc::SIOCGIFMTU.try_into().unwrap(), &mut ifreq) };
match res { match res {
0 => Ok(unsafe { ifreq.data.value as usize }), 0 => Ok(unsafe { ifreq.data.value as usize }),
_ => Err(IoError::last_os_error()) _ => Err(IoError::last_os_error())
} }
} }
#[allow(clippy::useless_conversion)]
fn get_device_addr(ifname: &str) -> io::Result<Ipv4Addr> { fn get_device_addr(ifname: &str) -> io::Result<Ipv4Addr> {
let sock = UdpSocket::bind("0.0.0.0:0")?; let sock = UdpSocket::bind("0.0.0.0:0")?;
let mut ifreq = IfReq::new(ifname); let mut ifreq = IfReq::new(ifname);
let res = unsafe { libc::ioctl(sock.as_raw_fd(), libc::SIOCGIFADDR, &mut ifreq) }; let res = unsafe { libc::ioctl(sock.as_raw_fd(), libc::SIOCGIFADDR.try_into().unwrap(), &mut ifreq) };
match res { match res {
0 => { 0 => {
let af = unsafe { ifreq.data.addr.0 }; let af = unsafe { ifreq.data.addr.0 };
@ -436,11 +440,12 @@ fn get_device_addr(ifname: &str) -> io::Result<Ipv4Addr> {
} }
} }
#[allow(clippy::useless_conversion)]
fn set_device_addr(ifname: &str, addr: Ipv4Addr) -> io::Result<()> { fn set_device_addr(ifname: &str, addr: Ipv4Addr) -> io::Result<()> {
let sock = UdpSocket::bind("0.0.0.0:0")?; let sock = UdpSocket::bind("0.0.0.0:0")?;
let mut ifreq = IfReq::new(ifname); let mut ifreq = IfReq::new(ifname);
ifreq.data.addr = (libc::AF_INET as libc::c_short, addr); ifreq.data.addr = (libc::AF_INET as libc::c_short, addr);
let res = unsafe { libc::ioctl(sock.as_raw_fd(), libc::SIOCSIFADDR, &mut ifreq) }; let res = unsafe { libc::ioctl(sock.as_raw_fd(), libc::SIOCSIFADDR.try_into().unwrap(), &mut ifreq) };
match res { match res {
0 => Ok(()), 0 => Ok(()),
_ => Err(IoError::last_os_error()) _ => Err(IoError::last_os_error())
@ -448,10 +453,11 @@ fn set_device_addr(ifname: &str, addr: Ipv4Addr) -> io::Result<()> {
} }
#[allow(dead_code)] #[allow(dead_code)]
#[allow(clippy::useless_conversion)]
fn get_device_netmask(ifname: &str) -> io::Result<Ipv4Addr> { fn get_device_netmask(ifname: &str) -> io::Result<Ipv4Addr> {
let sock = UdpSocket::bind("0.0.0.0:0")?; let sock = UdpSocket::bind("0.0.0.0:0")?;
let mut ifreq = IfReq::new(ifname); let mut ifreq = IfReq::new(ifname);
let res = unsafe { libc::ioctl(sock.as_raw_fd(), libc::SIOCGIFNETMASK, &mut ifreq) }; let res = unsafe { libc::ioctl(sock.as_raw_fd(), libc::SIOCGIFNETMASK.try_into().unwrap(), &mut ifreq) };
match res { match res {
0 => { 0 => {
let af = unsafe { ifreq.data.addr.0 }; let af = unsafe { ifreq.data.addr.0 };
@ -465,21 +471,23 @@ fn get_device_netmask(ifname: &str) -> io::Result<Ipv4Addr> {
} }
} }
#[allow(clippy::useless_conversion)]
fn set_device_netmask(ifname: &str, addr: Ipv4Addr) -> io::Result<()> { fn set_device_netmask(ifname: &str, addr: Ipv4Addr) -> io::Result<()> {
let sock = UdpSocket::bind("0.0.0.0:0")?; let sock = UdpSocket::bind("0.0.0.0:0")?;
let mut ifreq = IfReq::new(ifname); let mut ifreq = IfReq::new(ifname);
ifreq.data.addr = (libc::AF_INET as libc::c_short, addr); ifreq.data.addr = (libc::AF_INET as libc::c_short, addr);
let res = unsafe { libc::ioctl(sock.as_raw_fd(), libc::SIOCSIFNETMASK, &mut ifreq) }; let res = unsafe { libc::ioctl(sock.as_raw_fd(), libc::SIOCSIFNETMASK.try_into().unwrap(), &mut ifreq) };
match res { match res {
0 => Ok(()), 0 => Ok(()),
_ => Err(IoError::last_os_error()) _ => Err(IoError::last_os_error())
} }
} }
#[allow(clippy::useless_conversion)]
fn set_device_enabled(ifname: &str, up: bool) -> io::Result<()> { fn set_device_enabled(ifname: &str, up: bool) -> io::Result<()> {
let sock = UdpSocket::bind("0.0.0.0:0")?; let sock = UdpSocket::bind("0.0.0.0:0")?;
let mut ifreq = IfReq::new(ifname); let mut ifreq = IfReq::new(ifname);
if unsafe { libc::ioctl(sock.as_raw_fd(), libc::SIOCGIFFLAGS, &mut ifreq) } != 0 { if unsafe { libc::ioctl(sock.as_raw_fd(), libc::SIOCGIFFLAGS.try_into().unwrap(), &mut ifreq) } != 0 {
return Err(IoError::last_os_error()) return Err(IoError::last_os_error())
} }
if up { if up {
@ -487,7 +495,7 @@ fn set_device_enabled(ifname: &str, up: bool) -> io::Result<()> {
} else { } else {
unsafe { ifreq.data.value &= !libc::IFF_UP } unsafe { ifreq.data.value &= !libc::IFF_UP }
} }
let res = unsafe { libc::ioctl(sock.as_raw_fd(), libc::SIOCSIFFLAGS, &mut ifreq) }; let res = unsafe { libc::ioctl(sock.as_raw_fd(), libc::SIOCSIFFLAGS.try_into().unwrap(), &mut ifreq) };
match res { match res {
0 => Ok(()), 0 => Ok(()),
_ => Err(IoError::last_os_error()) _ => Err(IoError::last_os_error())

View File

@ -88,15 +88,21 @@ impl<TS: TimeSource> ClaimTable<TS> {
if let Some(entry) = self.cache.get(&addr) { if let Some(entry) = self.cache.get(&addr) {
return Some(entry.peer) return Some(entry.peer)
} }
let mut found = None;
let mut prefix_len = -1;
for entry in &self.claims { for entry in &self.claims {
if entry.claim.matches(addr) { if entry.claim.prefix_len as isize > prefix_len && entry.claim.matches(addr) {
found = Some(entry);
prefix_len = entry.claim.prefix_len as isize;
}
}
if let Some(entry) = found {
self.cache.insert(addr, CacheValue { self.cache.insert(addr, CacheValue {
peer: entry.peer, peer: entry.peer,
timeout: min(TS::now() + self.cache_timeout as Time, entry.timeout) timeout: min(TS::now() + self.cache_timeout as Time, entry.timeout)
}); });
return Some(entry.peer) return Some(entry.peer)
} }
}
None None
} }
@ -149,9 +155,9 @@ mod bench {
use super::*; use super::*;
use crate::util::MockTimeSource; use crate::util::MockTimeSource;
use test::Bencher;
use std::str::FromStr;
use smallvec::smallvec; use smallvec::smallvec;
use std::str::FromStr;
use test::Bencher;
#[bench] #[bench]
fn lookup_warm(b: &mut Bencher) { fn lookup_warm(b: &mut Bencher) {