Skip to content

Parent TCP sockets aren't disassociated from the network interface until all child sockets are closed #3563

@stevenengler

Description

@stevenengler

In Shadow's C TCP code, a parent socket is responsible for forwarding packets to its children. For example, if a listening socket accept()s a new child socket, any incoming packets for the child socket are received by the parent listening socket, and then forwarded to the child socket.

/* return TRUE if the packet should be retransmitted */
static void _tcp_processPacket(LegacySocket* socket, const Host* host, Packet* packet) {
TCP* tcp = _tcp_fromLegacyFile((LegacyFile*)socket);
MAGIC_ASSERT(tcp);
/* fetch the TCP info from the packet */
gsize packetLength = packet_getPayloadSize(packet);
/* if we run a server, the packet could be for an existing child */
tcp = _tcp_getSourceTCP(tcp, packet_getSourceIP(packet), packet_getSourcePort(packet));

This is problematic when the listening socket is closed before all of its children, or when the child sockets are close()d but need to spend some time in one of the closing states like LAST_ACK before being completely closed. When this happens, the listening socket cannot be disassociated from the network interface, which means that even if the listening socket is closed, another socket cannot bind to that same IP/port.

/*
* servers have to wait for all children to close.
* children need to notify their parents when closing.
*/
if (!tcp->server || !tcp->server->children ||
g_hash_table_size(tcp->server->children) <= 0) {
if(tcp->child && tcp->child->parent) {
TCP* parent = tcp->child->parent;
utility_debugAssert(parent->server);
/* tell my server to stop accepting packets for me
* this will destroy the child and NULL out tcp->child */
g_hash_table_remove(parent->server->children, &(tcp->child->key));
/* if i was the server's last child and its waiting to close, close it */
if((parent->state == TCPS_CLOSED) && (g_hash_table_size(parent->server->children) <= 0)) {
if (disassociate) {
/* this will unbind from the network interface and free socket */
host_disassociateInterface(
host, PTCP, sock_ip, sock_port, peer_ip, peer_port);
}
}
}
if (disassociate) {
/* TODO: we should only be disassociating non-child sockets */
host_disassociateInterface(host, PTCP, sock_ip, sock_port, peer_ip, peer_port);
}
}

Related: #2590, #1781

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type: BugError or flaw producing unexpected results

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions