Contiki-NG
Loading...
Searching...
No Matches
lwm2m-rd-client.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2015-2018, Yanzi Networks AB.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the copyright holder nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28 * OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/**
32 * \addtogroup lwm2m
33 * @{
34 */
35
36/**
37 * \file
38 * Implementation of the Contiki OMA LWM2M engine
39 * Registration and bootstrap client
40 * \author
41 * Joakim Eriksson <joakime@sics.se>
42 * Niclas Finne <nfi@sics.se>
43 * Joel Hoglund <joel@sics.se>
44 * Carlos Gonzalo Peces <carlosgp143@gmail.com>
45 */
46#include "lwm2m-engine.h"
47#include "lwm2m-object.h"
48#include "lwm2m-device.h"
49#include "lwm2m-plain-text.h"
50#include "lwm2m-json.h"
51#include "lwm2m-rd-client.h"
52#include "coap.h"
53#include "coap-engine.h"
54#include "coap-endpoint.h"
55#include "coap-callback-api.h"
56#include "lwm2m-security.h"
57#include "lib/list.h"
58#include <stdio.h>
59#include <string.h>
60#include <inttypes.h>
61
62#if UIP_CONF_IPV6_RPL
63#include "rpl.h"
64#endif /* UIP_CONF_IPV6_RPL */
65
66#if LWM2M_QUEUE_MODE_ENABLED
67#include "lwm2m-queue-mode.h"
69#endif /* LWM2M_QUEUE_MODE_ENABLED */
70
71/* Log configuration */
72#include "coap-log.h"
73#define LOG_MODULE "lwm2m-rd"
74#define LOG_LEVEL LOG_LEVEL_LWM2M
75
76#ifndef LWM2M_DEFAULT_CLIENT_LIFETIME
77#define LWM2M_DEFAULT_CLIENT_LIFETIME 30 /* sec */
78#endif
79
80#define REMOTE_PORT UIP_HTONS(COAP_DEFAULT_PORT)
81#define BS_REMOTE_PORT UIP_HTONS(5685)
82
83#define STATE_MACHINE_UPDATE_INTERVAL 500
84
85/* The states for the RD client state machine */
86/* When node is unregistered it ends up in UNREGISTERED
87 and this is going to be there until use X or Y kicks it
88 back into INIT again */
89#define INIT 0
90#define WAIT_NETWORK 1
91#define DO_BOOTSTRAP 3
92#define BOOTSTRAP_SENT 4
93#define BOOTSTRAP_DONE 5
94#define DO_REGISTRATION 6
95#define REGISTRATION_SENT 7
96#define REGISTRATION_DONE 8
97#define UPDATE_SENT 9
98#define DEREGISTER 10
99#define DEREGISTER_SENT 11
100#define DEREGISTER_FAILED 12
101#define DEREGISTERED 13
102#if LWM2M_QUEUE_MODE_ENABLED
103#define QUEUE_MODE_AWAKE 14
104#define QUEUE_MODE_SEND_UPDATE 15
105#endif
106
107#define FLAG_RD_DATA_DIRTY 0x01
108#define FLAG_RD_DATA_UPDATE_TRIGGERED 0x02
109#define FLAG_RD_DATA_UPDATE_ON_DIRTY 0x10
110
111LIST(session_info_list);
112
113/* Shared by all sessions, used by only one at a time in the FSM */
114static char query_data[64]; /* allocate some data for queries and updates */
115static uint8_t rd_data[128]; /* allocate some data for the RD */
116
117static coap_timer_t rd_timer; /* Timer to tick the FSM periodically */
118static char default_ep[20];
119
120#if LWM2M_QUEUE_MODE_ENABLED
121static coap_timer_t queue_mode_client_awake_timer; /* Timer to control the client's
122 * awake time
123 */
124static uint8_t queue_mode_client_awake; /* 1 - client is awake,
125 * 0 - client is sleeping
126 */
127static uint16_t queue_mode_client_awake_time; /* The time to be awake */
128/* Callback for the client awake timer */
129static void queue_mode_awake_timer_callback(coap_timer_t *timer);
130#endif
131
132static void check_periodic_observations();
133static void update_callback(coap_callback_request_state_t *callback_state);
134/*---------------------------------------------------------------------------*/
135static int
136set_rd_data(lwm2m_session_info_t *session_info)
137{
138 lwm2m_buffer_t outbuf;
139
140 /* setup the output buffer */
141 outbuf.buffer = rd_data;
142 outbuf.size = sizeof(rd_data);
143 outbuf.len = 0;
144
145 /* this will also set the request payload */
146 session_info->rd_more = lwm2m_engine_set_rd_data(&outbuf, 0);
147 coap_set_payload(session_info->request, rd_data, outbuf.len);
148
149 if(session_info->rd_more) {
150 /* set the first block here */
151 LOG_DBG("Setting block1 in request\n");
152 coap_set_header_block1(session_info->request, 0, 1, sizeof(rd_data));
153 }
154 return outbuf.len;
155}
156/*---------------------------------------------------------------------------*/
157static void
158prepare_update(lwm2m_session_info_t *session_info, int triggered)
159{
160 coap_init_message(session_info->request, COAP_TYPE_CON, COAP_POST, 0);
161 coap_set_header_uri_path(session_info->request, session_info->assigned_ep);
162
163 snprintf(query_data, sizeof(query_data) - 1, "?lt=%d&b=%s", session_info->lifetime, session_info->binding);
164 LOG_DBG("UPDATE:%s %s\n", session_info->assigned_ep, query_data);
165 coap_set_header_uri_query(session_info->request, query_data);
166
167 if((triggered || session_info->rd_flags & FLAG_RD_DATA_UPDATE_ON_DIRTY) && (session_info->rd_flags & FLAG_RD_DATA_DIRTY)) {
168 session_info->rd_flags &= ~FLAG_RD_DATA_DIRTY;
169 set_rd_data(session_info);
170 session_info->rd_callback = update_callback;
171 }
172}
173/*---------------------------------------------------------------------------*/
174static int
175has_network_access(void)
176{
177#if UIP_CONF_IPV6_RPL
178/* NATIVE PLATFORM is not really running RPL */
179#ifndef CONTIKI_TARGET_NATIVE
180 if(rpl_get_any_dag() == NULL) {
181 return 0;
182 }
183#endif
184#endif /* UIP_CONF_IPV6_RPL */
185 return 1;
186}
187/*---------------------------------------------------------------------------*/
188int
189lwm2m_rd_client_is_registered(lwm2m_session_info_t *session_info)
190{
191 return session_info->rd_state == REGISTRATION_DONE || session_info->rd_state == UPDATE_SENT;
192}
193/*---------------------------------------------------------------------------*/
194/* will take another argument when we support multiple sessions */
195void
196lwm2m_rd_client_set_session_callback(lwm2m_session_info_t *session_info, session_callback_t cb)
197{
198 session_info->callback = cb;
199}
200/*---------------------------------------------------------------------------*/
201static void
202perform_session_callback(lwm2m_session_info_t *session_info, int state)
203{
204 if(session_info->callback != NULL) {
205 LOG_DBG("Performing session callback: %d cb:%p\n",
206 state, session_info->callback);
207 session_info->callback(session_info, state);
208 }
209}
210/*---------------------------------------------------------------------------*/
211uint16_t
212lwm2m_rd_client_get_lifetime(lwm2m_session_info_t *session_info)
213{
214 return session_info->lifetime;
215}
216/*---------------------------------------------------------------------------*/
217void
218lwm2m_rd_client_set_lifetime(lwm2m_session_info_t *session_info, uint16_t lifetime)
219{
220 if(lifetime > 0) {
221 session_info->lifetime = lifetime;
222 } else {
223 session_info->lifetime = LWM2M_DEFAULT_CLIENT_LIFETIME;
224 }
225}
226/*---------------------------------------------------------------------------*/
227void
228lwm2m_rd_client_set_endpoint_name(lwm2m_session_info_t *session_info, const char *endpoint)
229{
230 if(endpoint != NULL) {
231 session_info->ep = endpoint;
232 }
233}
234/*---------------------------------------------------------------------------*/
235void
236lwm2m_rd_client_set_default_endpoint_name(const char *endpoint)
237{
238 strncpy(default_ep, endpoint, sizeof(default_ep) - 1);
239 default_ep[sizeof(default_ep) - 1] = '\0';
240}
241/*---------------------------------------------------------------------------*/
242void
243lwm2m_rd_client_set_update_rd(void)
244{
245 lwm2m_session_info_t *session_info = (lwm2m_session_info_t *)list_head(session_info_list);
246 while(session_info != NULL) {
247 session_info->rd_flags |= FLAG_RD_DATA_DIRTY;
248 session_info = session_info->next;
249 }
250}
251/*---------------------------------------------------------------------------*/
252void
253lwm2m_rd_client_set_automatic_update(lwm2m_session_info_t *session_info, int update)
254{
255 session_info->rd_flags = (session_info->rd_flags & ~FLAG_RD_DATA_UPDATE_ON_DIRTY) |
256 (update != 0 ? FLAG_RD_DATA_UPDATE_ON_DIRTY : 0);
257}
258/*---------------------------------------------------------------------------*/
259void
260lwm2m_rd_client_register_with_server(lwm2m_session_info_t *session_info,
261 const coap_endpoint_t *server,
262 lwm2m_rd_client_server_type_t server_type)
263{
264 if(session_info->ep == NULL) {
265 session_info->ep = default_ep;
266 }
267 /* default binding U = UDP, UQ = UDP Q-mode*/
268#if LWM2M_QUEUE_MODE_CONF_ENABLED
269 session_info->binding = "UQ";
270 /* Enough margin to ensure that the client is not unregistered (we
271 * do not know the time it can stay awake)
272 */
273 session_info->lifetime = (LWM2M_QUEUE_MODE_DEFAULT_CLIENT_SLEEP_TIME / 1000) * 2;
274#else
275 session_info->binding = "U";
276 if(session_info->lifetime == 0) {
277 session_info->lifetime = LWM2M_DEFAULT_CLIENT_LIFETIME;
278 }
279#endif /* LWM2M_QUEUE_MODE_CONF_ENABLED */
280
281 session_info->rd_flags = FLAG_RD_DATA_UPDATE_ON_DIRTY;
282 session_info->has_bs_server_info = 0;
283 session_info->has_registration_server_info = 0;
284 session_info->wait_until_network_check = 0;
285 session_info->last_update = 0;
286 session_info->last_rd_progress = 0;
287 session_info->bootstrapped = 0;
288 session_info->rd_state = INIT;
289
290 if(server_type == LWM2M_RD_CLIENT_BOOTSTRAP_SERVER) {
291 coap_endpoint_copy(&session_info->bs_server_ep, server);
292 session_info->has_bs_server_info = 1;
293 session_info->use_server_type = LWM2M_RD_CLIENT_BOOTSTRAP_SERVER;
294 } else {
295 coap_endpoint_copy(&session_info->server_ep, server);
296 session_info->use_server_type = LWM2M_RD_CLIENT_LWM2M_SERVER;
297 session_info->has_registration_server_info = 1;
298 }
299
300 list_add(session_info_list, session_info); /* Add to the list of sessions */
301}
302/*---------------------------------------------------------------------------*/
303int
304lwm2m_rd_client_deregister(lwm2m_session_info_t *session_info)
305{
306 if(lwm2m_rd_client_is_registered(session_info)) {
307 session_info->rd_state = DEREGISTER;
308 return 1;
309 }
310 /* Not registered */
311 return 0;
312}
313/*---------------------------------------------------------------------------*/
314static lwm2m_session_info_t *
315get_session_info_from_server_ep(const coap_endpoint_t *server_ep)
316{
317 lwm2m_session_info_t *session_info = (lwm2m_session_info_t *)list_head(session_info_list);
318 while(session_info != NULL) {
319 if(coap_endpoint_cmp(&session_info->server_ep, server_ep)) {
320 return session_info;
321 }
322 session_info = session_info->next;
323 }
324 return NULL;
325}
326/*---------------------------------------------------------------------------*/
327void
328lwm2m_rd_client_update_triggered(const coap_endpoint_t *server_ep)
329{
330 lwm2m_session_info_t *session_info = get_session_info_from_server_ep(server_ep);
331 if(session_info) {
332 session_info->rd_flags |= FLAG_RD_DATA_UPDATE_TRIGGERED;
333 }
334 /* Here we need to do an CoAP timer poll - to get a quick request transmission! */
335}
336/*---------------------------------------------------------------------------*/
337/*
338 * A client initiated bootstrap starts with a POST to /bs?ep={session_info.ep},
339 * on the bootstrap server. The server should reply with 2.04.
340 * The server will thereafter do DELETE and or PUT to write new client objects.
341 * The bootstrap finishes with the server doing POST to /bs on the client.
342 *
343 * Page 64 in 07 April 2016 spec.
344 *
345 * TODO
346 */
347static void
348bootstrap_callback(coap_callback_request_state_t *callback_state)
349{
350 coap_request_state_t *state = &callback_state->state;
351 LOG_DBG("Bootstrap callback Response: %d, ", state->response != NULL);
352
353 lwm2m_session_info_t *session_info = (lwm2m_session_info_t *)state->user_data;
354
355 if(state->status == COAP_REQUEST_STATUS_RESPONSE) {
356 if(CHANGED_2_04 == state->response->code) {
357 LOG_DBG_("Considered done!\n");
358 session_info->rd_state = BOOTSTRAP_DONE;
359 return;
360 }
361 /* Possible error response codes are 4.00 Bad request & 4.15 Unsupported content format */
362 LOG_DBG_("Failed with code %d. Retrying\n", state->response->code);
363 /* TODO Application callback? */
364 session_info->rd_state = INIT;
365 } else if(state->status == COAP_REQUEST_STATUS_TIMEOUT) {
366 LOG_DBG_("Server not responding! Retry?");
367 session_info->rd_state = DO_BOOTSTRAP;
368 } else if(state->status == COAP_REQUEST_STATUS_FINISHED) {
369 LOG_DBG_("Request finished. Ignore\n");
370 } else {
371 LOG_DBG_("Unexpected error! Retry?");
372 session_info->rd_state = DO_BOOTSTRAP;
373 }
374}
375/*---------------------------------------------------------------------------*/
376static void
377produce_more_rd(lwm2m_session_info_t *session_info)
378{
379 lwm2m_buffer_t outbuf;
380
381 LOG_DBG("GOT Continue!\n");
382
383 /* setup the output buffer */
384 outbuf.buffer = rd_data;
385 outbuf.size = sizeof(rd_data);
386 outbuf.len = 0;
387
388 session_info->rd_block1++;
389
390 /* this will also set the request payload */
391 session_info->rd_more = lwm2m_engine_set_rd_data(&outbuf, session_info->rd_block1);
392 coap_set_payload(session_info->request, rd_data, outbuf.len);
393
394 LOG_DBG("Setting block1 in request - block: %d more: %d\n",
395 (int)session_info->rd_block1, (int)session_info->rd_more);
396 coap_set_header_block1(session_info->request, session_info->rd_block1, session_info->rd_more, sizeof(rd_data));
397
398 coap_send_request(&session_info->rd_request_state, &session_info->server_ep, session_info->request, session_info->rd_callback);
399}
400/*---------------------------------------------------------------------------*/
401static void
402block1_rd_callback(coap_timer_t *timer)
403{
404 produce_more_rd((lwm2m_session_info_t *)timer->user_data);
405}
406/*---------------------------------------------------------------------------*/
407/*
408 * Page 65-66 in 07 April 2016 spec.
409 */
410static void
411registration_callback(coap_callback_request_state_t *callback_state)
412{
413 coap_request_state_t *state = &callback_state->state;
414 LOG_DBG("Registration callback. Status: %d. Response: %d, ", state->status, state->response != NULL);
415 lwm2m_session_info_t *session_info = (lwm2m_session_info_t *)state->user_data;
416
417 if(state->status == COAP_REQUEST_STATUS_RESPONSE) {
418 /* check state and possibly set registration to done */
419 /* If we get a continue - we need to call the rd generator one more time */
420
421 if(CONTINUE_2_31 == state->response->code) {
422 /* We assume that size never change?! */
423 coap_get_header_block1(state->response, &session_info->rd_block1, NULL, NULL, NULL);
424 coap_timer_set_user_data(&session_info->block1_timer, (void *)session_info);
425 coap_timer_set_callback(&session_info->block1_timer, block1_rd_callback);
426 coap_timer_set(&session_info->block1_timer, 1); /* delay 1 ms */
427 LOG_DBG_("Continue\n");
428 } else if(CREATED_2_01 == state->response->code) {
429 if(state->response->location_path_len < LWM2M_RD_CLIENT_ASSIGNED_ENDPOINT_MAX_LEN) {
430 memcpy(session_info->assigned_ep, state->response->location_path,
431 state->response->location_path_len);
432 session_info->assigned_ep[state->response->location_path_len] = 0;
433 /* if we decide to not pass the lt-argument on registration, we should force an initial "update" to register lifetime with server */
434#if LWM2M_QUEUE_MODE_ENABLED
435#if LWM2M_QUEUE_MODE_INCLUDE_DYNAMIC_ADAPTATION
436 if(lwm2m_queue_mode_get_dynamic_adaptation_flag()) {
437 lwm2m_queue_mode_set_first_request();
438 }
439#endif
440 lwm2m_rd_client_fsm_execute_queue_mode_awake(session_info); /* Avoid 500 ms delay and move directly to the state*/
441#else
442 session_info->rd_state = REGISTRATION_DONE;
443#endif
444 /* remember the last reg time */
445 session_info->last_update = coap_timer_uptime();
446 LOG_DBG_("Done (assigned EP='%s')!\n", session_info->assigned_ep);
447 perform_session_callback(session_info, LWM2M_RD_CLIENT_REGISTERED);
448 return;
449 }
450
451 LOG_DBG_("failed to handle assigned EP: '");
452 LOG_DBG_COAP_STRING(state->response->location_path,
453 state->response->location_path_len);
454 LOG_DBG_("'. Re-init network.\n");
455 } else {
456 /* Possible error response codes are 4.00 Bad request & 4.03 Forbidden */
457 LOG_DBG_("failed with code %d. Re-init network\n", state->response->code);
458 }
459 /* TODO Application callback? */
460 session_info->rd_state = INIT;
461 } else if(state->status == COAP_REQUEST_STATUS_TIMEOUT) {
462 LOG_DBG_("Server not responding, trying to reconnect\n");
463 session_info->rd_state = INIT;
464 } else if(state->status == COAP_REQUEST_STATUS_FINISHED) {
465 LOG_DBG_("Request finished. Ignore\n");
466 } else {
467 LOG_DBG_("Unexpected error, trying to reconnect\n");
468 session_info->rd_state = INIT;
469 }
470}
471/*---------------------------------------------------------------------------*/
472/*
473 * Page 65-66 in 07 April 2016 spec.
474 */
475static void
476update_callback(coap_callback_request_state_t *callback_state)
477{
478 coap_request_state_t *state = &callback_state->state;
479 LOG_DBG("Update callback. Status: %d. Response: %d, ", state->status, state->response != NULL);
480
481 lwm2m_session_info_t *session_info = (lwm2m_session_info_t *)state->user_data;
482
483 if(state->status == COAP_REQUEST_STATUS_RESPONSE) {
484 /* If we get a continue - we need to call the rd generator one more time */
485 if(CONTINUE_2_31 == state->response->code) {
486 /* We assume that size never change?! */
487 LOG_DBG_("Continue\n");
488 coap_get_header_block1(state->response, &session_info->rd_block1, NULL, NULL, NULL);
489 coap_timer_set_callback(&session_info->block1_timer, block1_rd_callback);
490 coap_timer_set(&session_info->block1_timer, 1); /* delay 1 ms */
491 } else if(CHANGED_2_04 == state->response->code) {
492 LOG_DBG_("Done!\n");
493 /* remember the last reg time */
494 session_info->last_update = coap_timer_uptime();
495#if LWM2M_QUEUE_MODE_ENABLED
496 /* If it has been waked up by a notification, send the stored notifications in queue */
497 if(lwm2m_queue_mode_is_waked_up_by_notification()) {
498
499 lwm2m_queue_mode_clear_waked_up_by_notification();
500 lwm2m_notification_queue_send_notifications();
501 }
502#if LWM2M_QUEUE_MODE_INCLUDE_DYNAMIC_ADAPTATION
503 if(lwm2m_queue_mode_get_dynamic_adaptation_flag()) {
504 lwm2m_queue_mode_set_first_request();
505 }
506#endif /* LWM2M_QUEUE_MODE_INCLUDE_DYNAMIC_ADAPTATION */
507 lwm2m_rd_client_fsm_execute_queue_mode_awake(session_info); /* Avoid 500 ms delay and move directly to the state*/
508#else
509
510 session_info->rd_state = REGISTRATION_DONE;
511 session_info->rd_flags &= ~FLAG_RD_DATA_UPDATE_TRIGGERED;
512#endif /* LWM2M_QUEUE_MODE_DEFAULT_CLIENT_SLEEP_TIME */
513 } else {
514 /* Possible error response codes are 4.00 Bad request & 4.04 Not Found */
515 LOG_DBG_("Failed with code %d. Retrying registration\n",
516 state->response->code);
517 session_info->rd_state = DO_REGISTRATION;
518 }
519 } else if(state->status == COAP_REQUEST_STATUS_TIMEOUT) {
520 LOG_DBG_("Server not responding, trying to reconnect\n");
521 session_info->rd_state = INIT;
522 } else if(state->status == COAP_REQUEST_STATUS_FINISHED) {
523 LOG_DBG_("Request finished. Ignore\n");
524 } else {
525 LOG_DBG_("Unexpected error, trying to reconnect\n");
526 session_info->rd_state = INIT;
527 }
528}
529/*---------------------------------------------------------------------------*/
530static void
531deregister_callback(coap_callback_request_state_t *callback_state)
532{
533 coap_request_state_t *state = &callback_state->state;
534 LOG_DBG("Deregister callback. Status: %d. Response Code: %d\n",
535 state->status,
536 state->response != NULL ? state->response->code : 0);
537
538 lwm2m_session_info_t *session_info = (lwm2m_session_info_t *)state->user_data;
539
540 if(state->status == COAP_REQUEST_STATUS_RESPONSE && (DELETED_2_02 == state->response->code)) {
541 LOG_DBG("Deregistration success\n");
542 session_info->rd_state = DEREGISTERED;
543 perform_session_callback(session_info, LWM2M_RD_CLIENT_DEREGISTERED);
544 } else {
545 LOG_DBG("Deregistration failed\n");
546 if(session_info->rd_state == DEREGISTER_SENT) {
547 session_info->rd_state = DEREGISTER_FAILED;
548 perform_session_callback(session_info, LWM2M_RD_CLIENT_DEREGISTER_FAILED);
549 }
550 }
551}
552/*---------------------------------------------------------------------------*/
553#if LWM2M_QUEUE_MODE_ENABLED
554static int
555all_sessions_in_queue_mode_state(void)
556{
557 lwm2m_session_info_t *session_info = (lwm2m_session_info_t *)list_head(session_info_list);
558 while(session_info != NULL) {
559 if(((session_info->rd_state & 0xF) != 0xE)) {
560 return 0;
561 }
562 session_info = session_info->next;
563 }
564 return 1;
565}
566/*---------------------------------------------------------------------------*/
567static int
568all_sessions_in_queue_mode_awake(void)
569{
570 lwm2m_session_info_t *session_info = (lwm2m_session_info_t *)list_head(session_info_list);
571 while(session_info != NULL) {
572 if(session_info->rd_state != QUEUE_MODE_AWAKE) {
573 return 0;
574 }
575 session_info = session_info->next;
576 }
577 return 1;
578}
579#endif /* LWM2M_QUEUE_MODE_ENABLED */
580/*---------------------------------------------------------------------------*/
581/* CoAP timer callback */
582static void
583periodic_process(coap_timer_t *timer)
584{
585 uint64_t now;
586
587 /* reschedule the CoAP timer */
588#if LWM2M_QUEUE_MODE_ENABLED
589 /* In Queue Mode, the machine is not executed periodically, but with the awake/sleeping times */
590 if(!all_sessions_in_queue_mode_state()) {
591 coap_timer_reset(&rd_timer, STATE_MACHINE_UPDATE_INTERVAL);
592 }
593#else
594 coap_timer_reset(&rd_timer, STATE_MACHINE_UPDATE_INTERVAL);
595#endif
596
597 now = coap_timer_uptime();
598
599 lwm2m_session_info_t *session_info = (lwm2m_session_info_t *)list_head(session_info_list);
600 while(session_info != NULL) {
601
602 LOG_DBG("RD Client with assigned ep: %s - state: %d, ms: %lu\n", session_info->assigned_ep, session_info->rd_state,
603 (unsigned long)coap_timer_uptime());
604
605 switch(session_info->rd_state) {
606 case INIT:
607 LOG_DBG("RD Client started with endpoint '%s' and client lifetime %d\n", session_info->ep, session_info->lifetime);
608 session_info->rd_state = WAIT_NETWORK;
609 break;
610 case WAIT_NETWORK:
611 if(now > session_info->wait_until_network_check) {
612 /* check each 10 seconds before next check */
613 LOG_DBG("Checking for network... %lu\n",
614 (unsigned long)session_info->wait_until_network_check);
615 session_info->wait_until_network_check = now + 10000;
616 if(has_network_access()) {
617 /* Either do bootstrap then registration */
618 if(session_info->use_server_type == LWM2M_RD_CLIENT_BOOTSTRAP_SERVER) {
619 session_info->rd_state = DO_BOOTSTRAP;
620 } else {
621 session_info->rd_state = DO_REGISTRATION;
622 }
623 }
624 /* Otherwise wait until for a network to join */
625 }
626 break;
627 case DO_BOOTSTRAP:
628 if(session_info->use_server_type == LWM2M_RD_CLIENT_BOOTSTRAP_SERVER &&
629 session_info->bootstrapped == 0 &&
630 session_info->has_bs_server_info) {
631
632 /* prepare request, TID is set by COAP_BLOCKING_REQUEST() */
633 coap_init_message(session_info->request, COAP_TYPE_CON, COAP_POST, 0);
634 coap_set_header_uri_path(session_info->request, "/bs");
635
636 snprintf(query_data, sizeof(query_data) - 1, "?ep=%s", session_info->ep);
637 coap_set_header_uri_query(session_info->request, query_data);
638 LOG_INFO("Registering ID with bootstrap server [");
639 LOG_INFO_COAP_EP(&session_info->bs_server_ep);
640 LOG_INFO_("] as '%s'\n", query_data);
641 /* Add session info as user data to use it in the callbacks */
642 session_info->rd_request_state.state.user_data = (void *)session_info;
643 if(coap_send_request(&session_info->rd_request_state, &session_info->bs_server_ep,
644 session_info->request, bootstrap_callback)) {
645 session_info->rd_state = BOOTSTRAP_SENT;
646 }
647 }
648 break;
649 case BOOTSTRAP_SENT:
650 /* Just wait for bootstrap to be done... */
651 break;
652 case BOOTSTRAP_DONE:
653 /* check that we should still use bootstrap */
654 if(session_info->use_server_type == LWM2M_RD_CLIENT_BOOTSTRAP_SERVER) {
655 lwm2m_security_server_t *security;
656 LOG_DBG("*** Bootstrap - checking for server info...\n");
657 /* get the security object - ignore bootstrap servers */
658 for(security = lwm2m_security_get_first();
659 security != NULL;
660 security = lwm2m_security_get_next(security)) {
661 if(security->bootstrap == 0) {
662 break;
663 }
664 }
665
666 if(security != NULL) {
667 /* get the server URI */
668 if(security->server_uri_len > 0) {
669 uint8_t secure = 0;
670
671 LOG_DBG("**** Found security instance using: ");
672 LOG_DBG_COAP_STRING((const char *)security->server_uri,
673 security->server_uri_len);
674 LOG_DBG_(" (len %d) \n", security->server_uri_len);
675 /* TODO Should verify it is a URI */
676 /* Check if secure */
677 secure = strncmp((const char *)security->server_uri,
678 "coaps:", 6) == 0;
679
680 if(!coap_endpoint_parse((const char *)security->server_uri,
681 security->server_uri_len,
682 &session_info->server_ep)) {
683 LOG_DBG("Failed to parse server URI!\n");
684 } else {
685 LOG_DBG("Server address:");
686 LOG_DBG_COAP_EP(&session_info->server_ep);
687 LOG_DBG_("\n");
688 if(secure) {
689 LOG_DBG("Secure CoAP requested but not supported - can not bootstrap\n");
690 } else {
691 lwm2m_rd_client_register_with_server(session_info, &session_info->server_ep, LWM2M_RD_CLIENT_LWM2M_SERVER);
692 session_info->bootstrapped++;
693 }
694 }
695 } else {
696 LOG_DBG("** failed to parse URI ");
697 LOG_DBG_COAP_STRING((const char *)security->server_uri,
698 security->server_uri_len);
699 LOG_DBG_("\n");
700 }
701 }
702
703 /* if we did not register above - then fail this and restart... */
704 if(session_info->bootstrapped == 0) {
705 /* Not ready. Lets retry with the bootstrap server again */
706 session_info->rd_state = DO_BOOTSTRAP;
707 } else {
708 session_info->rd_state = DO_REGISTRATION;
709 }
710 }
711 break;
712 case DO_REGISTRATION:
713 if(!coap_endpoint_is_connected(&session_info->server_ep)) {
714 /* Not connected... wait a bit... and retry connection */
715 coap_endpoint_connect(&session_info->server_ep);
716 LOG_DBG("Wait until connected... \n");
717 return;
718 }
719
720 if(session_info->use_server_type == LWM2M_RD_CLIENT_LWM2M_SERVER &&
721 !lwm2m_rd_client_is_registered(session_info) &&
722 session_info->has_registration_server_info) {
723 int len;
724
725 /* prepare request, TID was set by COAP_BLOCKING_REQUEST() */
726 coap_init_message(session_info->request, COAP_TYPE_CON, COAP_POST, 0);
727 coap_set_header_uri_path(session_info->request, "/rd");
728
729 snprintf(query_data, sizeof(query_data) - 1, "?lwm2m=%s&ep=%s&lt=%d&b=%s",
730 LWM2M_PROTOCOL_VERSION, session_info->ep, session_info->lifetime, session_info->binding);
731 coap_set_header_uri_query(session_info->request, query_data);
732
733 len = set_rd_data(session_info);
734 session_info->rd_callback = registration_callback;
735
736 LOG_INFO("Registering with [");
737 LOG_INFO_COAP_EP(&session_info->server_ep);
738 LOG_INFO_("] lwm2m endpoint '%s': '", query_data);
739 if(len) {
740 LOG_INFO_COAP_STRING((const char *)rd_data, len);
741 }
742 LOG_INFO_("' More:%d\n", session_info->rd_more);
743
744 /* Add session info as user data to use it in the callbacks */
745 session_info->rd_request_state.state.user_data = (void *)session_info;
746 if(coap_send_request(&session_info->rd_request_state, &session_info->server_ep,
747 session_info->request, registration_callback)) {
748
749 session_info->rd_state = REGISTRATION_SENT;
750 }
751 session_info->last_rd_progress = coap_timer_uptime();
752 }
753 break;
754 case REGISTRATION_SENT:
755 /* just wait until the callback kicks us to the next state... */
756 break;
757 case REGISTRATION_DONE:
758 /* All is done! */
759
760 check_periodic_observations(); /* TODO: manage periodic observations */
761
762 /* check if it is time for the next update */
763 if((session_info->rd_flags & FLAG_RD_DATA_UPDATE_TRIGGERED) ||
764 ((uint32_t)session_info->lifetime * 500) <= now - session_info->last_update) {
765 /* triggered or time to send an update to the server, at half-time! sec vs ms */
766 prepare_update(session_info, session_info->rd_flags & FLAG_RD_DATA_UPDATE_TRIGGERED);
767
768 /* Add session info as user data to use it in the callbacks */
769 session_info->rd_request_state.state.user_data = (void *)session_info;
770 if(coap_send_request(&session_info->rd_request_state, &session_info->server_ep, session_info->request,
771 update_callback)) {
772 session_info->rd_state = UPDATE_SENT;
773 }
774 session_info->last_rd_progress = coap_timer_uptime();
775 }
776 break;
777
778#if LWM2M_QUEUE_MODE_ENABLED
779 case QUEUE_MODE_AWAKE:
780 LOG_DBG("Queue Mode: Client is AWAKE at %lu\n", (unsigned long)coap_timer_uptime());
781 if((queue_mode_client_awake = all_sessions_in_queue_mode_awake())) {
782 queue_mode_client_awake_time = lwm2m_queue_mode_get_awake_time();
783 coap_timer_set(&queue_mode_client_awake_timer, queue_mode_client_awake_time);
784 }
785 break;
786 case QUEUE_MODE_SEND_UPDATE:
787 /* Define this macro to make the necessary actions for waking up,
788 * depending on the platform
789 */
790#ifdef LWM2M_QUEUE_MODE_WAKE_UP
791 LWM2M_QUEUE_MODE_WAKE_UP();
792#endif /* LWM2M_QUEUE_MODE_WAKE_UP */
793 prepare_update(session_info, session_info->rd_flags & FLAG_RD_DATA_UPDATE_TRIGGERED);
794 /* Add session info as user data to use it in the callbacks */
795 session_info->rd_request_state.state.user_data = (void *)session_info;
796 if(coap_send_request(&session_info->rd_request_state, &session_info->server_ep, session_info->request,
797 update_callback)) {
798 session_info->rd_state = UPDATE_SENT;
799 }
800 session_info->last_rd_progress = coap_timer_uptime();
801 break;
802#endif /* LWM2M_QUEUE_MODE_ENABLED */
803
804 case UPDATE_SENT:
805 /* just wait until the callback kicks us to the next state... */
806 break;
807 case DEREGISTER:
808 LOG_INFO("DEREGISTER %s\n", session_info->assigned_ep);
809 coap_init_message(session_info->request, COAP_TYPE_CON, COAP_DELETE, 0);
810 coap_set_header_uri_path(session_info->request, session_info->assigned_ep);
811
812 /* Add session info as user data to use it in the callbacks */
813 session_info->rd_request_state.state.user_data = (void *)session_info;
814 if(coap_send_request(&session_info->rd_request_state, &session_info->server_ep, session_info->request,
815 deregister_callback)) {
816 session_info->rd_state = DEREGISTER_SENT;
817 }
818 break;
819 case DEREGISTER_SENT:
820 break;
821 case DEREGISTER_FAILED:
822 break;
823 case DEREGISTERED:
824 break;
825
826 default:
827 LOG_WARN("Unhandled state: %d\n", session_info->rd_state);
828 }
829 session_info = session_info->next;
830 }
831}
832/*---------------------------------------------------------------------------*/
833void
834lwm2m_rd_client_init(const char *ep)
835{
836 lwm2m_rd_client_set_default_endpoint_name(ep);
837
838 /* call the RD client periodically */
839 coap_timer_set_callback(&rd_timer, periodic_process);
840 coap_timer_set(&rd_timer, STATE_MACHINE_UPDATE_INTERVAL);
841#if LWM2M_QUEUE_MODE_ENABLED
842 coap_timer_set_callback(&queue_mode_client_awake_timer, queue_mode_awake_timer_callback);
843#endif
844}
845/*---------------------------------------------------------------------------*/
846static void
847check_periodic_observations(void)
848{
849/* TODO */
850}
851/*---------------------------------------------------------------------------*/
852/*
853 * Queue Mode Support
854 */
855#if LWM2M_QUEUE_MODE_ENABLED
856/*---------------------------------------------------------------------------*/
857void
858lwm2m_rd_client_restart_client_awake_timer(void)
859{
860 coap_timer_set(&queue_mode_client_awake_timer, queue_mode_client_awake_time);
861}
862/*---------------------------------------------------------------------------*/
863uint8_t
864lwm2m_rd_client_is_client_awake(void)
865{
866 return queue_mode_client_awake;
867}
868/*---------------------------------------------------------------------------*/
869static void
870queue_mode_awake_timer_callback(coap_timer_t *timer)
871{
872 /* Timer has expired, no requests has been received, client can go to sleep */
873 LOG_DBG("Queue Mode: Client is SLEEPING at %lu\n", (unsigned long)coap_timer_uptime());
874 queue_mode_client_awake = 0;
875
876 lwm2m_session_info_t *session_info = (lwm2m_session_info_t *)list_head(session_info_list);
877 while(session_info != NULL) {
878 session_info->rd_state = QUEUE_MODE_SEND_UPDATE;
879 session_info = session_info->next;
880 }
881 coap_timer_set(&rd_timer, lwm2m_queue_mode_get_sleep_time());
882 /* Define this macro to enter sleep mode depending on the platform */
883#ifdef LWM2M_QUEUE_MODE_SLEEP_MS
884 LWM2M_QUEUE_MODE_SLEEP_MS(lwm2m_queue_mode_get_sleep_time());
885#endif /* LWM2M_QUEUE_MODE_SLEEP_MS */
886}
887/*---------------------------------------------------------------------------*/
888void
889lwm2m_rd_client_fsm_execute_queue_mode_awake(lwm2m_session_info_t *session_info)
890{
891 coap_timer_stop(&rd_timer);
892 session_info->rd_state = QUEUE_MODE_AWAKE;
893 periodic_process(&rd_timer);
894}
895/*---------------------------------------------------------------------------*/
896void
897lwm2m_rd_client_fsm_execute_queue_mode_update(lwm2m_session_info_t *session_info)
898{
899 coap_timer_stop(&rd_timer);
900 session_info->rd_state = QUEUE_MODE_SEND_UPDATE;
901 periodic_process(&rd_timer);
902}
903/*---------------------------------------------------------------------------*/
904#endif /* LWM2M_QUEUE_MODE_ENABLED */
905/*---------------------------------------------------------------------------*/
906/** @} */
Callback API for doing CoAP requests Adapted from the blocking API.
API to address CoAP endpoints.
CoAP engine implementation.
Log support for CoAP.
An implementation of the Constrained Application Protocol (RFC 7252).
static uint64_t coap_timer_uptime(void)
Get the time since boot in milliseconds.
Definition coap-timer.h:83
void coap_timer_reset(coap_timer_t *timer, uint64_t time)
Reset a CoAP timer to expire a specified time after the last expiration time.
Definition coap-timer.c:110
void coap_timer_stop(coap_timer_t *timer)
Stop a pending CoAP timer.
Definition coap-timer.c:92
static void coap_timer_set_user_data(coap_timer_t *timer, void *data)
Attach user data to a CoAP timer.
Definition coap-timer.h:130
void coap_timer_set(coap_timer_t *timer, uint64_t time)
Set a CoAP timer to expire after the specified time.
Definition coap-timer.c:103
static void coap_timer_set_callback(coap_timer_t *timer, void(*callback)(coap_timer_t *))
Set a callback function to be called when a CoAP timer expires.
Definition coap-timer.h:105
int coap_endpoint_cmp(const coap_endpoint_t *e1, const coap_endpoint_t *e2)
Compare two CoAP endpoints.
Definition coap-uip.c:163
int coap_endpoint_parse(const char *text, size_t size, coap_endpoint_t *ep)
Parse a CoAP endpoint.
Definition coap-uip.c:201
int coap_endpoint_connect(coap_endpoint_t *ep)
Request a connection to a CoAP endpoint.
Definition coap-uip.c:287
void coap_endpoint_copy(coap_endpoint_t *dest, const coap_endpoint_t *src)
Copy a CoAP endpoint from one memory area to another.
Definition coap-uip.c:154
int coap_endpoint_is_connected(const coap_endpoint_t *ep)
Check if a CoAP endpoint is connected.
Definition coap-uip.c:250
int coap_send_request(coap_callback_request_state_t *callback_state, coap_endpoint_t *endpoint, coap_message_t *request, void(*callback)(coap_callback_request_state_t *callback_state))
Send a CoAP request to a remote endpoint.
#define LIST(name)
Declare a linked list.
Definition list.h:90
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition list.c:71
static void * list_head(const_list_t list)
Get a pointer to the first element of a list.
Definition list.h:169
Linked list manipulation routines.
Header file for the Contiki OMA LWM2M device.
Header file for the Contiki OMA LWM2M engine.
Header file for the Contiki OMA LWM2M JSON writer.
Header file for functions to manage the queue to store notifications when waiting for the response to...
Header file for the LWM2M object API.
Header file for the Contiki OMA LWM2M plain text reader / writer.
Header file for the Contiki OMA LWM2M Queue Mode implementation to manage the parameters.
Header file for the Contiki OMA LWM2M Registration and Bootstrap Client.
A timer.
Definition timer.h:84