1 /*
2  * Copyright (c) 2023 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_DECLARE(net_shell);
9 
10 #include "net_shell_private.h"
11 #include <zephyr/net/socket.h>
12 #include <zephyr/net/socket_service.h>
13 
14 #if defined(CONFIG_NET_SOCKETS_OBJ_CORE)
15 struct socket_info {
16 	int opened;
17 	int closed;
18 };
19 
walk_sockets(struct k_obj_core * obj_core,void * user_data)20 int walk_sockets(struct k_obj_core *obj_core, void *user_data)
21 {
22 #if defined(CONFIG_THREAD_NAME)
23 #define THREAD_NAME_LEN CONFIG_THREAD_MAX_NAME_LEN
24 #else
25 #define THREAD_NAME_LEN 23
26 #endif
27 	struct sock_obj_type_raw_stats stats = { 0 };
28 	struct net_shell_user_data *data = user_data;
29 	const struct shell *sh = data->sh;
30 	struct socket_info *count = data->user_data;
31 	char thread_name[THREAD_NAME_LEN + 1];
32 	char fd[5] = { 0 };
33 	struct sock_obj *obj;
34 	int lifetime;
35 	int ret;
36 
37 	obj = CONTAINER_OF(obj_core, struct sock_obj, obj_core);
38 
39 	if (k_thread_name_copy(obj->creator, thread_name,
40 			       sizeof(thread_name) - 1) < 0) {
41 		snprintk(thread_name, sizeof(thread_name) - 1, "%p",
42 			 obj->creator);
43 	}
44 
45 	thread_name[sizeof(thread_name) - 1] = '\0';
46 
47 	ret = k_obj_core_stats_raw(K_OBJ_CORE(obj),
48 				   &stats, sizeof(stats));
49 	if (ret != 0) {
50 		PR_INFO("Failed to get statistics (%d)\n", ret);
51 	}
52 
53 	if (obj->fd < 0) {
54 		/* Already closed socket. The create time contains the
55 		 * actual lifetime as calculated in close()
56 		 */
57 		lifetime = obj->create_time;
58 		fd[0] = 'C';
59 		fd[1] = '\0';
60 		count->closed++;
61 	} else {
62 		lifetime = k_ticks_to_ms_ceil32(sys_clock_tick_get() -
63 						obj->create_time);
64 		snprintk(fd, sizeof(fd), "%d", obj->fd);
65 		count->opened++;
66 	}
67 
68 	PR("%25s  %-12s  %c%c%c\t%-5s%-13d   %-10" PRId64 "%-10" PRId64 "\n",
69 	   thread_name, obj->reg->name,
70 	   obj->socket_family == AF_INET6 ? '6' :
71 	   (obj->socket_family == AF_INET ? '4' :
72 	    (obj->socket_family == AF_NET_MGMT ? 'M' : ' ')),
73 	   obj->socket_type == SOCK_DGRAM ? 'D' :
74 	   (obj->socket_type == SOCK_STREAM ? 'S' :
75 	    (obj->socket_type == SOCK_RAW ? 'R' : ' ')),
76 	   obj->socket_proto == IPPROTO_UDP ? 'U' :
77 	   (obj->socket_proto == IPPROTO_TCP ? 'T' : ' '),
78 	   fd, lifetime, stats.sent, stats.received);
79 
80 	return 0;
81 }
82 #endif /* CONFIG_NET_SOCKETS_OBJ_CORE */
83 
84 #if defined(CONFIG_NET_SOCKETS_SERVICE)
85 
86 #if CONFIG_NET_SOCKETS_LOG_LEVEL >= LOG_LEVEL_DBG
87 #define MAX_OWNER_LEN 32
88 #else
89 #define MAX_OWNER_LEN sizeof("<unknown>")
90 #endif
91 
walk_socket_services(const struct net_socket_service_desc * svc,void * user_data)92 static void walk_socket_services(const struct net_socket_service_desc *svc,
93 				 void *user_data)
94 {
95 	struct net_shell_user_data *data = user_data;
96 	const struct shell *sh = data->sh;
97 	int *count = data->user_data;
98 	int len = 0;
99 	static char pev_output[sizeof("xxx,") * CONFIG_ZVFS_POLL_MAX];
100 	static char owner[MAX_OWNER_LEN + 1];
101 
102 	NET_ASSERT(svc->pev != NULL);
103 
104 	for (int i = 0; i < svc->pev_len; i++) {
105 		len += snprintk(pev_output + len, sizeof(pev_output) - len,
106 				"%d,", svc->pev[i].event.fd);
107 	}
108 
109 	if (len > 0) {
110 		pev_output[len - 1] = '\0';
111 	}
112 
113 #if CONFIG_NET_SOCKETS_LOG_LEVEL >= LOG_LEVEL_DBG
114 	len = strlen(svc->owner);
115 
116 	int offset = len > sizeof(owner) ?
117 			len -= (sizeof(owner) - 3) : 0;
118 
119 	snprintk(owner, sizeof(owner), "%s%s",
120 		 offset == 0 ? "" : "...",
121 		 svc->owner + offset + 1);
122 #else
123 	snprintk(owner, sizeof(owner), "<unknown>");
124 #endif
125 
126 	PR("%32s  %-5d %s\n", owner, svc->pev_len, pev_output);
127 
128 	(*count)++;
129 }
130 #endif /* CONFIG_NET_SOCKETS_SERVICE */
131 
cmd_net_sockets(const struct shell * sh,size_t argc,char * argv[])132 static int cmd_net_sockets(const struct shell *sh, size_t argc, char *argv[])
133 {
134 #if defined(CONFIG_NET_SOCKETS_OBJ_CORE)
135 	struct net_shell_user_data user_data;
136 	struct k_obj_type *obj_type;
137 	struct socket_info count = { 0 };
138 
139 	user_data.sh = sh;
140 	user_data.user_data = &count;
141 
142 	PR("%25s  %-12s  %-5s\t%-5s%-14s  %-10s%-10s\n",
143 	   "Creator", "Name", "Flags", "FD", "Lifetime (ms)", "Sent",
144 	   "Received");
145 	PR("\n");
146 
147 	obj_type = k_obj_type_find(K_OBJ_TYPE_SOCK);
148 	if (obj_type != NULL) {
149 		k_obj_type_walk_unlocked(obj_type, walk_sockets, (void *)&user_data);
150 	}
151 
152 	if (count.opened == 0 && count.closed == 0) {
153 		PR("No sockets found.\n");
154 	} else {
155 		if (count.opened > 0) {
156 			PR("\n%d active socket%s found.\n", count.opened,
157 			   count.opened == 1 ? "" : "s");
158 		}
159 
160 		if (count.closed > 0) {
161 			if (count.opened == 0) {
162 				PR("\n");
163 			}
164 
165 			PR("%d closed socket%s found.\n", count.closed,
166 			   count.closed == 1 ? "" : "s");
167 		}
168 	}
169 
170 #if defined(CONFIG_NET_SOCKETS_SERVICE)
171 	PR("\n");
172 #endif
173 #endif
174 
175 #if defined(CONFIG_NET_SOCKETS_SERVICE)
176 	struct net_shell_user_data svc_user_data;
177 	int svc_count = 0;
178 
179 	svc_user_data.sh = sh;
180 	svc_user_data.user_data = &svc_count;
181 
182 	PR("Services:\n");
183 	PR("%32s  %-5s %s\n", "Owner", "Count", "FDs");
184 	PR("\n");
185 
186 	net_socket_service_foreach(walk_socket_services, (void *)&svc_user_data);
187 
188 	if (svc_count == 0) {
189 		PR("No socket services found.\n");
190 	} else {
191 		PR("\n%d socket service%s found.\n", svc_count,
192 		   svc_count == 1 ? "" : "s");
193 	}
194 
195 #if !defined(CONFIG_NET_SOCKETS_OBJ_CORE)
196 	PR("\n");
197 #endif
198 #endif
199 
200 #if !defined(CONFIG_NET_SOCKETS_OBJ_CORE)
201 	ARG_UNUSED(argc);
202 	ARG_UNUSED(argv);
203 
204 	PR_INFO("Set %s to enable %s support.\n",
205 		"CONFIG_OBJ_CORE and CONFIG_NET_SOCKETS_OBJ_CORE",
206 		"socket information");
207 #endif
208 #if !defined(CONFIG_NET_SOCKETS_SERVICE)
209 	ARG_UNUSED(argc);
210 	ARG_UNUSED(argv);
211 
212 	PR_INFO("Socket service not supported.\n");
213 #endif
214 
215 	return 0;
216 }
217 
218 SHELL_SUBCMD_ADD((net), sockets, NULL,
219 		 "Show network sockets.",
220 		 cmd_net_sockets, 1, 0);
221