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 "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 
111 LIST(session_info_list);
112 
113 /* Shared by all sessions, used by only one at a time in the FSM */
114 static char query_data[64]; /* allocate some data for queries and updates */
115 static uint8_t rd_data[128]; /* allocate some data for the RD */
116 
117 static coap_timer_t rd_timer; /* Timer to tick the FSM periodically */
118 static char default_ep[20];
119 
120 #if LWM2M_QUEUE_MODE_ENABLED
121 static coap_timer_t queue_mode_client_awake_timer; /* Timer to control the client's
122  * awake time
123  */
124 static uint8_t queue_mode_client_awake; /* 1 - client is awake,
125  * 0 - client is sleeping
126  */
127 static uint16_t queue_mode_client_awake_time; /* The time to be awake */
128 /* Callback for the client awake timer */
129 static void queue_mode_awake_timer_callback(coap_timer_t *timer);
130 #endif
131 
132 static void check_periodic_observations();
133 static void update_callback(coap_callback_request_state_t *callback_state);
134 /*---------------------------------------------------------------------------*/
135 static int
136 set_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 /*---------------------------------------------------------------------------*/
157 static void
158 prepare_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 /*---------------------------------------------------------------------------*/
174 static int
175 has_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 /*---------------------------------------------------------------------------*/
188 int
189 lwm2m_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 */
195 void
196 lwm2m_rd_client_set_session_callback(lwm2m_session_info_t *session_info, session_callback_t cb)
197 {
198  session_info->callback = cb;
199 }
200 /*---------------------------------------------------------------------------*/
201 static void
202 perform_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 /*---------------------------------------------------------------------------*/
211 uint16_t
212 lwm2m_rd_client_get_lifetime(lwm2m_session_info_t *session_info)
213 {
214  return session_info->lifetime;
215 }
216 /*---------------------------------------------------------------------------*/
217 void
218 lwm2m_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 /*---------------------------------------------------------------------------*/
227 void
228 lwm2m_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 /*---------------------------------------------------------------------------*/
235 void
236 lwm2m_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 /*---------------------------------------------------------------------------*/
242 void
243 lwm2m_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 /*---------------------------------------------------------------------------*/
252 void
253 lwm2m_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 /*---------------------------------------------------------------------------*/
259 void
260 lwm2m_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 /*---------------------------------------------------------------------------*/
303 int
304 lwm2m_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 /*---------------------------------------------------------------------------*/
314 static lwm2m_session_info_t *
315 get_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 /*---------------------------------------------------------------------------*/
327 void
328 lwm2m_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  */
347 static void
348 bootstrap_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 /*---------------------------------------------------------------------------*/
376 static void
377 produce_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 /*---------------------------------------------------------------------------*/
401 static void
402 block1_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  */
410 static void
411 registration_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  */
475 static void
476 update_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 /*---------------------------------------------------------------------------*/
530 static void
531 deregister_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
554 static int
555 all_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 /*---------------------------------------------------------------------------*/
567 static int
568 all_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 */
582 static void
583 periodic_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, "?ep=%s&lt=%d&b=%s", session_info->ep, session_info->lifetime, session_info->binding);
730  coap_set_header_uri_query(session_info->request, query_data);
731 
732  len = set_rd_data(session_info);
733  session_info->rd_callback = registration_callback;
734 
735  LOG_INFO("Registering with [");
736  LOG_INFO_COAP_EP(&session_info->server_ep);
737  LOG_INFO_("] lwm2m endpoint '%s': '", query_data);
738  if(len) {
739  LOG_INFO_COAP_STRING((const char *)rd_data, len);
740  }
741  LOG_INFO_("' More:%d\n", session_info->rd_more);
742 
743  /* Add session info as user data to use it in the callbacks */
744  session_info->rd_request_state.state.user_data = (void *)session_info;
745  if(coap_send_request(&session_info->rd_request_state, &session_info->server_ep,
746  session_info->request, registration_callback)) {
747 
748  session_info->rd_state = REGISTRATION_SENT;
749  }
750  session_info->last_rd_progress = coap_timer_uptime();
751  }
752  break;
753  case REGISTRATION_SENT:
754  /* just wait until the callback kicks us to the next state... */
755  break;
756  case REGISTRATION_DONE:
757  /* All is done! */
758 
759  check_periodic_observations(); /* TODO: manage periodic observations */
760 
761  /* check if it is time for the next update */
762  if((session_info->rd_flags & FLAG_RD_DATA_UPDATE_TRIGGERED) ||
763  ((uint32_t)session_info->lifetime * 500) <= now - session_info->last_update) {
764  /* triggered or time to send an update to the server, at half-time! sec vs ms */
765  prepare_update(session_info, session_info->rd_flags & FLAG_RD_DATA_UPDATE_TRIGGERED);
766 
767  /* Add session info as user data to use it in the callbacks */
768  session_info->rd_request_state.state.user_data = (void *)session_info;
769  if(coap_send_request(&session_info->rd_request_state, &session_info->server_ep, session_info->request,
770  update_callback)) {
771  session_info->rd_state = UPDATE_SENT;
772  }
773  session_info->last_rd_progress = coap_timer_uptime();
774  }
775  break;
776 
777 #if LWM2M_QUEUE_MODE_ENABLED
778  case QUEUE_MODE_AWAKE:
779  LOG_DBG("Queue Mode: Client is AWAKE at %lu\n", (unsigned long)coap_timer_uptime());
780  if((queue_mode_client_awake = all_sessions_in_queue_mode_awake())) {
781  queue_mode_client_awake_time = lwm2m_queue_mode_get_awake_time();
782  coap_timer_set(&queue_mode_client_awake_timer, queue_mode_client_awake_time);
783  }
784  break;
785  case QUEUE_MODE_SEND_UPDATE:
786  /* Define this macro to make the necessary actions for waking up,
787  * depending on the platform
788  */
789 #ifdef LWM2M_QUEUE_MODE_WAKE_UP
790  LWM2M_QUEUE_MODE_WAKE_UP();
791 #endif /* LWM2M_QUEUE_MODE_WAKE_UP */
792  prepare_update(session_info, session_info->rd_flags & FLAG_RD_DATA_UPDATE_TRIGGERED);
793  /* Add session info as user data to use it in the callbacks */
794  session_info->rd_request_state.state.user_data = (void *)session_info;
795  if(coap_send_request(&session_info->rd_request_state, &session_info->server_ep, session_info->request,
796  update_callback)) {
797  session_info->rd_state = UPDATE_SENT;
798  }
799  session_info->last_rd_progress = coap_timer_uptime();
800  break;
801 #endif /* LWM2M_QUEUE_MODE_ENABLED */
802 
803  case UPDATE_SENT:
804  /* just wait until the callback kicks us to the next state... */
805  break;
806  case DEREGISTER:
807  LOG_INFO("DEREGISTER %s\n", session_info->assigned_ep);
808  coap_init_message(session_info->request, COAP_TYPE_CON, COAP_DELETE, 0);
809  coap_set_header_uri_path(session_info->request, session_info->assigned_ep);
810 
811  /* Add session info as user data to use it in the callbacks */
812  session_info->rd_request_state.state.user_data = (void *)session_info;
813  if(coap_send_request(&session_info->rd_request_state, &session_info->server_ep, session_info->request,
814  deregister_callback)) {
815  session_info->rd_state = DEREGISTER_SENT;
816  }
817  break;
818  case DEREGISTER_SENT:
819  break;
820  case DEREGISTER_FAILED:
821  break;
822  case DEREGISTERED:
823  break;
824 
825  default:
826  LOG_WARN("Unhandled state: %d\n", session_info->rd_state);
827  }
828  session_info = session_info->next;
829  }
830 }
831 /*---------------------------------------------------------------------------*/
832 void
833 lwm2m_rd_client_init(const char *ep)
834 {
835  lwm2m_rd_client_set_default_endpoint_name(ep);
836 
837  /* call the RD client periodically */
838  coap_timer_set_callback(&rd_timer, periodic_process);
839  coap_timer_set(&rd_timer, STATE_MACHINE_UPDATE_INTERVAL);
840 #if LWM2M_QUEUE_MODE_ENABLED
841  coap_timer_set_callback(&queue_mode_client_awake_timer, queue_mode_awake_timer_callback);
842 #endif
843 }
844 /*---------------------------------------------------------------------------*/
845 static void
846 check_periodic_observations(void)
847 {
848 /* TODO */
849 }
850 /*---------------------------------------------------------------------------*/
851 /*
852  * Queue Mode Support
853  */
854 #if LWM2M_QUEUE_MODE_ENABLED
855 /*---------------------------------------------------------------------------*/
856 void
857 lwm2m_rd_client_restart_client_awake_timer(void)
858 {
859  coap_timer_set(&queue_mode_client_awake_timer, queue_mode_client_awake_time);
860 }
861 /*---------------------------------------------------------------------------*/
862 uint8_t
863 lwm2m_rd_client_is_client_awake(void)
864 {
865  return queue_mode_client_awake;
866 }
867 /*---------------------------------------------------------------------------*/
868 static void
869 queue_mode_awake_timer_callback(coap_timer_t *timer)
870 {
871  /* Timer has expired, no requests has been received, client can go to sleep */
872  LOG_DBG("Queue Mode: Client is SLEEPING at %lu\n", (unsigned long)coap_timer_uptime());
873  queue_mode_client_awake = 0;
874 
875  lwm2m_session_info_t *session_info = (lwm2m_session_info_t *)list_head(session_info_list);
876  while(session_info != NULL) {
877  session_info->rd_state = QUEUE_MODE_SEND_UPDATE;
878  session_info = session_info->next;
879  }
880  coap_timer_set(&rd_timer, lwm2m_queue_mode_get_sleep_time());
881  /* Define this macro to enter sleep mode depending on the platform */
882 #ifdef LWM2M_QUEUE_MODE_SLEEP_MS
883  LWM2M_QUEUE_MODE_SLEEP_MS(lwm2m_queue_mode_get_sleep_time());
884 #endif /* LWM2M_QUEUE_MODE_SLEEP_MS */
885 }
886 /*---------------------------------------------------------------------------*/
887 void
888 lwm2m_rd_client_fsm_execute_queue_mode_awake(lwm2m_session_info_t *session_info)
889 {
890  coap_timer_stop(&rd_timer);
891  session_info->rd_state = QUEUE_MODE_AWAKE;
892  periodic_process(&rd_timer);
893 }
894 /*---------------------------------------------------------------------------*/
895 void
896 lwm2m_rd_client_fsm_execute_queue_mode_update(lwm2m_session_info_t *session_info)
897 {
898  coap_timer_stop(&rd_timer);
899  session_info->rd_state = QUEUE_MODE_SEND_UPDATE;
900  periodic_process(&rd_timer);
901 }
902 /*---------------------------------------------------------------------------*/
903 #endif /* LWM2M_QUEUE_MODE_ENABLED */
904 /*---------------------------------------------------------------------------*/
905 /** @} */
Log support for CoAP
Header file for the LWM2M object API
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:287
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:201
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
Linked list manipulation routines.
void * list_head(list_t list)
Get a pointer to the first element of a list.
Definition: list.c:82
rpl_dag_t * rpl_get_any_dag(void)
Returns pointer to any DAG (for compatibility with legagy RPL code)
Definition: rpl-dag.c:1055
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
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition: list.c:142
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
#define LIST(name)
Declare a linked list.
Definition: list.h:89
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_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
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:250
int coap_endpoint_cmp(const coap_endpoint_t *e1, const coap_endpoint_t *e2)
Compare two CoAP endpoints.
Definition: coap-uip.c:163