You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
On Linux, sockets generally should not have (local addr, remote addr) 4-tuples that are ambiguous (ignoring options like SO_REUSEADDR and SO_REUSEPORT). For example if a listening TCP socket is bound to 127.0.0.1:80, you should not be able to create any TCP sockets that have that same local address (ignoring IP_BIND_ADDRESS_NO_PORT and connections with more-specific 4-tuples such as those created from server.accept()). The confusing rules are somewhat explored in:
The rules on Linux are different between TCP and UDP, but Shadow generally doesn't make a distinction between the two.
Since Shadow's socket association table is indexed by the 5-tuple (protocol, local addr, remote addr), to prevent overlapping local addresses Shadow can only check for the existence of some concrete 5-tuple in the table. For example it can check if (tcp, 127.0.0.1:1000, 127.0.0.1:80) exists in the table, or if (tcp, 127.0.0.1:1000, 0.0.0.0:0) exists in the table, but not queries like (tcp, 127.0.0.1:1000, IP:PORT) for any concrete IP and PORT.
Shadow does contain some checks, such as the following in get_random_free_port():
// `is_addr_in_use` will check all interfaces in the case of INADDR_ANY
let specific_in_use = self
.is_addr_in_use(
protocol_type,
SocketAddrV4::new(interface_ip, random_port),
peer,
)
.unwrap_or(true);
let generic_in_use = self
.is_addr_in_use(
protocol_type,
SocketAddrV4::new(interface_ip, random_port),
SocketAddrV4::new(Ipv4Addr::UNSPECIFIED,0),
)
.unwrap_or(true);
if !specific_in_use && !generic_in_use {
returnSome(random_port);
}
Which when deciding if a port is free will check if (proto, local_ip:local_port, remote_ip:remote_port) or (proto, local_ip:local_port, 0.0.0.0:0) already exists. In the case of TCP this prevents binding a socket to the same local IP and port as a listening socket. For example if there exists a listening socket bound to 127.0.0.1:80, it will be in the table as (tcp, 127.0.0.1:80, 0.0.0.0:0). So if connect() is called on a new socket, this check prevents us from choosing port 80 since we'll check both (tcp, 127.0.0.1:80, peer_ip:peer_port) as well as (tcp, 127.0.0.1:80, 0.0.0.0:0), and this second check will fail since that 5-tuple is already in use.
This check doesn't help us in the reverse case though. If we connect() a socket to a peer and then bind() another socket to the same local IP and port, Shadow will allow this even though it's not allowed on Linux. For example on Linux:
There are likely other cases that don't work correctly in Shadow, and if we wanted to support options like SO_REUSEADDR, SO_REUSEPORT, and IP_BIND_ADDRESS_NO_PORT it would complicate things further. The behaviour is also different between TCP and UDP, and on Linux these associations are stored in two separate tables: tcp_hashinfo and udp_table.
The text was updated successfully, but these errors were encountered:
On Linux, sockets generally should not have (local addr, remote addr) 4-tuples that are ambiguous (ignoring options like
SO_REUSEADDR
andSO_REUSEPORT
). For example if a listening TCP socket is bound to 127.0.0.1:80, you should not be able to create any TCP sockets that have that same local address (ignoringIP_BIND_ADDRESS_NO_PORT
and connections with more-specific 4-tuples such as those created fromserver.accept()
). The confusing rules are somewhat explored in:The rules on Linux are different between TCP and UDP, but Shadow generally doesn't make a distinction between the two.
Since Shadow's socket association table is indexed by the 5-tuple (protocol, local addr, remote addr), to prevent overlapping local addresses Shadow can only check for the existence of some concrete 5-tuple in the table. For example it can check if
(tcp, 127.0.0.1:1000, 127.0.0.1:80)
exists in the table, or if(tcp, 127.0.0.1:1000, 0.0.0.0:0)
exists in the table, but not queries like(tcp, 127.0.0.1:1000, IP:PORT)
for any concreteIP
andPORT
.Shadow does contain some checks, such as the following in
get_random_free_port()
:shadow/src/main/host/network/namespace.rs
Lines 227 to 244 in 66b0712
Which when deciding if a port is free will check if
(proto, local_ip:local_port, remote_ip:remote_port)
or(proto, local_ip:local_port, 0.0.0.0:0)
already exists. In the case of TCP this prevents binding a socket to the same local IP and port as a listening socket. For example if there exists a listening socket bound to 127.0.0.1:80, it will be in the table as (tcp, 127.0.0.1:80, 0.0.0.0:0). So if connect() is called on a new socket, this check prevents us from choosing port 80 since we'll check both (tcp, 127.0.0.1:80, peer_ip:peer_port) as well as (tcp, 127.0.0.1:80, 0.0.0.0:0), and this second check will fail since that 5-tuple is already in use.This check doesn't help us in the reverse case though. If we connect() a socket to a peer and then bind() another socket to the same local IP and port, Shadow will allow this even though it's not allowed on Linux. For example on Linux:
Whereas in Shadow:
exits successfully with the log:
There are likely other cases that don't work correctly in Shadow, and if we wanted to support options like
SO_REUSEADDR
,SO_REUSEPORT
, andIP_BIND_ADDRESS_NO_PORT
it would complicate things further. The behaviour is also different between TCP and UDP, and on Linux these associations are stored in two separate tables:tcp_hashinfo
andudp_table
.The text was updated successfully, but these errors were encountered: