diff --git a/include/zephyr/net/lwm2m.h b/include/zephyr/net/lwm2m.h index a57b5542f88..6ac0458af87 100644 --- a/include/zephyr/net/lwm2m.h +++ b/include/zephyr/net/lwm2m.h @@ -2075,6 +2075,7 @@ enum lwm2m_rd_client_event { LWM2M_RD_CLIENT_EVENT_NETWORK_ERROR, LWM2M_RD_CLIENT_EVENT_REG_UPDATE, LWM2M_RD_CLIENT_EVENT_DEREGISTER, + LWM2M_RD_CLIENT_EVENT_SERVER_DISABLED, }; /** diff --git a/samples/net/lwm2m_client/src/lwm2m-client.c b/samples/net/lwm2m_client/src/lwm2m-client.c index a120506639d..c610f1ad8dd 100644 --- a/samples/net/lwm2m_client/src/lwm2m-client.c +++ b/samples/net/lwm2m_client/src/lwm2m-client.c @@ -191,6 +191,10 @@ static void rd_client_event(struct lwm2m_ctx *client, /* do nothing */ break; + case LWM2M_RD_CLIENT_EVENT_SERVER_DISABLED: + LOG_DBG("LwM2M server disabled"); + break; + case LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_REG_FAILURE: LOG_DBG("Bootstrap registration failure!"); break; diff --git a/subsys/net/lib/lwm2m/lwm2m_obj_server.c b/subsys/net/lib/lwm2m/lwm2m_obj_server.c index 10c7a1a1ea0..a9432340038 100644 --- a/subsys/net/lib/lwm2m/lwm2m_obj_server.c +++ b/subsys/net/lib/lwm2m/lwm2m_obj_server.c @@ -95,13 +95,21 @@ static struct lwm2m_engine_res_inst static int disable_cb(uint16_t obj_inst_id, uint8_t *args, uint16_t args_len) { - int i; + ARG_UNUSED(args); + ARG_UNUSED(args_len); - LOG_DBG("DISABLE %d", obj_inst_id); - for (i = 0; i < MAX_INSTANCE_COUNT; i++) { + int ret; + + for (int i = 0; i < MAX_INSTANCE_COUNT; i++) { if (inst[i].obj && inst[i].obj_inst_id == obj_inst_id) { - disabled_until[i] = sys_timepoint_calc(K_SECONDS(disabled_timeout[i])); - return 0; + LOG_DBG("DISABLE %d", obj_inst_id); + ret = lwm2m_rd_client_server_disabled(obj_inst_id); + if (ret == 0) { + disabled_until[i] = + sys_timepoint_calc(K_SECONDS(disabled_timeout[i])); + return 0; + } + return ret; } } diff --git a/subsys/net/lib/lwm2m/lwm2m_rd_client.c b/subsys/net/lib/lwm2m/lwm2m_rd_client.c index 6eacba0ee0d..6b3fbcccc68 100644 --- a/subsys/net/lib/lwm2m/lwm2m_rd_client.c +++ b/subsys/net/lib/lwm2m/lwm2m_rd_client.c @@ -72,6 +72,7 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME); #define DELAY_FOR_ACK 100U #define EXCHANGE_LIFETIME 247U #define MINIMUM_PERIOD 15 +#define DISABLE_TIMEOUT (K_SECONDS(CONFIG_LWM2M_RD_CLIENT_MAX_RETRIES * EXCHANGE_LIFETIME)) static void sm_handle_registration_update_failure(void); static int sm_send_registration_msg(void); @@ -80,6 +81,8 @@ static void lwm2m_rd_client_service(struct k_work *work); static int64_t calc_next_event(void); static void set_sm_state_delayed(uint8_t sm_state, int64_t delay_ms); static void set_sm_state(uint8_t sm_state); +/** Try to fallback to bootstrap. Return true if we did. */ +static bool fallback_to_bootstrap(void); /* The states for the RD client state machine */ /* @@ -101,6 +104,7 @@ enum sm_engine_state { ENGINE_REGISTRATION_DONE_RX_OFF, ENGINE_UPDATE_REGISTRATION, ENGINE_UPDATE_SENT, + ENGINE_SERVER_DISABLED, ENGINE_SUSPENDED, ENGINE_DEREGISTER, ENGINE_DEREGISTER_SENT, @@ -125,11 +129,11 @@ struct lwm2m_rd_client_info { char ep_name[CLIENT_EP_LEN]; char server_ep[CLIENT_EP_LEN]; - bool use_bootstrap : 1; - + bool use_bootstrap : 1; bool trigger_update : 1; bool update_objects : 1; - bool close_socket : 1; + bool close_socket : 1; + bool server_disabled: 1; } client; /* Allocate some data for queries and updates. Make sure it's large enough to @@ -201,23 +205,18 @@ static void set_sm_state_delayed(uint8_t sm_state, int64_t delay_ms) event = LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE; } else if (sm_state == ENGINE_REGISTRATION_DONE_RX_OFF) { event = LWM2M_RD_CLIENT_EVENT_QUEUE_MODE_RX_OFF; - } else if ((sm_state == ENGINE_INIT || - sm_state == ENGINE_DEREGISTERED) && + } else if (sm_state == ENGINE_DEREGISTERED && (client.engine_state >= ENGINE_DO_REGISTRATION && - client.engine_state <= ENGINE_DEREGISTER_SENT)) { + client.engine_state <= ENGINE_DEREGISTER_SENT) && !client.server_disabled) { event = LWM2M_RD_CLIENT_EVENT_DISCONNECT; - } else if (sm_state == ENGINE_NETWORK_ERROR) { - lwm2m_socket_close(client.ctx); - client.retry_delay = 1 << client.retries; - client.retries++; - if (client.retries > CONFIG_LWM2M_RD_CLIENT_MAX_RETRIES) { - client.retries = 0; - event = LWM2M_RD_CLIENT_EVENT_NETWORK_ERROR; - } } else if (sm_state == ENGINE_UPDATE_REGISTRATION) { event = LWM2M_RD_CLIENT_EVENT_REG_UPDATE; } else if (sm_state == ENGINE_DEREGISTER) { - event = LWM2M_RD_CLIENT_EVENT_DEREGISTER; + if (client.server_disabled) { + event = LWM2M_RD_CLIENT_EVENT_SERVER_DISABLED; + } else { + event = LWM2M_RD_CLIENT_EVENT_DEREGISTER; + } } if (sm_is_suspended()) { @@ -292,27 +291,22 @@ static uint8_t get_sm_state(void) return state; } -static void sm_handle_timeout_state(struct lwm2m_message *msg, - enum sm_engine_state sm_state) +static void sm_handle_timeout_state(enum sm_engine_state sm_state) { k_mutex_lock(&client.mutex, K_FOREVER); enum lwm2m_rd_client_event event = LWM2M_RD_CLIENT_EVENT_NONE; -#if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP) - if (client.engine_state == ENGINE_BOOTSTRAP_REG_SENT) { - event = LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_REG_FAILURE; - } else -#endif - { - if (client.engine_state == ENGINE_REGISTRATION_SENT) { - event = LWM2M_RD_CLIENT_EVENT_REG_TIMEOUT; - } else if (client.engine_state == ENGINE_UPDATE_SENT) { - event = LWM2M_RD_CLIENT_EVENT_REG_TIMEOUT; - } else if (client.engine_state == ENGINE_DEREGISTER_SENT) { - event = LWM2M_RD_CLIENT_EVENT_DEREGISTER_FAILURE; - } else { - /* TODO: unknown timeout state */ - } + /* Don't send BOOTSTRAP_REG_FAILURE event, that is only emitted from + * do_network_error() once we are out of retries. + */ + if (client.engine_state == ENGINE_REGISTRATION_SENT) { + event = LWM2M_RD_CLIENT_EVENT_REG_TIMEOUT; + } else if (client.engine_state == ENGINE_UPDATE_SENT) { + event = LWM2M_RD_CLIENT_EVENT_REG_TIMEOUT; + } else if (client.engine_state == ENGINE_DEREGISTER_SENT) { + event = LWM2M_RD_CLIENT_EVENT_DEREGISTER_FAILURE; + } else { + /* TODO: unknown timeout state */ } set_sm_state(sm_state); @@ -374,11 +368,14 @@ static void socket_fault_cb(int error) /* Network error state causes engine to re-register, * so only trigger that state if we are not stopping the * engine. + * Also when engine is going to be disabled, for a while, we might get spurious + * socket errors when closing, so ignore them. */ if (client.engine_state > ENGINE_IDLE && - client.engine_state < ENGINE_SUSPENDED) { - set_sm_state(ENGINE_NETWORK_ERROR); - } else if (client.engine_state != ENGINE_SUSPENDED) { + client.engine_state < ENGINE_SERVER_DISABLED) { + sm_handle_timeout_state(ENGINE_NETWORK_ERROR); + } else if (client.engine_state != ENGINE_SUSPENDED && + !client.server_disabled) { sm_handle_failure_state(ENGINE_IDLE); } } @@ -453,13 +450,7 @@ static int do_bootstrap_reply_cb(const struct coap_packet *response, static void do_bootstrap_reg_timeout_cb(struct lwm2m_message *msg) { LOG_WRN("Bootstrap Timeout"); - - /* TODO: - * Look for the "next" BOOTSTRAP server entry in our security info - */ - - /* Restart from scratch */ - sm_handle_timeout_state(msg, ENGINE_INIT); + sm_handle_timeout_state(ENGINE_NETWORK_ERROR); } #endif @@ -522,6 +513,8 @@ static int do_registration_reply_cb(const struct coap_packet *response, /* remember the last reg time */ client.last_update = k_uptime_get(); + client.server_disabled = false; + client.retries = 0; memcpy(client.server_ep, options[1].value, options[1].len); @@ -533,11 +526,12 @@ static int do_registration_reply_cb(const struct coap_packet *response, return 0; } - LOG_ERR("Failed with code %u.%u (%s). Not Retrying.", + LOG_ERR("Failed with code %u.%u (%s).", COAP_RESPONSE_CODE_CLASS(code), COAP_RESPONSE_CODE_DETAIL(code), code2str(code)); fail: - sm_handle_failure_state(ENGINE_IDLE); + lwm2m_server_disable(client.ctx->srv_obj_inst, DISABLE_TIMEOUT); + sm_handle_failure_state(ENGINE_NETWORK_ERROR); return ret; } @@ -546,8 +540,7 @@ static void do_registration_timeout_cb(struct lwm2m_message *msg) { LOG_WRN("Registration Timeout"); - /* Restart from scratch */ - sm_handle_timeout_state(msg, ENGINE_INIT); + sm_handle_timeout_state(ENGINE_NETWORK_ERROR); } static int do_update_reply_cb(const struct coap_packet *response, @@ -588,7 +581,7 @@ static void do_update_timeout_cb(struct lwm2m_message *msg) client.close_socket = true; } /* Re-do registration */ - sm_handle_timeout_state(msg, ENGINE_DO_REGISTRATION); + sm_handle_timeout_state(ENGINE_DO_REGISTRATION); } static int do_deregister_reply_cb(const struct coap_packet *response, @@ -612,7 +605,7 @@ static int do_deregister_reply_cb(const struct coap_packet *response, COAP_RESPONSE_CODE_CLASS(code), COAP_RESPONSE_CODE_DETAIL(code), code2str(code)); - sm_handle_failure_state(ENGINE_IDLE); + sm_handle_failure_state(ENGINE_DEREGISTERED); return 0; } @@ -621,10 +614,10 @@ static void do_deregister_timeout_cb(struct lwm2m_message *msg) { LOG_WRN("De-Registration Timeout"); - sm_handle_timeout_state(msg, ENGINE_IDLE); + sm_handle_timeout_state(ENGINE_DEREGISTERED); } -static bool sm_bootstrap_verify(bool bootstrap_server, int sec_obj_inst) +static bool is_bootsrap_server(int sec_obj_inst) { bool bootstrap; int ret; @@ -634,12 +627,7 @@ static bool sm_bootstrap_verify(bool bootstrap_server, int sec_obj_inst) LOG_WRN("Failed to check bootstrap, err %d", ret); return false; } - - if (bootstrap == bootstrap_server) { - return true; - } else { - return false; - } + return bootstrap; } static bool sm_update_lifetime(int srv_obj_inst, uint32_t *lifetime) @@ -665,58 +653,40 @@ static bool sm_update_lifetime(int srv_obj_inst, uint32_t *lifetime) return false; } -static int sm_select_server_inst(int sec_obj_inst, int *srv_obj_inst, - uint32_t *lifetime) -{ - uint16_t server_id; - int ret, obj_inst_id; - - ret = lwm2m_get_u16(&LWM2M_OBJ(0, sec_obj_inst, 10), &server_id); - if (ret < 0) { - LOG_WRN("Failed to obtain Short Server ID, err %d", ret); - return -EINVAL; - } - - obj_inst_id = lwm2m_server_short_id_to_inst(server_id); - if (obj_inst_id < 0) { - LOG_WRN("Failed to obtain Server Object instance, err %d", - obj_inst_id); - return -EINVAL; - } - - sm_update_lifetime(obj_inst_id, lifetime); - *srv_obj_inst = obj_inst_id; - - return 0; -} - -static int sm_select_security_inst(bool bootstrap_server, int *sec_obj_inst) +/** + * @brief Find the next security instance for bootstrapping. + * + * Search for the next security instance that has the bootstrap flag set and + * is not the same as current security instance. + * + * @param sec_obj_inst current security instance or -1. + * @return zero on success, negative on error. + */ +static int sm_next_bootstrap_inst(int *sec_obj_inst) { int i, obj_inst_id = -1; - /* lookup existing index */ - i = lwm2m_security_inst_id_to_index(*sec_obj_inst); - if (i >= 0 && sm_bootstrap_verify(bootstrap_server, *sec_obj_inst)) { - return 0; + if (*sec_obj_inst >= 0 && !is_bootsrap_server(*sec_obj_inst)) { + *sec_obj_inst = -1; } - *sec_obj_inst = -1; - /* Iterate over all instances to find the correct one. */ for (i = 0; i < CONFIG_LWM2M_SECURITY_INSTANCE_COUNT; i++) { obj_inst_id = lwm2m_security_index_to_inst_id(i); if (obj_inst_id < 0) { - LOG_WRN("Failed to get inst id for %d", i); + continue; + } + if (obj_inst_id == *sec_obj_inst) { continue; } - if (sm_bootstrap_verify(bootstrap_server, obj_inst_id)) { + if (is_bootsrap_server(obj_inst_id)) { *sec_obj_inst = obj_inst_id; return 0; } } - LOG_WRN("sec_obj_inst: No matching servers found."); + LOG_WRN("No Bootstrap servers found."); return -ENOENT; } @@ -726,24 +696,17 @@ static int sm_select_security_inst(bool bootstrap_server, int *sec_obj_inst) static int sm_do_init(void) { lwm2m_engine_stop(client.ctx); - client.ctx->sec_obj_inst = -1; - client.ctx->srv_obj_inst = -1; client.trigger_update = false; client.lifetime = 0U; - client.retries = 0U; client.last_update = 0U; client.close_socket = false; /* Do bootstrap or registration */ -#if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP) - if (client.use_bootstrap) { + if (client.use_bootstrap && IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)) { set_sm_state(ENGINE_DO_BOOTSTRAP_REG); } else { set_sm_state(ENGINE_DO_REGISTRATION); } -#else - set_sm_state(ENGINE_DO_REGISTRATION); -#endif return 0; } @@ -814,7 +777,7 @@ cleanup: return ret; } -static int sm_do_bootstrap_reg(void) +static void sm_do_bootstrap_reg(void) { int ret; @@ -824,23 +787,20 @@ static int sm_do_bootstrap_reg(void) } client.ctx->bootstrap_mode = true; - ret = sm_select_security_inst(client.ctx->bootstrap_mode, - &client.ctx->sec_obj_inst); + ret = sm_next_bootstrap_inst(&client.ctx->sec_obj_inst); if (ret < 0) { - /* no bootstrap server found, let's move to registration */ - LOG_WRN("Bootstrap server not found! Try normal registration."); - set_sm_state(ENGINE_DO_REGISTRATION); - return ret; + set_sm_state(ENGINE_NETWORK_ERROR); + return; } - LOG_INF("Bootstrap started with endpoint '%s' with client lifetime %d", - client.ep_name, client.lifetime); + LOG_INF("Bootstrap started with endpoint '%s' using security object %d", + client.ep_name, client.ctx->sec_obj_inst); ret = lwm2m_engine_start(client.ctx); if (ret < 0) { LOG_ERR("Cannot init LWM2M engine (%d)", ret); set_sm_state(ENGINE_NETWORK_ERROR); - return ret; + return; } ret = sm_send_bootstrap_registration(); @@ -851,7 +811,7 @@ static int sm_do_bootstrap_reg(void) set_sm_state(ENGINE_NETWORK_ERROR); } - return ret; + return; } void engine_bootstrap_finish(void) @@ -1047,8 +1007,9 @@ static int sm_send_registration_msg(void) return ret; } -static int sm_do_registration(void) +static void sm_do_registration(void) { + uint16_t ssid; int ret = 0; if (client.ctx->connection_suspended) { @@ -1056,10 +1017,16 @@ static int sm_do_registration(void) lwm2m_engine_context_close(client.ctx); /* perform full registration */ set_sm_state(ENGINE_DO_REGISTRATION); - return 0; + return; } } else { + bool select_srv = true; + uint16_t srv = (uint16_t) client.ctx->srv_obj_inst; + + client.last_update = 0; + client.ctx->bootstrap_mode = false; + /* clear out existing connection data */ if (client.ctx->sock_fd > -1) { if (client.close_socket) { @@ -1068,43 +1035,58 @@ static int sm_do_registration(void) lwm2m_engine_stop(client.ctx); } else { lwm2m_engine_context_close(client.ctx); + /* Keep current connection, retry registration with same server */ + select_srv = false; } } - client.last_update = 0; + if (select_srv) { + /* Select next one from the list, or fail */ + if (!lwm2m_server_select(&srv)) { + LOG_ERR("Unable to find a valid server instance."); + goto bootstrap_or_retry; + } - client.ctx->bootstrap_mode = false; - ret = sm_select_security_inst(client.ctx->bootstrap_mode, - &client.ctx->sec_obj_inst); - if (ret < 0) { - LOG_ERR("Unable to find a valid security instance."); - set_sm_state(ENGINE_INIT); - return -EINVAL; + client.ctx->srv_obj_inst = srv; + sm_update_lifetime(srv, &client.lifetime); + + ret = lwm2m_get_u16(&LWM2M_OBJ(1, client.ctx->srv_obj_inst, 0), &ssid); + if (ret < 0) { + LOG_ERR("Failed to read SSID"); + lwm2m_server_disable(srv, K_FOREVER); + goto bootstrap_or_retry; + } + + ret = lwm2m_security_short_id_to_inst(ssid); + if (ret < 0) { + LOG_ERR("Unable to find a valid security instance."); + lwm2m_server_disable(srv, K_FOREVER); + goto bootstrap_or_retry; + } + client.ctx->sec_obj_inst = (uint16_t) ret; } - ret = sm_select_server_inst(client.ctx->sec_obj_inst, - &client.ctx->srv_obj_inst, - &client.lifetime); - if (ret < 0) { - LOG_ERR("Unable to find a valid server instance."); - set_sm_state(ENGINE_INIT); - return -EINVAL; - } - - LOG_INF("RD Client started with endpoint '%s' with client lifetime %d", - client.ep_name, client.lifetime); + LOG_INF("RD Client started with endpoint '%s' with client lifetime %d using server " + "object %d", + client.ep_name, client.lifetime, client.ctx->srv_obj_inst); ret = lwm2m_engine_start(client.ctx); if (ret < 0) { LOG_ERR("Cannot init LWM2M engine (%d)", ret); - set_sm_state(ENGINE_NETWORK_ERROR); - return ret; + goto bootstrap_or_retry; } } - ret = sm_send_registration_msg(); + sm_send_registration_msg(); + return; - return ret; +bootstrap_or_retry: + lwm2m_engine_stop(client.ctx); + if (!client.server_disabled && fallback_to_bootstrap()) { + return; + } + + set_sm_state(ENGINE_NETWORK_ERROR); } static int64_t next_update(void) @@ -1269,32 +1251,116 @@ close_ctx: return ret; } +static bool fallback_to_bootstrap(void) +{ + if (IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)) { + bool fallback = true; + + (void)lwm2m_get_bool(&LWM2M_OBJ(LWM2M_OBJECT_SERVER_ID, client.ctx->srv_obj_inst, + SERVER_BOOTSTRAP_ON_REGISTRATION_FAILURE_ID), + &fallback); + if (fallback) { + client.use_bootstrap = true; + set_sm_state(ENGINE_INIT); + return true; + } + } + return false; +} + static void sm_do_network_error(void) { int err; + LOG_ERR("sm_do_network_error, retries %d", client.retries); + + lwm2m_socket_close(client.ctx); + if (client.retry_delay) { - client.retry_delay = 0; next_event_at(k_uptime_get() + client.retry_delay * MSEC_PER_SEC); + client.retry_delay = 0; return; } + client.retry_delay = 1 << client.retries; + client.retries++; + + /* Stop retrying and try fallback */ + if (client.retries > CONFIG_LWM2M_RD_CLIENT_MAX_RETRIES) { + LOG_ERR("Network error, max retries reached (%d)", client.retries); + + /* Disable current server for a period so lwm2m_server_select() does not pick it */ + if (client.ctx->srv_obj_inst > -1) { + lwm2m_server_disable(client.ctx->srv_obj_inst, DISABLE_TIMEOUT); + } + + /* Are we in bootstrap? Try if we can fallback to some other BS server */ + if (client.ctx->bootstrap_mode && + IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)) { + LOG_DBG("In bootstrap, try fallback srv"); + /* Do we have any other bootstrap server to back off to? */ + if (sm_next_bootstrap_inst(&client.ctx->sec_obj_inst) < 0) { + /* No, we are out of options, stop engine */ + goto stop_engine; + } + set_sm_state(ENGINE_INIT); + return; + } + + /* Try if there are other server to fall back to, + * Only allow fallback to higher priority server (lower value, or lower id) + * if we have successfully registered before. + * This should block us from looping the same list again. + * Instead we should fallback to bootstrap. + */ + uint16_t srv; + + if (lwm2m_server_select(&srv)) { + uint8_t p1, p2; + + p1 = lwm2m_server_get_prio(client.ctx->srv_obj_inst); + p2 = lwm2m_server_get_prio(srv); + if (p1 < p2 || client.last_update != 0) { + set_sm_state(ENGINE_INIT); + return; + } + } + + /* If we have been disabled by some server, don't fall back to bootstrap */ + if (client.server_disabled) { + set_sm_state(ENGINE_SERVER_DISABLED); + return; + } + + if (fallback_to_bootstrap()) { + return; + } + goto stop_engine; + } -#if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP) - if (client.ctx->bootstrap_mode) { - lwm2m_engine_context_close(client.ctx); - set_sm_state(ENGINE_DO_BOOTSTRAP_REG); - return; + /* Retry bootstrap */ + if (IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)) { + if (client.ctx->bootstrap_mode) { + lwm2m_engine_context_close(client.ctx); + /* If we don't have fallback BS server, retry with current one */ + if (sm_next_bootstrap_inst(&client.ctx->sec_obj_inst) < 0) { + client.ctx->sec_obj_inst = -1; + } + set_sm_state(ENGINE_DO_BOOTSTRAP_REG); + return; + } } -#endif if (!client.last_update || (k_uptime_get() - client.last_update) / MSEC_PER_SEC > client.lifetime) { /* do full registration as there is no active registration or lifetime exceeded */ - lwm2m_engine_context_close(client.ctx); + /* Keep the same server until out of retry */ set_sm_state(ENGINE_DO_REGISTRATION); return; } + /* Try if we can recover the DTLS session and try Update. + * This might fallback into full registration on sm_handle_registration_update_failure(). + */ err = lwm2m_socket_start(client.ctx); if (err) { LOG_ERR("Failed to start socket %d", err); @@ -1305,8 +1371,21 @@ static void sm_do_network_error(void) set_sm_state(ENGINE_NETWORK_ERROR); return; } - set_sm_state(ENGINE_UPDATE_REGISTRATION); + return; + +stop_engine: + + /* We are out of options, stop engine */ + if (client.ctx->event_cb) { + if (client.ctx->bootstrap_mode) { + client.ctx->event_cb(client.ctx, + LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_REG_FAILURE); + } else { + client.ctx->event_cb(client.ctx, LWM2M_RD_CLIENT_EVENT_NETWORK_ERROR); + } + } + set_sm_state(ENGINE_IDLE); } static void lwm2m_rd_client_service(struct k_work *work) @@ -1380,6 +1459,19 @@ static void lwm2m_rd_client_service(struct k_work *work) timeout = EXCHANGE_LIFETIME; break; + case ENGINE_SERVER_DISABLED: + if (lwm2m_server_select(NULL)) { + set_sm_state(ENGINE_INIT); + } else { + /* wait for server to be enabled. */ + /* + * TODO: Once engine is converted to use timepoint_t + * this should calculate the next event from the previous server. + */ + next_event_at(k_uptime_get() + SEC_PER_MIN * MSEC_PER_SEC); + } + break; + case ENGINE_DEREGISTER: sm_do_deregister(); break; @@ -1391,7 +1483,11 @@ static void lwm2m_rd_client_service(struct k_work *work) case ENGINE_DEREGISTERED: lwm2m_engine_stop(client.ctx); - set_sm_state(ENGINE_IDLE); + if (client.server_disabled) { + set_sm_state(ENGINE_SERVER_DISABLED); + } else { + set_sm_state(ENGINE_IDLE); + } break; case ENGINE_NETWORK_ERROR: @@ -1408,7 +1504,7 @@ static void lwm2m_rd_client_service(struct k_work *work) if (end < k_uptime_get()) { LOG_DBG("State machine have timed out"); - sm_handle_timeout_state(NULL, ENGINE_INIT); + sm_handle_timeout_state(ENGINE_INIT); } else if (client.next_event > end) { next_event_at(end); } @@ -1442,6 +1538,7 @@ int lwm2m_rd_client_start(struct lwm2m_ctx *client_ctx, const char *ep_name, } /* Init Context */ + lwm2m_server_reset_timestamps(); lwm2m_engine_context_init(client_ctx); client.ctx = client_ctx; @@ -1450,13 +1547,15 @@ int lwm2m_rd_client_start(struct lwm2m_ctx *client_ctx, const char *ep_name, client.ctx->observe_cb = observe_cb; client.ctx->event_cb = event_cb; client.use_bootstrap = flags & LWM2M_RD_CLIENT_FLAG_BOOTSTRAP; + client.ctx->srv_obj_inst = -1; + client.ctx->sec_obj_inst = -1; + client.retries = 0; - set_sm_state(ENGINE_INIT); strncpy(client.ep_name, ep_name, CLIENT_EP_LEN - 1); client.ep_name[CLIENT_EP_LEN - 1] = '\0'; LOG_INF("Start LWM2M Client: %s", client.ep_name); - next_event_at(0); + set_sm_state(ENGINE_INIT); k_mutex_unlock(&client.mutex); @@ -1477,9 +1576,10 @@ int lwm2m_rd_client_stop(struct lwm2m_ctx *client_ctx, client.ctx->event_cb = event_cb; rd_client_message_free(); - if (sm_is_registered() && deregister) { + if (sm_is_registered() && deregister && !client.server_disabled) { set_sm_state(ENGINE_DEREGISTER); } else { + client.server_disabled = false; set_sm_state(ENGINE_DEREGISTERED); } @@ -1557,7 +1657,7 @@ int lwm2m_rd_client_resume(void) #endif /* Or do we resume into registration state */ if (client.engine_state >= ENGINE_DO_REGISTRATION && - client.engine_state <= ENGINE_SUSPENDED) { + client.engine_state <= ENGINE_SERVER_DISABLED) { if (!client.last_update || (client.lifetime <= (k_uptime_get() - client.last_update) / MSEC_PER_SEC)) { /* No lifetime left, register again */ @@ -1575,6 +1675,29 @@ int lwm2m_rd_client_resume(void) return 0; } +int lwm2m_rd_client_server_disabled(uint16_t inst_id) +{ + if (client.ctx->srv_obj_inst != inst_id) { + return -EPERM; + } + + k_mutex_lock(&client.mutex, K_FOREVER); + + client.server_disabled = true; + + if (sm_is_registered()) { + LOG_INF("Server disabled, deregister"); + set_sm_state_delayed(ENGINE_DEREGISTER, DELAY_BEFORE_CLOSING); + } else { + LOG_INF("Server disabled"); + set_sm_state(ENGINE_DEREGISTERED); + } + + k_mutex_unlock(&client.mutex); + + return 0; +} + void lwm2m_rd_client_update(void) { engine_trigger_update(false); diff --git a/subsys/net/lib/lwm2m/lwm2m_rd_client.h b/subsys/net/lib/lwm2m/lwm2m_rd_client.h index 66a3aac20f3..5d71ccb30f6 100644 --- a/subsys/net/lib/lwm2m/lwm2m_rd_client.h +++ b/subsys/net/lib/lwm2m/lwm2m_rd_client.h @@ -56,4 +56,14 @@ int lwm2m_rd_client_connection_resume(struct lwm2m_ctx *client_ctx); void engine_update_tx_time(void); struct lwm2m_message *lwm2m_get_ongoing_rd_msg(void); +/** + * @brief Notify RD client that this server is disabled. + * + * This may return error -EPERM, if RD client is not registered on that server. + * + * @param inst_id server instance id + * @return int 0 on success, negative errno on failure. + */ +int lwm2m_rd_client_server_disabled(uint16_t inst_id); + #endif /* LWM2M_RD_CLIENT_H */ diff --git a/tests/net/lib/lwm2m/interop/src/lwm2m-client.c b/tests/net/lib/lwm2m/interop/src/lwm2m-client.c index 706091659a5..b6634069fbd 100644 --- a/tests/net/lib/lwm2m/interop/src/lwm2m-client.c +++ b/tests/net/lib/lwm2m/interop/src/lwm2m-client.c @@ -119,6 +119,10 @@ static void rd_client_event(struct lwm2m_ctx *client, /* do nothing */ break; + case LWM2M_RD_CLIENT_EVENT_SERVER_DISABLED: + LOG_DBG("LwM2M server disabled"); + break; + case LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_REG_FAILURE: LOG_DBG("Bootstrap registration failure!"); break; diff --git a/tests/net/lib/lwm2m/lwm2m_rd_client/src/main.c b/tests/net/lib/lwm2m/lwm2m_rd_client/src/main.c index 208298512d4..1d6a4e584fa 100644 --- a/tests/net/lib/lwm2m/lwm2m_rd_client/src/main.c +++ b/tests/net/lib/lwm2m/lwm2m_rd_client/src/main.c @@ -21,7 +21,7 @@ DEFINE_FFF_GLOBALS; /* Maximum number of iterations within the state machine of RD Client * service that is waited for until a possible event occurs */ -static const uint8_t RD_CLIENT_MAX_LOOKUP_ITERATIONS = 100; +#define RD_CLIENT_MAX_LOOKUP_ITERATIONS 500 FAKE_VOID_FUNC(show_lwm2m_event, enum lwm2m_rd_client_event); FAKE_VOID_FUNC(show_lwm2m_observe, enum lwm2m_observe_event); @@ -97,6 +97,9 @@ static void lwm2m_event_cb(struct lwm2m_ctx *client, enum lwm2m_rd_client_event case LWM2M_RD_CLIENT_EVENT_QUEUE_MODE_RX_OFF: LOG_INF("*** LWM2M_RD_CLIENT_EVENT_QUEUE_MODE_RX_OFF"); break; + case LWM2M_RD_CLIENT_EVENT_SERVER_DISABLED: + LOG_INF("*** LWM2M_RD_CLIENT_EVENT_SERVER_DISABLED"); + break; case LWM2M_RD_CLIENT_EVENT_ENGINE_SUSPENDED: LOG_INF("*** LWM2M_RD_CLIENT_EVENT_ENGINE_SUSPENDED"); break; @@ -165,6 +168,7 @@ static void my_suite_before(void *data) lwm2m_init_message_fake.custom_fake = lwm2m_init_message_fake_default; coap_header_get_code_fake.custom_fake = coap_header_get_code_fake_created; coap_packet_append_option_fake.custom_fake = NULL; + stub_lwm2m_server_disable(false); } static void my_suite_after(void *data) @@ -198,8 +202,6 @@ ZTEST(lwm2m_rd_client, test_start_registration_ok) (void)memset(&ctx, 0x0, sizeof(ctx)); - test_prepare_pending_message_cb(&message_reply_cb_default); - lwm2m_rd_client_init(); test_lwm2m_engine_start_service(); wait_for_service(1); @@ -207,6 +209,7 @@ ZTEST(lwm2m_rd_client, test_start_registration_ok) coap_find_options_fake.custom_fake = coap_find_options_do_registration_reply_cb_ok; zassert_true(lwm2m_rd_client_start(&ctx, "Test", 0, lwm2m_event_cb, lwm2m_observe_cb) == 0, NULL); + test_prepare_pending_message_cb(&message_reply_cb_default); zassert(lwm2m_rd_client_ctx() == &ctx, ""); zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE), NULL); @@ -228,8 +231,6 @@ ZTEST(lwm2m_rd_client, test_register_update_too_small_lifetime_to_default) (void)memset(&ctx, 0x0, sizeof(ctx)); - test_prepare_pending_message_cb(&message_reply_cb_default); - lwm2m_rd_client_init(); test_lwm2m_engine_start_service(); wait_for_service(1); @@ -237,6 +238,7 @@ ZTEST(lwm2m_rd_client, test_register_update_too_small_lifetime_to_default) coap_find_options_fake.custom_fake = coap_find_options_do_registration_reply_cb_ok; zassert_true(lwm2m_rd_client_start(&ctx, "Test", 0, lwm2m_event_cb, lwm2m_observe_cb) == 0, NULL); + test_prepare_pending_message_cb(&message_reply_cb_default); zassert(lwm2m_rd_client_ctx() == &ctx, ""); zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE), NULL); @@ -249,14 +251,13 @@ ZTEST(lwm2m_rd_client, test_timeout_resume_registration) (void)memset(&ctx, 0x0, sizeof(ctx)); - test_prepare_pending_message_cb(&message_reply_cb_default); - lwm2m_rd_client_init(); test_lwm2m_engine_start_service(); coap_find_options_fake.custom_fake = coap_find_options_do_registration_reply_cb_ok; zassert_true(lwm2m_rd_client_start(&ctx, "Test", 0, lwm2m_event_cb, lwm2m_observe_cb) == 0, NULL); + test_prepare_pending_message_cb(&message_reply_cb_default); zassert(lwm2m_rd_client_ctx() == &ctx, ""); zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE), NULL); @@ -273,16 +274,17 @@ ZTEST(lwm2m_rd_client, test_start_registration_timeout) (void)memset(&ctx, 0x0, sizeof(ctx)); - test_prepare_pending_message_cb(&message_reply_timeout_cb_default); - lwm2m_rd_client_init(); test_lwm2m_engine_start_service(); wait_for_service(1); zassert_true(lwm2m_rd_client_start(&ctx, "Test", 0, lwm2m_event_cb, lwm2m_observe_cb) == 0, NULL); - zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_DISCONNECT), NULL); + test_prepare_pending_message_cb(&message_reply_timeout_cb_default); zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REG_TIMEOUT), NULL); + zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REG_TIMEOUT), NULL); + zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REG_TIMEOUT), NULL); + zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_NETWORK_ERROR), NULL); } ZTEST(lwm2m_rd_client, test_start_registration_fail) @@ -291,8 +293,6 @@ ZTEST(lwm2m_rd_client, test_start_registration_fail) (void)memset(&ctx, 0x0, sizeof(ctx)); - test_prepare_pending_message_cb(&message_reply_cb_default); - lwm2m_rd_client_init(); test_lwm2m_engine_start_service(); wait_for_service(1); @@ -302,8 +302,15 @@ ZTEST(lwm2m_rd_client, test_start_registration_fail) lwm2m_init_message_fake.custom_fake = lwm2m_init_message_fake_default; zassert_true(lwm2m_rd_client_start(&ctx, "Test", 0, lwm2m_event_cb, lwm2m_observe_cb) == 0, NULL); + test_prepare_pending_message_cb(&message_reply_cb_default); zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REGISTRATION_FAILURE), NULL); + zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REGISTRATION_FAILURE), + NULL); + zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REGISTRATION_FAILURE), + NULL); + zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_NETWORK_ERROR), + NULL); } ZTEST(lwm2m_rd_client, test_start_registration_update) @@ -312,8 +319,6 @@ ZTEST(lwm2m_rd_client, test_start_registration_update) (void)memset(&ctx, 0x0, sizeof(ctx)); - test_prepare_pending_message_cb(&message_reply_cb_default); - lwm2m_rd_client_init(); test_lwm2m_engine_start_service(); wait_for_service(1); @@ -321,6 +326,7 @@ ZTEST(lwm2m_rd_client, test_start_registration_update) coap_find_options_fake.custom_fake = coap_find_options_do_registration_reply_cb_ok; zassert_true(lwm2m_rd_client_start(&ctx, "Test", 0, lwm2m_event_cb, lwm2m_observe_cb) == 0, NULL); + test_prepare_pending_message_cb(&message_reply_cb_default); zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE), NULL); @@ -335,8 +341,6 @@ ZTEST(lwm2m_rd_client, test_rx_off) (void)memset(&ctx, 0x0, sizeof(ctx)); - test_prepare_pending_message_cb(&message_reply_cb_default); - lwm2m_rd_client_init(); test_lwm2m_engine_start_service(); wait_for_service(1); @@ -344,6 +348,7 @@ ZTEST(lwm2m_rd_client, test_rx_off) coap_find_options_fake.custom_fake = coap_find_options_do_registration_reply_cb_ok; zassert_true(lwm2m_rd_client_start(&ctx, "Test", 0, lwm2m_event_cb, lwm2m_observe_cb) == 0, NULL); + test_prepare_pending_message_cb(&message_reply_cb_default); zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE), NULL); @@ -359,8 +364,6 @@ ZTEST(lwm2m_rd_client, test_start_registration_update_fail) (void)memset(&ctx, 0x0, sizeof(ctx)); - test_prepare_pending_message_cb(&message_reply_cb_default); - lwm2m_rd_client_init(); test_lwm2m_engine_start_service(); wait_for_service(1); @@ -368,6 +371,7 @@ ZTEST(lwm2m_rd_client, test_start_registration_update_fail) coap_find_options_fake.custom_fake = coap_find_options_do_registration_reply_cb_ok; zassert_true(lwm2m_rd_client_start(&ctx, "Test", 0, lwm2m_event_cb, lwm2m_observe_cb) == 0, NULL); + test_prepare_pending_message_cb(&message_reply_cb_default); zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE), NULL); @@ -384,8 +388,6 @@ ZTEST(lwm2m_rd_client, test_registration_update_timeout) (void)memset(&ctx, 0x0, sizeof(ctx)); - test_prepare_pending_message_cb(&message_reply_cb_default); - lwm2m_rd_client_init(); test_lwm2m_engine_start_service(); wait_for_service(1); @@ -393,6 +395,7 @@ ZTEST(lwm2m_rd_client, test_registration_update_timeout) coap_find_options_fake.custom_fake = coap_find_options_do_registration_reply_cb_ok; zassert_true(lwm2m_rd_client_start(&ctx, "Test", 0, lwm2m_event_cb, lwm2m_observe_cb) == 0, NULL); + test_prepare_pending_message_cb(&message_reply_cb_default); zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE), NULL); test_prepare_pending_message_cb(&message_reply_timeout_cb_default); @@ -415,8 +418,6 @@ ZTEST(lwm2m_rd_client, test_deregistration_timeout) (void)memset(&ctx, 0x0, sizeof(ctx)); - test_prepare_pending_message_cb(&message_reply_cb_default); - lwm2m_rd_client_init(); test_lwm2m_engine_start_service(); wait_for_service(1); @@ -424,6 +425,7 @@ ZTEST(lwm2m_rd_client, test_deregistration_timeout) coap_find_options_fake.custom_fake = coap_find_options_do_registration_reply_cb_ok; zassert_true(lwm2m_rd_client_start(&ctx, "Test", 0, lwm2m_event_cb, lwm2m_observe_cb) == 0, NULL); + test_prepare_pending_message_cb(&message_reply_cb_default); zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE), NULL); @@ -439,8 +441,6 @@ ZTEST(lwm2m_rd_client, test_error_on_registration_update) (void)memset(&ctx, 0x0, sizeof(ctx)); - test_prepare_pending_message_cb(&message_reply_cb_default); - lwm2m_rd_client_init(); test_lwm2m_engine_start_service(); wait_for_service(1); @@ -449,6 +449,8 @@ ZTEST(lwm2m_rd_client, test_error_on_registration_update) coap_packet_append_option_fake.custom_fake = NULL; zassert_true(lwm2m_rd_client_start(&ctx, "Test", 0, lwm2m_event_cb, lwm2m_observe_cb) == 0, NULL); + test_prepare_pending_message_cb(&message_reply_cb_default); + zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE), NULL); @@ -482,8 +484,6 @@ ZTEST(lwm2m_rd_client, test_suspend_resume_registration) (void)memset(&ctx, 0x0, sizeof(ctx)); - test_prepare_pending_message_cb(&message_reply_cb_default); - lwm2m_rd_client_init(); test_lwm2m_engine_start_service(); wait_for_service(1); @@ -491,6 +491,7 @@ ZTEST(lwm2m_rd_client, test_suspend_resume_registration) coap_find_options_fake.custom_fake = coap_find_options_do_registration_reply_cb_ok; zassert_true(lwm2m_rd_client_start(&ctx, "Test", 0, lwm2m_event_cb, lwm2m_observe_cb) == 0, NULL); + test_prepare_pending_message_cb(&message_reply_cb_default); zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE), NULL); zassert_true(!lwm2m_rd_client_is_suspended(&ctx), NULL); @@ -517,8 +518,6 @@ ZTEST(lwm2m_rd_client, test_suspend_stop_resume) (void)memset(&ctx, 0x0, sizeof(ctx)); - test_prepare_pending_message_cb(&message_reply_cb_default); - lwm2m_rd_client_init(); test_lwm2m_engine_start_service(); wait_for_service(1); @@ -527,6 +526,7 @@ ZTEST(lwm2m_rd_client, test_suspend_stop_resume) coap_find_options_fake.custom_fake = coap_find_options_do_registration_reply_cb_ok; zassert_true(lwm2m_rd_client_start(&ctx, "Test", 0, lwm2m_event_cb, lwm2m_observe_cb) == 0, NULL); + test_prepare_pending_message_cb(&message_reply_cb_default); zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE), NULL); zassert_true(lwm2m_rd_client_pause() == 0, NULL); @@ -544,8 +544,6 @@ ZTEST(lwm2m_rd_client, test_socket_error) (void)memset(&ctx, 0x0, sizeof(ctx)); - test_prepare_pending_message_cb(&message_reply_cb_default); - lwm2m_rd_client_init(); test_lwm2m_engine_start_service(); wait_for_service(1); @@ -554,6 +552,7 @@ ZTEST(lwm2m_rd_client, test_socket_error) coap_find_options_fake.custom_fake = coap_find_options_do_registration_reply_cb_ok; zassert_true(lwm2m_rd_client_start(&ctx, "Test", 0, lwm2m_event_cb, lwm2m_observe_cb) == 0, NULL); + test_prepare_pending_message_cb(&message_reply_cb_default); zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE), NULL); @@ -569,8 +568,6 @@ ZTEST(lwm2m_rd_client, test_socket_error_on_stop) (void)memset(&ctx, 0x0, sizeof(ctx)); - test_prepare_pending_message_cb(&message_reply_cb_default); - lwm2m_rd_client_init(); test_lwm2m_engine_start_service(); wait_for_service(1); @@ -579,6 +576,7 @@ ZTEST(lwm2m_rd_client, test_socket_error_on_stop) coap_find_options_fake.custom_fake = coap_find_options_do_registration_reply_cb_ok; zassert_true(lwm2m_rd_client_start(&ctx, "Test", 0, lwm2m_event_cb, lwm2m_observe_cb) == 0, NULL); + test_prepare_pending_message_cb(&message_reply_cb_default); zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE), NULL); @@ -608,8 +606,6 @@ ZTEST(lwm2m_rd_client, test_engine_trigger_bootstrap) (void)memset(&ctx, 0x0, sizeof(ctx)); - test_prepare_pending_message_cb(&message_reply_cb_default); - lwm2m_rd_client_init(); test_lwm2m_engine_start_service(); wait_for_service(1); @@ -617,6 +613,7 @@ ZTEST(lwm2m_rd_client, test_engine_trigger_bootstrap) coap_find_options_fake.custom_fake = coap_find_options_do_registration_reply_cb_ok; zassert_true(lwm2m_rd_client_start(&ctx, "Test", 0, lwm2m_event_cb, lwm2m_observe_cb) == 0, NULL); + test_prepare_pending_message_cb(&message_reply_cb_default); zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE), NULL); lwm2m_get_bool_fake.custom_fake = lwm2m_get_bool_fake_true; @@ -638,8 +635,6 @@ ZTEST(lwm2m_rd_client, test_bootstrap_timeout) (void)memset(&ctx, 0x0, sizeof(ctx)); - test_prepare_pending_message_cb(&message_reply_timeout_cb_default); - lwm2m_rd_client_init(); test_lwm2m_engine_start_service(); wait_for_service(1); @@ -651,6 +646,7 @@ ZTEST(lwm2m_rd_client, test_bootstrap_timeout) coap_find_options_fake.custom_fake = coap_find_options_do_registration_reply_cb_ok; zassert_true(lwm2m_rd_client_start(&ctx, "Test", 1, lwm2m_event_cb, lwm2m_observe_cb) == 0, NULL); + test_prepare_pending_message_cb(&message_reply_timeout_cb_default); zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_REG_FAILURE), NULL); } @@ -661,8 +657,6 @@ ZTEST(lwm2m_rd_client, test_bootstrap_fail) (void)memset(&ctx, 0x0, sizeof(ctx)); - test_prepare_pending_message_cb(&message_reply_cb_default); - lwm2m_rd_client_init(); test_lwm2m_engine_start_service(); wait_for_service(1); @@ -674,20 +668,19 @@ ZTEST(lwm2m_rd_client, test_bootstrap_fail) coap_find_options_fake.custom_fake = coap_find_options_do_registration_reply_cb_ok; zassert_true(lwm2m_rd_client_start(&ctx, "Test", 1, lwm2m_event_cb, lwm2m_observe_cb) == 0, NULL); + test_prepare_pending_message_cb(&message_reply_cb_default); zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_REG_FAILURE), NULL); } -ZTEST(lwm2m_rd_client, test_bootstrap_no_srv_fallback_to_register) +ZTEST(lwm2m_rd_client, test_bootstrap_no_srv) { struct lwm2m_ctx ctx; (void)memset(&ctx, 0x0, sizeof(ctx)); - test_prepare_pending_message_cb(&message_reply_cb_default); - lwm2m_rd_client_init(); test_lwm2m_engine_start_service(); wait_for_service(1); @@ -695,6 +688,134 @@ ZTEST(lwm2m_rd_client, test_bootstrap_no_srv_fallback_to_register) coap_find_options_fake.custom_fake = coap_find_options_do_registration_reply_cb_ok; zassert_true(lwm2m_rd_client_start(&ctx, "Test", 1, lwm2m_event_cb, lwm2m_observe_cb) == 0, NULL); + test_prepare_pending_message_cb(&message_reply_cb_default); + zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_REG_FAILURE), + NULL); +} + +ZTEST(lwm2m_rd_client, test_disable_server) +{ + struct lwm2m_ctx ctx; + + (void)memset(&ctx, 0x0, sizeof(ctx)); + + lwm2m_rd_client_init(); + test_lwm2m_engine_start_service(); + wait_for_service(1); + + coap_find_options_fake.custom_fake = coap_find_options_do_registration_reply_cb_ok; + zassert_true(lwm2m_rd_client_start(&ctx, "Test", 0, lwm2m_event_cb, lwm2m_observe_cb) == 0, + NULL); + test_prepare_pending_message_cb(&message_reply_cb_default); + zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE), + NULL); + coap_header_get_code_fake.custom_fake = coap_header_get_code_fake_deleted; + stub_lwm2m_server_disable(true); + lwm2m_rd_client_server_disabled(0); + zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_SERVER_DISABLED), + NULL); +} + +ZTEST(lwm2m_rd_client, test_disable_server_stop) +{ + struct lwm2m_ctx ctx; + + (void)memset(&ctx, 0x0, sizeof(ctx)); + + lwm2m_rd_client_init(); + test_lwm2m_engine_start_service(); + wait_for_service(1); + + coap_find_options_fake.custom_fake = coap_find_options_do_registration_reply_cb_ok; + zassert_true(lwm2m_rd_client_start(&ctx, "Test", 0, lwm2m_event_cb, lwm2m_observe_cb) == 0, + NULL); + test_prepare_pending_message_cb(&message_reply_cb_default); + zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE), + NULL); + coap_header_get_code_fake.custom_fake = coap_header_get_code_fake_deleted; + stub_lwm2m_server_disable(true); + lwm2m_rd_client_server_disabled(0); + zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_SERVER_DISABLED), + NULL); + wait_for_service(1); + zassert_true(lwm2m_rd_client_stop(&ctx, lwm2m_event_cb, true) == 0, NULL); + zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_DISCONNECT), NULL); +} + +ZTEST(lwm2m_rd_client, test_disable_server_connect) +{ + struct lwm2m_ctx ctx; + + (void)memset(&ctx, 0x0, sizeof(ctx)); + + lwm2m_rd_client_init(); + test_lwm2m_engine_start_service(); + wait_for_service(1); + + coap_find_options_fake.custom_fake = coap_find_options_do_registration_reply_cb_ok; + zassert_true(lwm2m_rd_client_start(&ctx, "Test", 0, lwm2m_event_cb, lwm2m_observe_cb) == 0, + NULL); + test_prepare_pending_message_cb(&message_reply_cb_default); + zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE), + NULL); + coap_header_get_code_fake.custom_fake = coap_header_get_code_fake_deleted; + stub_lwm2m_server_disable(true); + lwm2m_rd_client_server_disabled(0); + zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_SERVER_DISABLED), + NULL); + + wait_for_service(500); + + coap_header_get_code_fake.custom_fake = coap_header_get_code_fake_created; + stub_lwm2m_server_disable(false); + zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE), + NULL); +} + +ZTEST(lwm2m_rd_client, test_fallback_to_bootstrap) +{ + struct lwm2m_ctx ctx; + + (void)memset(&ctx, 0x0, sizeof(ctx)); + + lwm2m_rd_client_init(); + test_lwm2m_engine_start_service(); + wait_for_service(1); + + lwm2m_get_bool_fake.custom_fake = lwm2m_get_bool_fake_true; + zassert_true(lwm2m_rd_client_start(&ctx, "Test", 0, lwm2m_event_cb, lwm2m_observe_cb) == 0, + NULL); + test_prepare_pending_message_cb(&message_reply_timeout_cb_default); + zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REG_TIMEOUT), NULL); + zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REG_TIMEOUT), NULL); + zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REG_TIMEOUT), NULL); + + zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_REG_FAILURE), + NULL); +} + +ZTEST(lwm2m_rd_client, test_no_srv_fallback_to_bootstrap) +{ + struct lwm2m_ctx ctx; + + (void)memset(&ctx, 0x0, sizeof(ctx)); + + lwm2m_rd_client_init(); + test_lwm2m_engine_start_service(); + wait_for_service(1); + + coap_header_get_code_fake.custom_fake = coap_header_get_code_fake_changed; + lwm2m_get_bool_fake.custom_fake = lwm2m_get_bool_fake_true; + stub_lwm2m_server_disable(true); + zassert_true(lwm2m_rd_client_start(&ctx, "Test", 0, lwm2m_event_cb, lwm2m_observe_cb) == 0, + NULL); + test_prepare_pending_message_cb(&message_reply_cb_default); + zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_REG_COMPLETE), + NULL); + coap_header_get_code_fake.custom_fake = coap_header_get_code_fake_created; + coap_find_options_fake.custom_fake = coap_find_options_do_registration_reply_cb_ok; + stub_lwm2m_server_disable(false); + engine_bootstrap_finish(); zassert_true(expect_lwm2m_rd_client_event(LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE), NULL); } diff --git a/tests/net/lib/lwm2m/lwm2m_rd_client/src/stubs.c b/tests/net/lib/lwm2m/lwm2m_rd_client/src/stubs.c index 3cbde9060ce..621057f8496 100644 --- a/tests/net/lib/lwm2m/lwm2m_rd_client/src/stubs.c +++ b/tests/net/lib/lwm2m/lwm2m_rd_client/src/stubs.c @@ -116,6 +116,24 @@ char *lwm2m_sprint_ip_addr_fake_default(const struct sockaddr *addr) DEFINE_FAKE_VALUE_FUNC(int, lwm2m_server_short_id_to_inst, uint16_t); DEFINE_FAKE_VALUE_FUNC(int, lwm2m_security_index_to_inst_id, int); +DEFINE_FAKE_VALUE_FUNC(int, lwm2m_security_short_id_to_inst, uint16_t); +DEFINE_FAKE_VALUE_FUNC(int, lwm2m_server_disable, uint16_t, k_timeout_t); +DEFINE_FAKE_VALUE_FUNC(uint8_t, lwm2m_server_get_prio, uint16_t); +DEFINE_FAKE_VOID_FUNC(lwm2m_server_reset_timestamps); + +static bool srv_disabled; +bool lwm2m_server_select(uint16_t *obj_inst_id) +{ + if (obj_inst_id) { + *obj_inst_id = 0; + } + return !srv_disabled; +} + +void stub_lwm2m_server_disable(bool disable) +{ + srv_disabled = disable; +} k_work_handler_t service; int64_t next; @@ -177,9 +195,13 @@ void test_lwm2m_engine_start_service(void) void test_lwm2m_engine_stop_service(void) { + struct k_work_sync sync; + pending_message_cb = NULL; + pending_message = NULL; running = false; k_work_cancel(&service_work); + k_work_flush(&service_work, &sync); } /* subsys/net/lib/lwm2m/lwm2m_message_handling.h */ diff --git a/tests/net/lib/lwm2m/lwm2m_rd_client/src/stubs.h b/tests/net/lib/lwm2m/lwm2m_rd_client/src/stubs.h index 1276dd5c109..0a3ce31d9e8 100644 --- a/tests/net/lib/lwm2m/lwm2m_rd_client/src/stubs.h +++ b/tests/net/lib/lwm2m/lwm2m_rd_client/src/stubs.h @@ -59,6 +59,8 @@ DECLARE_FAKE_VALUE_FUNC(int, lwm2m_socket_close, struct lwm2m_ctx *); DECLARE_FAKE_VALUE_FUNC(int, lwm2m_close_socket, struct lwm2m_ctx *); DECLARE_FAKE_VALUE_FUNC(int, lwm2m_socket_suspend, struct lwm2m_ctx *); DECLARE_FAKE_VALUE_FUNC(int, lwm2m_security_inst_id_to_index, uint16_t); +DECLARE_FAKE_VALUE_FUNC(int, lwm2m_security_short_id_to_inst, uint16_t); +DECLARE_FAKE_VALUE_FUNC(int, lwm2m_security_index_to_inst_id, int); DECLARE_FAKE_VALUE_FUNC(int, lwm2m_engine_connection_resume, struct lwm2m_ctx *); DECLARE_FAKE_VALUE_FUNC(int, lwm2m_push_queued_buffers, struct lwm2m_ctx *); DECLARE_FAKE_VOID_FUNC(lwm2m_engine_context_init, struct lwm2m_ctx *); @@ -68,10 +70,15 @@ DECLARE_FAKE_VOID_FUNC(lwm2m_engine_context_close, struct lwm2m_ctx *); DECLARE_FAKE_VALUE_FUNC(char *, lwm2m_sprint_ip_addr, const struct sockaddr *); char *lwm2m_sprint_ip_addr_fake_default(const struct sockaddr *addr); DECLARE_FAKE_VALUE_FUNC(int, lwm2m_server_short_id_to_inst, uint16_t); -DECLARE_FAKE_VALUE_FUNC(int, lwm2m_security_index_to_inst_id, int); +DECLARE_FAKE_VALUE_FUNC(int, lwm2m_server_disable, uint16_t, k_timeout_t); +DECLARE_FAKE_VALUE_FUNC(uint8_t, lwm2m_server_get_prio, uint16_t); +DECLARE_FAKE_VOID_FUNC(lwm2m_server_reset_timestamps); + + void wait_for_service(uint16_t cycles); void test_lwm2m_engine_start_service(void); void test_lwm2m_engine_stop_service(void); +void stub_lwm2m_server_disable(bool disable); /* subsys/net/lib/lwm2m/lwm2m_message_handling.h */ DECLARE_FAKE_VALUE_FUNC(int, lwm2m_init_message, struct lwm2m_message *);