net: tcp: Report TCP errors through recv_cb()

Make use of the status argument in the recv_cb() callback function -
instead of blindly reporting ECONNRESET whenever TCP context is
dereferenced, indicate whether an actual error condition happened (by
setting respective errno value) or a graceful shutdown took place (by
setting status to 0).

Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
diff --git a/subsys/net/ip/tcp.c b/subsys/net/ip/tcp.c
index 3cadf57..4c8cf66 100644
--- a/subsys/net/ip/tcp.c
+++ b/subsys/net/ip/tcp.c
@@ -358,12 +358,13 @@
 }
 
 #if CONFIG_NET_TCP_LOG_LEVEL >= LOG_LEVEL_DBG
-#define tcp_conn_unref(conn)				\
-	tcp_conn_unref_debug(conn, __func__, __LINE__)
+#define tcp_conn_unref(conn, status)				\
+	tcp_conn_unref_debug(conn, status, __func__, __LINE__)
 
-static int tcp_conn_unref_debug(struct tcp *conn, const char *caller, int line)
+static int tcp_conn_unref_debug(struct tcp *conn, int status,
+				const char *caller, int line)
 #else
-static int tcp_conn_unref(struct tcp *conn)
+static int tcp_conn_unref(struct tcp *conn, int status)
 #endif
 {
 	int ref_count = atomic_get(&conn->ref_count);
@@ -409,7 +410,7 @@
 
 	if (conn->context->recv_cb) {
 		conn->context->recv_cb(conn->context, NULL, NULL, NULL,
-					-ECONNRESET, conn->recv_user_data);
+				       status, conn->recv_user_data);
 	}
 
 	conn->context->tcp = NULL;
@@ -446,7 +447,7 @@
 	NET_DBG("context: %p, conn: %p", context, context->tcp);
 
 	if (context->tcp) {
-		ref_count = tcp_conn_unref(context->tcp);
+		ref_count = tcp_conn_unref(context->tcp, 0);
 	}
 
 	return ref_count;
@@ -530,7 +531,7 @@
 	k_mutex_unlock(&conn->lock);
 
 	if (unref) {
-		tcp_conn_unref(conn);
+		tcp_conn_unref(conn, -ETIMEDOUT);
 	}
 }
 
@@ -929,7 +930,7 @@
 		k_work_schedule_for_queue(&tcp_work_q,
 					  &conn->send_timer, K_NO_WAIT);
 	} else if (tcp_send_process_no_lock(conn)) {
-		tcp_conn_unref(conn);
+		tcp_conn_unref(conn, -ETIMEDOUT);
 	}
 out:
 	return ret;
@@ -1172,7 +1173,7 @@
 	k_mutex_unlock(&conn->lock);
 
 	if (conn_unref) {
-		tcp_conn_unref(conn);
+		tcp_conn_unref(conn, -ETIMEDOUT);
 	}
 }
 
@@ -1192,7 +1193,7 @@
 	NET_DBG("Did not receive %s in %dms", "ACK", ACK_TIMEOUT_MS);
 	NET_DBG("conn: %p %s", conn, log_strdup(tcp_conn_state(conn, NULL)));
 
-	(void)tcp_conn_unref(conn);
+	(void)tcp_conn_unref(conn, -ETIMEDOUT);
 }
 
 static void tcp_fin_timeout(struct k_work *work)
@@ -1745,6 +1746,7 @@
 	size_t len;
 	int ret;
 	int sndbuf_opt = 0;
+	int close_status = 0;
 
 	if (th) {
 		/* Currently we ignore ECN and CWR flags */
@@ -1764,6 +1766,7 @@
 	if (th && th_off(th) < 5) {
 		tcp_out(conn, RST);
 		conn_state(conn, TCP_CLOSED);
+		close_status = -ECONNRESET;
 		goto next_state;
 	}
 
@@ -1777,6 +1780,7 @@
 
 		net_stats_update_tcp_seg_rst(net_pkt_iface(pkt));
 		conn_state(conn, TCP_CLOSED);
+		close_status = -ECONNRESET;
 		goto next_state;
 	}
 
@@ -1785,6 +1789,7 @@
 		NET_DBG("DROP: Invalid TCP option list");
 		tcp_out(conn, RST);
 		conn_state(conn, TCP_CLOSED);
+		close_status = -ECONNRESET;
 		goto next_state;
 	}
 
@@ -1953,6 +1958,7 @@
 				net_stats_update_tcp_seg_drop(conn->iface);
 				tcp_out(conn, RST);
 				conn_state(conn, TCP_CLOSED);
+				close_status = -ECONNRESET;
 				break;
 			}
 
@@ -1994,6 +2000,7 @@
 			if (ret < 0 && ret != -ENOBUFS) {
 				tcp_out(conn, RST);
 				conn_state(conn, TCP_CLOSED);
+				close_status = ret;
 				break;
 			}
 		}
@@ -2021,6 +2028,7 @@
 		if (th && FL(&fl, ==, ACK, th_seq(th) == conn->ack)) {
 			tcp_send_timer_cancel(conn);
 			next = TCP_CLOSED;
+			close_status = 0;
 		}
 		break;
 	case TCP_CLOSED:
@@ -2119,7 +2127,7 @@
 	 * to a deadlock.
 	 */
 	if (do_close) {
-		tcp_conn_unref(conn);
+		tcp_conn_unref(conn, close_status);
 	}
 }
 
@@ -2277,7 +2285,7 @@
 
 	ret = tcp_send_queued_data(conn);
 	if (ret < 0 && ret != -ENOBUFS) {
-		tcp_conn_unref(conn);
+		tcp_conn_unref(conn, ret);
 		goto out;
 	}
 
@@ -2433,7 +2441,7 @@
 		if (k_sem_take(&conn->connect_sem, timeout) != 0 &&
 		    conn->state != TCP_ESTABLISHED) {
 			conn->in_connect = false;
-			tcp_conn_unref(conn);
+			tcp_conn_unref(conn, -ETIMEDOUT);
 			ret = -ETIMEDOUT;
 			goto out;
 		}
@@ -2739,7 +2747,7 @@
 
 				conn = (void *)sys_slist_peek_head(&tcp_conns);
 				context = conn->context;
-				while (tcp_conn_unref(conn))
+				while (tcp_conn_unref(conn, 0))
 					;
 				tcp_free(context);
 			}