blob: 24b528537bd38fa3b4f6d8424d57c81ae5a9efed [file] [log] [blame]
/*
* Copyright (c) 2016 Intel Corporation
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(net_shell);
#include "net_shell_private.h"
#if defined(CONFIG_NET_TCP)
#include "tcp_internal.h"
#include <zephyr/sys/slist.h>
#endif
#if defined(CONFIG_NET_OFFLOAD) || defined(CONFIG_NET_NATIVE)
static void context_cb(struct net_context *context, void *user_data)
{
#if defined(CONFIG_NET_IPV6) && !defined(CONFIG_NET_IPV4)
#define ADDR_LEN NET_IPV6_ADDR_LEN
#elif defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_IPV6)
#define ADDR_LEN NET_IPV4_ADDR_LEN
#else
#define ADDR_LEN NET_IPV6_ADDR_LEN
#endif
struct net_shell_user_data *data = user_data;
const struct shell *sh = data->sh;
int *count = data->user_data;
/* +7 for []:port */
char addr_local[ADDR_LEN + 7];
char addr_remote[ADDR_LEN + 7] = "";
get_addresses(context, addr_local, sizeof(addr_local),
addr_remote, sizeof(addr_remote));
PR("[%2d] %p\t%d %c%c%c %16s\t%16s\n",
(*count) + 1, context,
net_if_get_by_iface(net_context_get_iface(context)),
net_context_get_family(context) == AF_INET6 ? '6' :
(net_context_get_family(context) == AF_INET ? '4' : ' '),
net_context_get_type(context) == SOCK_DGRAM ? 'D' :
(net_context_get_type(context) == SOCK_STREAM ? 'S' :
(net_context_get_type(context) == SOCK_RAW ? 'R' : ' ')),
net_context_get_proto(context) == IPPROTO_UDP ? 'U' :
(net_context_get_proto(context) == IPPROTO_TCP ? 'T' : ' '),
addr_local, addr_remote);
(*count)++;
}
#endif /* CONFIG_NET_OFFLOAD || CONFIG_NET_NATIVE */
#if CONFIG_NET_CONN_LOG_LEVEL >= LOG_LEVEL_DBG
static void conn_handler_cb(struct net_conn *conn, void *user_data)
{
#if defined(CONFIG_NET_IPV6) && !defined(CONFIG_NET_IPV4)
#define ADDR_LEN NET_IPV6_ADDR_LEN
#elif defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_IPV6)
#define ADDR_LEN NET_IPV4_ADDR_LEN
#else
#define ADDR_LEN NET_IPV6_ADDR_LEN
#endif
struct net_shell_user_data *data = user_data;
const struct shell *sh = data->sh;
int *count = data->user_data;
/* +7 for []:port */
char addr_local[ADDR_LEN + 7];
char addr_remote[ADDR_LEN + 7] = "";
if (IS_ENABLED(CONFIG_NET_IPV6) && conn->local_addr.sa_family == AF_INET6) {
snprintk(addr_local, sizeof(addr_local), "[%s]:%u",
net_sprint_ipv6_addr(
&net_sin6(&conn->local_addr)->sin6_addr),
ntohs(net_sin6(&conn->local_addr)->sin6_port));
snprintk(addr_remote, sizeof(addr_remote), "[%s]:%u",
net_sprint_ipv6_addr(
&net_sin6(&conn->remote_addr)->sin6_addr),
ntohs(net_sin6(&conn->remote_addr)->sin6_port));
} else if (IS_ENABLED(CONFIG_NET_IPV4) && conn->local_addr.sa_family == AF_INET) {
snprintk(addr_local, sizeof(addr_local), "%s:%d",
net_sprint_ipv4_addr(
&net_sin(&conn->local_addr)->sin_addr),
ntohs(net_sin(&conn->local_addr)->sin_port));
snprintk(addr_remote, sizeof(addr_remote), "%s:%d",
net_sprint_ipv4_addr(
&net_sin(&conn->remote_addr)->sin_addr),
ntohs(net_sin(&conn->remote_addr)->sin_port));
} else if (conn->local_addr.sa_family == AF_UNSPEC) {
snprintk(addr_local, sizeof(addr_local), "AF_UNSPEC");
} else {
snprintk(addr_local, sizeof(addr_local), "AF_UNK(%d)",
conn->local_addr.sa_family);
}
PR("[%2d] %p %p\t%s\t%16s\t%16s\n",
(*count) + 1, conn, conn->cb,
net_proto2str(conn->local_addr.sa_family, conn->proto),
addr_local, addr_remote);
(*count)++;
}
#endif /* CONFIG_NET_CONN_LOG_LEVEL >= LOG_LEVEL_DBG */
#if CONFIG_NET_TCP_LOG_LEVEL >= LOG_LEVEL_DBG
struct tcp_detail_info {
int printed_send_queue_header;
int printed_details;
int count;
};
#endif
#if defined(CONFIG_NET_TCP) && \
(defined(CONFIG_NET_OFFLOAD) || defined(CONFIG_NET_NATIVE))
static void tcp_cb(struct tcp *conn, void *user_data)
{
struct net_shell_user_data *data = user_data;
const struct shell *sh = data->sh;
int *count = data->user_data;
uint16_t recv_mss = net_tcp_get_supported_mss(conn);
PR("%p %p %5u %5u %10u %10u %5u %s\n",
conn, conn->context,
ntohs(net_sin6_ptr(&conn->context->local)->sin6_port),
ntohs(net_sin6(&conn->context->remote)->sin6_port),
conn->seq, conn->ack, recv_mss,
net_tcp_state_str(net_tcp_get_state(conn)));
(*count)++;
}
#if CONFIG_NET_TCP_LOG_LEVEL >= LOG_LEVEL_DBG
static void tcp_sent_list_cb(struct tcp *conn, void *user_data)
{
struct net_shell_user_data *data = user_data;
const struct shell *sh = data->sh;
struct tcp_detail_info *details = data->user_data;
struct net_pkt *pkt;
sys_snode_t *node;
if (conn->state != TCP_LISTEN) {
if (!details->printed_details) {
PR("\nTCP Ref Recv_win Send_win Pending "
"Unacked Flags Queue\n");
details->printed_details = true;
}
PR("%p %ld %u\t %u\t %zd\t %d\t %d/%d/%d %s\n",
conn, atomic_get(&conn->ref_count), conn->recv_win,
conn->send_win, conn->send_data_total, conn->unacked_len,
conn->in_retransmission, conn->in_connect, conn->in_close,
sys_slist_is_empty(&conn->send_queue) ? "empty" : "data");
details->count++;
}
if (sys_slist_is_empty(&conn->send_queue)) {
return;
}
if (!details->printed_send_queue_header) {
PR("\nTCP packets waiting ACK:\n");
PR("TCP net_pkt[ref/totlen]->net_buf[ref/len]..."
"\n");
}
PR("%p ", conn);
node = sys_slist_peek_head(&conn->send_queue);
if (node) {
pkt = CONTAINER_OF(node, struct net_pkt, next);
if (pkt) {
struct net_buf *frag = pkt->frags;
if (!details->printed_send_queue_header) {
PR("%p[%ld/%zd]", pkt,
atomic_get(&pkt->atomic_ref),
net_pkt_get_len(pkt));
details->printed_send_queue_header = true;
} else {
PR(" %p[%ld/%zd]",
pkt, atomic_get(&pkt->atomic_ref),
net_pkt_get_len(pkt));
}
if (frag) {
PR("->");
}
while (frag) {
PR("%p[%d/%d]", frag, frag->ref, frag->len);
frag = frag->frags;
if (frag) {
PR("->");
}
}
PR("\n");
}
}
details->printed_send_queue_header = true;
}
#endif /* CONFIG_NET_TCP_LOG_LEVEL >= LOG_LEVEL_DBG */
#endif /* TCP */
static int cmd_net_conn(const struct shell *sh, size_t argc, char *argv[])
{
ARG_UNUSED(argc);
ARG_UNUSED(argv);
#if defined(CONFIG_NET_OFFLOAD) || defined(CONFIG_NET_NATIVE)
struct net_shell_user_data user_data;
int count = 0;
PR(" Context \tIface Flags Local Remote\n");
user_data.sh = sh;
user_data.user_data = &count;
net_context_foreach(context_cb, &user_data);
if (count == 0) {
PR("No connections\n");
}
#if CONFIG_NET_CONN_LOG_LEVEL >= LOG_LEVEL_DBG
PR("\n Handler Callback \tProto\tLocal \tRemote\n");
count = 0;
net_conn_foreach(conn_handler_cb, &user_data);
if (count == 0) {
PR("No connection handlers found.\n");
}
#endif
#if defined(CONFIG_NET_TCP)
PR("\nTCP Context Src port Dst port "
"Send-Seq Send-Ack MSS State\n");
count = 0;
net_tcp_foreach(tcp_cb, &user_data);
if (count == 0) {
PR("No TCP connections\n");
} else {
#if CONFIG_NET_TCP_LOG_LEVEL >= LOG_LEVEL_DBG
/* Print information about pending packets */
struct tcp_detail_info details;
count = 0;
if (IS_ENABLED(CONFIG_NET_TCP)) {
memset(&details, 0, sizeof(details));
user_data.user_data = &details;
}
net_tcp_foreach(tcp_sent_list_cb, &user_data);
if (IS_ENABLED(CONFIG_NET_TCP)) {
if (details.count == 0) {
PR("No active connections.\n");
}
}
#endif /* CONFIG_NET_TCP_LOG_LEVEL >= LOG_LEVEL_DBG */
}
#if CONFIG_NET_TCP_LOG_LEVEL < LOG_LEVEL_DBG
PR_INFO("Set %s to enable %s support.\n",
"CONFIG_NET_TCP_LOG_LEVEL_DBG", "TCP debugging");
#endif /* CONFIG_NET_TCP_LOG_LEVEL < LOG_LEVEL_DBG */
#endif
#if defined(CONFIG_NET_IPV6_FRAGMENT)
count = 0;
net_ipv6_frag_foreach(ipv6_frag_cb, &user_data);
/* Do not print anything if no fragments are pending atm */
#endif
#else
PR_INFO("Set %s to enable %s support.\n",
"CONFIG_NET_OFFLOAD or CONFIG_NET_NATIVE",
"connection information");
#endif /* CONFIG_NET_OFFLOAD || CONFIG_NET_NATIVE */
return 0;
}
SHELL_SUBCMD_ADD((net), conn, NULL, "Print information about network connections.",
cmd_net_conn, 1, 0);