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