1 /*
2 * Copyright (c) 2018-2021 mcumgr authors
3 * Copyright (c) 2022-2023 Nordic Semiconductor ASA
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 /** SMP - Simple Management Protocol. */
9
10 #include <zephyr/sys/byteorder.h>
11 #include <zephyr/net/buf.h>
12 #include <zephyr/mgmt/mcumgr/mgmt/mgmt.h>
13 #include <zephyr/mgmt/mcumgr/smp/smp.h>
14 #include <zephyr/mgmt/mcumgr/smp/smp_client.h>
15 #include <zephyr/mgmt/mcumgr/transport/smp.h>
16 #include <assert.h>
17 #include <string.h>
18
19 #include <zcbor_common.h>
20 #include <zcbor_decode.h>
21 #include <zcbor_encode.h>
22
23 #include <mgmt/mcumgr/transport/smp_internal.h>
24
25 #ifdef CONFIG_MCUMGR_MGMT_NOTIFICATION_HOOKS
26 #include <zephyr/mgmt/mcumgr/mgmt/callbacks.h>
27 #endif
28
29 #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
30 /*
31 * @brief Translate SMP version 2 error code to legacy SMP version 1 MCUmgr error code.
32 *
33 * @param group #mcumgr_group_t group ID
34 * @param err Group-specific error code
35 *
36 * @return #mcumgr_err_t error code
37 */
smp_translate_error_code(uint16_t group,uint16_t err)38 static int smp_translate_error_code(uint16_t group, uint16_t err)
39 {
40 smp_translate_error_fn translate_error_function = NULL;
41
42 translate_error_function = mgmt_find_error_translation_function(group);
43
44 if (translate_error_function == NULL) {
45 return MGMT_ERR_EUNKNOWN;
46 }
47
48 return translate_error_function(err);
49 }
50 #endif
51
cbor_nb_reader_init(struct cbor_nb_reader * cnr,struct net_buf * nb)52 static void cbor_nb_reader_init(struct cbor_nb_reader *cnr, struct net_buf *nb)
53 {
54 cnr->nb = nb;
55 zcbor_new_decode_state(cnr->zs, ARRAY_SIZE(cnr->zs), nb->data,
56 nb->len, 1, NULL, 0);
57 }
58
cbor_nb_writer_init(struct cbor_nb_writer * cnw,struct net_buf * nb)59 static void cbor_nb_writer_init(struct cbor_nb_writer *cnw, struct net_buf *nb)
60 {
61 net_buf_reset(nb);
62 cnw->nb = nb;
63 cnw->nb->len = sizeof(struct smp_hdr);
64 zcbor_new_encode_state(cnw->zs, ARRAY_SIZE(cnw->zs), nb->data + sizeof(struct smp_hdr),
65 net_buf_tailroom(nb), 0);
66 }
67
68 /**
69 * Converts a request opcode to its corresponding response opcode.
70 */
smp_rsp_op(uint8_t req_op)71 static uint8_t smp_rsp_op(uint8_t req_op)
72 {
73 if (req_op == MGMT_OP_READ) {
74 return MGMT_OP_READ_RSP;
75 } else {
76 return MGMT_OP_WRITE_RSP;
77 }
78 }
79
smp_make_rsp_hdr(const struct smp_hdr * req_hdr,struct smp_hdr * rsp_hdr,size_t len)80 static void smp_make_rsp_hdr(const struct smp_hdr *req_hdr, struct smp_hdr *rsp_hdr, size_t len)
81 {
82 *rsp_hdr = (struct smp_hdr) {
83 .nh_len = sys_cpu_to_be16(len),
84 .nh_flags = 0,
85 .nh_op = smp_rsp_op(req_hdr->nh_op),
86 .nh_group = sys_cpu_to_be16(req_hdr->nh_group),
87 .nh_seq = req_hdr->nh_seq,
88 .nh_id = req_hdr->nh_id,
89 .nh_version = (req_hdr->nh_version > SMP_MCUMGR_VERSION_2 ? SMP_MCUMGR_VERSION_2 :
90 req_hdr->nh_version),
91 };
92 }
93
smp_read_hdr(const struct net_buf * nb,struct smp_hdr * dst_hdr)94 static int smp_read_hdr(const struct net_buf *nb, struct smp_hdr *dst_hdr)
95 {
96 if (nb->len < sizeof(*dst_hdr)) {
97 return MGMT_ERR_EINVAL;
98 }
99
100 memcpy(dst_hdr, nb->data, sizeof(*dst_hdr));
101 dst_hdr->nh_len = sys_be16_to_cpu(dst_hdr->nh_len);
102 dst_hdr->nh_group = sys_be16_to_cpu(dst_hdr->nh_group);
103
104 return 0;
105 }
106
smp_write_hdr(struct smp_streamer * streamer,const struct smp_hdr * src_hdr)107 static inline int smp_write_hdr(struct smp_streamer *streamer, const struct smp_hdr *src_hdr)
108 {
109 memcpy(streamer->writer->nb->data, src_hdr, sizeof(*src_hdr));
110 return 0;
111 }
112
smp_build_err_rsp(struct smp_streamer * streamer,const struct smp_hdr * req_hdr,int status,const char * rc_rsn)113 static int smp_build_err_rsp(struct smp_streamer *streamer, const struct smp_hdr *req_hdr,
114 int status, const char *rc_rsn)
115 {
116 struct smp_hdr rsp_hdr;
117 struct cbor_nb_writer *nbw = streamer->writer;
118 zcbor_state_t *zsp = nbw->zs;
119 bool ok;
120
121 ok = zcbor_map_start_encode(zsp, 2) &&
122 zcbor_tstr_put_lit(zsp, "rc") &&
123 zcbor_int32_put(zsp, status);
124
125 #ifdef CONFIG_MCUMGR_SMP_VERBOSE_ERR_RESPONSE
126 if (ok && rc_rsn != NULL) {
127 ok = zcbor_tstr_put_lit(zsp, "rsn") &&
128 zcbor_tstr_put_term(zsp, rc_rsn, CONFIG_ZCBOR_MAX_STR_LEN);
129 }
130 #else
131 ARG_UNUSED(rc_rsn);
132 #endif
133 ok &= zcbor_map_end_encode(zsp, 2);
134
135 if (!ok) {
136 return MGMT_ERR_EMSGSIZE;
137 }
138
139 smp_make_rsp_hdr(req_hdr, &rsp_hdr,
140 zsp->payload_mut - nbw->nb->data - MGMT_HDR_SIZE);
141 nbw->nb->len = zsp->payload_mut - nbw->nb->data;
142 smp_write_hdr(streamer, &rsp_hdr);
143
144 return 0;
145 }
146
147 /**
148 * Processes a single SMP request and generates a response payload (i.e.,
149 * everything after the management header). On success, the response payload
150 * is written to the supplied cbuf but not transmitted. On failure, no error
151 * response gets written; the caller is expected to build an error response
152 * from the return code.
153 *
154 * @param cbuf A cbuf containing the request and response buffer.
155 * @param req_hdr The management header belonging to the incoming request (host-byte order).
156 *
157 * @return A MGMT_ERR_[...] error code.
158 */
smp_handle_single_payload(struct smp_streamer * cbuf,const struct smp_hdr * req_hdr)159 static int smp_handle_single_payload(struct smp_streamer *cbuf, const struct smp_hdr *req_hdr)
160 {
161 const struct mgmt_group *group;
162 const struct mgmt_handler *handler;
163 mgmt_handler_fn handler_fn;
164 int rc;
165 #if defined(CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS)
166 enum mgmt_cb_return status;
167 struct mgmt_evt_op_cmd_arg cmd_recv;
168 int32_t err_rc;
169 uint16_t err_group;
170 #endif
171
172 group = mgmt_find_group(req_hdr->nh_group);
173 if (group == NULL) {
174 return MGMT_ERR_ENOTSUP;
175 }
176
177 handler = mgmt_get_handler(group, req_hdr->nh_id);
178 if (handler == NULL) {
179 return MGMT_ERR_ENOTSUP;
180 }
181
182 switch (req_hdr->nh_op) {
183 case MGMT_OP_READ:
184 handler_fn = handler->mh_read;
185 break;
186
187 case MGMT_OP_WRITE:
188 handler_fn = handler->mh_write;
189 break;
190
191 default:
192 return MGMT_ERR_EINVAL;
193 }
194
195 if (handler_fn) {
196 bool ok;
197
198 #if defined(CONFIG_MCUMGR_MGMT_CUSTOM_PAYLOAD)
199 if (!group->custom_payload) {
200 #endif
201 ok = zcbor_map_start_encode(cbuf->writer->zs,
202 CONFIG_MCUMGR_SMP_CBOR_MAX_MAIN_MAP_ENTRIES);
203
204 MGMT_CTXT_SET_RC_RSN(cbuf, NULL);
205
206 if (!ok) {
207 return MGMT_ERR_EMSGSIZE;
208 }
209 #if defined(CONFIG_MCUMGR_MGMT_CUSTOM_PAYLOAD)
210 }
211 #endif
212
213 #if defined(CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS)
214 cmd_recv.group = req_hdr->nh_group;
215 cmd_recv.id = req_hdr->nh_id;
216 cmd_recv.op = req_hdr->nh_op;
217
218 /* Send request to application to check if handler should run or not. */
219 status = mgmt_callback_notify(MGMT_EVT_OP_CMD_RECV, &cmd_recv, sizeof(cmd_recv),
220 &err_rc, &err_group);
221
222 /* Skip running the command if a handler reported an error and return that
223 * instead.
224 */
225 if (status != MGMT_CB_OK) {
226 if (status == MGMT_CB_ERROR_RC) {
227 rc = err_rc;
228 } else {
229 ok = smp_add_cmd_err(cbuf->writer->zs, err_group,
230 (uint16_t)err_rc);
231
232 rc = (ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE);
233 }
234
235 goto end;
236 }
237 #endif
238
239 rc = handler_fn(cbuf);
240
241 #if defined(CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS)
242 end:
243 #endif
244 #if defined(CONFIG_MCUMGR_MGMT_CUSTOM_PAYLOAD)
245 if (!group->custom_payload) {
246 #endif
247 /* End response payload. */
248 if (!zcbor_map_end_encode(cbuf->writer->zs,
249 CONFIG_MCUMGR_SMP_CBOR_MAX_MAIN_MAP_ENTRIES) &&
250 rc == 0) {
251 rc = MGMT_ERR_EMSGSIZE;
252 }
253 #if defined(CONFIG_MCUMGR_MGMT_CUSTOM_PAYLOAD)
254 }
255 #endif
256 } else {
257 rc = MGMT_ERR_ENOTSUP;
258 }
259
260 return rc;
261 }
262
263 /**
264 * Processes a single SMP request and generates a complete response (i.e.,
265 * header and payload). On success, the response is written using the supplied
266 * streamer but not transmitted. On failure, no error response gets written;
267 * the caller is expected to build an error response from the return code.
268 *
269 * @param streamer The SMP streamer to use for reading the request and writing the response.
270 * @param req_hdr The management header belonging to the incoming request (host-byte order).
271 *
272 * @return A MGMT_ERR_[...] error code.
273 */
smp_handle_single_req(struct smp_streamer * streamer,const struct smp_hdr * req_hdr,const char ** rsn)274 static int smp_handle_single_req(struct smp_streamer *streamer, const struct smp_hdr *req_hdr,
275 const char **rsn)
276 {
277 struct smp_hdr rsp_hdr;
278 struct cbor_nb_writer *nbw = streamer->writer;
279 zcbor_state_t *zsp = nbw->zs;
280 int rc;
281
282 #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
283 nbw->error_group = 0;
284 nbw->error_ret = 0;
285 #else
286 if (req_hdr->nh_version == SMP_MCUMGR_VERSION_1) {
287 /* Support for the original version is excluded in this build */
288 return MGMT_ERR_UNSUPPORTED_TOO_OLD;
289 }
290 #endif
291
292 /* We do not currently support future versions of the protocol */
293 if (req_hdr->nh_version > SMP_MCUMGR_VERSION_2) {
294 return MGMT_ERR_UNSUPPORTED_TOO_NEW;
295 }
296
297 /* Process the request and write the response payload. */
298 rc = smp_handle_single_payload(streamer, req_hdr);
299 if (rc != 0) {
300 *rsn = MGMT_CTXT_RC_RSN(streamer);
301 return rc;
302 }
303
304 #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
305 /* If using the legacy protocol, translate the error code to a return code */
306 if (nbw->error_ret != 0 && req_hdr->nh_version == 0) {
307 rc = smp_translate_error_code(nbw->error_group, nbw->error_ret);
308 *rsn = MGMT_CTXT_RC_RSN(streamer);
309 return rc;
310 }
311 #endif
312
313 smp_make_rsp_hdr(req_hdr, &rsp_hdr,
314 zsp->payload_mut - nbw->nb->data - MGMT_HDR_SIZE);
315 nbw->nb->len = zsp->payload_mut - nbw->nb->data;
316 smp_write_hdr(streamer, &rsp_hdr);
317
318 return 0;
319 }
320
321 /**
322 * Attempts to transmit an SMP error response. This function consumes both
323 * supplied buffers.
324 *
325 * @param streamer The SMP streamer for building and transmitting the response.
326 * @param req_hdr The header of the request which elicited the error.
327 * @param req The buffer holding the request.
328 * @param rsp The buffer holding the response, or NULL if none was allocated.
329 * @param status The status to indicate in the error response.
330 * @param rsn The text explanation to @status encoded as "rsn" into CBOR
331 * response.
332 */
smp_on_err(struct smp_streamer * streamer,const struct smp_hdr * req_hdr,void * req,void * rsp,int status,const char * rsn)333 static void smp_on_err(struct smp_streamer *streamer, const struct smp_hdr *req_hdr,
334 void *req, void *rsp, int status, const char *rsn)
335 {
336 int rc;
337
338 /* Prefer the response buffer for holding the error response. If no
339 * response buffer was allocated, use the request buffer instead.
340 */
341 if (rsp == NULL) {
342 rsp = req;
343 req = NULL;
344 }
345
346 /* Clear the partial response from the buffer, if any. */
347 cbor_nb_writer_init(streamer->writer, rsp);
348
349 /* Build and transmit the error response. */
350 rc = smp_build_err_rsp(streamer, req_hdr, status, rsn);
351 if (rc == 0) {
352 streamer->smpt->functions.output(rsp);
353 rsp = NULL;
354 }
355
356 /* Free any extra buffers. */
357 smp_free_buf(req, streamer->smpt);
358 smp_free_buf(rsp, streamer->smpt);
359 }
360
361 /**
362 * Processes all SMP requests in an incoming packet. Requests are processed
363 * sequentially from the start of the packet to the end. Each response is sent
364 * individually in its own packet. If a request elicits an error response,
365 * processing of the packet is aborted. This function consumes the supplied
366 * request buffer regardless of the outcome.
367 * The function will return MGMT_ERR_EOK (0) when given an empty input stream,
368 * and will also release the buffer from the stream; it does not return
369 * MTMT_ERR_ECORRUPT, or any other MGMT error, because there was no error while
370 * processing of the input stream, it is callers fault that an empty stream has
371 * been passed to the function.
372 *
373 * @param streamer The streamer to use for reading, writing, and transmitting.
374 * @param req A buffer containing the request packet.
375 *
376 * @return 0 on success or when input stream is empty;
377 * MGMT_ERR_ECORRUPT if buffer starts with non SMP data header or there
378 * is not enough bytes to process header, or other MGMT_ERR_[...] code on
379 * failure.
380 */
smp_process_request_packet(struct smp_streamer * streamer,void * vreq)381 int smp_process_request_packet(struct smp_streamer *streamer, void *vreq)
382 {
383 struct smp_hdr req_hdr = { 0 };
384 void *rsp;
385 struct net_buf *req = vreq;
386 bool valid_hdr = false;
387 bool handler_found = false;
388 int rc = 0;
389 const char *rsn = NULL;
390
391 #if defined(CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS)
392 struct mgmt_evt_op_cmd_arg cmd_done_arg;
393 int32_t err_rc;
394 uint16_t err_group;
395 #endif
396
397 rsp = NULL;
398
399 while (req->len > 0) {
400 handler_found = false;
401 valid_hdr = false;
402
403 /* Read the management header and strip it from the request. */
404 rc = smp_read_hdr(req, &req_hdr);
405 if (rc != 0) {
406 rc = MGMT_ERR_ECORRUPT;
407 break;
408 }
409
410 valid_hdr = true;
411 /* Skip the smp_hdr */
412 net_buf_pull(req, sizeof(struct smp_hdr));
413 /* Does buffer contain whole message? */
414 if (req->len < req_hdr.nh_len) {
415 rc = MGMT_ERR_ECORRUPT;
416 break;
417 }
418
419 if (req_hdr.nh_op == MGMT_OP_READ || req_hdr.nh_op == MGMT_OP_WRITE) {
420 rsp = smp_alloc_rsp(req, streamer->smpt);
421 if (rsp == NULL) {
422 rc = MGMT_ERR_ENOMEM;
423 break;
424 }
425
426 cbor_nb_reader_init(streamer->reader, req);
427 cbor_nb_writer_init(streamer->writer, rsp);
428
429 /* Process the request payload and build the response. */
430 rc = smp_handle_single_req(streamer, &req_hdr, &rsn);
431 handler_found = (rc != MGMT_ERR_ENOTSUP);
432 if (rc != 0) {
433 break;
434 }
435
436 /* Send the response. */
437 rc = streamer->smpt->functions.output(rsp);
438 rsp = NULL;
439 } else if (IS_ENABLED(CONFIG_SMP_CLIENT) && (req_hdr.nh_op == MGMT_OP_READ_RSP ||
440 req_hdr.nh_op == MGMT_OP_WRITE_RSP)) {
441 rc = smp_client_single_response(req, &req_hdr);
442
443 if (rc == MGMT_ERR_EOK) {
444 handler_found = true;
445 } else {
446 /* Server shuold not send error response for response */
447 valid_hdr = false;
448 }
449
450 } else {
451 rc = MGMT_ERR_ENOTSUP;
452 }
453
454 if (rc != 0) {
455 break;
456 }
457 /* Trim processed request to free up space for subsequent responses. */
458 net_buf_pull(req, req_hdr.nh_len);
459
460 #if defined(CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS)
461 cmd_done_arg.group = req_hdr.nh_group;
462 cmd_done_arg.id = req_hdr.nh_id;
463 cmd_done_arg.err = MGMT_ERR_EOK;
464
465 (void)mgmt_callback_notify(MGMT_EVT_OP_CMD_DONE, &cmd_done_arg,
466 sizeof(cmd_done_arg), &err_rc, &err_group);
467 #endif
468 }
469
470 if (rc != 0 && valid_hdr) {
471 smp_on_err(streamer, &req_hdr, req, rsp, rc, rsn);
472
473 if (handler_found) {
474 #if defined(CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS)
475 cmd_done_arg.group = req_hdr.nh_group;
476 cmd_done_arg.id = req_hdr.nh_id;
477 cmd_done_arg.err = rc;
478
479 (void)mgmt_callback_notify(MGMT_EVT_OP_CMD_DONE, &cmd_done_arg,
480 sizeof(cmd_done_arg), &err_rc, &err_group);
481 #endif
482 }
483
484 return rc;
485 }
486
487 smp_free_buf(req, streamer->smpt);
488 smp_free_buf(rsp, streamer->smpt);
489
490 return rc;
491 }
492
smp_add_cmd_err(zcbor_state_t * zse,uint16_t group,uint16_t ret)493 bool smp_add_cmd_err(zcbor_state_t *zse, uint16_t group, uint16_t ret)
494 {
495 bool ok = true;
496
497 if (ret != 0) {
498 #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
499 struct cbor_nb_writer *container = CONTAINER_OF(zse, struct cbor_nb_writer, zs[0]);
500
501 container->error_group = group;
502 container->error_ret = ret;
503 #endif
504
505 ok = zcbor_tstr_put_lit(zse, "err") &&
506 zcbor_map_start_encode(zse, 2) &&
507 zcbor_tstr_put_lit(zse, "group") &&
508 zcbor_uint32_put(zse, (uint32_t)group) &&
509 zcbor_tstr_put_lit(zse, "rc") &&
510 zcbor_uint32_put(zse, (uint32_t)ret) &&
511 zcbor_map_end_encode(zse, 2);
512 }
513
514 return ok;
515 }
516