1 /*
2  * Copyright (c) 2021 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/sys/byteorder.h>
9 #include <zephyr/net_buf.h>
10 #include <zephyr/mgmt/mcumgr/mgmt/mgmt.h>
11 #include <zephyr/mgmt/mcumgr/smp/smp.h>
12 #include <zephyr/mgmt/mcumgr/transport/smp.h>
13 
14 #include <mgmt/mcumgr/transport/smp_internal.h>
15 
16 #define MCUMGR_TRANSPORT_NETBUF_SIZE CONFIG_MCUMGR_TRANSPORT_NETBUF_SIZE
17 
smp_reassembly_init(struct smp_transport * smpt)18 void smp_reassembly_init(struct smp_transport *smpt)
19 {
20 	smpt->__reassembly.current = NULL;
21 	smpt->__reassembly.expected = 0;
22 }
23 
smp_reassembly_expected(const struct smp_transport * smpt)24 int smp_reassembly_expected(const struct smp_transport *smpt)
25 {
26 	if (smpt->__reassembly.current == NULL) {
27 		return -EINVAL;
28 	}
29 
30 	return smpt->__reassembly.expected;
31 }
32 
smp_reassembly_collect(struct smp_transport * smpt,const void * buf,uint16_t len)33 int smp_reassembly_collect(struct smp_transport *smpt, const void *buf, uint16_t len)
34 {
35 	if (smpt->__reassembly.current == NULL) {
36 		/*
37 		 * Collecting the first fragment: need to allocate buffer for it and prepare
38 		 * the reassembly context.
39 		 */
40 		if (len >= sizeof(struct smp_hdr)) {
41 			uint16_t expected = sys_be16_to_cpu(((struct smp_hdr *)buf)->nh_len);
42 
43 			/*
44 			 * The length field in the header does not count the header size,
45 			 * but the reassembly does so the size needs to be added to the number of
46 			 * expected bytes.
47 			 */
48 			expected += sizeof(struct smp_hdr);
49 
50 			/* Joining net_bufs not supported yet */
51 			if (len > MCUMGR_TRANSPORT_NETBUF_SIZE ||
52 			    expected > MCUMGR_TRANSPORT_NETBUF_SIZE) {
53 				return -ENOSR;
54 			}
55 
56 			if (len > expected) {
57 				return -EOVERFLOW;
58 			}
59 
60 			smpt->__reassembly.current = smp_packet_alloc();
61 			if (smpt->__reassembly.current != NULL) {
62 				smpt->__reassembly.expected = expected;
63 			} else {
64 				return -ENOMEM;
65 			}
66 		} else {
67 			/* Not enough data to even collect header */
68 			return -ENODATA;
69 		}
70 	}
71 
72 	/* len is expected to be > 0 */
73 	if (smpt->__reassembly.expected >= len) {
74 		net_buf_add_mem(smpt->__reassembly.current, buf, len);
75 		smpt->__reassembly.expected -= len;
76 	} else {
77 		/*
78 		 * A fragment is longer than the expected size and will not fit into the buffer.
79 		 */
80 		return -EOVERFLOW;
81 	}
82 
83 	return smpt->__reassembly.expected;
84 }
85 
smp_reassembly_complete(struct smp_transport * smpt,bool force)86 int smp_reassembly_complete(struct smp_transport *smpt, bool force)
87 {
88 	if (smpt->__reassembly.current == NULL) {
89 		return -EINVAL;
90 	}
91 
92 	if (smpt->__reassembly.expected == 0 || force) {
93 		int expected = smpt->__reassembly.expected;
94 
95 		smp_rx_req(smpt, smpt->__reassembly.current);
96 		smpt->__reassembly.expected = 0;
97 		smpt->__reassembly.current = NULL;
98 		return expected;
99 	}
100 	return -ENODATA;
101 }
102 
smp_reassembly_drop(struct smp_transport * smpt)103 int smp_reassembly_drop(struct smp_transport *smpt)
104 {
105 	if (smpt->__reassembly.current == NULL) {
106 		return -EINVAL;
107 	}
108 
109 	smp_packet_free(smpt->__reassembly.current);
110 	smpt->__reassembly.expected = 0;
111 	smpt->__reassembly.current = NULL;
112 
113 	return 0;
114 }
115 
smp_reassembly_get_ud(const struct smp_transport * smpt)116 void *smp_reassembly_get_ud(const struct smp_transport *smpt)
117 {
118 	if (smpt->__reassembly.current != NULL) {
119 		return net_buf_user_data(smpt->__reassembly.current);
120 	}
121 
122 	return NULL;
123 }
124