pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/libosmo-netif/+/38987?usp=email )
Change subject: stream_cli: Fix discard 1st msg received quick after connect ......................................................................
stream_cli: Fix discard 1st msg received quick after connect
Even if setting osmo_iofd_notify_connected(), it may happen that a read call-back is triggered towards the user before this special write-callback is triggered. stream_cli was already accounting for that in stream_cli_iofd_read_cb() state STREAM_CLI_STATE_CONNECTING, but was discarding the msgb instead of pushing it upwards towards the user through read_cb after transitioning to STREAM_CLI_STATE_CONNECTED.
As a result, eg when an ipaccess client using stream_cli (BTS, liboamo-abis e1_line ipaccess driver) connected to an ipaccess server (BSC) and the server quickly transmitted an IPA ID GET, it would get lost.
Related: libosmocore.git Change-Id Ica20a050b98d117995a5b625b23ab9faa61aabee Change-Id: I98cd51d4bb87d3572245446648ced44a23a622ef --- M src/stream_cli.c 1 file changed, 51 insertions(+), 31 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/libosmo-netif refs/changes/87/38987/1
diff --git a/src/stream_cli.c b/src/stream_cli.c index fe62607..131969d 100644 --- a/src/stream_cli.c +++ b/src/stream_cli.c @@ -409,7 +409,8 @@ #endif }
-static void stream_cli_handle_connecting(struct osmo_stream_cli *cli, int res) +/* returns true if cli is freed */ +static bool stream_cli_handle_connecting(struct osmo_stream_cli *cli, int res) { int error, ret = res; socklen_t len = sizeof(error); @@ -419,14 +420,12 @@
if (ret < 0) { LOGSCLI(cli, LOGL_ERROR, "connect failed (%d)\n", res); - (void)stream_cli_reconnect(cli); - return; + return stream_cli_reconnect(cli); } ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len); if (ret >= 0 && error > 0) { LOGSCLI(cli, LOGL_ERROR, "connect so_error (%d)\n", error); - (void)stream_cli_reconnect(cli); - return; + return stream_cli_reconnect(cli); }
/* If messages got enqueued while 'connecting', keep WRITE flag @@ -458,7 +457,7 @@ if (cli->connect_cb) cli->connect_cb(cli); cli->in_cb_mask &= ~IN_CB_MASK_CONNECT_CB; - (void)free_delayed_if_needed(cli); + return free_delayed_if_needed(cli); }
static int osmo_stream_cli_fd_cb(struct osmo_fd *ofd, unsigned int what) @@ -528,8 +527,14 @@
switch (cli->state) { case STREAM_CLI_STATE_CONNECTING: - msgb_free(msg); - stream_cli_handle_connecting(cli, res); + freed = stream_cli_handle_connecting(cli, res); + if (freed) + return; /* msg was also freed as part of the talloc tree. */ + if (cli->state != STREAM_CLI_STATE_CONNECTED) { + msgb_free(msg); + return; + } + /* Follow below common path submitting read_cb(msg) to user. */ break; case STREAM_CLI_STATE_CONNECTED: switch (res) { @@ -549,35 +554,41 @@ } if (freed) return; /* msg was also freed as part of the talloc tree. */ - /* Notify user of new data or error: */ - if (!cli->iofd_read_cb) { - msgb_free(msg); - return; - } - cli->in_cb_mask |= IN_CB_MASK_READ_CB; - cli->iofd_read_cb(cli, res, msg); - cli->in_cb_mask &= ~IN_CB_MASK_READ_CB; - OSMO_ASSERT(cli->in_cb_mask == 0); - (void)free_delayed_if_needed(cli); + /* Follow below common path submitting read_cb(msg) to user. */ break; default: osmo_panic("%s() called with unexpected state %d\n", __func__, cli->state); } + + /* Notify user of new data or error: */ + if (!cli->iofd_read_cb) { + msgb_free(msg); + return; + } + cli->in_cb_mask |= IN_CB_MASK_READ_CB; + cli->iofd_read_cb(cli, res, msg); + cli->in_cb_mask &= ~IN_CB_MASK_READ_CB; + OSMO_ASSERT(cli->in_cb_mask == 0); + (void)free_delayed_if_needed(cli); }
static void stream_cli_iofd_write_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg) { struct osmo_stream_cli *cli = osmo_iofd_get_data(iofd); + /* msgb is not owned by us here, no need to free it. */
switch (cli->state) { case STREAM_CLI_STATE_CONNECTING: - stream_cli_handle_connecting(cli, res); + (void)stream_cli_handle_connecting(cli, res); break; case STREAM_CLI_STATE_CONNECTED: if (msg && res <= 0) { LOGSCLI(cli, LOGL_ERROR, "received error %d in response to send\n", res); (void)stream_cli_reconnect(cli); } + /* res=0 && msgb=NULL: "connected notify", but we already received before a read_cb + * which moved us to CONNECTED state. Do nothing. + */ break; default: osmo_panic("%s() called with unexpected state %d\n", __func__, cli->state); @@ -601,8 +612,15 @@
switch (cli->state) { case STREAM_CLI_STATE_CONNECTING: - msgb_free(msg); - stream_cli_handle_connecting(cli, res); + LOGSCLI(cli, LOGL_NOTICE, "stream_cli_iofd_read_cb(res=%d, msg=%p msg_len=%d)\n", res, msg, msg ? msgb_length(msg) : -1); + freed = stream_cli_handle_connecting(cli, res); + if (freed) + return; /* msg was also freed as part of the talloc tree. */ + if (cli->state != STREAM_CLI_STATE_CONNECTED) { + msgb_free(msg); + return; + } + /* Follow below common path submitting read_cb(msg) to user. */ break; case STREAM_CLI_STATE_CONNECTED: switch (res) { @@ -621,20 +639,22 @@ } if (freed) return; /* msg was also freed as part of the talloc tree. */ - /* Notify user of new data or error: */ - if (!cli->iofd_read_cb) { - msgb_free(msg); - return; - } - cli->in_cb_mask |= IN_CB_MASK_READ_CB; - cli->iofd_read_cb(cli, res, msg); - cli->in_cb_mask &= ~IN_CB_MASK_READ_CB; - OSMO_ASSERT(cli->in_cb_mask == 0); - (void)free_delayed_if_needed(cli); + /* Follow below common path submitting read_cb(msg) to user. */ break; default: osmo_panic("%s() called with unexpected state %d\n", __func__, cli->state); } + + /* Notify user of new data or error: */ + if (!cli->iofd_read_cb) { + msgb_free(msg); + return; + } + cli->in_cb_mask |= IN_CB_MASK_READ_CB; + cli->iofd_read_cb(cli, res, msg); + cli->in_cb_mask &= ~IN_CB_MASK_READ_CB; + OSMO_ASSERT(cli->in_cb_mask == 0); + (void)free_delayed_if_needed(cli); }
static const struct osmo_io_ops osmo_stream_cli_ioops_sctp = {