Skip to content

Commit

Permalink
net: lwm2m: Select server candidate and allow fallback
Browse files Browse the repository at this point in the history
If server registration fails, allow fallback to secondary server,
or fallback to bootstrap.
Also allow fallback to different bootstrap server.

Changes to RD state machine:
* All retry logic should be handled in NETWORK_ERROR state.
* New state SERVER_DISABLED.
* Internally disable servers that reject registration
* Temporary disable server on network error.
* Clean up all "disable timers" on start.
* Select server first, then find security object for it.
* State functions return void, error handling is done using states.
* DISCONNECT event will only come when client is requested to stop.
* NETWORK_ERROR will stop engine. This is generic error for all kinds
  of registration or network failures.
* BOOTSTRAP_REG_FAILURE also stops engine. This is fatal, and we cannot
  recover.

Refactoring:
* Move lwm2m_server functions and macros to own header.
* Server selection logic is inside server object.
* sm_handle_timeout_state() does not require msg parameter. Unused.
* When bootstrap fail, we should NOT back off to registration.
  This is a fatal error, and it stops the engine and informs application.

Signed-off-by: Seppo Takalo <seppo.takalo@nordicsemi.no>
  • Loading branch information
SeppoTakalo committed Nov 30, 2023
1 parent 1fc9e88 commit ddcbabf
Show file tree
Hide file tree
Showing 13 changed files with 695 additions and 313 deletions.
1 change: 1 addition & 0 deletions include/zephyr/net/lwm2m.h
Expand Up @@ -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,
};

/**
Expand Down
4 changes: 4 additions & 0 deletions samples/net/lwm2m_client/src/lwm2m-client.c
Expand Up @@ -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;
Expand Down
100 changes: 0 additions & 100 deletions subsys/net/lib/lwm2m/lwm2m_engine.h
Expand Up @@ -30,34 +30,6 @@
/* length of time in milliseconds to wait for buffer allocations */
#define BUF_ALLOC_TIMEOUT K_SECONDS(1)

/* Server resource IDs */
#define SERVER_SHORT_SERVER_ID 0
#define SERVER_LIFETIME_ID 1
#define SERVER_DEFAULT_MIN_PERIOD_ID 2
#define SERVER_DEFAULT_MAX_PERIOD_ID 3
#define SERVER_DISABLE_ID 4
#define SERVER_DISABLE_TIMEOUT_ID 5
#define SERVER_STORE_NOTIFY_ID 6
#define SERVER_TRANSPORT_BINDING_ID 7
#define SERVER_REG_UPDATE_TRIGGER_ID 8
/* Server object version 1.1 resource IDs */
#define SERVER_BOOTSTRAP_UPDATE_TRIGGER_ID 9
#define SERVER_APN_LINK_ID 10
#define SERVER_TLS_DTLS_ALERT_CODE_ID 11
#define SERVER_LAST_BOOTSTRAPPED_ID 12
#define SERVER_REGISTRATION_PRIORITY_ORDER_ID 13
#define SERVER_INITIAL_REGISTRATION_DELAY_TIMER_ID 14
#define SERVER_REGISTRATION_FAILURE_BLOCK_ID 15
#define SERVER_BOOTSTRAP_ON_REGISTRATION_FAILURE_ID 16
#define SERVER_COMMUNICATION_RETRY_COUNT_ID 17
#define SERVER_COMMUNICATION_RETRY_TIMER_ID 18
#define SERVER_COMMUNICATION_SEQUENCE_DELAY_TIMER_ID 19
#define SERVER_COMMUNICATION_SEQUENCE_RETRY_TIMER_ID 20
#define SERVER_SMS_TRIGGER_ID 21
#define SERVER_PREFERRED_TRANSPORT_ID 22
#define SERVER_MUTE_SEND_ID 23



/**
* @brief Validates that writing is a legal operation on the field given by the object in
Expand Down Expand Up @@ -179,78 +151,6 @@ int lwm2m_security_short_id_to_inst(uint16_t short_id);
*/
int lwm2m_security_index_to_inst_id(int index);

/**
* @brief Returns the default minimum period for an observation set for the server
* with object instance id given by @p obj_inst_id.
*
* @param[in] obj_inst_id Object instance id of the server object instance
* @return int32_t pmin value
*/
int32_t lwm2m_server_get_pmin(uint16_t obj_inst_id);

/**
* @brief Returns the default maximum period for an observation set for the server
* with object instance id given by @p obj_inst_id.
*
* @param[in] obj_inst_id Object instance id of the server object instance
* @return int32_t pmax value
*/
int32_t lwm2m_server_get_pmax(uint16_t obj_inst_id);

/**
* @brief Returns the Short Server ID of the server object instance with
* object instance id given by @p obj_inst_id.
*
* @param[in] obj_inst_id Object instance id of the server object
* @return SSID or negative in case not found
*/
int lwm2m_server_get_ssid(uint16_t obj_inst_id);

/**
* @brief Returns the object instance id of the server having ssid given by @p short_id.
*
* @param[in] short_id ssid of the server object
* @return Object instance id or negative in case not found
*/
int lwm2m_server_short_id_to_inst(uint16_t short_id);

/**
* @brief Check if given server instance is not disabled
*
* @param[in] obj_inst_id server instance
* @return true if not disabled, false otherwise.
*/
bool lwm2m_server_is_enabled(uint16_t obj_inst_id);

/**
* @brief Select server instance.
*
* Find possible server instance considering values on server data.
* Server candidates cannot be in disabled state and if priority values are set,
* those are compared and lowest values are considered first.
*
* If @ref obj_inst_id is NULL, this can be used to check if there are any server available.
*
* @param[out] obj_inst_id where selected server instance ID is written. Can be NULL.
* @return true if server instance was found, false otherwise.
*/
bool lwm2m_server_select(uint16_t *obj_inst_id);

/**
* @brief Disable server instance for a period of time.
*
* Timeout values can be calculated using kernel macros like K_SECONDS().
* Values like K_FOREVER or K_NO_WAIT are also accepted.
*
* @param timeout Timeout value.
* @return zero on success, negative error code otherwise.
*/
int lwm2m_server_disable(uint16_t obj_inst_id, k_timeout_t timeout);

#if defined(CONFIG_LWM2M_SERVER_OBJECT_VERSION_1_1)
bool lwm2m_server_get_mute_send(uint16_t obj_inst_id);
#endif

#if defined(CONFIG_LWM2M_FIRMWARE_UPDATE_OBJ_SUPPORT)
/**
* @brief Sets the update state (as specified in LWM2M SPEC E.6 regarding the firmware update)
Expand Down
1 change: 1 addition & 0 deletions subsys/net/lib/lwm2m/lwm2m_message_handling.c
Expand Up @@ -45,6 +45,7 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME);
#include "lwm2m_engine.h"
#include "lwm2m_object.h"
#include "lwm2m_obj_access_control.h"
#include "lwm2m_obj_server.h"
#include "lwm2m_rw_link_format.h"
#include "lwm2m_rw_oma_tlv.h"
#include "lwm2m_rw_plain_text.h"
Expand Down
115 changes: 77 additions & 38 deletions subsys/net/lib/lwm2m/lwm2m_obj_server.c
Expand Up @@ -15,8 +15,9 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME);
#include <zephyr/init.h>

#include "lwm2m_object.h"
#include "lwm2m_engine.h"
#include "lwm2m_obj_server.h"
#include "lwm2m_rd_client.h"
#include "lwm2m_registry.h"

#define SERVER_VERSION_MAJOR 1
#if defined(CONFIG_LWM2M_SERVER_OBJECT_VERSION_1_1)
Expand Down Expand Up @@ -50,11 +51,10 @@ static k_timepoint_t disabled_until[MAX_INSTANCE_COUNT];
static uint32_t disabled_timeout[MAX_INSTANCE_COUNT];
static uint8_t server_flag_store_notify[MAX_INSTANCE_COUNT];
static char transport_binding[MAX_INSTANCE_COUNT][TRANSPORT_BINDING_LEN];
#if defined(CONFIG_LWM2M_SERVER_OBJECT_VERSION_1_1)
/* Server object version 1.1 */
static uint8_t priority[MAX_INSTANCE_COUNT];
static bool mute_send[MAX_INSTANCE_COUNT];
static bool boostrap_on_fail[MAX_INSTANCE_COUNT];
#endif

static struct lwm2m_engine_obj server;
static struct lwm2m_engine_obj_field fields[] = {
Expand Down Expand Up @@ -98,11 +98,18 @@ static int disable_cb(uint16_t obj_inst_id, uint8_t *args, uint16_t args_len)
ARG_UNUSED(args);
ARG_UNUSED(args_len);

int ret;

for (int i = 0; i < MAX_INSTANCE_COUNT; i++) {
if (inst[i].obj && inst[i].obj_inst_id == obj_inst_id) {
LOG_DBG("DISABLE %d", obj_inst_id);
disabled_until[i] = sys_timepoint_calc(K_SECONDS(disabled_timeout[i]));
return 0;
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;
}
}

Expand All @@ -116,7 +123,6 @@ static int update_trigger_cb(uint16_t obj_inst_id,
return 0;
}

#if defined(CONFIG_LWM2M_SERVER_OBJECT_VERSION_1_1)
static int bootstrap_trigger_cb(uint16_t obj_inst_id,
uint8_t *args, uint16_t args_len)
{
Expand All @@ -134,7 +140,6 @@ bool lwm2m_server_get_mute_send(uint16_t obj_inst_id)
}
return false;
}
#endif /* defined(CONFIG_LWM2M_SERVER_OBJECT_VERSION_1_1) */


static int lifetime_write_cb(uint16_t obj_inst_id, uint16_t res_id,
Expand Down Expand Up @@ -237,6 +242,23 @@ int lwm2m_server_disable(uint16_t obj_inst_id, k_timeout_t timeout)
return 0;
}

k_timepoint_t lwm2m_server_get_disabled_time(uint16_t obj_inst_id)
{
int idx = lwm2m_server_inst_id_to_index(obj_inst_id);

if (idx < 0) {
return sys_timepoint_calc(K_FOREVER);
}
return disabled_until[idx];
}

void lwm2m_server_reset_timestamps(void)
{
for (int i = 0; i < ARRAY_SIZE(inst); i++) {
disabled_until[i] = sys_timepoint_calc(K_NO_WAIT);
}
}

bool lwm2m_server_select(uint16_t *obj_inst_id)
{
uint8_t min = UINT8_MAX;
Expand Down Expand Up @@ -285,6 +307,20 @@ bool lwm2m_server_select(uint16_t *obj_inst_id)
return false;
}

uint8_t lwm2m_server_get_prio(uint16_t obj_inst_id)
{
if (IS_ENABLED(CONFIG_LWM2M_SERVER_OBJECT_VERSION_1_1)) {
int idx = lwm2m_server_inst_id_to_index(obj_inst_id);

if (idx < 0) {
return UINT8_MAX;
}
return priority[idx];
}

return (uint8_t)obj_inst_id % UINT8_MAX;
}

static struct lwm2m_engine_obj_inst *server_create(uint16_t obj_inst_id)
{
int index, i = 0, j = 0;
Expand Down Expand Up @@ -318,10 +354,7 @@ static struct lwm2m_engine_obj_inst *server_create(uint16_t obj_inst_id)
default_min_period[index] = CONFIG_LWM2M_SERVER_DEFAULT_PMIN;
default_max_period[index] = CONFIG_LWM2M_SERVER_DEFAULT_PMAX;
disabled_timeout[index] = 86400U;
#if defined(CONFIG_LWM2M_SERVER_OBJECT_VERSION_1_1)
mute_send[index] = false;
priority[index] = 0;
#endif
boostrap_on_fail[index] = true;

lwm2m_engine_get_binding(transport_binding[index]);

Expand Down Expand Up @@ -358,33 +391,39 @@ static struct lwm2m_engine_obj_inst *server_create(uint16_t obj_inst_id)
transport_binding[index], TRANSPORT_BINDING_LEN,
strlen(transport_binding[index]) + 1);
INIT_OBJ_RES_EXECUTE(SERVER_REG_UPDATE_TRIGGER_ID, res[index], i, update_trigger_cb);
#if defined(CONFIG_LWM2M_SERVER_OBJECT_VERSION_1_1)
INIT_OBJ_RES_EXECUTE(SERVER_BOOTSTRAP_UPDATE_TRIGGER_ID, res[index], i,
bootstrap_trigger_cb);
INIT_OBJ_RES_OPTDATA(SERVER_APN_LINK_ID, res[index], i, res_inst[index], j);
INIT_OBJ_RES_OPTDATA(SERVER_TLS_DTLS_ALERT_CODE_ID, res[index], i, res_inst[index], j);
INIT_OBJ_RES_OPTDATA(SERVER_LAST_BOOTSTRAPPED_ID, res[index], i, res_inst[index], j);
INIT_OBJ_RES_DATA(SERVER_REGISTRATION_PRIORITY_ORDER_ID, res[index], i, res_inst[index],
j, &priority[index], sizeof(uint8_t));
INIT_OBJ_RES_OPTDATA(SERVER_INITIAL_REGISTRATION_DELAY_TIMER_ID, res[index], i,
res_inst[index], j);
INIT_OBJ_RES_OPTDATA(SERVER_REGISTRATION_FAILURE_BLOCK_ID, res[index], i, res_inst[index],
j);
INIT_OBJ_RES_DATA(SERVER_BOOTSTRAP_ON_REGISTRATION_FAILURE_ID, res[index], i,
res_inst[index], j, &boostrap_on_fail[index], sizeof(bool));
INIT_OBJ_RES_OPTDATA(SERVER_COMMUNICATION_RETRY_COUNT_ID, res[index], i, res_inst[index],
j);
INIT_OBJ_RES_OPTDATA(SERVER_COMMUNICATION_RETRY_TIMER_ID, res[index], i, res_inst[index],
j);
INIT_OBJ_RES_OPTDATA(SERVER_COMMUNICATION_SEQUENCE_DELAY_TIMER_ID, res[index], i,
res_inst[index], j);
INIT_OBJ_RES_OPTDATA(SERVER_COMMUNICATION_SEQUENCE_RETRY_TIMER_ID, res[index], i,
res_inst[index], j);
INIT_OBJ_RES_OPTDATA(SERVER_SMS_TRIGGER_ID, res[index], i, res_inst[index], j);
INIT_OBJ_RES_OPTDATA(SERVER_PREFERRED_TRANSPORT_ID, res[index], i, res_inst[index], j);
INIT_OBJ_RES_DATA(SERVER_MUTE_SEND_ID, res[index], i, res_inst[index], j, &mute_send[index],
sizeof(bool));
#endif /* defined(CONFIG_LWM2M_SERVER_OBJECT_VERSION_1_1) */

if (IS_ENABLED(CONFIG_LWM2M_SERVER_OBJECT_VERSION_1_1)) {
mute_send[index] = false;
priority[index] = 0;
INIT_OBJ_RES_EXECUTE(SERVER_BOOTSTRAP_UPDATE_TRIGGER_ID, res[index], i,
bootstrap_trigger_cb);
INIT_OBJ_RES_OPTDATA(SERVER_APN_LINK_ID, res[index], i, res_inst[index], j);
INIT_OBJ_RES_OPTDATA(SERVER_TLS_DTLS_ALERT_CODE_ID, res[index], i, res_inst[index],
j);
INIT_OBJ_RES_OPTDATA(SERVER_LAST_BOOTSTRAPPED_ID, res[index], i, res_inst[index],
j);
INIT_OBJ_RES_DATA(SERVER_REGISTRATION_PRIORITY_ORDER_ID, res[index], i,
res_inst[index], j, &priority[index], sizeof(uint8_t));
INIT_OBJ_RES_OPTDATA(SERVER_INITIAL_REGISTRATION_DELAY_TIMER_ID, res[index], i,
res_inst[index], j);
INIT_OBJ_RES_OPTDATA(SERVER_REGISTRATION_FAILURE_BLOCK_ID, res[index], i,
res_inst[index], j);
INIT_OBJ_RES_DATA(SERVER_BOOTSTRAP_ON_REGISTRATION_FAILURE_ID, res[index], i,
res_inst[index], j, &boostrap_on_fail[index], sizeof(bool));
INIT_OBJ_RES_OPTDATA(SERVER_COMMUNICATION_RETRY_COUNT_ID, res[index], i,
res_inst[index], j);
INIT_OBJ_RES_OPTDATA(SERVER_COMMUNICATION_RETRY_TIMER_ID, res[index], i,
res_inst[index], j);
INIT_OBJ_RES_OPTDATA(SERVER_COMMUNICATION_SEQUENCE_DELAY_TIMER_ID, res[index], i,
res_inst[index], j);
INIT_OBJ_RES_OPTDATA(SERVER_COMMUNICATION_SEQUENCE_RETRY_TIMER_ID, res[index], i,
res_inst[index], j);
INIT_OBJ_RES_OPTDATA(SERVER_SMS_TRIGGER_ID, res[index], i, res_inst[index], j);
INIT_OBJ_RES_OPTDATA(SERVER_PREFERRED_TRANSPORT_ID, res[index], i, res_inst[index],
j);
INIT_OBJ_RES_DATA(SERVER_MUTE_SEND_ID, res[index], i, res_inst[index], j,
&mute_send[index], sizeof(bool));
}

inst[index].resources = res[index];
inst[index].resource_count = i;
Expand Down

0 comments on commit ddcbabf

Please sign in to comment.