From 6ebfc047dd3461acb975b1987ba76b68d5efdfda Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 24 Jun 2020 21:14:36 +0200 Subject: Fix race during fast re-establishment of inbound M3UA connections When a client closes and instantaneously re-opens a SCTP socket for an M3UA connection, there is a chance that both the "shutdwon event" (old connection socket becomes readable for sctp event) and the "init event" (listen-fd becomes readable) happen during the same scheduler interval / select() cycle. As there is no guaranteed order by which we call our file descriptor callbacks, it means that we may end up processing then new connection (accept) before we get the notification that the old one is dead. The fact that the fd number of the accept-fd is mostly lower than the fd number of the individual per-client connection actually makes it likely that the order is exactly the opposite of what would feel "logical". As the ASP is identified by the tuple of (src-port, src-ip, dst-port, dst-ip), both the old connection and the new connection map to the same ASP object. So we need to handle this situation gracefully: If we get a new connection for a tuple that we already [think we still] have one, close the old one and use the new. Change-Id: I9b3ae6dfcf6efeabb7fb6c33503d1d7924fec2fa Closes: OS#4625 --- src/osmo_ss7.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/osmo_ss7.c b/src/osmo_ss7.c index b7d69cb..9aeed9b 100644 --- a/src/osmo_ss7.c +++ b/src/osmo_ss7.c @@ -1832,6 +1832,17 @@ static int xua_accept_cb(struct osmo_stream_srv_link *link, int fd) if (asp) { LOGP(DLSS7, LOGL_INFO, "%s: matched connection to ASP %s\n", sock_name, asp->cfg.name); + /* we need to check if we already have a socket associated, and close it. Otherwise it might + * happen that both the listen-fd for this accept() and the old socket are marked 'readable' + * during the same scheduling interval, and we're processing them in the "wrong" order, i.e. + * we first see the accept of the new fd before we see the close on the old fd */ + if (asp->server) { + LOGPASP(asp, DLSS7, LOGL_FATAL, "accept of new connection from %s before old was closed " + "-> close old one\n", sock_name); + osmo_stream_srv_set_data(asp->server, NULL); + osmo_stream_srv_destroy(asp->server); + asp->server = NULL; + } } else { if (!oxs->cfg.accept_dyn_reg) { LOGP(DLSS7, LOGL_NOTICE, "%s: %s connection without matching " @@ -1870,13 +1881,13 @@ static int xua_accept_cb(struct osmo_stream_srv_link *link, int fd) talloc_free(sock_name); return -1; } + llist_add_tail(&asp->siblings, &oxs->asp_list); } /* update the ASP reference back to the server over which the * connection came in */ asp->server = srv; asp->xua_server = oxs; - llist_add_tail(&asp->siblings, &oxs->asp_list); /* update the ASP socket name */ if (asp->sock_name) talloc_free(asp->sock_name); -- cgit v1.2.3