1 /*
2  * Copyright (c) 2019, Prevas A/S
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  * @brief UDP transport for the mcumgr SMP protocol.
10  */
11 
12 #include <zephyr.h>
13 #include <init.h>
14 #include <net/socket.h>
15 #include <errno.h>
16 #include <mgmt/mgmt.h>
17 #include <mgmt/mcumgr/smp_udp.h>
18 #include <mgmt/mcumgr/buf.h>
19 #include <mgmt/mcumgr/smp.h>
20 
21 #define LOG_LEVEL CONFIG_MCUMGR_LOG_LEVEL
22 #include <logging/log.h>
23 LOG_MODULE_REGISTER(smp_udp);
24 
25 struct config {
26 	int sock;
27 	const char *proto;
28 	struct zephyr_smp_transport smp_transport;
29 	char recv_buffer[CONFIG_MCUMGR_SMP_UDP_MTU];
30 	struct k_thread thread;
31 	K_KERNEL_STACK_MEMBER(stack, CONFIG_MCUMGR_SMP_UDP_STACK_SIZE);
32 };
33 
34 struct configs {
35 #if CONFIG_MCUMGR_SMP_UDP_IPV4
36 	struct config ipv4;
37 #endif
38 #if CONFIG_MCUMGR_SMP_UDP_IPV6
39 	struct config ipv6;
40 #endif
41 };
42 
43 static struct configs configs = {
44 #if CONFIG_MCUMGR_SMP_UDP_IPV4
45 	.ipv4 = {
46 		.proto = "IPv4",
47 	},
48 #endif
49 #if CONFIG_MCUMGR_SMP_UDP_IPV6
50 	.ipv6 = {
51 		.proto = "IPv6",
52 	},
53 #endif
54 };
55 
56 #if CONFIG_MCUMGR_SMP_UDP_IPV4
smp_udp4_tx(struct zephyr_smp_transport * zst,struct net_buf * nb)57 static int smp_udp4_tx(struct zephyr_smp_transport *zst, struct net_buf *nb)
58 {
59 	ARG_UNUSED(zst);
60 
61 	struct sockaddr *addr = net_buf_user_data(nb);
62 	int ret = sendto(configs.ipv4.sock, nb->data, nb->len,
63 			 0, addr, sizeof(*addr));
64 	mcumgr_buf_free(nb);
65 
66 	return ret < 0 ? MGMT_ERR_EINVAL : MGMT_ERR_EOK;
67 }
68 #endif
69 
70 #if CONFIG_MCUMGR_SMP_UDP_IPV6
smp_udp6_tx(struct zephyr_smp_transport * zst,struct net_buf * nb)71 static int smp_udp6_tx(struct zephyr_smp_transport *zst, struct net_buf *nb)
72 {
73 	ARG_UNUSED(zst);
74 
75 	struct sockaddr *addr = net_buf_user_data(nb);
76 	int ret = sendto(configs.ipv6.sock, nb->data, nb->len,
77 			 0, addr, sizeof(*addr));
78 	mcumgr_buf_free(nb);
79 
80 	return ret < 0 ? MGMT_ERR_EINVAL : MGMT_ERR_EOK;
81 }
82 #endif
83 
smp_udp_get_mtu(const struct net_buf * nb)84 static uint16_t smp_udp_get_mtu(const struct net_buf *nb)
85 {
86 	ARG_UNUSED(nb);
87 
88 	return CONFIG_MCUMGR_SMP_UDP_MTU;
89 }
90 
smp_udp_ud_copy(struct net_buf * dst,const struct net_buf * src)91 static int smp_udp_ud_copy(struct net_buf *dst, const struct net_buf *src)
92 {
93 	struct sockaddr *src_ud = net_buf_user_data(src);
94 	struct sockaddr *dst_ud = net_buf_user_data(dst);
95 
96 	net_ipaddr_copy(dst_ud, src_ud);
97 
98 	return MGMT_ERR_EOK;
99 }
100 
smp_udp_receive_thread(void * p1,void * p2,void * p3)101 static void smp_udp_receive_thread(void *p1, void *p2, void *p3)
102 {
103 	struct config *conf = (struct config *)p1;
104 
105 	ARG_UNUSED(p2);
106 	ARG_UNUSED(p3);
107 
108 	LOG_INF("Started (%s)", conf->proto);
109 
110 	while (1) {
111 		struct sockaddr addr;
112 		socklen_t addr_len = sizeof(addr);
113 
114 		int len = recvfrom(conf->sock, conf->recv_buffer,
115 				   CONFIG_MCUMGR_SMP_UDP_MTU,
116 				   0, &addr, &addr_len);
117 
118 		if (len > 0) {
119 			struct sockaddr *ud;
120 			struct net_buf *nb;
121 
122 			/* store sender address in user data for reply */
123 			nb = mcumgr_buf_alloc();
124 			if (!nb) {
125 				LOG_ERR("Failed to allocate mcumgr buffer");
126 				/* No free space, drop smp frame */
127 				continue;
128 			}
129 			net_buf_add_mem(nb, conf->recv_buffer, len);
130 			ud = net_buf_user_data(nb);
131 			net_ipaddr_copy(ud, &addr);
132 
133 			zephyr_smp_rx_req(&conf->smp_transport, nb);
134 		} else if (len < 0) {
135 			LOG_ERR("recvfrom error (%s): %i", conf->proto, errno);
136 		}
137 	}
138 }
139 
smp_udp_init(const struct device * dev)140 static int smp_udp_init(const struct device *dev)
141 {
142 	ARG_UNUSED(dev);
143 
144 #if CONFIG_MCUMGR_SMP_UDP_IPV4
145 	zephyr_smp_transport_init(&configs.ipv4.smp_transport,
146 				  smp_udp4_tx, smp_udp_get_mtu,
147 				  smp_udp_ud_copy, NULL);
148 #endif
149 
150 #if CONFIG_MCUMGR_SMP_UDP_IPV6
151 	zephyr_smp_transport_init(&configs.ipv6.smp_transport,
152 				  smp_udp6_tx, smp_udp_get_mtu,
153 				  smp_udp_ud_copy, NULL);
154 #endif
155 
156 	return MGMT_ERR_EOK;
157 }
158 
create_socket(struct sockaddr * addr,const char * proto)159 static int create_socket(struct sockaddr *addr, const char *proto)
160 {
161 	int sock = socket(addr->sa_family, SOCK_DGRAM, IPPROTO_UDP);
162 	int err = errno;
163 
164 	if (sock < 0) {
165 		LOG_ERR("Could not open receive socket (%s), err: %i",
166 			proto, err);
167 
168 		return -err;
169 	}
170 
171 	if (bind(sock, addr, sizeof(*addr)) < 0) {
172 		err = errno;
173 		LOG_ERR("Could not bind to receive socket (%s), err: %i",
174 			proto, err);
175 
176 		close(sock);
177 
178 		return -err;
179 	}
180 
181 	return sock;
182 }
183 
create_thread(struct config * conf,const char * name)184 static void create_thread(struct config *conf, const char *name)
185 {
186 	k_thread_create(&(conf->thread), conf->stack,
187 			K_KERNEL_STACK_SIZEOF(conf->stack),
188 			smp_udp_receive_thread, conf, NULL, NULL,
189 			CONFIG_MCUMGR_SMP_UDP_THREAD_PRIO, 0, K_FOREVER);
190 
191 	k_thread_name_set(&(conf->thread), name);
192 	k_thread_start(&(conf->thread));
193 }
194 
195 SYS_INIT(smp_udp_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
196 
smp_udp_open(void)197 int smp_udp_open(void)
198 {
199 	struct config *conf;
200 
201 #if CONFIG_MCUMGR_SMP_UDP_IPV4
202 	struct sockaddr_in addr4;
203 
204 	memset(&addr4, 0, sizeof(addr4));
205 	addr4.sin_family = AF_INET;
206 	addr4.sin_port = htons(CONFIG_MCUMGR_SMP_UDP_PORT);
207 	inet_pton(AF_INET, INADDR_ANY, &addr4.sin_addr);
208 
209 	conf = &configs.ipv4;
210 	conf->sock = create_socket((struct sockaddr *)&addr4, conf->proto);
211 
212 	if (conf->sock < 0) {
213 		return -MGMT_ERR_EUNKNOWN;
214 	}
215 
216 	create_thread(conf, "smp_udp4");
217 #endif
218 
219 #if CONFIG_MCUMGR_SMP_UDP_IPV6
220 	struct sockaddr_in6 addr6;
221 
222 	memset(&addr6, 0, sizeof(addr6));
223 	addr6.sin6_family = AF_INET6;
224 	addr6.sin6_port = htons(CONFIG_MCUMGR_SMP_UDP_PORT);
225 	addr6.sin6_addr = in6addr_any;
226 
227 	conf = &configs.ipv6;
228 	conf->sock = create_socket((struct sockaddr *)&addr6, conf->proto);
229 
230 	if (conf->sock < 0) {
231 		return -MGMT_ERR_EUNKNOWN;
232 	}
233 
234 	create_thread(conf, "smp_udp6");
235 #endif
236 
237 	return MGMT_ERR_EOK;
238 }
239 
smp_udp_close(void)240 int smp_udp_close(void)
241 {
242 #if CONFIG_MCUMGR_SMP_UDP_IPV4
243 	k_thread_abort(&(configs.ipv4.thread));
244 	close(configs.ipv4.sock);
245 #endif
246 
247 #if CONFIG_MCUMGR_SMP_UDP_IPV6
248 	k_thread_abort(&(configs.ipv6.thread));
249 	close(configs.ipv6.sock);
250 #endif
251 
252 	return MGMT_ERR_EOK;
253 }
254