net: tcp: Handle special case where accepted socket is closed
Handle this corner case with TCP connection closing: 1) Client A connects, it is accepted and can send data to us 2) Client B connects, the application needs to call accept() before we will receive any data from client A to the application. The app has not yet called accept() at this point (for whatever reason). 3) Client B then disconnects and we receive FIN. The connection cleanup is a bit tricky as the client is in half-connected state meaning that the connection is in established state but the accept_q in socket queue contains still data which needs to be cleared. 4) Client A then disconnects, all data is sent etc The above was not working correctly as the system did not handle the step 3) properly. The client B was accepted in the application even if the connection was closing. After this commit, the commit called "net: tcp: Accept connections only in LISTENING state" and related other commits are no longer needed and are reverted. Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
This commit is contained in:
parent
f093b710b5
commit
d88f25bd76
3 changed files with 113 additions and 7 deletions
|
@ -58,6 +58,12 @@ enum net_context_state {
|
|||
/** Remote address set */
|
||||
#define NET_CONTEXT_REMOTE_ADDR_SET BIT(8)
|
||||
|
||||
/** Is the socket accepting connections */
|
||||
#define NET_CONTEXT_ACCEPTING_SOCK BIT(9)
|
||||
|
||||
/** Is the socket closing / closed */
|
||||
#define NET_CONTEXT_CLOSING_SOCK BIT(10)
|
||||
|
||||
struct net_context;
|
||||
|
||||
/**
|
||||
|
@ -334,6 +340,70 @@ static inline bool net_context_is_used(struct net_context *context)
|
|||
return context->flags & NET_CONTEXT_IN_USE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Is this context is accepting data now.
|
||||
*
|
||||
* @param context Network context.
|
||||
*
|
||||
* @return True if the context is accepting connections, False otherwise.
|
||||
*/
|
||||
static inline bool net_context_is_accepting(struct net_context *context)
|
||||
{
|
||||
NET_ASSERT(context);
|
||||
|
||||
return context->flags & NET_CONTEXT_ACCEPTING_SOCK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set this context to accept data now.
|
||||
*
|
||||
* @param context Network context.
|
||||
* @param accepting True if accepting, False if not
|
||||
*/
|
||||
static inline void net_context_set_accepting(struct net_context *context,
|
||||
bool accepting)
|
||||
{
|
||||
NET_ASSERT(context);
|
||||
|
||||
if (accepting) {
|
||||
context->flags |= NET_CONTEXT_ACCEPTING_SOCK;
|
||||
} else {
|
||||
context->flags &= ~NET_CONTEXT_ACCEPTING_SOCK;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Is this context closing.
|
||||
*
|
||||
* @param context Network context.
|
||||
*
|
||||
* @return True if the context is closing, False otherwise.
|
||||
*/
|
||||
static inline bool net_context_is_closing(struct net_context *context)
|
||||
{
|
||||
NET_ASSERT(context);
|
||||
|
||||
return context->flags & NET_CONTEXT_CLOSING_SOCK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set this context to closing.
|
||||
*
|
||||
* @param context Network context.
|
||||
* @param closing True if closing, False if not
|
||||
*/
|
||||
static inline void net_context_set_closing(struct net_context *context,
|
||||
bool closing)
|
||||
{
|
||||
NET_ASSERT(context);
|
||||
|
||||
if (closing) {
|
||||
context->flags |= NET_CONTEXT_CLOSING_SOCK;
|
||||
} else {
|
||||
context->flags &= ~NET_CONTEXT_CLOSING_SOCK;
|
||||
}
|
||||
}
|
||||
|
||||
#define NET_CONTEXT_STATE_SHIFT 1
|
||||
#define NET_CONTEXT_STATE_MASK 0x03
|
||||
|
||||
|
|
|
@ -1943,6 +1943,7 @@ NET_CONN_CB(tcp_established)
|
|||
struct net_context *context = (struct net_context *)user_data;
|
||||
struct net_tcp_hdr *tcp_hdr = proto_hdr->tcp;
|
||||
enum net_verdict ret = NET_OK;
|
||||
bool do_not_send_ack = false;
|
||||
u8_t tcp_flags;
|
||||
u16_t data_len;
|
||||
|
||||
|
@ -2051,6 +2052,8 @@ resend_ack:
|
|||
*/
|
||||
k_delayed_work_submit(&context->tcp->ack_timer,
|
||||
ACK_TIMEOUT);
|
||||
|
||||
net_context_set_closing(context, true);
|
||||
} else if (net_tcp_get_state(context->tcp)
|
||||
== NET_TCP_FIN_WAIT_2) {
|
||||
/* Received FIN on FIN_WAIT_2, so cancel the timer */
|
||||
|
@ -2062,7 +2065,14 @@ resend_ack:
|
|||
context->tcp->fin_rcvd = 1U;
|
||||
}
|
||||
|
||||
if (!IS_ENABLED(CONFIG_NET_TCP_AUTO_ACCEPT) &&
|
||||
net_context_is_accepting(context)) {
|
||||
data_len = 0;
|
||||
do_not_send_ack = true;
|
||||
} else {
|
||||
data_len = net_pkt_remaining_data(pkt);
|
||||
}
|
||||
|
||||
if (data_len > net_tcp_get_recv_wnd(context->tcp)) {
|
||||
/* In case we have zero window, we should still accept
|
||||
* Zero Window Probes from peer, which per convention
|
||||
|
@ -2095,6 +2105,7 @@ resend_ack:
|
|||
net_pkt_unref(pkt);
|
||||
}
|
||||
|
||||
if (do_not_send_ack == false) {
|
||||
/* Increment the ack */
|
||||
context->tcp->send_ack += data_len;
|
||||
if (tcp_flags & NET_TCP_FIN) {
|
||||
|
@ -2102,6 +2113,7 @@ resend_ack:
|
|||
}
|
||||
|
||||
send_ack(context, &conn->remote_addr, false);
|
||||
}
|
||||
|
||||
clean_up:
|
||||
if (net_tcp_get_state(context->tcp) == NET_TCP_TIME_WAIT) {
|
||||
|
@ -2452,6 +2464,12 @@ NET_CONN_CB(tcp_syn_rcvd)
|
|||
*/
|
||||
new_context->tcp->state = NET_TCP_ESTABLISHED;
|
||||
|
||||
/* Mark the new context to be still accepting so that we
|
||||
* can do proper cleanup if connection is closed before
|
||||
* we have called accept()
|
||||
*/
|
||||
net_context_set_accepting(new_context, true);
|
||||
|
||||
net_context_set_state(new_context, NET_CONTEXT_CONNECTED);
|
||||
|
||||
if (new_context->remote.sa_family == AF_INET) {
|
||||
|
|
|
@ -406,6 +406,7 @@ int zsock_accept_ctx(struct net_context *parent, struct sockaddr *addr,
|
|||
{
|
||||
s32_t timeout = K_FOREVER;
|
||||
struct net_context *ctx;
|
||||
struct net_pkt *last_pkt;
|
||||
int fd;
|
||||
|
||||
fd = z_reserve_fd();
|
||||
|
@ -423,6 +424,23 @@ int zsock_accept_ctx(struct net_context *parent, struct sockaddr *addr,
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* Check if the connection is already disconnected */
|
||||
last_pkt = k_fifo_peek_tail(&ctx->recv_q);
|
||||
if (last_pkt) {
|
||||
if (net_pkt_eof(last_pkt)) {
|
||||
sock_set_eof(ctx);
|
||||
errno = ECONNABORTED;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (net_context_is_closing(ctx)) {
|
||||
errno = ECONNABORTED;
|
||||
return -1;
|
||||
}
|
||||
|
||||
net_context_set_accepting(ctx, false);
|
||||
|
||||
#ifdef CONFIG_USERSPACE
|
||||
z_object_recycle(ctx);
|
||||
#endif
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue