From 1c9fb5488b742b4c5cdfa2c1cf1483354ae3fd31 Mon Sep 17 00:00:00 2001 From: Robert Lubos Date: Wed, 25 Nov 2020 17:12:08 +0100 Subject: [PATCH] net: lwm2m: Implement bootstrap discovery Bootstrap discovery was not implemented properly in the LwM2M engine. Although, there were some indications in the source code that it is implemented, it was not done according to spec (and actually broken). Given that Bootstrap Discovery procedure differs a lot from the regular Device Management Discovery (different permissions, different information returned), it's easier to implement it as a separate function (`bootstrap_discovery()`) instead of making the existing `do_discovery_op()` function even more complicated. Signed-off-by: Robert Lubos --- subsys/net/lib/lwm2m/lwm2m_engine.c | 150 ++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/subsys/net/lib/lwm2m/lwm2m_engine.c b/subsys/net/lib/lwm2m/lwm2m_engine.c index 2c05ce3fcd8..3bd58efc1ae 100644 --- a/subsys/net/lib/lwm2m/lwm2m_engine.c +++ b/subsys/net/lib/lwm2m/lwm2m_engine.c @@ -3390,6 +3390,149 @@ static int bootstrap_delete(struct lwm2m_message *msg) return ret; } + +static inline int bs_discover_fill_enabler(struct lwm2m_message *msg) +{ + char buf[sizeof("lwm2m='" LWM2M_PROTOCOL_VERSION "'")]; + + snprintk(buf, sizeof(buf), "lwm2m=\"%s\"", LWM2M_PROTOCOL_VERSION); + return buf_append(CPKT_BUF_WRITE(msg->out.out_cpkt), buf, strlen(buf)); +} + +static inline int bs_discover_fill_object(struct lwm2m_engine_obj *obj, + struct lwm2m_message *msg) +{ + char buf[sizeof(",")]; + + snprintk(buf, sizeof(buf), ",", obj->obj_id); + return buf_append(CPKT_BUF_WRITE(msg->out.out_cpkt), buf, strlen(buf)); +} + +static int bs_discover_fill_instance(struct lwm2m_engine_obj_inst *obj_inst, + struct lwm2m_message *msg) +{ + char buf[sizeof(",")]; + bool bootstrap_inst; + uint16_t server_id; + int ret; + + snprintk(buf, sizeof(buf), ",", obj_inst->obj->obj_id, + obj_inst->obj_inst_id); + + ret = buf_append(CPKT_BUF_WRITE(msg->out.out_cpkt), buf, strlen(buf)); + if (ret < 0) { + return ret; + } + + /* Security and Server instnaces shall report Short Server ID associated + * with them (but only if not bootstrap instance). + */ + if (obj_inst->obj->obj_id == LWM2M_OBJECT_SECURITY_ID) { + snprintk(buf, sizeof(buf), "0/%d/1", obj_inst->obj_inst_id); + ret = lwm2m_engine_get_bool(buf, &bootstrap_inst); + if (ret < 0) { + return ret; + } + + if (!bootstrap_inst) { + snprintk(buf, sizeof(buf), "0/%d/10", + obj_inst->obj_inst_id); + ret = lwm2m_engine_get_u16(buf, &server_id); + if (ret < 0) { + return ret; + } + + snprintk(buf, sizeof(buf), ";ssid=%d", server_id); + ret = buf_append(CPKT_BUF_WRITE(msg->out.out_cpkt), + buf, strlen(buf)); + if (ret < 0) { + return ret; + } + } + } + + if (obj_inst->obj->obj_id == LWM2M_OBJECT_SERVER_ID) { + snprintk(buf, sizeof(buf), "1/%d/0", obj_inst->obj_inst_id); + ret = lwm2m_engine_get_u16(buf, &server_id); + if (ret < 0) { + return ret; + } + + snprintk(buf, sizeof(buf), ";ssid=%d", server_id); + ret = buf_append(CPKT_BUF_WRITE(msg->out.out_cpkt), + buf, strlen(buf)); + if (ret < 0) { + return ret; + } + } + + return 0; +} + +static int bootstrap_discover(struct lwm2m_message *msg) +{ + struct lwm2m_engine_obj *obj; + struct lwm2m_engine_obj_inst *obj_inst; + int ret; + bool reported = false; + + /* set output content-format */ + ret = coap_append_option_int(msg->out.out_cpkt, + COAP_OPTION_CONTENT_FORMAT, + LWM2M_FORMAT_APP_LINK_FORMAT); + if (ret < 0) { + LOG_ERR("Error setting response content-format: %d", ret); + return ret; + } + + ret = coap_packet_append_payload_marker(msg->out.out_cpkt); + if (ret < 0) { + return ret; + } + + /* + * lwm2m spec 20170208-A sec 5.2.7.3 bootstrap discover on "/" + * - prefixed w/ lwm2m enabler version. e.g. lwm2m="1.0" + * - returns object and object instances only + */ + ret = bs_discover_fill_enabler(msg); + if (ret < 0) { + return ret; + } + + SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_list, obj, node) { + if (msg->path.level > 0 && msg->path.obj_id != obj->obj_id) { + continue; + } + + /* Only report when no instance available */ + if (obj->instance_count == 0U) { + ret = bs_discover_fill_object(obj, msg); + if (ret < 0) { + return ret; + } + + reported = true; + continue; + } + + SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_inst_list, + obj_inst, node) { + if (obj_inst->obj->obj_id != obj->obj_id) { + continue; + } + + ret = bs_discover_fill_instance(obj_inst, msg); + if (ret < 0) { + return ret; + } + + reported = true; + } + } + + return reported ? 0 : -ENOENT; +} #endif static int handle_request(struct coap_packet *request, @@ -3449,6 +3592,7 @@ static int handle_request(struct coap_packet *request, switch (code & COAP_REQUEST_MASK) { #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP) case COAP_METHOD_DELETE: + case COAP_METHOD_GET: if (msg->ctx->bootstrap_mode) { break; } @@ -3705,6 +3849,12 @@ static int handle_request(struct coap_packet *request, break; case LWM2M_OP_DISCOVER: +#if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP) + if (msg->ctx->bootstrap_mode) { + r = bootstrap_discover(msg); + break; + } +#endif r = do_discover_op(msg, well_known); break;