network debugging

This commit is contained in:
John Smith
2022-03-08 22:32:12 -05:00
parent 98799b4d3a
commit 64ea00f8cc
21 changed files with 891 additions and 345 deletions

View File

@@ -52,10 +52,12 @@ struct NetworkInner {
wss_port: u16,
interfaces: NetworkInterfaces,
// udp
bound_first_udp: BTreeMap<u16, (socket2::Socket, socket2::Socket)>,
inbound_udp_protocol_handlers: BTreeMap<SocketAddr, RawUdpProtocolHandler>,
outbound_udpv4_protocol_handler: Option<RawUdpProtocolHandler>,
outbound_udpv6_protocol_handler: Option<RawUdpProtocolHandler>,
//tcp
bound_first_tcp: BTreeMap<u16, (socket2::Socket, socket2::Socket)>,
tls_acceptor: Option<TlsAcceptor>,
listener_states: BTreeMap<SocketAddr, Arc<RwLock<ListenerState>>>,
}
@@ -91,9 +93,11 @@ impl Network {
ws_port: 0u16,
wss_port: 0u16,
interfaces: NetworkInterfaces::new(),
bound_first_udp: BTreeMap::new(),
inbound_udp_protocol_handlers: BTreeMap::new(),
outbound_udpv4_protocol_handler: None,
outbound_udpv6_protocol_handler: None,
bound_first_tcp: BTreeMap::new(),
tls_acceptor: None,
listener_states: BTreeMap::new(),
}
@@ -420,6 +424,10 @@ impl Network {
if protocol_config.tcp_listen {
self.start_tcp_listeners().await?;
}
// release caches of available listener ports
// this releases the 'first bound' ports we use to guarantee
// that we have ports available to us
self.free_bound_first_ports();
info!("network started");
self.inner.lock().network_started = true;

View File

@@ -89,15 +89,17 @@ impl ProtocolNetworkConnection {
pub fn new_unbound_shared_udp_socket(domain: Domain) -> Result<socket2::Socket, String> {
let socket = Socket::new(domain, Type::DGRAM, Some(Protocol::UDP))
.map_err(|e| format!("Couldn't create UDP socket: {}", e))?;
if let Err(e) = socket.set_reuse_address(true) {
log_net!(error "Couldn't set reuse address: {}", e);
if domain == Domain::IPV6 {
socket
.set_only_v6(true)
.map_err(|e| format!("Couldn't set IPV6_V6ONLY: {}", e))?;
}
socket
.set_reuse_address(true)
.map_err(|e| format!("Couldn't set reuse address: {}", e))?;
cfg_if! {
if #[cfg(unix)] {
if let Err(e) = socket.set_reuse_port(true) {
log_net!(error "Couldn't set reuse port: {}", e);
}
socket.set_reuse_port(true).map_err(|e| format!("Couldn't set reuse port: {}", e))?;
}
}
Ok(socket)
@@ -116,6 +118,36 @@ pub fn new_bound_shared_udp_socket(local_address: SocketAddr) -> Result<socket2:
Ok(socket)
}
pub fn new_bound_first_udp_socket(local_address: SocketAddr) -> Result<socket2::Socket, String> {
let domain = Domain::for_address(local_address);
let socket = Socket::new(domain, Type::DGRAM, Some(Protocol::UDP))
.map_err(|e| format!("Couldn't create UDP socket: {}", e))?;
if domain == Domain::IPV6 {
socket
.set_only_v6(true)
.map_err(|e| format!("Couldn't set IPV6_V6ONLY: {}", e))?;
}
// Bind the socket -first- before turning on 'reuse address' this way it will
// fail if the port is already taken
let socket2_addr = socket2::SockAddr::from(local_address);
socket
.bind(&socket2_addr)
.map_err(|e| format!("failed to bind UDP socket: {}", e))?;
// Set 'reuse address' so future binds to this port will succeed
socket
.set_reuse_address(true)
.map_err(|e| format!("Couldn't set reuse address: {}", e))?;
cfg_if! {
if #[cfg(unix)] {
socket.set_reuse_port(true).map_err(|e| format!("Couldn't set reuse port: {}", e))?;
}
}
log_net!("created shared udp socket on {:?}", &local_address);
Ok(socket)
}
pub fn new_unbound_shared_tcp_socket(domain: Domain) -> Result<socket2::Socket, String> {
let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))
.map_err(map_to_string)
@@ -126,14 +158,17 @@ pub fn new_unbound_shared_tcp_socket(domain: Domain) -> Result<socket2::Socket,
if let Err(e) = socket.set_nodelay(true) {
log_net!(error "Couldn't set TCP nodelay: {}", e);
}
if let Err(e) = socket.set_reuse_address(true) {
log_net!(error "Couldn't set reuse address: {}", e);
if domain == Domain::IPV6 {
socket
.set_only_v6(true)
.map_err(|e| format!("Couldn't set IPV6_V6ONLY: {}", e))?;
}
socket
.set_reuse_address(true)
.map_err(|e| format!("Couldn't set reuse address: {}", e))?;
cfg_if! {
if #[cfg(unix)] {
if let Err(e) = socket.set_reuse_port(true) {
log_net!(error "Couldn't set reuse port: {}", e);
}
socket.set_reuse_port(true).map_err(|e| format!("Couldn't set reuse port: {}", e))?;
}
}
Ok(socket)
@@ -151,3 +186,40 @@ pub fn new_bound_shared_tcp_socket(local_address: SocketAddr) -> Result<socket2:
Ok(socket)
}
pub fn new_bound_first_tcp_socket(local_address: SocketAddr) -> Result<socket2::Socket, String> {
let domain = Domain::for_address(local_address);
let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))
.map_err(map_to_string)
.map_err(logthru_net!("failed to create TCP socket"))?;
if let Err(e) = socket.set_linger(None) {
log_net!(error "Couldn't set TCP linger: {}", e);
}
if let Err(e) = socket.set_nodelay(true) {
log_net!(error "Couldn't set TCP nodelay: {}", e);
}
if domain == Domain::IPV6 {
socket
.set_only_v6(true)
.map_err(|e| format!("Couldn't set IPV6_V6ONLY: {}", e))?;
}
// Bind the socket -first- before turning on 'reuse address' this way it will
// fail if the port is already taken
let socket2_addr = socket2::SockAddr::from(local_address);
socket
.bind(&socket2_addr)
.map_err(|e| format!("failed to bind TCP socket: {}", e))?;
// Set 'reuse address' so future binds to this port will succeed
socket
.set_reuse_address(true)
.map_err(|e| format!("Couldn't set reuse address: {}", e))?;
cfg_if! {
if #[cfg(unix)] {
socket.set_reuse_port(true).map_err(|e| format!("Couldn't set reuse port: {}", e))?;
}
}
Ok(socket)
}

View File

@@ -29,7 +29,6 @@ impl RawUdpProtocolHandler {
remote_addr
);
// Process envelope
let peer_addr = PeerAddress::new(
SocketAddress::from_socket_addr(remote_addr),
ProtocolType::UDP,

View File

@@ -1,11 +1,148 @@
use super::*;
impl Network {
pub(super) async fn start_udp_listeners(&self) -> Result<(), String> {
// First, create outbound sockets and we'll listen on them too
self.create_udp_outbound_sockets().await?;
/////////////////////////////////////////////////////
// Support for binding first on ports to ensure nobody binds ahead of us
// or two copies of the app don't accidentally collide. This is tricky
// because we use 'reuseaddr/port' and we can accidentally bind in front of ourselves :P
// Now create udp inbound sockets for whatever interfaces we're listening on
fn bind_first_udp_port(&self, udp_port: u16) -> bool {
let mut inner = self.inner.lock();
if inner.bound_first_udp.contains_key(&udp_port) {
return true;
}
// If the address is specified, only use the specified port and fail otherwise
let mut bound_first_socket_v4 = None;
let mut bound_first_socket_v6 = None;
if let Ok(bfs4) =
new_bound_first_udp_socket(SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), udp_port))
{
if let Ok(bfs6) = new_bound_first_udp_socket(SocketAddr::new(
IpAddr::V6(Ipv6Addr::UNSPECIFIED),
udp_port,
)) {
bound_first_socket_v4 = Some(bfs4);
bound_first_socket_v6 = Some(bfs6);
}
}
if let (Some(bfs4), Some(bfs6)) = (bound_first_socket_v4, bound_first_socket_v6) {
inner.bound_first_udp.insert(udp_port, (bfs4, bfs6));
true
} else {
false
}
}
fn bind_first_tcp_port(&self, tcp_port: u16) -> bool {
let mut inner = self.inner.lock();
if inner.bound_first_tcp.contains_key(&tcp_port) {
return true;
}
// If the address is specified, only use the specified port and fail otherwise
let mut bound_first_socket_v4 = None;
let mut bound_first_socket_v6 = None;
if let Ok(bfs4) =
new_bound_first_tcp_socket(SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), tcp_port))
{
if let Ok(bfs6) = new_bound_first_tcp_socket(SocketAddr::new(
IpAddr::V6(Ipv6Addr::UNSPECIFIED),
tcp_port,
)) {
bound_first_socket_v4 = Some(bfs4);
bound_first_socket_v6 = Some(bfs6);
}
}
if let (Some(bfs4), Some(bfs6)) = (bound_first_socket_v4, bound_first_socket_v6) {
inner.bound_first_tcp.insert(tcp_port, (bfs4, bfs6));
true
} else {
false
}
}
pub(super) fn free_bound_first_ports(&self) {
let mut inner = self.inner.lock();
inner.bound_first_udp.clear();
inner.bound_first_tcp.clear();
}
/////////////////////////////////////////////////////
fn find_available_udp_port(&self) -> Result<u16, String> {
// If the address is empty, iterate ports until we find one we can use.
let mut udp_port = 5150u16;
loop {
if self.bind_first_udp_port(udp_port) {
break;
}
if udp_port == 65535 {
return Err("Could not find free udp port to listen on".to_owned());
}
udp_port += 1;
}
Ok(udp_port)
}
fn find_available_tcp_port(&self) -> Result<u16, String> {
// If the address is empty, iterate ports until we find one we can use.
let mut tcp_port = 5150u16;
loop {
if self.bind_first_tcp_port(tcp_port) {
break;
}
if tcp_port == 65535 {
return Err("Could not find free tcp port to listen on".to_owned());
}
tcp_port += 1;
}
Ok(tcp_port)
}
async fn allocate_udp_port(&self, listen_address: String) -> Result<u16, String> {
if listen_address.is_empty() {
// If listen address is empty, find us a port iteratively
self.find_available_udp_port()
} else if let Some(sa) = listen_address
.to_socket_addrs()
.await
.map_err(|e| format!("Unable to resolve address: {}\n{}", listen_address, e))?
.next()
{
// If the address is specified, only use the specified port and fail otherwise
if self.bind_first_udp_port(sa.port()) {
Ok(sa.port())
} else {
Err("Could not find free udp port to listen on".to_owned())
}
} else {
Err(format!("No valid listen address: {}", listen_address))
}
}
async fn allocate_tcp_port(&self, listen_address: String) -> Result<u16, String> {
if listen_address.is_empty() {
// If listen address is empty, find us a port iteratively
self.find_available_tcp_port()
} else if let Some(sa) = listen_address
.to_socket_addrs()
.await
.map_err(|e| format!("Unable to resolve address: {}\n{}", listen_address, e))?
.next()
{
// If the address is specified, only use the specified port and fail otherwise
if self.bind_first_tcp_port(sa.port()) {
Ok(sa.port())
} else {
Err("Could not find free tcp port to listen on".to_owned())
}
} else {
Err(format!("No valid listen address: {}", listen_address))
}
}
/////////////////////////////////////////////////////
pub(super) async fn start_udp_listeners(&self) -> Result<(), String> {
let routing_table = self.routing_table();
let (listen_address, public_address) = {
let c = self.config.get();
@@ -14,15 +151,27 @@ impl Network {
c.network.protocol.udp.public_address.clone(),
)
};
// Pick out UDP port we're going to use everywhere
// Keep sockets around until the end of this function
// to keep anyone else from binding in front of us
let udp_port = self.allocate_udp_port(listen_address.clone()).await?;
// Save the bound udp port for use later on
self.inner.lock().udp_port = udp_port;
// First, create outbound sockets
// (unlike tcp where we create sockets for every connection)
// and we'll add protocol handlers for them too
self.create_udp_outbound_sockets().await?;
// Now create udp inbound sockets for whatever interfaces we're listening on
info!("UDP: starting listener at {:?}", listen_address);
let dial_infos = self
.create_udp_inbound_sockets(listen_address.clone())
.await?;
let mut static_public = false;
for di in &dial_infos {
// Pick out UDP port for outbound connections (they will all be the same)
self.inner.lock().udp_port = di.port();
// Register local dial info only here if we specify a public address
if public_address.is_none() && di.is_global() {
// Register global dial info if no public address is specified
@@ -73,6 +222,15 @@ impl Network {
c.network.protocol.ws.path.clone(),
)
};
// Pick out TCP port we're going to use everywhere
// Keep sockets around until the end of this function
// to keep anyone else from binding in front of us
let ws_port = self.allocate_tcp_port(listen_address.clone()).await?;
// Save the bound ws port for use later on
self.inner.lock().ws_port = ws_port;
trace!("WS: starting listener at {:?}", listen_address);
let socket_addresses = self
.start_tcp_listener(
@@ -85,9 +243,6 @@ impl Network {
let mut static_public = false;
for socket_address in socket_addresses {
// Pick out WS port for outbound connections (they will all be the same)
self.inner.lock().ws_port = socket_address.port();
if url.is_none() && socket_address.address().is_global() {
// Build global dial info request url
let global_url = format!("ws://{}/{}", socket_address, path);
@@ -155,8 +310,17 @@ impl Network {
c.network.protocol.wss.url.clone(),
)
};
// Pick out TCP port we're going to use everywhere
// Keep sockets around until the end of this function
// to keep anyone else from binding in front of us
let wss_port = self.allocate_tcp_port(listen_address.clone()).await?;
// Save the bound wss port for use later on
self.inner.lock().wss_port = wss_port;
trace!("WSS: starting listener at {}", listen_address);
let socket_addresses = self
let _socket_addresses = self
.start_tcp_listener(
listen_address.clone(),
true,
@@ -169,11 +333,6 @@ impl Network {
// If the hostname is specified, it is the public dialinfo via the URL. If no hostname
// is specified, then TLS won't validate, so no local dialinfo is possible.
// This is not the case with unencrypted websockets, which can be specified solely by an IP address
//
if let Some(socket_address) = socket_addresses.first() {
// Pick out WSS port for outbound connections (they will all be the same)
self.inner.lock().wss_port = socket_address.port();
}
// Add static public dialinfo if it's configured
if let Some(url) = url.as_ref() {
@@ -217,6 +376,15 @@ impl Network {
c.network.protocol.tcp.public_address.clone(),
)
};
// Pick out TCP port we're going to use everywhere
// Keep sockets around until the end of this function
// to keep anyone else from binding in front of us
let tcp_port = self.allocate_tcp_port(listen_address.clone()).await?;
// Save the bound tcp port for use later on
self.inner.lock().tcp_port = tcp_port;
trace!("TCP: starting listener at {}", &listen_address);
let socket_addresses = self
.start_tcp_listener(
@@ -229,9 +397,6 @@ impl Network {
let mut static_public = false;
for socket_address in socket_addresses {
// Pick out TCP port for outbound connections (they will all be the same)
self.inner.lock().tcp_port = socket_address.port();
let di = DialInfo::tcp(socket_address);
// Register local dial info only here if we specify a public address