mgmt: smp: add UDP transport for SMP

Adds a UDP driver dedicated to transporting mcumgr SMP requests and
responses.

Signed-off-by: Mikkel Jakobsen <mikkel.aunsbjerg@prevas.dk>
This commit is contained in:
Mikkel Jakobsen 2019-06-03 17:10:19 +02:00 committed by Jukka Rissanen
commit 7288f51519
6 changed files with 356 additions and 1 deletions

View file

@ -435,6 +435,7 @@
/subsys/logging/ @nordic-krch /subsys/logging/ @nordic-krch
/subsys/logging/log_backend_net.c @nordic-krch @jukkar /subsys/logging/log_backend_net.c @nordic-krch @jukkar
/subsys/mgmt/ @carlescufi @nvlsianpu /subsys/mgmt/ @carlescufi @nvlsianpu
/subsys/mgmt/smp_udp.c @aunsbjerg
/subsys/net/buf.c @jukkar @jhedberg @tbursztyka @pfalcon /subsys/net/buf.c @jukkar @jhedberg @tbursztyka @pfalcon
/subsys/net/ip/ @jukkar @tbursztyka @pfalcon /subsys/net/ip/ @jukkar @tbursztyka @pfalcon
/subsys/net/lib/ @jukkar @tbursztyka @pfalcon /subsys/net/lib/ @jukkar @tbursztyka @pfalcon

37
include/mgmt/smp_udp.h Normal file
View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2019, Prevas A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief UDP transport for the mcumgr SMP protocol.
*/
#ifndef ZEPHYR_INCLUDE_MGMT_SMP_UDP_H_
#define ZEPHYR_INCLUDE_MGMT_SMP_UDP_H_
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Opens a UDP socket for the SMP UDP service.
*
* @return 0 on success; negative error code on failure.
*/
int smp_udp_open(void);
/**
* @brief Closes the UDP socket for the SMP UDP service.
*
* @return 0 on success; negative error code on failure.
*/
int smp_udp_close(void);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -6,6 +6,7 @@ zephyr_library_sources(smp.c)
zephyr_library_sources_ifdef(CONFIG_MCUMGR_SMP_BT smp_bt.c) zephyr_library_sources_ifdef(CONFIG_MCUMGR_SMP_BT smp_bt.c)
zephyr_library_sources_ifdef(CONFIG_MCUMGR_SMP_SHELL smp_shell.c) zephyr_library_sources_ifdef(CONFIG_MCUMGR_SMP_SHELL smp_shell.c)
zephyr_library_sources_ifdef(CONFIG_MCUMGR_SMP_UART smp_uart.c) zephyr_library_sources_ifdef(CONFIG_MCUMGR_SMP_UART smp_uart.c)
zephyr_library_sources_ifdef(CONFIG_MCUMGR_SMP_UDP smp_udp.c)
zephyr_library_link_libraries(MCUMGR) zephyr_library_link_libraries(MCUMGR)
if (CONFIG_MCUMGR_SMP_SHELL OR CONFIG_MCUMGR_SMP_UART) if (CONFIG_MCUMGR_SMP_SHELL OR CONFIG_MCUMGR_SMP_UART)

View file

@ -64,9 +64,69 @@ config MCUMGR_SMP_UART_MTU
source "subsys/mgmt/Kconfig.mcumgr" source "subsys/mgmt/Kconfig.mcumgr"
config MCUMGR_SMP_UDP
bool "UDP mcumgr SMP transport"
select MCUMGR
select NETWORKING
select NET_UDP
select NET_SOCKETS
select NET_SOCKETS_POSIX_NAMES
help
Enables handling of SMP commands received over UDP.
Will start a thread for listening on the configured UDP port.
config MCUMGR_SMP_UDP_IPV4
bool "UDP SMP using IPv4"
depends on MCUMGR_SMP_UDP
depends on NET_IPV4
default y
help
Enable SMP UDP using IPv4 addressing.
Can be enabled alongside IPv6 addressing.
config MCUMGR_SMP_UDP_IPV6
bool "UDP SMP using IPv6"
depends on MCUMGR_SMP_UDP
depends on NET_IPV6
help
Enable SMP UDP using IPv6 addressing.
Can be enabled alongside IPv4 addressing.
config MCUMGR_SMP_UDP_PORT
int "UDP SMP port"
depends on MCUMGR_SMP_UDP
default 1337
help
UDP port that SMP server will listen for SMP commands on.
config MCUMGR_SMP_UDP_STACK_SIZE
int "UDP SMP stack size"
depends on MCUMGR_SMP_UDP
default 512
help
Stack size of the SMP UDP listening thread
config MCUMGR_SMP_UDP_THREAD_PRIO
int "UDP SMP thread priority"
depends on MCUMGR_SMP_UDP
default 0
help
Scheduling priority of the SMP UDP listening thread.
config MCUMGR_SMP_UDP_MTU
int "UDP SMP MTU"
depends on MCUMGR_SMP_UDP
default 1500
help
Maximum size of SMP frames sent and received over UDP, in bytes.
This value must satisfy the following relation:
MCUMGR_SMP_UDP_MTU <= MCUMGR_BUF_SIZE + SMP msg overhead - address size
where address size is determined by IPv4/IPv6 selection.
if MCUMGR if MCUMGR
config MCUMGR_BUF_COUNT config MCUMGR_BUF_COUNT
int "Number of mcumgr buffers" int "Number of mcumgr buffers"
default 2 if MCUMGR_SMP_UDP
default 4 default 4
help help
The number of net_bufs to allocate for mcumgr. These buffers are The number of net_bufs to allocate for mcumgr. These buffers are
@ -74,6 +134,7 @@ config MCUMGR_BUF_COUNT
config MCUMGR_BUF_SIZE config MCUMGR_BUF_SIZE
int "Size of each mcumgr buffer" int "Size of each mcumgr buffer"
default 2048 if MCUMGR_SMP_UDP
default 384 default 384
help help
The size, in bytes, of each mcumgr buffer. This value must satisfy The size, in bytes, of each mcumgr buffer. This value must satisfy
@ -82,11 +143,18 @@ config MCUMGR_BUF_SIZE
config MCUMGR_BUF_USER_DATA_SIZE config MCUMGR_BUF_USER_DATA_SIZE
int "Size of mcumgr buffer user data" int "Size of mcumgr buffer user data"
default 24 if MCUMGR_SMP_UDP && MCUMGR_SMP_UDP_IPV6
default 8 if MCUMGR_SMP_UDP && MCUMGR_SMP_UDP_IPV4
default 4 default 4
help help
The size, in bytes, of user data to allocate for each mcumgr buffer. The size, in bytes, of user data to allocate for each mcumgr buffer.
Different mcumgr transports impose different requirements for this Different mcumgr transports impose different requirements for this
setting. A value of 4 is sufficient for UART, shell, and bluetooth. setting. A value of 4 is sufficient for UART, shell, and bluetooth.
For UDP, the userdata must be large enough to hold a IPv4/IPv6 address.
Note that CONFIG_NET_BUF_USER_DATA_SIZE must be at least as big as
MCUMGR_BUF_USER_DATA_SIZE.
endif # MCUMGR endif # MCUMGR
endmenu endmenu

View file

@ -10,6 +10,10 @@ config MCUMGR
if MCUMGR if MCUMGR
module = MCUMGR
module-str = mcumgr
source "subsys/logging/Kconfig.template.log_config"
config MGMT_CBORATTR_MAX_SIZE config MGMT_CBORATTR_MAX_SIZE
int "The maximum size of a CBOR attribute during decoding" int "The maximum size of a CBOR attribute during decoding"
default 512 default 512

244
subsys/mgmt/smp_udp.c Normal file
View file

@ -0,0 +1,244 @@
/*
* Copyright (c) 2019, Prevas A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief UDP transport for the mcumgr SMP protocol.
*/
#include <zephyr.h>
#include <init.h>
#include <net/socket.h>
#include <errno.h>
#include <mgmt/mgmt.h>
#include <mgmt/smp_udp.h>
#include <mgmt/buf.h>
#include <mgmt/smp.h>
#define LOG_LEVEL CONFIG_MCUMGR_LOG_LEVEL
#include <logging/log.h>
LOG_MODULE_REGISTER(smp_udp);
struct config {
int sock;
const char *proto;
struct zephyr_smp_transport smp_transport;
char recv_buffer[CONFIG_MCUMGR_SMP_UDP_MTU];
struct k_thread thread;
K_THREAD_STACK_MEMBER(stack, CONFIG_MCUMGR_SMP_UDP_STACK_SIZE);
};
struct configs {
#if CONFIG_MCUMGR_SMP_UDP_IPV4
struct config ipv4;
#endif
#if CONFIG_MCUMGR_SMP_UDP_IPV6
struct config ipv6;
#endif
};
static struct configs configs = {
#if CONFIG_MCUMGR_SMP_UDP_IPV4
.ipv4 = {
.proto = "IPv4",
},
#endif
#if CONFIG_MCUMGR_SMP_UDP_IPV6
.ipv6 = {
.proto = "IPv6",
},
#endif
};
#if CONFIG_MCUMGR_SMP_UDP_IPV4
static int smp_udp4_tx(struct zephyr_smp_transport *zst, struct net_buf *nb)
{
ARG_UNUSED(zst);
struct sockaddr *addr = net_buf_user_data(nb);
int ret = sendto(configs.ipv4.sock, nb->data, nb->len,
0, addr, sizeof(*addr));
mcumgr_buf_free(nb);
return ret < 0 ? MGMT_ERR_EINVAL : MGMT_ERR_EOK;
}
#endif
#if CONFIG_MCUMGR_SMP_UDP_IPV6
static int smp_udp6_tx(struct zephyr_smp_transport *zst, struct net_buf *nb)
{
ARG_UNUSED(zst);
struct sockaddr *addr = net_buf_user_data(nb);
int ret = sendto(configs.ipv6.sock, nb->data, nb->len,
0, addr, sizeof(*addr));
mcumgr_buf_free(nb);
return ret < 0 ? MGMT_ERR_EINVAL : MGMT_ERR_EOK;
}
#endif
static uint16_t smp_udp_get_mtu(const struct net_buf *nb)
{
ARG_UNUSED(nb);
return CONFIG_MCUMGR_SMP_UDP_MTU;
}
static int smp_udp_ud_copy(struct net_buf *dst, const struct net_buf *src)
{
struct sockaddr *src_ud = net_buf_user_data(src);
struct sockaddr *dst_ud = net_buf_user_data(dst);
net_ipaddr_copy(dst_ud, src_ud);
return MGMT_ERR_EOK;
}
static void smp_udp_receive_thread(void *p1, void *p2, void *p3)
{
struct config *conf = (struct config *)p1;
ARG_UNUSED(p2);
ARG_UNUSED(p3);
LOG_INF("Started (%s)", conf->proto);
while (1) {
struct sockaddr addr;
socklen_t addr_len = sizeof(addr);
int len = recvfrom(conf->sock, conf->recv_buffer,
CONFIG_MCUMGR_SMP_UDP_MTU,
0, &addr, &addr_len);
if (len > 0) {
struct sockaddr *ud;
struct net_buf *nb;
/* store sender address in user data for reply */
nb = mcumgr_buf_alloc();
net_buf_add_mem(nb, conf->recv_buffer, len);
ud = net_buf_user_data(nb);
net_ipaddr_copy(ud, &addr);
zephyr_smp_rx_req(&conf->smp_transport, nb);
} else if (len < 0) {
LOG_ERR("recvfrom error (%s): %i", conf->proto, errno);
}
}
}
static int smp_udp_init(struct device *dev)
{
ARG_UNUSED(dev);
#if CONFIG_MCUMGR_SMP_UDP_IPV4
zephyr_smp_transport_init(&configs.ipv4.smp_transport,
smp_udp4_tx, smp_udp_get_mtu,
smp_udp_ud_copy, NULL);
#endif
#if CONFIG_MCUMGR_SMP_UDP_IPV6
zephyr_smp_transport_init(&configs.ipv6.smp_transport,
smp_udp6_tx, smp_udp_get_mtu,
smp_udp_ud_copy, NULL);
#endif
return MGMT_ERR_EOK;
}
static int create_socket(struct sockaddr *addr, const char *proto)
{
int sock = socket(addr->sa_family, SOCK_DGRAM, IPPROTO_UDP);
if (sock < 0) {
LOG_ERR("Could not open receive socket (%s), err: %i",
proto, errno);
return -errno;
}
if (bind(sock, addr, sizeof(*addr)) < 0) {
LOG_ERR("Could not bind to receive socket (%s), err: %i",
proto, errno);
return -errno;
}
return sock;
}
static void create_thread(struct config *conf, const char *name)
{
k_thread_create(&(conf->thread), conf->stack,
K_THREAD_STACK_SIZEOF(conf->stack),
smp_udp_receive_thread, conf, NULL, NULL,
CONFIG_MCUMGR_SMP_UDP_THREAD_PRIO, 0, K_FOREVER);
k_thread_name_set(&(conf->thread), name);
k_thread_start(&(conf->thread));
}
SYS_INIT(smp_udp_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
int smp_udp_open(void)
{
struct config *conf;
#if CONFIG_MCUMGR_SMP_UDP_IPV4
struct sockaddr_in addr4;
memset(&addr4, 0, sizeof(addr4));
addr4.sin_family = AF_INET;
addr4.sin_port = htons(CONFIG_MCUMGR_SMP_UDP_PORT);
inet_pton(AF_INET, INADDR_ANY, &addr4.sin_addr);
conf = &configs.ipv4;
conf->sock = create_socket((struct sockaddr *)&addr4, conf->proto);
if (conf->sock < 0) {
return -MGMT_ERR_EUNKNOWN;
}
create_thread(conf, "smp_udp4");
#endif
#if CONFIG_MCUMGR_SMP_UDP_IPV6
struct sockaddr_in6 addr6;
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(CONFIG_MCUMGR_SMP_UDP_PORT);
addr6.sin6_addr = in6addr_any;
conf = &configs.ipv6;
conf->sock = create_socket((struct sockaddr *)&addr6, conf->proto);
if (conf->sock < 0) {
return -MGMT_ERR_EUNKNOWN;
}
create_thread(conf, "smp_udp6");
#endif
return MGMT_ERR_EOK;
}
int smp_udp_close(void)
{
#if CONFIG_MCUMGR_SMP_UDP_IPV4
k_thread_abort(&(configs.ipv4.thread));
close(configs.ipv4.sock);
#endif
#if CONFIG_MCUMGR_SMP_UDP_IPV6
k_thread_abort(&(configs.ipv6.thread));
close(configs.ipv6.sock);
#endif
return MGMT_ERR_EOK;
}