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_callback_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 
122 static char query_data[64]; /* allocate some data for queries and updates */
123 static uint8_t rd_data[128]; /* allocate some data for the RD */
124 
125 static uint32_t rd_block1;
126 static uint8_t rd_more;
127 static coap_timer_t rd_timer;
128 static void (*rd_callback)(coap_callback_request_state_t *callback_state);
129 
130 static coap_timer_t block1_timer;
131 
132 #if LWM2M_QUEUE_MODE_ENABLED
133 static coap_timer_t queue_mode_client_awake_timer; /* Timer to control the client's
134  * awake time
135  */
136 static uint8_t queue_mode_client_awake; /* 1 - client is awake,
137  * 0 - client is sleeping
138  */
139 static uint16_t queue_mode_client_awake_time; /* The time to be awake */
140 /* Callback for the client awake timer */
141 static void queue_mode_awake_timer_callback(coap_timer_t *timer);
142 #endif
143 
144 static void check_periodic_observations();
145 static void update_callback(coap_callback_request_state_t *callback_state);
146 
147 static int
148 set_rd_data(coap_message_t *request)
149 {
150  lwm2m_buffer_t outbuf;
151 
152  /* setup the output buffer */
153  outbuf.buffer = rd_data;
154  outbuf.size = sizeof(rd_data);
155  outbuf.len = 0;
156 
157  /* this will also set the request payload */
158  rd_more = lwm2m_engine_set_rd_data(&outbuf, 0);
159  coap_set_payload(request, rd_data, outbuf.len);
160 
161  if(rd_more) {
162  /* set the first block here */
163  LOG_DBG("Setting block1 in request\n");
164  coap_set_header_block1(request, 0, 1, sizeof(rd_data));
165  }
166  return outbuf.len;
167 }
168 /*---------------------------------------------------------------------------*/
169 static void
170 prepare_update(coap_message_t *request, int triggered)
171 {
172  coap_init_message(request, COAP_TYPE_CON, COAP_POST, 0);
173  coap_set_header_uri_path(request, session_info.assigned_ep);
174 
175  snprintf(query_data, sizeof(query_data) - 1, "?lt=%d&b=%s", session_info.lifetime, session_info.binding);
176  LOG_DBG("UPDATE:%s %s\n", session_info.assigned_ep, query_data);
177  coap_set_header_uri_query(request, query_data);
178 
179  if((triggered || rd_flags & FLAG_RD_DATA_UPDATE_ON_DIRTY) && (rd_flags & FLAG_RD_DATA_DIRTY)) {
180  rd_flags &= ~FLAG_RD_DATA_DIRTY;
181  set_rd_data(request);
182  rd_callback = update_callback;
183  }
184 }
185 /*---------------------------------------------------------------------------*/
186 static int
187 has_network_access(void)
188 {
189 #if UIP_CONF_IPV6_RPL
190 /* NATIVE PLATFORM is not really running RPL */
191 #ifndef CONTIKI_TARGET_NATIVE
192  if(rpl_get_any_dag() == NULL) {
193  return 0;
194  }
195 #endif
196 #endif /* UIP_CONF_IPV6_RPL */
197  return 1;
198 }
199 /*---------------------------------------------------------------------------*/
200 int
201 lwm2m_rd_client_is_registered(void)
202 {
203  return rd_state == REGISTRATION_DONE || rd_state == UPDATE_SENT;
204 }
205 /*---------------------------------------------------------------------------*/
206 void
207 lwm2m_rd_client_use_bootstrap_server(int use)
208 {
209  session_info.use_bootstrap = use != 0;
210  if(session_info.use_bootstrap) {
211  rd_state = INIT;
212  }
213 }
214 /*---------------------------------------------------------------------------*/
215 /* will take another argument when we support multiple sessions */
216 void
217 lwm2m_rd_client_set_session_callback(session_callback_t cb)
218 {
219  session_info.callback = cb;
220 }
221 /*---------------------------------------------------------------------------*/
222 static void
223 perform_session_callback(int state)
224 {
225  if(session_info.callback != NULL) {
226  LOG_DBG("Performing session callback: %d cb:%p\n",
227  state, session_info.callback);
228  session_info.callback(&session_info, state);
229  }
230 }
231 /*---------------------------------------------------------------------------*/
232 void
233 lwm2m_rd_client_use_registration_server(int use)
234 {
235  session_info.use_registration = use != 0;
236  if(session_info.use_registration) {
237  rd_state = INIT;
238  }
239 }
240 /*---------------------------------------------------------------------------*/
241 uint16_t
242 lwm2m_rd_client_get_lifetime(void)
243 {
244  return session_info.lifetime;
245 }
246 /*---------------------------------------------------------------------------*/
247 void
248 lwm2m_rd_client_set_lifetime(uint16_t lifetime)
249 {
250  if(lifetime > 0) {
251  session_info.lifetime = lifetime;
252  } else {
253  session_info.lifetime = LWM2M_DEFAULT_CLIENT_LIFETIME;
254  }
255 }
256 /*---------------------------------------------------------------------------*/
257 void
258 lwm2m_rd_client_set_update_rd(void)
259 {
260  rd_flags |= FLAG_RD_DATA_DIRTY;
261 }
262 /*---------------------------------------------------------------------------*/
263 void
264 lwm2m_rd_client_set_automatic_update(int update)
265 {
266  rd_flags = (rd_flags & ~FLAG_RD_DATA_UPDATE_ON_DIRTY) |
267  (update != 0 ? FLAG_RD_DATA_UPDATE_ON_DIRTY : 0);
268 }
269 /*---------------------------------------------------------------------------*/
270 void
271 lwm2m_rd_client_register_with_server(const coap_endpoint_t *server)
272 {
273  coap_endpoint_copy(&session_info.server_ep, server);
274  session_info.has_registration_server_info = 1;
275  session_info.registered = 0;
276  if(session_info.use_registration) {
277  rd_state = INIT;
278  }
279 }
280 /*---------------------------------------------------------------------------*/
281 static int
282 update_registration_server(void)
283 {
284  if(session_info.has_registration_server_info) {
285  return 1;
286  }
287 
288 #if UIP_CONF_IPV6_RPL
289  {
290  rpl_dag_t *dag;
291 
292  /* Use the DAG id as server address if no other has been specified */
293  dag = rpl_get_any_dag();
294  if(dag != NULL) {
295  /* create coap-endpoint? */
296  /* uip_ipaddr_copy(&server_ipaddr, &dag->dag_id); */
297  /* server_port = REMOTE_PORT; */
298  return 1;
299  }
300  }
301 #endif /* UIP_CONF_IPV6_RPL */
302 
303  return 0;
304 }
305 /*---------------------------------------------------------------------------*/
306 void
307 lwm2m_rd_client_register_with_bootstrap_server(const coap_endpoint_t *server)
308 {
309  coap_endpoint_copy(&session_info.bs_server_ep, server);
310  session_info.has_bs_server_info = 1;
311  session_info.bootstrapped = 0;
312  session_info.registered = 0;
313  if(session_info.use_bootstrap) {
314  rd_state = INIT;
315  }
316 }
317 /*---------------------------------------------------------------------------*/
318 int
319 lwm2m_rd_client_deregister(void)
320 {
321  if(lwm2m_rd_client_is_registered()) {
322  rd_state = DEREGISTER;
323  return 1;
324  }
325  /* Not registered */
326  return 0;
327 }
328 /*---------------------------------------------------------------------------*/
329 void
330 lwm2m_rd_client_update_triggered(void)
331 {
332  rd_flags |= FLAG_RD_DATA_UPDATE_TRIGGERED;
333  /* Here we need to do an CoAP timer poll - to get a quick request transmission! */
334 }
335 /*---------------------------------------------------------------------------*/
336 static int
337 update_bootstrap_server(void)
338 {
339  if(session_info.has_bs_server_info) {
340  return 1;
341  }
342 
343 #if UIP_CONF_IPV6_RPL
344  {
345  rpl_dag_t *dag;
346 
347  /* Use the DAG id as server address if no other has been specified */
348  dag = rpl_get_any_dag();
349  if(dag != NULL) {
350  /* create coap endpoint */
351  /* uip_ipaddr_copy(&bs_server_ipaddr, &dag->dag_id); */
352  /* bs_server_port = REMOTE_PORT; */
353  return 1;
354  }
355  }
356 #endif /* UIP_CONF_IPV6_RPL */
357 
358  return 0;
359 }
360 /*---------------------------------------------------------------------------*/
361 /*
362  * A client initiated bootstrap starts with a POST to /bs?ep={session_info.ep},
363  * on the bootstrap server. The server should reply with 2.04.
364  * The server will thereafter do DELETE and or PUT to write new client objects.
365  * The bootstrap finishes with the server doing POST to /bs on the client.
366  *
367  * Page 64 in 07 April 2016 spec.
368  *
369  * TODO
370  */
371 static void
372 bootstrap_callback(coap_callback_request_state_t *callback_state)
373 {
374  coap_request_state_t *state = &callback_state->state;
375  LOG_DBG("Bootstrap callback Response: %d, ", state->response != NULL);
376  if(state->status == COAP_REQUEST_STATUS_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(state->status == COAP_REQUEST_STATUS_TIMEOUT) {
387  LOG_DBG_("Server not responding! Retry?");
388  rd_state = DO_BOOTSTRAP;
389  } else if(state->status == COAP_REQUEST_STATUS_FINISHED) {
390  LOG_DBG_("Request finished. Ignore\n");
391  } else {
392  LOG_DBG_("Unexpected error! Retry?");
393  rd_state = DO_BOOTSTRAP;
394  }
395 }
396 /*---------------------------------------------------------------------------*/
397 static void
398 produce_more_rd(void)
399 {
400  lwm2m_buffer_t outbuf;
401 
402  LOG_DBG("GOT Continue!\n");
403 
404  /* setup the output buffer */
405  outbuf.buffer = rd_data;
406  outbuf.size = sizeof(rd_data);
407  outbuf.len = 0;
408 
409  rd_block1++;
410 
411  /* this will also set the request payload */
412  rd_more = lwm2m_engine_set_rd_data(&outbuf, rd_block1);
413  coap_set_payload(request, rd_data, outbuf.len);
414 
415  LOG_DBG("Setting block1 in request - block: %d more: %d\n",
416  (int)rd_block1, (int)rd_more);
417  coap_set_header_block1(request, rd_block1, rd_more, sizeof(rd_data));
418 
419  coap_send_request(&rd_request_state, &session_info.server_ep, request, rd_callback);
420 }
421 /*---------------------------------------------------------------------------*/
422 static void
423 block1_rd_callback(coap_timer_t *timer)
424 {
425  produce_more_rd();
426 }
427 /*---------------------------------------------------------------------------*/
428 /*
429  * Page 65-66 in 07 April 2016 spec.
430  */
431 static void
432 registration_callback(coap_callback_request_state_t *callback_state)
433 {
434  coap_request_state_t *state = &callback_state->state;
435  LOG_DBG("Registration callback. Status: %d. Response: %d, ", state->status, state->response != NULL);
436  if(state->status == COAP_REQUEST_STATUS_RESPONSE) {
437  /* check state and possibly set registration to done */
438  /* If we get a continue - we need to call the rd generator one more time */
439  if(CONTINUE_2_31 == state->response->code) {
440  /* We assume that size never change?! */
441  coap_get_header_block1(state->response, &rd_block1, NULL, NULL, NULL);
442  coap_timer_set_callback(&block1_timer, block1_rd_callback);
443  coap_timer_set(&block1_timer, 1); /* delay 1 ms */
444  LOG_DBG_("Continue\n");
445  } else if(CREATED_2_01 == state->response->code) {
446  if(state->response->location_path_len < LWM2M_RD_CLIENT_ASSIGNED_ENDPOINT_MAX_LEN) {
447  memcpy(session_info.assigned_ep, state->response->location_path,
448  state->response->location_path_len);
449  session_info.assigned_ep[state->response->location_path_len] = 0;
450  /* if we decide to not pass the lt-argument on registration, we should force an initial "update" to register lifetime with server */
451 #if LWM2M_QUEUE_MODE_ENABLED
452 #if LWM2M_QUEUE_MODE_INCLUDE_DYNAMIC_ADAPTATION
453  if(lwm2m_queue_mode_get_dynamic_adaptation_flag()) {
454  lwm2m_queue_mode_set_first_request();
455  }
456 #endif
457  lwm2m_rd_client_fsm_execute_queue_mode_awake(); /* Avoid 500 ms delay and move directly to the state*/
458 #else
459  rd_state = REGISTRATION_DONE;
460 #endif
461  /* remember the last reg time */
462  last_update = coap_timer_uptime();
463  LOG_DBG_("Done (assigned EP='%s')!\n", session_info.assigned_ep);
464  perform_session_callback(LWM2M_RD_CLIENT_REGISTERED);
465  return;
466  }
467 
468  LOG_DBG_("failed to handle assigned EP: '");
469  LOG_DBG_COAP_STRING(state->response->location_path,
470  state->response->location_path_len);
471  LOG_DBG_("'. Re-init network.\n");
472  } else {
473  /* Possible error response codes are 4.00 Bad request & 4.03 Forbidden */
474  LOG_DBG_("failed with code %d. Re-init network\n", state->response->code);
475  }
476  /* TODO Application callback? */
477  rd_state = INIT;
478  } else if(state->status == COAP_REQUEST_STATUS_TIMEOUT) {
479  LOG_DBG_("Server not responding, trying to reconnect\n");
480  rd_state = INIT;
481  } else if(state->status == COAP_REQUEST_STATUS_FINISHED){
482  LOG_DBG_("Request finished. Ignore\n");
483  } else {
484  LOG_DBG_("Unexpected error, trying to reconnect\n");
485  rd_state = INIT;
486  }
487 }
488 /*---------------------------------------------------------------------------*/
489 /*
490  * Page 65-66 in 07 April 2016 spec.
491  */
492 static void
493 update_callback(coap_callback_request_state_t *callback_state)
494 {
495  coap_request_state_t *state = &callback_state->state;
496  LOG_DBG("Update callback. Status: %d. Response: %d, ", state->status, state->response != NULL);
497 
498  if(state->status == COAP_REQUEST_STATUS_RESPONSE) {
499  /* If we get a continue - we need to call the rd generator one more time */
500  if(CONTINUE_2_31 == state->response->code) {
501  /* We assume that size never change?! */
502  LOG_DBG_("Continue\n");
503  coap_get_header_block1(state->response, &rd_block1, NULL, NULL, NULL);
504  coap_timer_set_callback(&block1_timer, block1_rd_callback);
505  coap_timer_set(&block1_timer, 1); /* delay 1 ms */
506  } else if(CHANGED_2_04 == state->response->code) {
507  LOG_DBG_("Done!\n");
508  /* remember the last reg time */
509  last_update = coap_timer_uptime();
510 #if LWM2M_QUEUE_MODE_ENABLED
511  /* If it has been waked up by a notification, send the stored notifications in queue */
512  if(lwm2m_queue_mode_is_waked_up_by_notification()) {
513 
514  lwm2m_queue_mode_clear_waked_up_by_notification();
515  lwm2m_notification_queue_send_notifications();
516  }
517 #if LWM2M_QUEUE_MODE_INCLUDE_DYNAMIC_ADAPTATION
518  if(lwm2m_queue_mode_get_dynamic_adaptation_flag()) {
519  lwm2m_queue_mode_set_first_request();
520  }
521 #endif /* LWM2M_QUEUE_MODE_INCLUDE_DYNAMIC_ADAPTATION */
522  lwm2m_rd_client_fsm_execute_queue_mode_awake(); /* Avoid 500 ms delay and move directly to the state*/
523 #else
524  rd_state = REGISTRATION_DONE;
525  rd_flags &= ~FLAG_RD_DATA_UPDATE_TRIGGERED;
526 #endif /* LWM2M_QUEUE_MODE_ENABLED */
527  } else {
528  /* Possible error response codes are 4.00 Bad request & 4.04 Not Found */
529  LOG_DBG_("Failed with code %d. Retrying registration\n",
530  state->response->code);
531  rd_state = DO_REGISTRATION;
532  }
533  } else if(state->status == COAP_REQUEST_STATUS_TIMEOUT) {
534  LOG_DBG_("Server not responding, trying to reconnect\n");
535  rd_state = INIT;
536  } else if(state->status == COAP_REQUEST_STATUS_FINISHED){
537  LOG_DBG_("Request finished. Ignore\n");
538  } else {
539  LOG_DBG_("Unexpected error, trying to reconnect\n");
540  rd_state = INIT;
541  }
542 }
543 /*---------------------------------------------------------------------------*/
544 static void
545 deregister_callback(coap_callback_request_state_t *callback_state)
546 {
547  coap_request_state_t *state = &callback_state->state;
548  LOG_DBG("Deregister callback. Status: %d. Response Code: %d\n",
549  state->status,
550  state->response != NULL ? state->response->code : 0);
551 
552  if(state->status == COAP_REQUEST_STATUS_RESPONSE && (DELETED_2_02 == state->response->code)) {
553  LOG_DBG("Deregistration success\n");
554  rd_state = DEREGISTERED;
555  perform_session_callback(LWM2M_RD_CLIENT_DEREGISTERED);
556  } else {
557  LOG_DBG("Deregistration failed\n");
558  if(rd_state == DEREGISTER_SENT) {
559  rd_state = DEREGISTER_FAILED;
560  perform_session_callback(LWM2M_RD_CLIENT_DEREGISTER_FAILED);
561  }
562  }
563 }
564 /*---------------------------------------------------------------------------*/
565 /* CoAP timer callback */
566 static void
567 periodic_process(coap_timer_t *timer)
568 {
569  uint64_t now;
570 
571  /* reschedule the CoAP timer */
572 #if LWM2M_QUEUE_MODE_ENABLED
573  /* In Queue Mode, the machine is not executed periodically, but with the awake/sleeping times */
574  if(!((rd_state & 0xF) == 0xE)) {
575  coap_timer_reset(&rd_timer, STATE_MACHINE_UPDATE_INTERVAL);
576  }
577 #else
578  coap_timer_reset(&rd_timer, STATE_MACHINE_UPDATE_INTERVAL);
579 #endif
580 
581  now = coap_timer_uptime();
582 
583  LOG_DBG("RD Client - state: %d, ms: %lu\n", rd_state,
584  (unsigned long)coap_timer_uptime());
585 
586  switch(rd_state) {
587  case INIT:
588  LOG_DBG("RD Client started with endpoint '%s' and client lifetime %d\n", session_info.ep, session_info.lifetime);
589  rd_state = WAIT_NETWORK;
590  break;
591  case WAIT_NETWORK:
592  if(now > wait_until_network_check) {
593  /* check each 10 seconds before next check */
594  LOG_DBG("Checking for network... %lu\n",
595  (unsigned long)wait_until_network_check);
596  wait_until_network_check = now + 10000;
597  if(has_network_access()) {
598  /* Either do bootstrap then registration */
599  if(session_info.use_bootstrap) {
600  rd_state = DO_BOOTSTRAP;
601  } else {
602  rd_state = DO_REGISTRATION;
603  }
604  }
605  /* Otherwise wait until for a network to join */
606  }
607  break;
608  case DO_BOOTSTRAP:
609  if(session_info.use_bootstrap && session_info.bootstrapped == 0) {
610  if(update_bootstrap_server()) {
611  /* prepare request, TID is set by COAP_BLOCKING_REQUEST() */
612  coap_init_message(request, COAP_TYPE_CON, COAP_POST, 0);
613  coap_set_header_uri_path(request, "/bs");
614 
615  snprintf(query_data, sizeof(query_data) - 1, "?ep=%s", session_info.ep);
616  coap_set_header_uri_query(request, query_data);
617  LOG_INFO("Registering ID with bootstrap server [");
618  LOG_INFO_COAP_EP(&session_info.bs_server_ep);
619  LOG_INFO_("] as '%s'\n", query_data);
620 
621  if(coap_send_request(&rd_request_state, &session_info.bs_server_ep,
622  request, bootstrap_callback)) {
623  rd_state = BOOTSTRAP_SENT;
624  }
625  }
626  }
627  break;
628  case BOOTSTRAP_SENT:
629  /* Just wait for bootstrap to be done... */
630  break;
631  case BOOTSTRAP_DONE:
632  /* check that we should still use bootstrap */
633  if(session_info.use_bootstrap) {
634  lwm2m_security_server_t *security;
635  LOG_DBG("*** Bootstrap - checking for server info...\n");
636  /* get the security object - ignore bootstrap servers */
637  for(security = lwm2m_security_get_first();
638  security != NULL;
639  security = lwm2m_security_get_next(security)) {
640  if(security->bootstrap == 0) {
641  break;
642  }
643  }
644 
645  if(security != NULL) {
646  /* get the server URI */
647  if(security->server_uri_len > 0) {
648  uint8_t secure = 0;
649 
650  LOG_DBG("**** Found security instance using: ");
651  LOG_DBG_COAP_STRING((const char *)security->server_uri,
652  security->server_uri_len);
653  LOG_DBG_(" (len %d) \n", security->server_uri_len);
654  /* TODO Should verify it is a URI */
655  /* Check if secure */
656  secure = strncmp((const char *)security->server_uri,
657  "coaps:", 6) == 0;
658 
659  if(!coap_endpoint_parse((const char *)security->server_uri,
660  security->server_uri_len,
661  &session_info.server_ep)) {
662  LOG_DBG("Failed to parse server URI!\n");
663  } else {
664  LOG_DBG("Server address:");
665  LOG_DBG_COAP_EP(&session_info.server_ep);
666  LOG_DBG_("\n");
667  if(secure) {
668  LOG_DBG("Secure CoAP requested but not supported - can not bootstrap\n");
669  } else {
670  lwm2m_rd_client_register_with_server(&session_info.server_ep);
671  session_info.bootstrapped++;
672  }
673  }
674  } else {
675  LOG_DBG("** failed to parse URI ");
676  LOG_DBG_COAP_STRING((const char *)security->server_uri,
677  security->server_uri_len);
678  LOG_DBG_("\n");
679  }
680  }
681 
682  /* if we did not register above - then fail this and restart... */
683  if(session_info.bootstrapped == 0) {
684  /* Not ready. Lets retry with the bootstrap server again */
685  rd_state = DO_BOOTSTRAP;
686  } else {
687  rd_state = DO_REGISTRATION;
688  }
689  }
690  break;
691  case DO_REGISTRATION:
692  if(!coap_endpoint_is_connected(&session_info.server_ep)) {
693  /* Not connected... wait a bit... and retry connection */
694  coap_endpoint_connect(&session_info.server_ep);
695  LOG_DBG("Wait until connected... \n");
696  return;
697  }
698  if(session_info.use_registration && !session_info.registered &&
699  update_registration_server()) {
700  int len;
701 
702  /* prepare request, TID was set by COAP_BLOCKING_REQUEST() */
703  coap_init_message(request, COAP_TYPE_CON, COAP_POST, 0);
704  coap_set_header_uri_path(request, "/rd");
705 
706  snprintf(query_data, sizeof(query_data) - 1, "?ep=%s&lt=%d&b=%s", session_info.ep, session_info.lifetime, session_info.binding);
707  coap_set_header_uri_query(request, query_data);
708 
709  len = set_rd_data(request);
710  rd_callback = registration_callback;
711 
712  LOG_INFO("Registering with [");
713  LOG_INFO_COAP_EP(&session_info.server_ep);
714  LOG_INFO_("] lwm2m endpoint '%s': '", query_data);
715  if(len) {
716  LOG_INFO_COAP_STRING((const char *)rd_data, len);
717  }
718  LOG_INFO_("' More:%d\n", rd_more);
719 
720  if(coap_send_request(&rd_request_state, &session_info.server_ep,
721  request, registration_callback)){
722  rd_state = REGISTRATION_SENT;
723  }
724  }
725  break;
726  case REGISTRATION_SENT:
727  /* just wait until the callback kicks us to the next state... */
728  break;
729  case REGISTRATION_DONE:
730  /* All is done! */
731 
732  check_periodic_observations(); /* TODO: manage periodic observations */
733 
734  /* check if it is time for the next update */
735  if((rd_flags & FLAG_RD_DATA_UPDATE_TRIGGERED) ||
736  ((uint32_t)session_info.lifetime * 500) <= now - last_update) {
737  /* triggered or time to send an update to the server, at half-time! sec vs ms */
738  prepare_update(request, rd_flags & FLAG_RD_DATA_UPDATE_TRIGGERED);
739  if(coap_send_request(&rd_request_state, &session_info.server_ep, request,
740  update_callback)) {
741  rd_state = UPDATE_SENT;
742  }
743  }
744  break;
745 #if LWM2M_QUEUE_MODE_ENABLED
746  case QUEUE_MODE_AWAKE:
747  LOG_DBG("Queue Mode: Client is AWAKE at %lu\n", (unsigned long)coap_timer_uptime());
748  queue_mode_client_awake = 1;
749  queue_mode_client_awake_time = lwm2m_queue_mode_get_awake_time();
750  coap_timer_set(&queue_mode_client_awake_timer, queue_mode_client_awake_time);
751  break;
752  case QUEUE_MODE_SEND_UPDATE:
753 /* Define this macro to make the necessary actions for waking up,
754  * depending on the platform
755  */
756 #ifdef LWM2M_QUEUE_MODE_WAKE_UP
757  LWM2M_QUEUE_MODE_WAKE_UP();
758 #endif /* LWM2M_QUEUE_MODE_WAKE_UP */
759  prepare_update(request, rd_flags & FLAG_RD_DATA_UPDATE_TRIGGERED);
760  if(coap_send_request(&rd_request_state, &session_info.server_ep, request,
761  update_callback)) {
762  rd_state = UPDATE_SENT;
763  }
764  break;
765 #endif /* LWM2M_QUEUE_MODE_ENABLED */
766 
767  case UPDATE_SENT:
768  /* just wait until the callback kicks us to the next state... */
769  break;
770  case DEREGISTER:
771  LOG_INFO("DEREGISTER %s\n", session_info.assigned_ep);
772  coap_init_message(request, COAP_TYPE_CON, COAP_DELETE, 0);
773  coap_set_header_uri_path(request, session_info.assigned_ep);
774  if(coap_send_request(&rd_request_state, &session_info.server_ep, request,
775  deregister_callback)) {
776  rd_state = DEREGISTER_SENT;
777  }
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
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.
CoAP engine implementation.
int coap_endpoint_connect(coap_endpoint_t *ep)
Request a connection to a CoAP endpoint.
Definition: coap-uip.c:295
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:209
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
rpl_dag_t * rpl_get_any_dag(void)
Returns pointer to any DAG (for compatibility with legagy RPL code)
Definition: rpl-dag.c:1050
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:162
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:258