network debugging
This commit is contained in:
@@ -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;
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -29,7 +29,6 @@ impl RawUdpProtocolHandler {
|
||||
remote_addr
|
||||
);
|
||||
|
||||
// Process envelope
|
||||
let peer_addr = PeerAddress::new(
|
||||
SocketAddress::from_socket_addr(remote_addr),
|
||||
ProtocolType::UDP,
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user