net: lwm2m: migrate LwM2M library to BSD-sockets API

This commit removes the net_app layer from the LwM2M library and
replaces it with BSD-sockets APIs.

Signed-off-by: Michael Scott <mike@foundries.io>
This commit is contained in:
Michael Scott 2019-01-28 11:55:23 -08:00 committed by Anas Nashif
commit d1cb39e7ce
5 changed files with 395 additions and 399 deletions

View file

@ -8,7 +8,7 @@
#ifndef ZEPHYR_INCLUDE_NET_LWM2M_H_
#define ZEPHYR_INCLUDE_NET_LWM2M_H_
#include <net/net_app.h>
#include <kernel.h>
#include <net/coap_sock.h>
/* LWM2M Objects defined by OMA */
@ -33,40 +33,19 @@
* @details Context structure for the LwM2M high-level API.
*
* @param remote_addr Stored remote IP address of the LwM2M client
* @param net_app_ctx Related network application context.
* @param net_init_timeout Used if the net_app API needs to do some time
* consuming operation, like resolving DNS address.
* @param net_timeout How long to wait for the network connection before
* giving up.
*/
struct lwm2m_ctx {
/** destination address storage */
struct sockaddr remote_addr;
/** Net app context structure */
struct net_app_ctx net_app_ctx;
s32_t net_init_timeout;
s32_t net_timeout;
/** Private CoAP and networking structures */
struct coap_pending pendings[CONFIG_LWM2M_ENGINE_MAX_PENDING];
struct coap_reply replies[CONFIG_LWM2M_ENGINE_MAX_REPLIES];
struct k_delayed_work retransmit_work;
#if defined(CONFIG_NET_APP_DTLS)
/** Pre-Shared Key Information*/
unsigned char *client_psk;
size_t client_psk_len;
char *client_psk_id;
size_t client_psk_id_len;
/** DTLS support structures */
char *cert_host;
u8_t *dtls_result_buf;
size_t dtls_result_buf_len;
struct k_mem_pool *dtls_pool;
k_thread_stack_t *dtls_stack;
size_t dtls_stack_len;
#if defined(CONFIG_LWM2M_DTLS_SUPPORT)
/** DTLS settings */
int tls_tag;
#endif
bool use_dtls;
@ -76,6 +55,9 @@ struct lwm2m_ctx {
/** Packet Flow Settings */
bool handle_separate_response;
/** Socket File Descriptor */
int sock_fd;
};
typedef void *(*lwm2m_engine_get_data_cb_t)(u16_t obj_inst_id,

View file

@ -7,8 +7,9 @@
menuconfig LWM2M
bool "OMA LWM2M protocol stack"
select COAP
select NET_APP_CLIENT
select HTTP_PARSER_URL
select NET_SOCKETS
select NET_SOCKETS_POSIX_NAMES
help
This option adds logic for managing OMA LWM2M data
@ -23,7 +24,9 @@ config LWM2M_DTLS_SUPPORT
bool "Enable DTLS support in the LwM2M client"
select MBEDTLS
select MBEDTLS_ENABLE_HEAP
select NET_APP_DTLS
select TLS_CREDENTIALS
select NET_SOCKETS_SOCKOPT_TLS
select NET_SOCKETS_ENABLE_DTLS
config LWM2M_ENGINE_STACK_SIZE
int "LWM2M engine stack size"

View file

@ -33,10 +33,11 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME);
#include <init.h>
#include <misc/printk.h>
#include <net/net_ip.h>
#include <net/udp.h>
#include <net/net_pkt.h>
#include <net/net_app.h>
#include <net/http_parser_url.h>
#include <net/socket.h>
#if defined(CONFIG_LWM2M_DTLS_SUPPORT)
#include <net/tls_credentials.h>
#endif
#include "lwm2m_object.h"
#include "lwm2m_engine.h"
@ -73,10 +74,6 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME);
#define REG_PREFACE ""
#endif
#if defined(CONFIG_NET_APP_DTLS)
#define INSTANCE_INFO "Zephyr DTLS LwM2M-client"
#endif
#if defined(CONFIG_COAP_EXTENDED_OPTIONS_LEN)
#define COAP_OPTION_BUF_LEN (CONFIG_COAP_EXTENDED_OPTIONS_LEN_VALUE + 1)
#else
@ -131,6 +128,16 @@ static sys_slist_t engine_obj_inst_list;
static sys_slist_t engine_observer_list;
static sys_slist_t engine_service_list;
static K_THREAD_STACK_DEFINE(engine_thread_stack,
CONFIG_LWM2M_ENGINE_STACK_SIZE);
static struct k_thread engine_thread_data;
#define MAX_POLL_FD CONFIG_NET_SOCKETS_POLL_MAX
static struct lwm2m_ctx *sock_ctx[MAX_POLL_FD];
static struct pollfd sock_fds[MAX_POLL_FD];
static int sock_nfds;
#define NUM_BLOCK1_CONTEXT CONFIG_LWM2M_NUM_BLOCK1_CONTEXT
/* TODO: figure out what's correct value */
@ -1014,7 +1021,6 @@ cleanup:
int lwm2m_send_message(struct lwm2m_message *msg)
{
struct net_pkt *pkt;
int ret;
if (!msg || !msg->ctx) {
@ -1028,30 +1034,12 @@ int lwm2m_send_message(struct lwm2m_message *msg)
msg->send_attempts++;
pkt = net_app_get_net_pkt(&msg->ctx->net_app_ctx, AF_UNSPEC,
BUF_ALLOC_TIMEOUT);
if (!pkt) {
LOG_ERR("Unable to get TX packet, not enough memory.");
ret = -ENOMEM;
goto cleanup;
}
if (!net_pkt_append(pkt, msg->cpkt.offset, msg->cpkt.data,
BUF_ALLOC_TIMEOUT)) {
LOG_ERR("Unable to add packet data, not enough memory.");
ret = -ENOMEM;
goto cleanup;
}
ret = net_app_send_pkt(&msg->ctx->net_app_ctx, pkt,
&msg->ctx->remote_addr,
NET_SOCKADDR_MAX_SIZE, K_NO_WAIT, NULL);
ret = send(msg->ctx->sock_fd, msg->cpkt.data, msg->cpkt.offset, 0);
if (ret < 0) {
if (msg->type == COAP_TYPE_CON) {
coap_pending_clear(msg->pending);
}
net_pkt_unref(pkt);
return ret;
}
@ -1067,13 +1055,6 @@ int lwm2m_send_message(struct lwm2m_message *msg)
lwm2m_reset_message(msg, true);
}
return ret;
cleanup:
if (pkt) {
net_pkt_unref(pkt);
}
return ret;
}
@ -3458,60 +3439,20 @@ error:
return 0;
}
static void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx, struct net_pkt *pkt,
static void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx,
u8_t *buf, u16_t buf_len,
struct sockaddr *from_addr,
udp_request_handler_cb_t udp_request_handler)
{
struct lwm2m_message *msg = NULL;
struct net_udp_hdr hdr, *udp_hdr;
struct coap_pending *pending;
struct coap_reply *reply;
struct coap_packet response;
struct sockaddr from_addr;
static u8_t in_buf[NET_IPV6_MTU];
size_t recv_len;
int r;
u8_t token[8];
u8_t tkl;
udp_hdr = net_udp_get_hdr(pkt, &hdr);
if (!udp_hdr) {
LOG_ERR("Invalid UDP data");
return;
}
/* Save the from address */
#if defined(CONFIG_NET_IPV6)
if (net_pkt_family(pkt) == AF_INET6) {
net_ipaddr_copy(&net_sin6(&from_addr)->sin6_addr,
&NET_IPV6_HDR(pkt)->src);
net_sin6(&from_addr)->sin6_port = udp_hdr->src_port;
net_sin6(&from_addr)->sin6_family = AF_INET6;
}
#endif
#if defined(CONFIG_NET_IPV4)
if (net_pkt_family(pkt) == AF_INET) {
net_ipaddr_copy(&net_sin(&from_addr)->sin_addr,
&NET_IPV4_HDR(pkt)->src);
net_sin(&from_addr)->sin_port = udp_hdr->src_port;
net_sin(&from_addr)->sin_family = AF_INET;
}
#endif
/* copy data from pkt */
recv_len = net_pkt_appdatalen(pkt);
if (recv_len > sizeof(in_buf)) {
recv_len = sizeof(in_buf);
}
(void)net_frag_linearize(in_buf, recv_len, pkt,
net_pkt_appdata(pkt) - pkt->frags->data,
recv_len);
/* now that we have data, free pkt */
net_pkt_unref(pkt);
r = coap_packet_parse(&response, in_buf, recv_len, NULL, 0);
r = coap_packet_parse(&response, buf, buf_len, NULL, 0);
if (r < 0) {
LOG_ERR("Invalid data received (err:%d)", r);
return;
@ -3531,8 +3472,8 @@ static void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx, struct net_pkt *pkt,
}
LOG_DBG("checking for reply from [%s]",
lwm2m_sprint_ip_addr(&from_addr));
reply = coap_response_received(&response, &from_addr,
lwm2m_sprint_ip_addr(from_addr));
reply = coap_response_received(&response, from_addr,
client_ctx->replies,
CONFIG_LWM2M_ENGINE_MAX_REPLIES);
if (reply) {
@ -3611,19 +3552,8 @@ static void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx, struct net_pkt *pkt,
}
}
static void udp_receive(struct net_app_ctx *app_ctx, struct net_pkt *pkt,
int status, void *user_data)
{
struct lwm2m_ctx *client_ctx = CONTAINER_OF(app_ctx,
struct lwm2m_ctx,
net_app_ctx);
lwm2m_udp_receive(client_ctx, pkt, handle_request);
}
static void retransmit_request(struct k_work *work)
{
struct net_pkt *pkt;
struct lwm2m_ctx *client_ctx;
struct lwm2m_message *msg;
struct coap_pending *pending;
@ -3658,43 +3588,13 @@ static void retransmit_request(struct k_work *work)
LOG_DBG("Resending message: %p", msg);
msg->send_attempts++;
pkt = net_app_get_net_pkt(&msg->ctx->net_app_ctx, AF_UNSPEC,
BUF_ALLOC_TIMEOUT);
if (!pkt) {
LOG_ERR("Unable to get TX packet, not enough memory.");
goto cleanup;
}
if (!net_pkt_append(pkt, msg->cpkt.offset, msg->cpkt.data,
BUF_ALLOC_TIMEOUT)) {
LOG_ERR("Unable to add packet data, not enough memory.");
goto cleanup;
}
/*
* Don't use lwm2m_send_message() because it calls
* coap_pending_cycle() / coap_pending_cycle() in a different order
* and under different circumstances. It also does it's own ref /
* unref of the net_pkt. Keep it simple and call net_app_send_pkt()
* directly here.
*/
r = net_app_send_pkt(&msg->ctx->net_app_ctx, pkt,
&msg->ctx->remote_addr,
NET_SOCKADDR_MAX_SIZE, K_NO_WAIT, NULL);
r = send(msg->ctx->sock_fd, msg->cpkt.data, msg->cpkt.offset, 0);
if (r < 0) {
LOG_ERR("Error sending lwm2m message: %d", r);
/* don't error here, retry until timeout */
net_pkt_unref(pkt);
}
k_delayed_work_submit(&client_ctx->retransmit_work, pending->timeout);
return;
cleanup:
if (pkt) {
net_pkt_unref(pkt);
}
}
static int notify_message_reply_cb(const struct coap_packet *response,
@ -3926,173 +3826,332 @@ static void lwm2m_engine_service(struct k_work *work)
int lwm2m_engine_context_close(struct lwm2m_ctx *client_ctx)
{
int sock_fd = client_ctx->sock_fd;
k_delayed_work_cancel(&client_ctx->retransmit_work);
net_app_close(&client_ctx->net_app_ctx);
net_app_release(&client_ctx->net_app_ctx);
lwm2m_socket_del(client_ctx);
client_ctx->sock_fd = -1;
if (sock_fd >= 0) {
return close(sock_fd);
} else {
return 0;
}
}
void lwm2m_engine_context_init(struct lwm2m_ctx *client_ctx)
{
k_delayed_work_init(&client_ctx->retransmit_work, retransmit_request);
}
#if defined(CONFIG_NET_APP_DTLS)
static int setup_cert(struct net_app_ctx *app_ctx, void *cert)
{
#if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
struct lwm2m_ctx *client_ctx = CONTAINER_OF(app_ctx,
struct lwm2m_ctx,
net_app_ctx);
return mbedtls_ssl_conf_psk(
&app_ctx->tls.mbedtls.conf,
(const unsigned char *)client_ctx->client_psk,
client_ctx->client_psk_len,
(const unsigned char *)client_ctx->client_psk_id,
client_ctx->client_psk_id_len);
#else
return 0;
#endif
}
#endif /* CONFIG_NET_APP_DTLS */
/* LwM2M Socket Integration */
int lwm2m_net_app_start(struct lwm2m_ctx *client_ctx,
char *peer_str, u16_t peer_port)
int lwm2m_socket_add(struct lwm2m_ctx *ctx)
{
struct sockaddr client_addr;
int i;
if (sock_nfds < MAX_POLL_FD) {
i = sock_nfds++;
} else {
for (i = 0; i < MAX_POLL_FD; i++) {
if (sock_ctx[i] == NULL) {
goto found;
}
}
return -ENOMEM;
}
found:
sock_ctx[i] = ctx;
sock_fds[i].fd = ctx->sock_fd;
sock_fds[i].events = POLLIN;
return 0;
}
void lwm2m_socket_del(struct lwm2m_ctx *ctx)
{
for (int i = 0; i < sock_nfds; i++) {
if (sock_ctx[i] == ctx) {
sock_ctx[i] = NULL;
sock_fds[i].fd = -1;
sock_nfds--;
break;
}
}
}
/* LwM2M main work loop */
static void socket_receive_loop(void)
{
static u8_t in_buf[NET_IPV6_MTU];
static struct sockaddr from_addr;
socklen_t from_addr_len;
ssize_t len;
int i;
from_addr_len = sizeof(from_addr);
while (1) {
/* wait for sockets */
if (sock_nfds < 1) {
k_sleep(ENGINE_UPDATE_INTERVAL);
continue;
}
/*
* FIXME: Currently we timeout and restart poll in case fds
* were modified.
*/
if (poll(sock_fds, sock_nfds, ENGINE_UPDATE_INTERVAL) < 0) {
LOG_ERR("Error in poll:%d", errno);
errno = 0;
k_sleep(ENGINE_UPDATE_INTERVAL);
continue;
}
for (i = 0; i < sock_nfds; i++) {
if (sock_fds[i].revents & POLLERR) {
LOG_ERR("Error in poll.. waiting a moment.");
k_sleep(ENGINE_UPDATE_INTERVAL);
continue;
}
if (!(sock_fds[i].revents & POLLIN) ||
sock_ctx[i] == NULL) {
sock_fds[i].revents = 0;
continue;
}
sock_fds[i].revents = 0;
len = recvfrom(sock_ctx[i]->sock_fd, in_buf,
sizeof(in_buf) - 1, 0,
&from_addr, &from_addr_len);
if (errno) {
LOG_ERR("Sock RECV error: %d", errno);
/* TODO: handle error? */
continue;
}
if (len < 0) {
LOG_ERR("Error reading response: %d", errno);
continue;
}
if (len == 0) {
LOG_ERR("Zero length recv");
continue;
}
in_buf[len] = 0;
lwm2m_udp_receive(sock_ctx[i], in_buf, len, &from_addr,
handle_request);
}
}
}
#if defined(CONFIG_LWM2M_DTLS_SUPPORT)
static int load_tls_credential(struct lwm2m_ctx *client_ctx, u16_t res_id,
enum tls_credential_type type)
{
int ret = 0;
void *cred = NULL;
u16_t cred_len;
u8_t cred_flags;
char pathstr[MAX_RESOURCE_LEN];
/* ignore error value */
tls_credential_delete(client_ctx->tls_tag, type);
snprintk(pathstr, sizeof(pathstr), "0/%d/%u", client_ctx->sec_obj_inst,
res_id);
ret = lwm2m_engine_get_res_data(pathstr, &cred, &cred_len, &cred_flags);
if (ret < 0) {
LOG_ERR("Unable to get resource data for '%s'", pathstr);
return ret;
}
/* Set correct PSK_ID length */
if (type == TLS_CREDENTIAL_PSK_ID) {
cred_len = strlen(cred);
}
ret = tls_credential_add(client_ctx->tls_tag, type, cred, cred_len);
if (ret < 0) {
LOG_ERR("Unable to get resource data for '%s'", pathstr);
}
return ret;
}
#endif /* CONFIG_LWM2M_DTLS_SUPPORT */
int lwm2m_socket_start(struct lwm2m_ctx *client_ctx)
{
#if defined(CONFIG_LWM2M_DTLS_SUPPORT)
int ret;
/* setup the local client port */
(void)memset(&client_addr, 0, sizeof(client_addr));
#if defined(CONFIG_NET_IPV6)
client_addr.sa_family = AF_INET6;
net_sin6(&client_addr)->sin6_port = htons(CONFIG_LWM2M_LOCAL_PORT);
#elif defined(CONFIG_NET_IPV4)
client_addr.sa_family = AF_INET;
net_sin(&client_addr)->sin_port = htons(CONFIG_LWM2M_LOCAL_PORT);
#endif
ret = net_app_init_udp_client(&client_ctx->net_app_ctx,
&client_addr, NULL,
peer_str,
peer_port,
client_ctx->net_init_timeout,
client_ctx);
if (ret) {
LOG_ERR("net_app_init_udp_client err:%d", ret);
goto error_start;
ret = load_tls_credential(client_ctx, 3, TLS_CREDENTIAL_PSK_ID);
if (ret < 0) {
return ret;
}
/* set net_app callbacks */
ret = net_app_set_cb(&client_ctx->net_app_ctx,
NULL, udp_receive, NULL, NULL);
if (ret) {
LOG_ERR("Could not set receive callback (err:%d)", ret);
goto error_start;
ret = load_tls_credential(client_ctx, 5, TLS_CREDENTIAL_PSK);
if (ret < 0) {
return ret;
}
#if defined(CONFIG_NET_APP_DTLS)
if (client_ctx->use_dtls) {
ret = net_app_client_tls(&client_ctx->net_app_ctx,
client_ctx->dtls_result_buf,
client_ctx->dtls_result_buf_len,
INSTANCE_INFO,
strlen(INSTANCE_INFO),
setup_cert,
client_ctx->cert_host,
NULL,
client_ctx->dtls_pool,
client_ctx->dtls_stack,
client_ctx->dtls_stack_len);
if (ret < 0) {
LOG_ERR("Cannot init DTLS (%d)", ret);
goto error_start;
}
}
#endif
ret = net_app_connect(&client_ctx->net_app_ctx,
client_ctx->net_timeout);
if (ret < 0) {
LOG_ERR("Cannot connect UDP (%d)", ret);
goto error_start;
}
/* save remote addr */
#if defined(CONFIG_LWM2M_DTLS_SUPPORT)
if (client_ctx->use_dtls && client_ctx->net_app_ctx.dtls.ctx) {
memcpy(&client_ctx->remote_addr,
&client_ctx->net_app_ctx.dtls.ctx->remote,
sizeof(client_ctx->remote_addr));
client_ctx->sock_fd = socket(client_ctx->remote_addr.sa_family,
SOCK_DGRAM, IPPROTO_DTLS_1_2);
} else
#endif
#endif /* CONFIG_LWM2M_DTLS_SUPPORT */
{
memcpy(&client_ctx->remote_addr,
&client_ctx->net_app_ctx.default_ctx->remote,
sizeof(client_ctx->remote_addr));
client_ctx->sock_fd = socket(client_ctx->remote_addr.sa_family,
SOCK_DGRAM, IPPROTO_UDP);
}
return 0;
error_start:
if (client_ctx->sock_fd < 0) {
LOG_ERR("Failed to create socket: %d", errno);
return -errno;
}
#if defined(CONFIG_LWM2M_DTLS_SUPPORT)
if (client_ctx->use_dtls) {
sec_tag_t tls_tag_list[] = {
client_ctx->tls_tag,
};
ret = setsockopt(client_ctx->sock_fd, SOL_TLS, TLS_SEC_TAG_LIST,
tls_tag_list, sizeof(tls_tag_list));
if (ret < 0) {
LOG_ERR("Failed to set TLS_SEC_TAG_LIST option: %d",
errno);
lwm2m_engine_context_close(client_ctx);
return -errno;
}
}
#endif /* CONFIG_LWM2M_DTLS_SUPPORT */
if (connect(client_ctx->sock_fd, &client_ctx->remote_addr,
NET_SOCKADDR_MAX_SIZE) < 0) {
LOG_ERR("Cannot connect UDP (-%d)", errno);
lwm2m_engine_context_close(client_ctx);
return -errno;
}
lwm2m_socket_add(client_ctx);
return 0;
}
int lwm2m_parse_peerinfo(char *url, struct sockaddr *addr, bool *use_dtls)
{
struct http_parser_url parser;
int ret;
u16_t off, len;
u8_t tmp;
http_parser_url_init(&parser);
ret = http_parser_parse_url(url, strlen(url), 0, &parser);
if (ret < 0) {
LOG_ERR("Invalid url: %s", url);
return -ENOTSUP;
}
off = parser.field_data[UF_SCHEMA].off;
len = parser.field_data[UF_SCHEMA].len;
/* check for supported protocol */
if (strncmp(url + off, "coaps", len) != 0) {
return -EPROTONOSUPPORT;
}
/* check for DTLS requirement */
*use_dtls = false;
if (len == 5 && strncmp(url + off, "coaps", len) == 0) {
#if defined(CONFIG_LWM2M_DTLS_SUPPORT)
*use_dtls = true;
#else
return -EPROTONOSUPPORT;
#endif /* CONFIG_LWM2M_DTLS_SUPPORT */
}
if (!(parser.field_set & (1 << UF_PORT))) {
/* Set to default port of CoAP */
parser.port = CONFIG_LWM2M_PEER_PORT;
}
off = parser.field_data[UF_HOST].off;
len = parser.field_data[UF_HOST].len;
/* truncate host portion */
tmp = url[off + len];
url[off + len] = '\0';
/* initialize addr */
(void)memset(addr, 0, sizeof(*addr));
/* try and set IP address directly */
ret = -EINVAL;
#if defined(CONFIG_NET_IPV6)
addr->sa_family = AF_INET6;
ret = net_addr_pton(AF_INET6, url + off,
&((struct sockaddr_in6 *)addr)->sin6_addr);
#endif /* CONFIG_NET_IPV6 */
#if defined(CONFIG_NET_IPV4)
if (ret < 0) {
addr->sa_family = AF_INET;
ret = net_addr_pton(AF_INET, url + off,
&((struct sockaddr_in *)addr)->sin_addr);
}
#endif /* CONFIG_NET_IPV4 */
if (ret < 0) {
goto cleanup;
}
/* set port */
if (addr->sa_family == AF_INET6) {
net_sin6(addr)->sin6_port = htons(parser.port);
} else if (addr->sa_family == AF_INET) {
net_sin(addr)->sin_port = htons(parser.port);
} else {
ret = -EPROTONOSUPPORT;
}
cleanup:
/* restore host separator */
url[off + len] = tmp;
return ret;
}
int lwm2m_engine_start(struct lwm2m_ctx *client_ctx)
{
char pathstr[MAX_RESOURCE_LEN];
char *data_ptr, *peer_str;
u16_t peer_strlen;
u8_t peer_data_flags;
char *url;
u16_t url_len;
u8_t url_data_flags;
int ret = 0U;
/* get the server URL */
snprintk(pathstr, sizeof(pathstr), "0/%d/0", client_ctx->sec_obj_inst);
ret = lwm2m_engine_get_res_data(pathstr, (void **)&data_ptr,
&peer_strlen, &peer_data_flags);
ret = lwm2m_engine_get_res_data(pathstr, (void **)&url, &url_len,
&url_data_flags);
if (ret < 0) {
return ret;
}
/* TODO: use http parser for URL to get protocol and server */
/* walk forward till colon shifting to lower case */
peer_str = data_ptr;
while (*peer_str != '\0' && *peer_str != ':') {
*peer_str = tolower(*peer_str);
peer_str += 1;
url[url_len] = '\0';
ret = lwm2m_parse_peerinfo(url, &client_ctx->remote_addr,
&client_ctx->use_dtls);
if (ret < 0) {
return ret;
}
/* check to make sure there was a colon */
if (*peer_str != ':') {
return -EINVAL;
}
if (strncmp(data_ptr, "coap:", 5) != 0 &&
strncmp(data_ptr, "coaps:", 6) != 0) {
return -EPROTONOSUPPORT;
}
client_ctx->use_dtls = false;
if (strncmp(data_ptr, "coaps:", 6) == 0) {
#if defined(CONFIG_LWM2M_DTLS_SUPPORT)
client_ctx->use_dtls = true;
#else
return -EPROTONOSUPPORT;
#endif /* CONFIG_LWM2M_DTLS_SUPPORT */
}
/* skip the colons and slashes */
while (*peer_str == ':' || *peer_str == '/') {
peer_str += 1;
}
LOG_DBG("URL: %s", data_ptr);
lwm2m_engine_context_init(client_ctx);
return lwm2m_net_app_start(client_ctx, peer_str,
CONFIG_LWM2M_PEER_PORT);
return lwm2m_socket_start(client_ctx);
}
static int lwm2m_engine_init(struct device *dev)
@ -4100,6 +4159,18 @@ static int lwm2m_engine_init(struct device *dev)
(void)memset(block1_contexts, 0,
sizeof(struct block_context) * NUM_BLOCK1_CONTEXT);
/* start sock receive thread */
k_thread_create(&engine_thread_data,
&engine_thread_stack[0],
K_THREAD_STACK_SIZEOF(engine_thread_stack),
(k_thread_entry_t) socket_receive_loop,
NULL, NULL, NULL,
/* Lowest priority cooperative thread */
K_PRIO_COOP(CONFIG_NUM_COOP_PRIORITIES - 1),
0, K_NO_WAIT);
k_thread_name_set(&engine_thread_data, "lwm2m-sock-recv");
LOG_DBG("LWM2M engine socket receive thread started");
k_delayed_work_init(&periodic_work, lwm2m_engine_service);
k_delayed_work_submit(&periodic_work, K_MSEC(2000));
LOG_DBG("LWM2M engine periodic work started");

View file

@ -109,8 +109,10 @@ void lwm2m_firmware_set_update_result(u8_t result);
u8_t lwm2m_firmware_get_update_result(void);
#endif
/* Network API Layer */
int lwm2m_net_app_start(struct lwm2m_ctx *client_ctx,
char *peer_str, u16_t peer_port);
/* Network Layer */
int lwm2m_socket_add(struct lwm2m_ctx *ctx);
void lwm2m_socket_del(struct lwm2m_ctx *ctx);
int lwm2m_socket_start(struct lwm2m_ctx *client_ctx);
int lwm2m_parse_peerinfo(char *url, struct sockaddr *addr, bool *use_dtls);
#endif /* LWM2M_ENGINE_H */

View file

@ -16,7 +16,7 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME);
#include <string.h>
#include <net/http_parser.h>
#include <net/net_app.h>
#include <net/socket.h>
#include "lwm2m_object.h"
#include "lwm2m_engine.h"
@ -29,7 +29,6 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME);
static struct k_work firmware_work;
static char firmware_uri[URI_LEN];
static struct http_parser_url parsed_uri;
static struct lwm2m_ctx firmware_ctx;
static int firmware_retry;
static struct coap_block_context firmware_block_ctx;
@ -68,14 +67,11 @@ static int transfer_request(struct coap_block_context *ctx,
{
struct lwm2m_message *msg;
int ret;
u16_t off;
u16_t len;
char *cursor;
#if !defined(CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_COAP_PROXY_SUPPORT)
int i;
int path_len;
#else
char *uri_path;
struct http_parser_url parser;
u16_t off, len;
char *next_slash;
#endif
msg = lwm2m_get_message(&firmware_ctx);
@ -99,22 +95,11 @@ static int transfer_request(struct coap_block_context *ctx,
}
#if defined(CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_COAP_PROXY_SUPPORT)
/* if path is not available, off/len will be zero */
off = parsed_uri.field_data[UF_SCHEMA].off;
len = parsed_uri.field_data[UF_SCHEMA].len;
cursor = firmware_uri + off;
/* TODO: convert to lower case */
if (len < 4 || len > 5) {
ret = -EPROTONOSUPPORT;
LOG_ERR("Unsupported schema");
goto cleanup;
}
if (strncmp(cursor, (len == 4 ? "http" : "https"), len) == 0) {
uri_path = COAP2HTTP_PROXY_URI_PATH;
} else if (strncmp(cursor, (len == 4 ? "coap" : "coaps"), len) == 0) {
uri_path = COAP2COAP_PROXY_URI_PATH;
/* TODO: shift to lower case */
if (strncmp(firmware_uri, "http", 4) == 0) {
cursor = COAP2HTTP_PROXY_URI_PATH;
} else if (strncmp(firmware_uri, "coap", 4) == 0) {
cursor = COAP2COAP_PROXY_URI_PATH;
} else {
ret = -EPROTONOSUPPORT;
LOG_ERR("Unsupported schema");
@ -122,50 +107,53 @@ static int transfer_request(struct coap_block_context *ctx,
}
ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH,
uri_path, strlen(uri_path));
cursor, strlen(cursor));
if (ret < 0) {
LOG_ERR("Error adding URI_PATH '%s'", uri_path);
LOG_ERR("Error adding URI_PATH '%s'", cursor);
goto cleanup;
}
#else
/* if path is not available, off/len will be zero */
off = parsed_uri.field_data[UF_PATH].off;
len = parsed_uri.field_data[UF_PATH].len;
cursor = firmware_uri + off;
path_len = 0;
http_parser_url_init(&parser);
ret = http_parser_parse_url(firmware_uri, strlen(firmware_uri), 0,
&parser);
if (ret < 0) {
LOG_ERR("Invalid firmware url: %s", firmware_uri);
ret = -ENOTSUP;
goto cleanup;
}
for (i = 0; i < len; i++) {
if (firmware_uri[off + i] == '/') {
if (path_len > 0) {
/* if path is not available, off/len will be zero */
off = parser.field_data[UF_PATH].off;
len = parser.field_data[UF_PATH].len;
cursor = firmware_uri + off;
/* add path portions (separated by slashes) */
while (len > 0 && (next_slash = strchr(cursor, '/')) != NULL) {
if (next_slash != cursor) {
ret = coap_packet_append_option(&msg->cpkt,
COAP_OPTION_URI_PATH,
cursor, path_len);
cursor,
next_slash - cursor);
if (ret < 0) {
LOG_ERR("Error adding URI_PATH");
goto cleanup;
}
cursor += path_len + 1;
path_len = 0;
} else {
/* skip current slash */
cursor += 1;
}
continue;
}
if (i == len - 1) {
/* skip slash */
len -= (next_slash - cursor) + 1;
cursor = next_slash + 1;
}
if (len > 0) {
/* flush the rest */
ret = coap_packet_append_option(&msg->cpkt,
COAP_OPTION_URI_PATH,
cursor, path_len + 1);
cursor, len);
if (ret < 0) {
LOG_ERR("Error adding URI_PATH");
goto cleanup;
}
break;
}
path_len += 1;
}
#endif
@ -402,15 +390,9 @@ static void do_transmit_timeout_cb(struct lwm2m_message *msg)
static void firmware_transfer(struct k_work *work)
{
int ret, family;
u16_t off;
u16_t len;
char tmp;
int ret;
char *server_addr;
/* Server Peer IP information */
family = AF_INET;
#if defined(CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_COAP_PROXY_SUPPORT)
server_addr = CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_COAP_PROXY_ADDR;
if (strlen(server_addr) >= URI_LEN) {
@ -426,59 +408,21 @@ static void firmware_transfer(struct k_work *work)
server_addr = firmware_uri;
#endif
http_parser_url_init(&parsed_uri);
ret = http_parser_parse_url(server_addr,
strlen(server_addr),
0,
&parsed_uri);
if (ret != 0) {
LOG_ERR("Invalid firmware URI: %s", server_addr);
ret = -ENOTSUP;
ret = lwm2m_parse_peerinfo(server_addr, &firmware_ctx.remote_addr,
&firmware_ctx.use_dtls);
if (ret < 0) {
goto error;
}
/* Check schema and only support coap for now */
if (!(parsed_uri.field_set & (1 << UF_SCHEMA))) {
LOG_ERR("No schema in package uri");
ret = -ENOTSUP;
goto error;
}
/* TODO: enable coaps when DTLS is ready */
off = parsed_uri.field_data[UF_SCHEMA].off;
len = parsed_uri.field_data[UF_SCHEMA].len;
if (len != 4 || memcmp(server_addr + off, "coap", 4)) {
LOG_ERR("Unsupported schema");
ret = -EPROTONOSUPPORT;
goto error;
}
if (!(parsed_uri.field_set & (1 << UF_PORT))) {
/* Set to default port of CoAP */
parsed_uri.port = 5683;
}
off = parsed_uri.field_data[UF_HOST].off;
len = parsed_uri.field_data[UF_HOST].len;
/* truncate host portion */
tmp = server_addr[off + len];
server_addr[off + len] = '\0';
lwm2m_engine_context_init(&firmware_ctx);
firmware_ctx.handle_separate_response = true;
ret = lwm2m_net_app_start(&firmware_ctx, &server_addr[off],
parsed_uri.port);
server_addr[off + len] = tmp;
ret = lwm2m_socket_start(&firmware_ctx);
if (ret < 0) {
LOG_ERR("Could not get an UDP context (err:%d)", ret);
ret = -ENOMSG;
LOG_ERR("Cannot start a firmware-pull connection:%d", ret);
goto error;
}
LOG_INF("Connecting to server %s, port %d", server_addr + off,
parsed_uri.port);
LOG_INF("Connecting to server %s", firmware_uri);
/* reset block transfer context */
coap_block_transfer_init(&firmware_block_ctx,
@ -486,15 +430,11 @@ static void firmware_transfer(struct k_work *work)
ret = transfer_request(&firmware_block_ctx, coap_next_token(), 8,
do_firmware_transfer_reply_cb);
if (ret < 0) {
goto cleanup;
goto error;
}
return;
cleanup:
net_app_close(&firmware_ctx.net_app_ctx);
net_app_release(&firmware_ctx.net_app_ctx);
error:
set_update_result_from_error(ret);
}
@ -507,16 +447,14 @@ int lwm2m_firmware_cancel_transfer(void)
int lwm2m_firmware_start_transfer(char *package_uri)
{
/* free up old context */
if (firmware_ctx.net_app_ctx.is_init) {
net_app_close(&firmware_ctx.net_app_ctx);
net_app_release(&firmware_ctx.net_app_ctx);
/* close old socket */
if (firmware_ctx.sock_fd > 0) {
lwm2m_socket_del(&firmware_ctx);
close(firmware_ctx.sock_fd);
}
(void)memset(&firmware_ctx, 0, sizeof(struct lwm2m_ctx));
firmware_retry = 0;
firmware_ctx.net_init_timeout = NETWORK_INIT_TIMEOUT;
firmware_ctx.net_timeout = NETWORK_CONNECT_TIMEOUT;
k_work_init(&firmware_work, firmware_transfer);
lwm2m_firmware_set_update_state(STATE_DOWNLOADING);