1 /*
2  * Copyright (c) 2019-2020, Prevas A/S
3  * Copyright (c) 2022-2023 Nordic Semiconductor ASA
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 /**
9  * @file
10  * @brief UDP transport for the mcumgr SMP protocol.
11  */
12 
13 #include <assert.h>
14 #include <zephyr/kernel.h>
15 #include <zephyr/init.h>
16 #if defined(CONFIG_POSIX_API)
17 #include <zephyr/posix/unistd.h>
18 #include <zephyr/posix/sys/socket.h>
19 #else
20 #include <zephyr/net/socket.h>
21 #endif
22 #include <zephyr/mgmt/mcumgr/mgmt/mgmt.h>
23 #include <zephyr/mgmt/mcumgr/smp/smp.h>
24 #include <zephyr/mgmt/mcumgr/transport/smp.h>
25 #include <zephyr/mgmt/mcumgr/transport/smp_udp.h>
26 #include <zephyr/mgmt/mcumgr/mgmt/handlers.h>
27 #include <zephyr/net/net_mgmt.h>
28 #include <zephyr/net/net_event.h>
29 #include <zephyr/net/conn_mgr_monitor.h>
30 #include <errno.h>
31 
32 #include <mgmt/mcumgr/transport/smp_internal.h>
33 
34 #define LOG_LEVEL CONFIG_MCUMGR_LOG_LEVEL
35 #include <zephyr/logging/log.h>
36 LOG_MODULE_REGISTER(smp_udp);
37 
38 BUILD_ASSERT(CONFIG_MCUMGR_TRANSPORT_UDP_MTU != 0, "CONFIG_MCUMGR_TRANSPORT_UDP_MTU must be > 0");
39 
40 #if !defined(CONFIG_MCUMGR_TRANSPORT_UDP_IPV4) && !defined(CONFIG_MCUMGR_TRANSPORT_UDP_IPV6)
41 BUILD_ASSERT(0, "Either IPv4 or IPv6 SMP must be enabled for the MCUmgr UDP SMP transport using "
42 		"CONFIG_MCUMGR_TRANSPORT_UDP_IPV4 or CONFIG_MCUMGR_TRANSPORT_UDP_IPV6");
43 #endif
44 
45 #define IS_THREAD_RUNNING(thread)					\
46 	(thread.base.thread_state & (_THREAD_PENDING |			\
47 				     _THREAD_PRESTART |			\
48 				     _THREAD_SUSPENDED |		\
49 				     _THREAD_QUEUED) ? true : false)
50 
51 enum proto_type {
52 	PROTOCOL_IPV4 = 0,
53 	PROTOCOL_IPV6,
54 };
55 
56 struct config {
57 	int sock;
58 	enum proto_type proto;
59 	struct k_sem network_ready_sem;
60 	struct smp_transport smp_transport;
61 	char recv_buffer[CONFIG_MCUMGR_TRANSPORT_UDP_MTU];
62 	struct k_thread thread;
63 	K_KERNEL_STACK_MEMBER(stack, CONFIG_MCUMGR_TRANSPORT_UDP_STACK_SIZE);
64 };
65 
66 struct configs {
67 #ifdef CONFIG_MCUMGR_TRANSPORT_UDP_IPV4
68 	struct config ipv4;
69 #ifdef CONFIG_SMP_CLIENT
70 	struct smp_client_transport_entry ipv4_transport;
71 #endif
72 #endif
73 #ifdef CONFIG_MCUMGR_TRANSPORT_UDP_IPV6
74 	struct config ipv6;
75 #ifdef CONFIG_SMP_CLIENT
76 	struct smp_client_transport_entry ipv6_transport;
77 #endif
78 #endif
79 };
80 
81 static struct configs configs = {
82 #ifdef CONFIG_MCUMGR_TRANSPORT_UDP_IPV4
83 	.ipv4 = {
84 		.proto = PROTOCOL_IPV4,
85 		.sock  = -1,
86 	},
87 #endif
88 #ifdef CONFIG_MCUMGR_TRANSPORT_UDP_IPV6
89 	.ipv6 = {
90 		.proto = PROTOCOL_IPV6,
91 		.sock  = -1,
92 	},
93 #endif
94 };
95 
96 static struct net_mgmt_event_callback smp_udp_mgmt_cb;
97 
smp_udp_proto_to_name(enum proto_type proto)98 static const char *smp_udp_proto_to_name(enum proto_type proto)
99 {
100 #ifdef CONFIG_MCUMGR_TRANSPORT_UDP_IPV4
101 	if (proto == PROTOCOL_IPV4) {
102 		return "IPv4";
103 	}
104 #endif
105 
106 #ifdef CONFIG_MCUMGR_TRANSPORT_UDP_IPV6
107 	if (proto == PROTOCOL_IPV6) {
108 		return "IPv6";
109 	}
110 #endif
111 
112 	return "??";
113 }
114 
115 #ifdef CONFIG_MCUMGR_TRANSPORT_UDP_IPV4
smp_udp4_tx(struct net_buf * nb)116 static int smp_udp4_tx(struct net_buf *nb)
117 {
118 	int ret;
119 	struct sockaddr *addr = net_buf_user_data(nb);
120 
121 	ret = sendto(configs.ipv4.sock, nb->data, nb->len, 0, addr, sizeof(*addr));
122 
123 	if (ret < 0) {
124 		ret = MGMT_ERR_EINVAL;
125 	} else {
126 		ret = MGMT_ERR_EOK;
127 	}
128 
129 	smp_packet_free(nb);
130 
131 	return ret;
132 }
133 #endif
134 
135 #ifdef CONFIG_MCUMGR_TRANSPORT_UDP_IPV6
smp_udp6_tx(struct net_buf * nb)136 static int smp_udp6_tx(struct net_buf *nb)
137 {
138 	int ret;
139 	struct sockaddr *addr = net_buf_user_data(nb);
140 
141 	ret = sendto(configs.ipv6.sock, nb->data, nb->len, 0, addr, sizeof(*addr));
142 
143 	if (ret < 0) {
144 		ret = MGMT_ERR_EINVAL;
145 	} else {
146 		ret = MGMT_ERR_EOK;
147 	}
148 
149 	smp_packet_free(nb);
150 
151 	return ret;
152 }
153 #endif
154 
smp_udp_get_mtu(const struct net_buf * nb)155 static uint16_t smp_udp_get_mtu(const struct net_buf *nb)
156 {
157 	ARG_UNUSED(nb);
158 
159 	return CONFIG_MCUMGR_TRANSPORT_UDP_MTU;
160 }
161 
smp_udp_ud_copy(struct net_buf * dst,const struct net_buf * src)162 static int smp_udp_ud_copy(struct net_buf *dst, const struct net_buf *src)
163 {
164 	struct sockaddr *src_ud = net_buf_user_data(src);
165 	struct sockaddr *dst_ud = net_buf_user_data(dst);
166 
167 	net_ipaddr_copy(dst_ud, src_ud);
168 
169 	return MGMT_ERR_EOK;
170 }
171 
create_socket(enum proto_type proto,int * sock)172 static int create_socket(enum proto_type proto, int *sock)
173 {
174 	int tmp_sock;
175 	int err;
176 	struct sockaddr *addr;
177 
178 #ifdef CONFIG_MCUMGR_TRANSPORT_UDP_IPV4
179 	struct sockaddr_in addr4;
180 #endif
181 
182 #ifdef CONFIG_MCUMGR_TRANSPORT_UDP_IPV6
183 	struct sockaddr_in6 addr6;
184 #endif
185 
186 #ifdef CONFIG_MCUMGR_TRANSPORT_UDP_IPV4
187 	if (proto == PROTOCOL_IPV4) {
188 		memset(&addr4, 0, sizeof(addr4));
189 		addr4.sin_family = AF_INET;
190 		addr4.sin_port = htons(CONFIG_MCUMGR_TRANSPORT_UDP_PORT);
191 		addr4.sin_addr.s_addr = htonl(INADDR_ANY);
192 		addr = (struct sockaddr *)&addr4;
193 	}
194 #endif
195 
196 #ifdef CONFIG_MCUMGR_TRANSPORT_UDP_IPV6
197 	if (proto == PROTOCOL_IPV6) {
198 		memset(&addr6, 0, sizeof(addr6));
199 		addr6.sin6_family = AF_INET6;
200 		addr6.sin6_port = htons(CONFIG_MCUMGR_TRANSPORT_UDP_PORT);
201 		addr6.sin6_addr = in6addr_any;
202 		addr = (struct sockaddr *)&addr6;
203 	}
204 #endif
205 
206 	tmp_sock = socket(addr->sa_family, SOCK_DGRAM, IPPROTO_UDP);
207 	err = errno;
208 
209 	if (tmp_sock < 0) {
210 		LOG_ERR("Could not open receive socket (%s), err: %i",
211 			smp_udp_proto_to_name(proto), err);
212 
213 		return -err;
214 	}
215 
216 	if (bind(tmp_sock, addr, sizeof(*addr)) < 0) {
217 		err = errno;
218 		LOG_ERR("Could not bind to receive socket (%s), err: %i",
219 			smp_udp_proto_to_name(proto), err);
220 
221 		close(tmp_sock);
222 
223 		return -err;
224 	}
225 
226 	*sock = tmp_sock;
227 	return 0;
228 }
229 
smp_udp_receive_thread(void * p1,void * p2,void * p3)230 static void smp_udp_receive_thread(void *p1, void *p2, void *p3)
231 {
232 	struct config *conf = (struct config *)p1;
233 	int rc;
234 
235 	ARG_UNUSED(p2);
236 	ARG_UNUSED(p3);
237 
238 	(void)k_sem_take(&conf->network_ready_sem, K_FOREVER);
239 	rc = create_socket(conf->proto, &conf->sock);
240 
241 	if (rc < 0) {
242 		return;
243 	}
244 
245 	__ASSERT(rc >= 0, "Socket is invalid");
246 	LOG_INF("Started (%s)", smp_udp_proto_to_name(conf->proto));
247 
248 	while (1) {
249 		struct sockaddr addr;
250 		socklen_t addr_len = sizeof(addr);
251 
252 		int len = recvfrom(conf->sock, conf->recv_buffer,
253 				   CONFIG_MCUMGR_TRANSPORT_UDP_MTU,
254 				   0, &addr, &addr_len);
255 
256 		if (len > 0) {
257 			struct sockaddr *ud;
258 			struct net_buf *nb;
259 
260 			/* Store sender address in user data for reply */
261 			nb = smp_packet_alloc();
262 			if (!nb) {
263 				LOG_ERR("Failed to allocate mcumgr buffer");
264 				/* No free space, drop SMP frame */
265 				continue;
266 			}
267 			net_buf_add_mem(nb, conf->recv_buffer, len);
268 			ud = net_buf_user_data(nb);
269 			net_ipaddr_copy(ud, &addr);
270 
271 			smp_rx_req(&conf->smp_transport, nb);
272 		} else if (len < 0) {
273 			LOG_ERR("recvfrom error (%s): %i, %d", smp_udp_proto_to_name(conf->proto),
274 				errno, len);
275 		}
276 	}
277 }
278 
smp_udp_net_event_handler(struct net_mgmt_event_callback * cb,uint32_t mgmt_event,struct net_if * iface)279 static void smp_udp_net_event_handler(struct net_mgmt_event_callback *cb, uint32_t mgmt_event,
280 				      struct net_if *iface)
281 {
282 	ARG_UNUSED(cb);
283 	ARG_UNUSED(iface);
284 
285 	if (mgmt_event == NET_EVENT_L4_CONNECTED) {
286 		LOG_INF("Network connected");
287 #ifdef CONFIG_MCUMGR_TRANSPORT_UDP_IPV4
288 		if (IS_THREAD_RUNNING(configs.ipv4.thread)) {
289 			k_sem_give(&configs.ipv4.network_ready_sem);
290 		}
291 #endif
292 
293 #ifdef CONFIG_MCUMGR_TRANSPORT_UDP_IPV6
294 		if (IS_THREAD_RUNNING(configs.ipv6.thread)) {
295 			k_sem_give(&configs.ipv6.network_ready_sem);
296 		}
297 #endif
298 	} else if (mgmt_event == NET_EVENT_L4_DISCONNECTED) {
299 		LOG_INF("Network disconnected");
300 	}
301 }
302 
create_thread(struct config * conf,const char * name)303 static void create_thread(struct config *conf, const char *name)
304 {
305 	k_thread_create(&(conf->thread), conf->stack,
306 			K_KERNEL_STACK_SIZEOF(conf->stack),
307 			smp_udp_receive_thread, conf, NULL, NULL,
308 			CONFIG_MCUMGR_TRANSPORT_UDP_THREAD_PRIO, 0, K_FOREVER);
309 
310 	k_thread_name_set(&(conf->thread), name);
311 	k_thread_start(&(conf->thread));
312 }
313 
smp_udp_open(void)314 int smp_udp_open(void)
315 {
316 	bool started = false;
317 
318 #ifdef CONFIG_MCUMGR_TRANSPORT_UDP_IPV4
319 	if (!IS_THREAD_RUNNING(configs.ipv4.thread)) {
320 		(void)k_sem_reset(&configs.ipv4.network_ready_sem);
321 		create_thread(&configs.ipv4, "smp_udp4");
322 		started = true;
323 	} else {
324 		LOG_ERR("IPv4 UDP MCUmgr thread is already running");
325 	}
326 #endif
327 
328 #ifdef CONFIG_MCUMGR_TRANSPORT_UDP_IPV6
329 	if (!IS_THREAD_RUNNING(configs.ipv6.thread)) {
330 		(void)k_sem_reset(&configs.ipv6.network_ready_sem);
331 		create_thread(&configs.ipv6, "smp_udp6");
332 		started = true;
333 	} else {
334 		LOG_ERR("IPv6 UDP MCUmgr thread is already running");
335 	}
336 #endif
337 
338 	if (started) {
339 		/* One or more threads were started, send interface notifications */
340 		conn_mgr_mon_resend_status();
341 	}
342 
343 	return 0;
344 }
345 
smp_udp_close(void)346 int smp_udp_close(void)
347 {
348 #ifdef CONFIG_MCUMGR_TRANSPORT_UDP_IPV4
349 	if (IS_THREAD_RUNNING(configs.ipv4.thread)) {
350 		k_thread_abort(&(configs.ipv4.thread));
351 
352 		if (configs.ipv4.sock >= 0) {
353 			close(configs.ipv4.sock);
354 			configs.ipv4.sock = -1;
355 		}
356 	} else {
357 		LOG_ERR("IPv4 UDP MCUmgr thread is not running");
358 	}
359 #endif
360 
361 #ifdef CONFIG_MCUMGR_TRANSPORT_UDP_IPV6
362 	if (IS_THREAD_RUNNING(configs.ipv6.thread)) {
363 		k_thread_abort(&(configs.ipv6.thread));
364 
365 		if (configs.ipv6.sock >= 0) {
366 			close(configs.ipv6.sock);
367 			configs.ipv6.sock = -1;
368 		}
369 	} else {
370 		LOG_ERR("IPv6 UDP MCUmgr thread is not running");
371 	}
372 #endif
373 
374 	return 0;
375 }
376 
smp_udp_start(void)377 static void smp_udp_start(void)
378 {
379 	int rc;
380 
381 #ifdef CONFIG_MCUMGR_TRANSPORT_UDP_IPV4
382 	k_sem_init(&configs.ipv4.network_ready_sem, 0, 1);
383 	configs.ipv4.smp_transport.functions.output = smp_udp4_tx;
384 	configs.ipv4.smp_transport.functions.get_mtu = smp_udp_get_mtu;
385 	configs.ipv4.smp_transport.functions.ud_copy = smp_udp_ud_copy;
386 
387 	rc = smp_transport_init(&configs.ipv4.smp_transport);
388 #ifdef CONFIG_SMP_CLIENT
389 	if (rc == 0) {
390 		configs.ipv4_transport.smpt = &configs.ipv4.smp_transport;
391 		configs.ipv4_transport.smpt_type = SMP_UDP_IPV4_TRANSPORT;
392 		smp_client_transport_register(&configs.ipv4_transport);
393 	}
394 #endif
395 	if (rc) {
396 		LOG_ERR("Failed to register IPv4 UDP MCUmgr SMP transport: %d", rc);
397 	}
398 #endif
399 
400 #ifdef CONFIG_MCUMGR_TRANSPORT_UDP_IPV6
401 	k_sem_init(&configs.ipv6.network_ready_sem, 0, 1);
402 	configs.ipv6.smp_transport.functions.output = smp_udp6_tx;
403 	configs.ipv6.smp_transport.functions.get_mtu = smp_udp_get_mtu;
404 	configs.ipv6.smp_transport.functions.ud_copy = smp_udp_ud_copy;
405 
406 	rc = smp_transport_init(&configs.ipv6.smp_transport);
407 #ifdef CONFIG_SMP_CLIENT
408 	if (rc == 0) {
409 		configs.ipv6_transport.smpt = &configs.ipv6.smp_transport;
410 		configs.ipv6_transport.smpt_type = SMP_UDP_IPV6_TRANSPORT;
411 		smp_client_transport_register(&configs.ipv6_transport);
412 	}
413 #endif
414 
415 	if (rc) {
416 		LOG_ERR("Failed to register IPv6 UDP MCUmgr SMP transport: %d", rc);
417 	}
418 #endif
419 
420 	net_mgmt_init_event_callback(&smp_udp_mgmt_cb, smp_udp_net_event_handler,
421 				     (NET_EVENT_L4_CONNECTED | NET_EVENT_L4_DISCONNECTED));
422 	net_mgmt_add_event_callback(&smp_udp_mgmt_cb);
423 
424 #ifdef CONFIG_MCUMGR_TRANSPORT_UDP_AUTOMATIC_INIT
425 	smp_udp_open();
426 #endif
427 }
428 
429 MCUMGR_HANDLER_DEFINE(smp_udp, smp_udp_start);
430