56 #include "lwm2m-security.h" 65 #if LWM2M_QUEUE_MODE_ENABLED 72 #define LOG_MODULE "lwm2m-rd" 73 #define LOG_LEVEL LOG_LEVEL_LWM2M 75 #ifndef LWM2M_DEFAULT_CLIENT_LIFETIME 76 #define LWM2M_DEFAULT_CLIENT_LIFETIME 30 79 #define MAX_RD_UPDATE_WAIT 5000 81 #define REMOTE_PORT UIP_HTONS(COAP_DEFAULT_PORT) 82 #define BS_REMOTE_PORT UIP_HTONS(5685) 84 #define STATE_MACHINE_UPDATE_INTERVAL 500 86 static struct lwm2m_session_info session_info;
87 static coap_callback_request_state_t rd_request_state;
89 static coap_message_t request[1];
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 113 #define FLAG_RD_DATA_DIRTY 0x01 114 #define FLAG_RD_DATA_UPDATE_TRIGGERED 0x02 115 #define FLAG_RD_DATA_UPDATE_ON_DIRTY 0x10 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;
122 static char query_data[64];
123 static uint8_t rd_data[128];
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);
130 static coap_timer_t block1_timer;
132 #if LWM2M_QUEUE_MODE_ENABLED 133 static coap_timer_t queue_mode_client_awake_timer;
136 static uint8_t queue_mode_client_awake;
139 static uint16_t queue_mode_client_awake_time;
141 static void queue_mode_awake_timer_callback(coap_timer_t *
timer);
144 static void check_periodic_observations();
145 static void update_callback(coap_callback_request_state_t *callback_state);
148 set_rd_data(coap_message_t *request)
150 lwm2m_buffer_t outbuf;
153 outbuf.buffer = rd_data;
154 outbuf.size =
sizeof(rd_data);
158 rd_more = lwm2m_engine_set_rd_data(&outbuf, 0);
159 coap_set_payload(request, rd_data, outbuf.len);
163 LOG_DBG(
"Setting block1 in request\n");
164 coap_set_header_block1(request, 0, 1,
sizeof(rd_data));
170 prepare_update(coap_message_t *request,
int triggered)
172 coap_init_message(request, COAP_TYPE_CON, COAP_POST, 0);
173 coap_set_header_uri_path(request, session_info.assigned_ep);
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);
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;
187 has_network_access(
void)
189 #if UIP_CONF_IPV6_RPL 191 #ifndef CONTIKI_TARGET_NATIVE 201 lwm2m_rd_client_is_registered(
void)
203 return rd_state == REGISTRATION_DONE || rd_state == UPDATE_SENT;
207 lwm2m_rd_client_use_bootstrap_server(
int use)
209 session_info.use_bootstrap = use != 0;
210 if(session_info.use_bootstrap) {
217 lwm2m_rd_client_set_session_callback(session_callback_t cb)
219 session_info.callback = cb;
223 perform_session_callback(
int state)
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);
233 lwm2m_rd_client_use_registration_server(
int use)
235 session_info.use_registration = use != 0;
236 if(session_info.use_registration) {
242 lwm2m_rd_client_get_lifetime(
void)
244 return session_info.lifetime;
248 lwm2m_rd_client_set_lifetime(uint16_t lifetime)
251 session_info.lifetime = lifetime;
253 session_info.lifetime = LWM2M_DEFAULT_CLIENT_LIFETIME;
258 lwm2m_rd_client_set_update_rd(
void)
260 rd_flags |= FLAG_RD_DATA_DIRTY;
264 lwm2m_rd_client_set_automatic_update(
int update)
266 rd_flags = (rd_flags & ~FLAG_RD_DATA_UPDATE_ON_DIRTY) |
267 (update != 0 ? FLAG_RD_DATA_UPDATE_ON_DIRTY : 0);
271 lwm2m_rd_client_register_with_server(
const coap_endpoint_t *server)
274 session_info.has_registration_server_info = 1;
275 session_info.registered = 0;
276 if(session_info.use_registration) {
282 update_registration_server(
void)
284 if(session_info.has_registration_server_info) {
288 #if UIP_CONF_IPV6_RPL 307 lwm2m_rd_client_register_with_bootstrap_server(
const coap_endpoint_t *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) {
319 lwm2m_rd_client_deregister(
void)
321 if(lwm2m_rd_client_is_registered()) {
322 rd_state = DEREGISTER;
330 lwm2m_rd_client_update_triggered(
void)
332 rd_flags |= FLAG_RD_DATA_UPDATE_TRIGGERED;
337 update_bootstrap_server(
void)
339 if(session_info.has_bs_server_info) {
343 #if UIP_CONF_IPV6_RPL 372 bootstrap_callback(coap_callback_request_state_t *callback_state)
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;
383 LOG_DBG_(
"Failed with code %d. Retrying\n", state->response->code);
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");
392 LOG_DBG_(
"Unexpected error! Retry?");
393 rd_state = DO_BOOTSTRAP;
398 produce_more_rd(
void)
400 lwm2m_buffer_t outbuf;
402 LOG_DBG(
"GOT Continue!\n");
405 outbuf.buffer = rd_data;
406 outbuf.size =
sizeof(rd_data);
412 rd_more = lwm2m_engine_set_rd_data(&outbuf, rd_block1);
413 coap_set_payload(request, rd_data, outbuf.len);
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));
419 coap_send_request(&rd_request_state, &session_info.server_ep, request, rd_callback);
423 block1_rd_callback(coap_timer_t *
timer)
432 registration_callback(coap_callback_request_state_t *callback_state)
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) {
439 if(CONTINUE_2_31 == state->response->code) {
441 coap_get_header_block1(state->response, &rd_block1, NULL, NULL, NULL);
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;
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();
457 lwm2m_rd_client_fsm_execute_queue_mode_awake();
459 rd_state = REGISTRATION_DONE;
463 LOG_DBG_(
"Done (assigned EP='%s')!\n", session_info.assigned_ep);
464 perform_session_callback(LWM2M_RD_CLIENT_REGISTERED);
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");
474 LOG_DBG_(
"failed with code %d. Re-init network\n", state->response->code);
478 }
else if(state->status == COAP_REQUEST_STATUS_TIMEOUT) {
479 LOG_DBG_(
"Server not responding, trying to reconnect\n");
481 }
else if(state->status == COAP_REQUEST_STATUS_FINISHED){
482 LOG_DBG_(
"Request finished. Ignore\n");
484 LOG_DBG_(
"Unexpected error, trying to reconnect\n");
493 update_callback(coap_callback_request_state_t *callback_state)
495 coap_request_state_t *state = &callback_state->state;
496 LOG_DBG(
"Update callback. Status: %d. Response: %d, ", state->status, state->response != NULL);
498 if(state->status == COAP_REQUEST_STATUS_RESPONSE) {
500 if(CONTINUE_2_31 == state->response->code) {
502 LOG_DBG_(
"Continue\n");
503 coap_get_header_block1(state->response, &rd_block1, NULL, NULL, NULL);
506 }
else if(CHANGED_2_04 == state->response->code) {
510 #if LWM2M_QUEUE_MODE_ENABLED 512 if(lwm2m_queue_mode_is_waked_up_by_notification()) {
514 lwm2m_queue_mode_clear_waked_up_by_notification();
515 lwm2m_notification_queue_send_notifications();
517 #if LWM2M_QUEUE_MODE_INCLUDE_DYNAMIC_ADAPTATION 518 if(lwm2m_queue_mode_get_dynamic_adaptation_flag()) {
519 lwm2m_queue_mode_set_first_request();
522 lwm2m_rd_client_fsm_execute_queue_mode_awake();
524 rd_state = REGISTRATION_DONE;
525 rd_flags &= ~FLAG_RD_DATA_UPDATE_TRIGGERED;
529 LOG_DBG_(
"Failed with code %d. Retrying registration\n",
530 state->response->code);
531 rd_state = DO_REGISTRATION;
533 }
else if(state->status == COAP_REQUEST_STATUS_TIMEOUT) {
534 LOG_DBG_(
"Server not responding, trying to reconnect\n");
536 }
else if(state->status == COAP_REQUEST_STATUS_FINISHED){
537 LOG_DBG_(
"Request finished. Ignore\n");
539 LOG_DBG_(
"Unexpected error, trying to reconnect\n");
545 deregister_callback(coap_callback_request_state_t *callback_state)
547 coap_request_state_t *state = &callback_state->state;
548 LOG_DBG(
"Deregister callback. Status: %d. Response Code: %d\n",
550 state->response != NULL ? state->response->code : 0);
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);
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);
567 periodic_process(coap_timer_t *
timer)
572 #if LWM2M_QUEUE_MODE_ENABLED 574 if(!((rd_state & 0xF) == 0xE)) {
583 LOG_DBG(
"RD Client - state: %d, ms: %lu\n", rd_state,
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;
592 if(now > wait_until_network_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()) {
599 if(session_info.use_bootstrap) {
600 rd_state = DO_BOOTSTRAP;
602 rd_state = DO_REGISTRATION;
609 if(session_info.use_bootstrap && session_info.bootstrapped == 0) {
610 if(update_bootstrap_server()) {
612 coap_init_message(request, COAP_TYPE_CON, COAP_POST, 0);
613 coap_set_header_uri_path(request,
"/bs");
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);
622 request, bootstrap_callback)) {
623 rd_state = BOOTSTRAP_SENT;
633 if(session_info.use_bootstrap) {
634 lwm2m_security_server_t *security;
635 LOG_DBG(
"*** Bootstrap - checking for server info...\n");
637 for(security = lwm2m_security_get_first();
639 security = lwm2m_security_get_next(security)) {
640 if(security->bootstrap == 0) {
645 if(security != NULL) {
647 if(security->server_uri_len > 0) {
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);
656 secure = strncmp((
const char *)security->server_uri,
660 security->server_uri_len,
661 &session_info.server_ep)) {
662 LOG_DBG(
"Failed to parse server URI!\n");
664 LOG_DBG(
"Server address:");
665 LOG_DBG_COAP_EP(&session_info.server_ep);
668 LOG_DBG(
"Secure CoAP requested but not supported - can not bootstrap\n");
670 lwm2m_rd_client_register_with_server(&session_info.server_ep);
671 session_info.bootstrapped++;
675 LOG_DBG(
"** failed to parse URI ");
676 LOG_DBG_COAP_STRING((
const char *)security->server_uri,
677 security->server_uri_len);
683 if(session_info.bootstrapped == 0) {
685 rd_state = DO_BOOTSTRAP;
687 rd_state = DO_REGISTRATION;
691 case DO_REGISTRATION:
695 LOG_DBG(
"Wait until connected... \n");
698 if(session_info.use_registration && !session_info.registered &&
699 update_registration_server()) {
703 coap_init_message(request, COAP_TYPE_CON, COAP_POST, 0);
704 coap_set_header_uri_path(request,
"/rd");
706 snprintf(query_data,
sizeof(query_data) - 1,
"?ep=%s<=%d&b=%s", session_info.ep, session_info.lifetime, session_info.binding);
707 coap_set_header_uri_query(request, query_data);
709 len = set_rd_data(request);
710 rd_callback = registration_callback;
712 LOG_INFO(
"Registering with [");
713 LOG_INFO_COAP_EP(&session_info.server_ep);
714 LOG_INFO_(
"] lwm2m endpoint '%s': '", query_data);
716 LOG_INFO_COAP_STRING((
const char *)rd_data, len);
718 LOG_INFO_(
"' More:%d\n", rd_more);
721 request, registration_callback)){
722 rd_state = REGISTRATION_SENT;
726 case REGISTRATION_SENT:
729 case REGISTRATION_DONE:
732 check_periodic_observations();
735 if((rd_flags & FLAG_RD_DATA_UPDATE_TRIGGERED) ||
736 ((uint32_t)session_info.lifetime * 500) <= now - last_update) {
738 prepare_update(request, rd_flags & FLAG_RD_DATA_UPDATE_TRIGGERED);
741 rd_state = UPDATE_SENT;
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);
752 case QUEUE_MODE_SEND_UPDATE:
756 #ifdef LWM2M_QUEUE_MODE_WAKE_UP 757 LWM2M_QUEUE_MODE_WAKE_UP();
759 prepare_update(request, rd_flags & FLAG_RD_DATA_UPDATE_TRIGGERED);
762 rd_state = UPDATE_SENT;
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);
775 deregister_callback)) {
776 rd_state = DEREGISTER_SENT;
779 case DEREGISTER_SENT:
781 case DEREGISTER_FAILED:
787 LOG_WARN(
"Unhandled state: %d\n", rd_state);
792 lwm2m_rd_client_init(
const char *ep)
794 session_info.ep = ep;
796 #if LWM2M_QUEUE_MODE_ENABLED 797 session_info.binding =
"UQ";
801 session_info.lifetime = (LWM2M_QUEUE_MODE_DEFAULT_CLIENT_SLEEP_TIME / 1000) * 2;
803 session_info.binding =
"U";
804 if(session_info.lifetime == 0) {
805 session_info.lifetime = LWM2M_DEFAULT_CLIENT_LIFETIME;
814 #if LWM2M_QUEUE_MODE_ENABLED 820 check_periodic_observations(
void)
828 #if LWM2M_QUEUE_MODE_ENABLED 831 lwm2m_rd_client_restart_client_awake_timer(
void)
833 coap_timer_set(&queue_mode_client_awake_timer, queue_mode_client_awake_time);
837 lwm2m_rd_client_is_client_awake(
void)
839 return queue_mode_client_awake;
843 queue_mode_awake_timer_callback(coap_timer_t *timer)
846 LOG_DBG(
"Queue Mode: Client is SLEEPING at %lu\n", (
unsigned long)
coap_timer_uptime());
847 queue_mode_client_awake = 0;
850 #ifdef LWM2M_QUEUE_MODE_SLEEP_MS 851 LWM2M_QUEUE_MODE_SLEEP_MS(lwm2m_queue_mode_get_sleep_time());
853 rd_state = QUEUE_MODE_SEND_UPDATE;
858 lwm2m_rd_client_fsm_execute_queue_mode_awake()
861 rd_state = QUEUE_MODE_AWAKE;
862 periodic_process(&rd_timer);
866 lwm2m_rd_client_fsm_execute_queue_mode_update()
869 rd_state = QUEUE_MODE_SEND_UPDATE;
870 periodic_process(&rd_timer);
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.
Header file for the Contiki OMA LWM2M plain text reader / writer
int coap_endpoint_parse(const char *text, size_t size, coap_endpoint_t *ep)
Parse a CoAP endpoint.
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.
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.
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)
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.
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.
Header file for the Contiki OMA LWM2M engine
void coap_timer_stop(coap_timer_t *timer)
Stop a pending CoAP timer.
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.
int coap_endpoint_is_connected(const coap_endpoint_t *ep)
Check if a CoAP endpoint is connected.