Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backport v3.5-branch] net: tcp: Send RST reply for unexpected TCP packets #64321

Merged
merged 5 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions subsys/net/ip/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,13 @@ config NET_TCP_WORKER_PRIO
execution to the lower layer network stack, with a high risk of
running out of net_bufs.

config NET_TCP_REJECT_CONN_WITH_RST
bool "Reject connection attempts on unbound TCP ports with RST"
default y
help
If enabled, TCP stack will reject connection attempts on unbound ports
with TCP RST packet.

config NET_TEST_PROTOCOL
bool "JSON based test protocol (UDP)"
help
Expand Down
8 changes: 5 additions & 3 deletions subsys/net/ip/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -828,10 +828,12 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,

if (IS_ENABLED(CONFIG_NET_IP) && (pkt_family == AF_INET || pkt_family == AF_INET6) &&
!(is_mcast_pkt || is_bcast_pkt)) {
conn_send_icmp_error(pkt);

if (IS_ENABLED(CONFIG_NET_TCP) && proto == IPPROTO_TCP) {
if (IS_ENABLED(CONFIG_NET_TCP) && proto == IPPROTO_TCP &&
IS_ENABLED(CONFIG_NET_TCP_REJECT_CONN_WITH_RST)) {
net_tcp_reply_rst(pkt);
net_stats_update_tcp_seg_connrst(pkt_iface);
} else {
conn_send_icmp_error(pkt);
}
}

Expand Down
81 changes: 81 additions & 0 deletions subsys/net/ip/tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1162,6 +1162,82 @@ static bool is_destination_local(struct net_pkt *pkt)
return false;
}

void net_tcp_reply_rst(struct net_pkt *pkt)
{
NET_PKT_DATA_ACCESS_DEFINE(tcp_access_rst, struct tcphdr);
struct tcphdr *th_pkt = th_get(pkt);
struct tcphdr *th_rst;
struct net_pkt *rst;
int ret;

if (th_pkt == NULL || (th_flags(th_pkt) & RST)) {
/* Don't reply to a RST segment. */
return;
}

rst = tcp_pkt_alloc_no_conn(pkt->iface, pkt->family,
sizeof(struct tcphdr));
if (rst == NULL) {
return;
}

/* IP header */
if (IS_ENABLED(CONFIG_NET_IPV4) && net_pkt_family(pkt) == AF_INET) {
ret = net_ipv4_create(rst,
(struct in_addr *)NET_IPV4_HDR(pkt)->dst,
(struct in_addr *)NET_IPV4_HDR(pkt)->src);
} else if (IS_ENABLED(CONFIG_NET_IPV6) && net_pkt_family(pkt) == AF_INET6) {
ret = net_ipv6_create(rst,
(struct in6_addr *)NET_IPV6_HDR(pkt)->dst,
(struct in6_addr *)NET_IPV6_HDR(pkt)->src);
} else {
ret = -EINVAL;
}

if (ret < 0) {
goto err;
}

/* TCP header */
th_rst = (struct tcphdr *)net_pkt_get_data(rst, &tcp_access_rst);
if (th_rst == NULL) {
goto err;
}

memset(th_rst, 0, sizeof(struct tcphdr));

UNALIGNED_PUT(th_pkt->th_dport, &th_rst->th_sport);
UNALIGNED_PUT(th_pkt->th_sport, &th_rst->th_dport);
th_rst->th_off = 5;

if (th_flags(th_pkt) & ACK) {
UNALIGNED_PUT(RST, &th_rst->th_flags);
UNALIGNED_PUT(th_pkt->th_ack, &th_rst->th_seq);
} else {
uint32_t ack = ntohl(th_pkt->th_seq) + tcp_data_len(pkt);

UNALIGNED_PUT(RST | ACK, &th_rst->th_flags);
UNALIGNED_PUT(htonl(ack), &th_rst->th_ack);
}

ret = net_pkt_set_data(rst, &tcp_access_rst);
if (ret < 0) {
goto err;
}

ret = tcp_finalize_pkt(rst);
if (ret < 0) {
goto err;
}

tcp_send(rst);

return;

err:
tcp_pkt_unref(rst);
}

static int tcp_out_ext(struct tcp *conn, uint8_t flags, struct net_pkt *data,
uint32_t seq)
{
Expand Down Expand Up @@ -1783,6 +1859,8 @@ static enum net_verdict tcp_recv(struct net_conn *net_conn,
in:
if (conn) {
verdict = tcp_in(conn, pkt);
} else {
net_tcp_reply_rst(pkt);
}

return verdict;
Expand Down Expand Up @@ -2509,7 +2587,10 @@ static enum net_verdict tcp_in(struct tcp *conn, struct net_pkt *pkt)
* priority.
*/
connection_ok = true;
} else if (pkt) {
net_tcp_reply_rst(pkt);
}

break;
case TCP_ESTABLISHED:
/* full-close */
Expand Down
14 changes: 14 additions & 0 deletions subsys/net/ip/tcp_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,20 @@ struct k_sem *net_tcp_tx_sem_get(struct net_context *context);
*/
struct k_sem *net_tcp_conn_sem_get(struct net_context *context);

/**
* @brief Send a TCP RST reply for the received packet w/o associated connection.
*
* @param pkt TCP packet to reply for.
*/
#if defined(CONFIG_NET_NATIVE_TCP)
void net_tcp_reply_rst(struct net_pkt *pkt);
#else
static inline void net_tcp_reply_rst(struct net_pkt *pkt)
{
ARG_UNUSED(pkt);
}
#endif

#ifdef __cplusplus
}
#endif
Expand Down
17 changes: 17 additions & 0 deletions subsys/net/ip/tcp_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,23 @@
_pkt; \
})

#define tcp_pkt_alloc_no_conn(_iface, _family, _len) \
({ \
struct net_pkt *_pkt; \
\
if ((_len) > 0) { \
_pkt = net_pkt_alloc_with_buffer( \
(_iface), (_len), (_family), \
IPPROTO_TCP, \
TCP_PKT_ALLOC_TIMEOUT); \
} else { \
_pkt = net_pkt_alloc(TCP_PKT_ALLOC_TIMEOUT); \
} \
\
tp_pkt_alloc(_pkt, tp_basename(__FILE__), __LINE__); \
\
_pkt; \
})

#if defined(CONFIG_NET_TEST_PROTOCOL)
#define conn_seq(_conn, _req) \
Expand Down
1 change: 1 addition & 0 deletions tests/lib/thrift/ThriftTest/prj.conf
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ CONFIG_TEST_RANDOM_GENERATOR=y
CONFIG_NET_TEST=y
CONFIG_NET_DRIVERS=y
CONFIG_NET_LOOPBACK=y
CONFIG_NET_TCP_TIME_WAIT_DELAY=100

# Some platforms require relatively large stack sizes.
# This can be tuned per-board.
Expand Down
5 changes: 5 additions & 0 deletions tests/lib/thrift/ThriftTest/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ static void thrift_test_before(void *data)
rv = pthread_create(&context.server_thread, attrp, server_func, nullptr);
zassert_equal(0, rv, "pthread_create failed: %d", rv);

/* Give the server thread a chance to start and prepare the socket */
k_msleep(50);

// set up client
context.client = setup_client();
}
Expand All @@ -160,6 +163,8 @@ static void thrift_test_after(void *data)

context.client.reset();
context.server.reset();

k_msleep(CONFIG_NET_TCP_TIME_WAIT_DELAY);
}

ZTEST_SUITE(thrift, NULL, thrift_test_setup, thrift_test_before, thrift_test_after, NULL);
Loading