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