From 0abbff72a92f6ee41d91c8a97f7254cb4366a17c Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Sun, 30 Dec 2018 11:44:10 -0800 Subject: [PATCH] Move SocketAddr normalization into Endpoint This reduces the amount of conversion necessary. --- quinn-proto/src/connection.rs | 12 ++++++------ quinn-proto/src/crypto.rs | 18 ++++++++++++------ quinn-proto/src/endpoint.rs | 32 +++++++++++++++++++++++--------- quinn-proto/src/tests.rs | 21 ++++++++------------- quinn/src/lib.rs | 26 ++++++-------------------- 5 files changed, 55 insertions(+), 54 deletions(-) diff --git a/quinn-proto/src/connection.rs b/quinn-proto/src/connection.rs index b5f93308c..7d1fc9214 100644 --- a/quinn-proto/src/connection.rs +++ b/quinn-proto/src/connection.rs @@ -1,5 +1,5 @@ use std::collections::{hash_map, BTreeMap, HashMap, VecDeque}; -use std::net::SocketAddrV6; +use std::net::SocketAddr; use std::sync::Arc; use std::{cmp, io, mem}; @@ -37,7 +37,7 @@ pub struct Connection { loc_cids: HashMap, rem_cid: ConnectionId, rem_cid_seq: u64, - remote: SocketAddrV6, + remote: SocketAddr, state: State, side: Side, mtu: u16, @@ -131,7 +131,7 @@ impl Connection { init_cid: ConnectionId, loc_cid: ConnectionId, rem_cid: ConnectionId, - remote: SocketAddrV6, + remote: SocketAddr, client_config: Option, tls: TlsSession, ) -> Self { @@ -875,7 +875,7 @@ impl Connection { pub fn handle_decode( &mut self, now: u64, - remote: SocketAddrV6, + remote: SocketAddr, ecn: Option, partial_decode: PartialDecode, ) -> Option { @@ -2361,7 +2361,7 @@ impl Connection { self.rem_cid } - pub fn remote(&self) -> SocketAddrV6 { + pub fn remote(&self) -> SocketAddr { self.remote } @@ -2794,7 +2794,7 @@ const MAX_ACK_BLOCKS: usize = 64; #[derive(Debug)] pub enum Io { Transmit { - destination: SocketAddrV6, + destination: SocketAddr, /// Explicit congestion notification bits to set on the packet ecn: Option, packet: Box<[u8]>, diff --git a/quinn-proto/src/crypto.rs b/quinn-proto/src/crypto.rs index 9b69b8614..6c6b8817e 100644 --- a/quinn-proto/src/crypto.rs +++ b/quinn-proto/src/crypto.rs @@ -1,4 +1,4 @@ -use std::net::SocketAddrV6; +use std::net::{IpAddr, SocketAddr}; use std::ops::{Deref, DerefMut}; use std::sync::Arc; use std::time::{Duration, SystemTime, UNIX_EPOCH}; @@ -429,7 +429,7 @@ impl TokenKey { pub(crate) fn generate( &self, - address: &SocketAddrV6, + address: &SocketAddr, dst_cid: &ConnectionId, issued: SystemTime, ) -> Vec { @@ -443,7 +443,10 @@ impl TokenKey { .unwrap_or(0), ); let signature_pos = buf.len(); - buf.put_slice(&address.ip().octets()); + match address.ip() { + IpAddr::V4(x) => buf.put_slice(&x.octets()), + IpAddr::V6(x) => buf.put_slice(&x.octets()), + } buf.write(address.port()); let signature = hmac::sign(&self.inner, &buf); // No reason to actually encode the IP in the token, since we always have the remote addr for an incoming packet. @@ -454,7 +457,7 @@ impl TokenKey { pub(crate) fn check( &self, - address: &SocketAddrV6, + address: &SocketAddr, data: &[u8], ) -> Option<(ConnectionId, SystemTime)> { let mut reader = io::Cursor::new(data); @@ -471,7 +474,10 @@ impl TokenKey { let mut buf = Vec::new(); buf.put_slice(&data[0..signature_start]); - buf.put_slice(&address.ip().octets()); + match address.ip() { + IpAddr::V4(x) => buf.put_slice(&x.octets()), + IpAddr::V6(x) => buf.put_slice(&x.octets()), + } buf.write(address.port()); hmac::verify_with_own_key(&self.inner, &buf, &data[signature_start..]).ok()?; @@ -576,7 +582,7 @@ mod test { let mut key = [0; TokenKey::SIZE]; rand::thread_rng().fill_bytes(&mut key); let key = TokenKey::new(&key); - let addr = SocketAddrV6::new(Ipv6Addr::LOCALHOST, 4433, 0, 0); + let addr = SocketAddr::new(Ipv6Addr::LOCALHOST.into(), 4433); let dst_cid = ConnectionId::random(&mut rand::thread_rng(), MAX_CID_SIZE); let issued = UNIX_EPOCH + Duration::new(42, 0); // Fractional seconds would be lost let token = key.generate(&addr, &dst_cid, issued); diff --git a/quinn-proto/src/endpoint.rs b/quinn-proto/src/endpoint.rs index 4332a585e..0a9cf01d6 100644 --- a/quinn-proto/src/endpoint.rs +++ b/quinn-proto/src/endpoint.rs @@ -1,6 +1,6 @@ use std::cmp; use std::collections::VecDeque; -use std::net::SocketAddrV6; +use std::net::{SocketAddr, SocketAddrV4}; use std::sync::Arc; use std::time::{Duration, SystemTime}; @@ -39,7 +39,7 @@ pub struct Endpoint { incoming: VecDeque, connection_ids_initial: FnvHashMap, connection_ids: FnvHashMap, - connection_remotes: FnvHashMap, + connection_remotes: FnvHashMap, pub(crate) connections: Slab, config: Arc, server_config: Option, @@ -138,10 +138,11 @@ impl Endpoint { pub fn handle( &mut self, now: u64, - remote: SocketAddrV6, + remote: SocketAddr, ecn: Option, mut data: BytesMut, ) { + let remote = normalize(remote); let datagram_len = data.len(); while !data.is_empty() { match PartialDecode::new(data, self.config.local_cid_len) { @@ -210,7 +211,7 @@ impl Endpoint { fn handle_decode( &mut self, now: u64, - remote: SocketAddrV6, + remote: SocketAddr, ecn: Option, partial_decode: PartialDecode, datagram_len: usize, @@ -308,7 +309,7 @@ impl Endpoint { fn stateless_reset( &mut self, inciting_dgram_len: usize, - remote: SocketAddrV6, + remote: SocketAddr, dst_cid: &ConnectionId, ) { /// Minimum amount of padding for the stateless reset to look like a short-header packet @@ -347,10 +348,11 @@ impl Endpoint { /// Initiate a connection pub fn connect( &mut self, - remote: SocketAddrV6, + remote: SocketAddr, config: &Arc, server_name: &str, ) -> Result { + let remote = normalize(remote); let remote_id = ConnectionId::random(&mut self.rng, MAX_CID_SIZE); trace!(self.log, "initial dcid"; "value" => %remote_id); let conn = self.add_connection( @@ -380,7 +382,7 @@ impl Endpoint { &mut self, initial_id: ConnectionId, remote_id: ConnectionId, - remote: SocketAddrV6, + remote: SocketAddr, opts: ConnectionOpts, ) -> Result { let local_id = self.new_cid(); @@ -431,7 +433,7 @@ impl Endpoint { fn handle_initial( &mut self, now: u64, - remote: SocketAddrV6, + remote: SocketAddr, ecn: Option, mut packet: Packet, crypto: &Crypto, @@ -927,7 +929,7 @@ impl From for Event { #[derive(Debug)] pub enum Io { Transmit { - destination: SocketAddrV6, + destination: SocketAddr, /// Explicit congestion notification bits to set on the packet ecn: Option, packet: Box<[u8]>, @@ -973,3 +975,15 @@ enum ConnectionOpts { Client(ClientConfig), Server { orig_dst_cid: Option }, } + +/// Convert IPv4-mapped IPv6 SocketAddrs into normal IPv4 addrs for consistent hashing/comparison +fn normalize(x: SocketAddr) -> SocketAddr { + if let SocketAddr::V6(x) = x { + if let Some(ip) = x.ip().to_ipv4() { + if x.ip().segments()[5] == 0xFFFF { + return SocketAddr::V4(SocketAddrV4::new(ip, x.port())); + } + } + } + x +} diff --git a/quinn-proto/src/tests.rs b/quinn-proto/src/tests.rs index df034e9a3..7e9242556 100644 --- a/quinn-proto/src/tests.rs +++ b/quinn-proto/src/tests.rs @@ -1,6 +1,6 @@ use std::collections::VecDeque; use std::io::{self, Read, Write}; -use std::net::{Ipv6Addr, SocketAddrV6, UdpSocket}; +use std::net::{Ipv6Addr, SocketAddr, UdpSocket}; use std::ops::RangeFrom; use std::sync::{Arc, Mutex}; use std::time::Duration; @@ -132,18 +132,13 @@ impl Pair { .unwrap(); let client = Endpoint::new(log.new(o!("side" => "Client")), client_config, None).unwrap(); - let localhost = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); - let server_addr = SocketAddrV6::new( - localhost, + let server_addr = SocketAddr::new( + Ipv6Addr::LOCALHOST.into(), SERVER_PORTS.lock().unwrap().next().unwrap(), - 0, - 0, ); - let client_addr = SocketAddrV6::new( - localhost, + let client_addr = SocketAddr::new( + Ipv6Addr::LOCALHOST.into(), CLIENT_PORTS.lock().unwrap().next().unwrap(), - 0, - 0, ); Self { log, @@ -245,7 +240,7 @@ impl Pair { struct TestEndpoint { side: Side, endpoint: Endpoint, - addr: SocketAddrV6, + addr: SocketAddr, socket: Option, timers: [u64; 4], conn: Option, @@ -255,7 +250,7 @@ struct TestEndpoint { } impl TestEndpoint { - fn new(side: Side, endpoint: Endpoint, addr: SocketAddrV6) -> Self { + fn new(side: Side, endpoint: Endpoint, addr: SocketAddr) -> Self { let socket = if env::var_os("SSLKEYLOGFILE").is_some() { let socket = UdpSocket::bind(addr).expect("failed to bind UDP socket"); socket @@ -278,7 +273,7 @@ impl TestEndpoint { } } - fn drive(&mut self, log: &Logger, now: u64, remote: SocketAddrV6) { + fn drive(&mut self, log: &Logger, now: u64, remote: SocketAddr) { if let Some(ref socket) = self.socket { loop { let mut buf = [0; 8192]; diff --git a/quinn/src/lib.rs b/quinn/src/lib.rs index 67223f13f..27dc416bf 100644 --- a/quinn/src/lib.rs +++ b/quinn/src/lib.rs @@ -58,7 +58,7 @@ mod udp; use std::borrow::Cow; use std::cell::RefCell; use std::collections::{hash_map, VecDeque}; -use std::net::{SocketAddr, SocketAddrV6, ToSocketAddrs}; +use std::net::{SocketAddr, ToSocketAddrs}; use std::rc::Rc; use std::str; use std::sync::Arc; @@ -119,7 +119,7 @@ struct EndpointInner { log: Logger, socket: UdpSocket, inner: quinn::Endpoint, - outgoing: VecDeque<(SocketAddrV6, Option, Box<[u8]>)>, + outgoing: VecDeque<(SocketAddr, Option, Box<[u8]>)>, epoch: Instant, pending: FnvHashMap, // TODO: Replace this with something custom that avoids using oneshots to cancel @@ -591,9 +591,7 @@ impl Endpoint { let (send, recv) = oneshot::channel(); let handle = { let mut endpoint = self.inner.borrow_mut(); - let handle = endpoint - .inner - .connect(normalize(*addr), config, server_name)?; + let handle = endpoint.inner.connect(*addr, config, server_name)?; endpoint.pending.insert(handle, Pending::new(Some(send))); handle }; @@ -662,9 +660,7 @@ impl Future for Driver { loop { match endpoint.socket.poll_recv(&mut buf) { Ok(Async::Ready((n, addr, ecn))) => { - endpoint - .inner - .handle(now, normalize(addr), ecn, (&buf[0..n]).into()); + endpoint.inner.handle(now, addr, ecn, (&buf[0..n]).into()); } Ok(Async::NotReady) => { break; @@ -763,10 +759,7 @@ impl Future for Driver { while !endpoint.outgoing.is_empty() { { let (destination, ecn, packet) = endpoint.outgoing.front().unwrap(); - match endpoint - .socket - .poll_send(&(*destination).into(), *ecn, packet) - { + match endpoint.socket.poll_send(destination, *ecn, packet) { Ok(Async::Ready(_)) => {} Ok(Async::NotReady) => { blocked = true; @@ -792,7 +785,7 @@ impl Future for Driver { ecn, } => { if !blocked { - match endpoint.socket.poll_send(&destination.into(), ecn, &packet) { + match endpoint.socket.poll_send(&destination, ecn, &packet) { Ok(Async::Ready(_)) => {} Ok(Async::NotReady) => { blocked = true; @@ -933,13 +926,6 @@ fn micros_from(x: Duration) -> u64 { x.as_secs() * 1000 * 1000 + x.subsec_micros() as u64 } -fn normalize(x: SocketAddr) -> SocketAddrV6 { - match x { - SocketAddr::V6(x) => x, - SocketAddr::V4(x) => SocketAddrV6::new(x.ip().to_ipv6_mapped(), x.port(), 0, 0), - } -} - struct ConnectionInner { endpoint: Rc>, conn: ConnectionHandle,