From 7875939995aa67710e3529016a482e52776cd7ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Ku=C4=87ma?= Date: Tue, 28 May 2024 11:43:26 +0200 Subject: [PATCH] Anjay-zephyr 3.8.0 Bugfixes: - On nRF91 platforms, initialization of Anjay is now delayed until the Connectivity Monitoring object can be properly populated --- CHANGELOG.md | 5 ++ Kconfig.anjay | 14 ++++++ Kconfig.anjay_zephyr | 8 ++-- config/avsystem/coap/avs_coap_config.h | 14 +++++- deps/anjay | 2 +- src/lwm2m.c | 15 ++++-- src/network/network_nrf91.c | 66 +++++++++++++++++++++----- src/nrf_lc_info.c | 10 +++- src/nrf_lc_info.h | 1 + src/persistence.c | 6 ++- src/utils.c | 9 ++++ src/utils.h | 4 +- 12 files changed, 130 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d9563c..d64dd98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 3.8.0 (May 28th, 2024) + +### Bugfixes +- On nRF91 platforms, initialization of Anjay is now delayed until the Connectivity Monitoring object can be properly populated + ## 3.7.0 (February 16th, 2024) ### Improvements diff --git a/Kconfig.anjay b/Kconfig.anjay index 6c8dda2..78daf40 100644 --- a/Kconfig.anjay +++ b/Kconfig.anjay @@ -43,6 +43,20 @@ choice ANJAY_COMPAT_SECURITY default y depends on ANJAY_COMPAT_ZEPHYR_TLS + config ANJAY_COMPAT_ZEPHYR_TLS_SESSION_CACHE_PURGE_ON_START + bool "Purge modem when Anjay security config is not restored from persistence." + default y + depends on ANJAY_COMPAT_ZEPHYR_TLS + depends on ANJAY_COMPAT_ZEPHYR_TLS_SESSION_CACHE + depends on NRF_MODEM_LIB + depends on MODEM_KEY_MGMT + help + When reconfiguring Anjay Client, modem can store in cache DTLS Session ID + valid for a given LwM2M Server, but not for a specific Endpoint, which + results in hard to debug successful Handshake and rejected registration + attempts. For production configuration it is advised to disable it for + data transmitted optimization. + config ANJAY_COMPAT_ZEPHYR_TLS_EPHEMERAL_SEC_TAG_BASE int "Lowest security tag number to use for Anjay's ephemeral credentials" default 2652900 diff --git a/Kconfig.anjay_zephyr b/Kconfig.anjay_zephyr index 7914b1c..8c7826d 100644 --- a/Kconfig.anjay_zephyr +++ b/Kconfig.anjay_zephyr @@ -24,7 +24,7 @@ config ANJAY_ZEPHYR_MODEL_NUMBER config ANJAY_ZEPHYR_VERSION string "Client Version" - default "3.7.0" + default "3.8.0" config ANJAY_ZEPHYR_AUTOGENERATE_ENDPOINT_NAME bool "Autogenerate endpoint name" @@ -237,7 +237,7 @@ menu "GPS on nRF9160-based devices" config ANJAY_ZEPHYR_GPS_NRF_A_GPS bool "Enable A-GPS using Nordic Location Services over LwM2M" - depends on ANJAY_ZEPHYR_GPS_NRF + depends on ANJAY_ZEPHYR_GPS_NRF && ANJAY_ZEPHYR_NRF_LC_INFO select NRF_CLOUD_AGNSS select NRF_CLOUD_AGPS help @@ -339,8 +339,6 @@ config ANJAY_ZEPHYR_OTA_MCUBOOT config ANJAY_ZEPHYR_PERSISTENCE bool "Enable persistence" - select ANJAY_WITH_ACCESS_CONTROL - select ANJAY_WITH_MODULE_ACCESS_CONTROL help Enables persistence of Access Control Object, Security Object and Server Object. @@ -368,6 +366,8 @@ config ANJAY_ZEPHYR_FACTORY_PROVISIONING_INITIAL_FLASH depends on ANJAY_ZEPHYR_FACTORY_PROVISIONING select ANJAY_WITH_CBOR select ANJAY_WITH_MODULE_FACTORY_PROVISIONING + select ANJAY_WITH_ACCESS_CONTROL + select ANJAY_WITH_MODULE_ACCESS_CONTROL select FILE_SYSTEM select MCUMGR select MCUMGR_CMD_FS_MGMT diff --git a/config/avsystem/coap/avs_coap_config.h b/config/avsystem/coap/avs_coap_config.h index d959ff5..a5e5ace 100644 --- a/config/avsystem/coap/avs_coap_config.h +++ b/config/avsystem/coap/avs_coap_config.h @@ -77,11 +77,23 @@ # define WITH_AVS_COAP_OBSERVE #endif // CONFIG_ANJAY_WITH_OBSERVE +/** + * Turn on cancelling observation on a timeout. + * + * Only meaningful if WITH_AVS_COAP_OBSERVE is enabled. + * + * NOTE: LwM2M specification requires LwM2M server to send Cancel Observation + * request. Meanwhile CoAP RFC 7641 states that timeout on notification should + * cancel it. This setting is to enable both of these behaviors with default + * focused on keeping observations in case of bad connectivity. + */ +/* #undef WITH_AVS_COAP_OBSERVE_CANCEL_ON_TIMEOUT */ + /** * Enable support for observation persistence (avs_coap_observe_persist() * and avs_coap_observe_restore() calls). * - * Only meaningful WITH_AVS_COAP_OBSERVE is enabled. + * Only meaningful if WITH_AVS_COAP_OBSERVE is enabled. */ #ifdef CONFIG_ANJAY_WITH_OBSERVE_PERSISTENCE # define WITH_AVS_COAP_OBSERVE_PERSISTENCE diff --git a/deps/anjay b/deps/anjay index fc26744..3ba32ac 160000 --- a/deps/anjay +++ b/deps/anjay @@ -1 +1 @@ -Subproject commit fc267441825e37233d62342ef715257ef674746c +Subproject commit 3ba32ace53bd7b16e34d8d9cecae4912eb29e36b diff --git a/src/lwm2m.c b/src/lwm2m.c index bf8e094..fc87a84 100644 --- a/src/lwm2m.c +++ b/src/lwm2m.c @@ -27,7 +27,9 @@ #include #include -#include +#ifdef CONFIG_ANJAY_WITH_MODULE_ACCESS_CONTROL +# include +#endif // CONFIG_ANJAY_WITH_MODULE_ACCESS_CONTROL #include #include #include @@ -478,11 +480,11 @@ static anjay_t *initialize_anjay(void) { if (anjay_security_object_install(anjay) || anjay_server_object_install(anjay) -#ifdef CONFIG_ANJAY_ZEPHYR_PERSISTENCE +#ifdef CONFIG_ANJAY_WITH_MODULE_ACCESS_CONTROL // Access Control object is necessary if Server Object with many // servers is loaded || anjay_access_control_install(anjay) -#endif // CONFIG_ANJAY_ZEPHYR_PERSISTENCE +#endif // CONFIG_ANJAY_WITH_MODULE_ACCESS_CONTROL ) { LOG_ERR("Failed to install necessary modules"); goto error; @@ -555,6 +557,13 @@ static anjay_t *initialize_anjay(void) { return anjay; } #endif // CONFIG_ANJAY_ZEPHYR_PERSISTENCE + +#if defined(CONFIG_ANJAY_COMPAT_ZEPHYR_TLS_SESSION_CACHE_PURGE_ON_START) + // Modem caching might be pretty aggresive. We clear it on startup unless + // Anjay security config is restored from presistence + (void) _anjay_zephyr_tls_session_cache_purge(); +#endif // defined(CONFIG_ANJAY_COMPAT_ZEPHYR_TLS_SESSION_CACHE_PURGE_ON_START) + #ifdef CONFIG_ANJAY_ZEPHYR_FACTORY_PROVISIONING if (!_anjay_zephyr_restore_anjay_from_factory_provisioning(anjay)) { return anjay; diff --git a/src/network/network_nrf91.c b/src/network/network_nrf91.c index d5d8a85..e0525ba 100644 --- a/src/network/network_nrf91.c +++ b/src/network/network_nrf91.c @@ -29,6 +29,10 @@ #include "../gps.h" #include "../utils.h" +#ifdef CONFIG_ANJAY_ZEPHYR_NRF_LC_INFO +# include "../nrf_lc_info.h" +#endif // CONFIG_ANJAY_ZEPHYR_NRF_LC_INFO + #if __has_include("ncs_version.h") # include "ncs_version.h" #endif // __has_include("ncs_version.h") @@ -37,14 +41,46 @@ LOG_MODULE_REGISTER(anjay_zephyr_network_nrf91); static volatile atomic_int lte_nw_reg_status; // enum lte_lc_nw_reg_status static volatile atomic_int lte_mode; // enum lte_lc_lte_mode +#ifdef CONFIG_ANJAY_ZEPHYR_NRF_LC_INFO +static volatile atomic_uint_least32_t lte_cell_id; +#endif // CONFIG_ANJAY_ZEPHYR_NRF_LC_INFO + +static bool is_network_connected(void) { + if (atomic_load(<e_mode) == LTE_LC_LTE_MODE_NONE) { + return false; + } + + int nw_reg_status = atomic_load(<e_nw_reg_status); + return nw_reg_status == LTE_LC_NW_REG_REGISTERED_HOME + || nw_reg_status == LTE_LC_NW_REG_REGISTERED_ROAMING; +} static void lte_evt_handler(const struct lte_lc_evt *const evt) { if (evt) { - if (evt->type == LTE_LC_EVT_NW_REG_STATUS) { - atomic_store(<e_nw_reg_status, (int) evt->nw_reg_status); - } else if (evt->type == LTE_LC_EVT_LTE_MODE_UPDATE) { - atomic_store(<e_mode, (int) evt->lte_mode); +#ifdef CONFIG_ANJAY_ZEPHYR_NRF_LC_INFO + if (evt->type == LTE_LC_EVT_NEIGHBOR_CELL_MEAS) { + atomic_store(<e_cell_id, evt->cells_info.current_cell.id); + } else { + // NOTE: This may look suspicious, as we are carelessly reading + // atomic variables without even trying to consider that they might + // change in between calls. However, please note that + // lte_nw_reg_status and lte_mode are only ever modified from this + // function, so this is actually safe. Those variables are atomic + // to allow safely reading them from other threads, but this code + // does not cause a race condition. + bool was_connected = is_network_connected(); +#endif // CONFIG_ANJAY_ZEPHYR_NRF_LC_INFO + if (evt->type == LTE_LC_EVT_NW_REG_STATUS) { + atomic_store(<e_nw_reg_status, (int) evt->nw_reg_status); + } else if (evt->type == LTE_LC_EVT_LTE_MODE_UPDATE) { + atomic_store(<e_mode, (int) evt->lte_mode); + } +#ifdef CONFIG_ANJAY_ZEPHYR_NRF_LC_INFO + if (!was_connected && is_network_connected()) { + _anjay_zephyr_nrf_lc_info_schedule_refresh_now(); + } } +#endif // CONFIG_ANJAY_ZEPHYR_NRF_LC_INFO } _anjay_zephyr_network_internal_connection_state_changed(); } @@ -81,6 +117,10 @@ int _anjay_zephyr_network_internal_platform_initialize(void) { lte_lc_register_handler(lte_evt_handler); +#ifdef CONFIG_ANJAY_ZEPHYR_NRF_LC_INFO + atomic_store(<e_cell_id, LTE_LC_CELL_EUTRAN_ID_INVALID); +#endif // CONFIG_ANJAY_ZEPHYR_NRF_LC_INFO + return 0; } @@ -109,18 +149,22 @@ enum anjay_zephyr_network_bearer_t _anjay_zephyr_network_current_bearer(void) { } #endif // CONFIG_ANJAY_ZEPHYR_GPS_NRF - if (atomic_load(<e_mode) == LTE_LC_LTE_MODE_NONE) { + if (!is_network_connected()) { return ANJAY_ZEPHYR_NETWORK_BEARER_LIMIT; } - int status = atomic_load(<e_nw_reg_status); - - if (status == LTE_LC_NW_REG_REGISTERED_HOME - || status == LTE_LC_NW_REG_REGISTERED_ROAMING) { - return ANJAY_ZEPHYR_NETWORK_BEARER_CELLULAR; - } else { +#ifdef CONFIG_ANJAY_ZEPHYR_NRF_LC_INFO + // When CONFIG_ANJAY_ZEPHYR_NRF_LC_INFO is enabled, the Connectivity + // Monitoring object is present in the data model. That object is only + // filled with data if the cell ID is available. For this reason, we only + // treat the connection as valid and connected if the cell ID is available, + // to avoid populating the object with null data. + if (atomic_load(<e_cell_id) == LTE_LC_CELL_EUTRAN_ID_INVALID) { return ANJAY_ZEPHYR_NETWORK_BEARER_LIMIT; } +#endif // CONFIG_ANJAY_ZEPHYR_NRF_LC_INFO + + return ANJAY_ZEPHYR_NETWORK_BEARER_CELLULAR; } void _anjay_zephyr_network_disconnect(void) { diff --git a/src/nrf_lc_info.c b/src/nrf_lc_info.c index 46bc885..a0e5230 100644 --- a/src/nrf_lc_info.c +++ b/src/nrf_lc_info.c @@ -29,7 +29,10 @@ LOG_MODULE_REGISTER(anjay_zephyr_nrf_lc_info); -static struct k_work_delayable periodic_search_dwork; +static void periodic_search_work_handler(struct k_work *work); + +static K_WORK_DELAYABLE_DEFINE(periodic_search_dwork, + periodic_search_work_handler); static struct anjay_zephyr_nrf_lc_info nrf_lc_info = { .cells = { .current_cell.id = LTE_LC_CELL_EUTRAN_ID_INVALID, @@ -134,7 +137,6 @@ int _anjay_zephyr_initialize_nrf_lc_info_listener(void) { } lte_lc_register_handler(lte_lc_evt_handler); - k_work_init_delayable(&periodic_search_dwork, periodic_search_work_handler); res = _anjay_zephyr_k_work_schedule(&periodic_search_dwork, K_NO_WAIT); if (res < 0) { @@ -166,3 +168,7 @@ void _anjay_zephyr_nrf_lc_info_get(struct anjay_zephyr_nrf_lc_info *out) { out->cells.neighbor_cells = out->storage.neighbor_cells; } } + +int _anjay_zephyr_nrf_lc_info_schedule_refresh_now(void) { + return _anjay_zephyr_k_work_reschedule(&periodic_search_dwork, K_NO_WAIT); +} diff --git a/src/nrf_lc_info.h b/src/nrf_lc_info.h index c61f5b3..87a8828 100644 --- a/src/nrf_lc_info.h +++ b/src/nrf_lc_info.h @@ -40,3 +40,4 @@ int _anjay_zephyr_initialize_nrf_lc_info_listener(void); void _anjay_zephyr_nrf_lc_info_get(struct anjay_zephyr_nrf_lc_info *out); bool _anjay_zephyr_nrf_lc_info_get_if_changed( struct anjay_zephyr_nrf_lc_info *out); +int _anjay_zephyr_nrf_lc_info_schedule_refresh_now(void); diff --git a/src/persistence.c b/src/persistence.c index 3ecaa00..c54a223 100644 --- a/src/persistence.c +++ b/src/persistence.c @@ -18,7 +18,9 @@ #include #include -#include +#ifdef CONFIG_ANJAY_WITH_MODULE_ACCESS_CONTROL +# include +#endif // CONFIG_ANJAY_WITH_MODULE_ACCESS_CONTROL #ifdef CONFIG_ANJAY_ZEPHYR_PERSISTENCE_ATTR_STORAGE # include #endif // CONFIG_ANJAY_ZEPHYR_PERSISTENCE_ATTR_STORAGE @@ -71,7 +73,9 @@ static bool previous_attempt_failed; static const struct persistence_target targets[] = { DECL_TARGET(security_object), DECL_TARGET(server_object), +#ifdef CONFIG_ANJAY_WITH_MODULE_ACCESS_CONTROL DECL_TARGET(access_control), +#endif // CONFIG_ANJAY_WITH_MODULE_ACCESS_CONTROL #ifdef CONFIG_ANJAY_ZEPHYR_PERSISTENCE_ATTR_STORAGE DECL_TARGET(attr_storage), #endif // CONFIG_ANJAY_ZEPHYR_PERSISTENCE_ATTR_STORAGE diff --git a/src/utils.c b/src/utils.c index 2cab090..99b1b43 100644 --- a/src/utils.c +++ b/src/utils.c @@ -240,3 +240,12 @@ int _anjay_zephyr_k_work_schedule(struct k_work_delayable *dwork, return k_work_schedule(dwork, delay); #endif // CONFIG_ANJAY_ZEPHYR_WORKQUEUE_ENABLE } + +int _anjay_zephyr_k_work_reschedule(struct k_work_delayable *dwork, + k_timeout_t delay) { +#ifdef CONFIG_ANJAY_ZEPHYR_WORKQUEUE_ENABLE + return k_work_reschedule_for_queue(&anjay_zephyr_workqueue, dwork, delay); +#else + return k_work_reschedule(dwork, delay); +#endif // CONFIG_ANJAY_ZEPHYR_WORKQUEUE_ENABLE +} diff --git a/src/utils.h b/src/utils.h index f551f07..598cd5b 100644 --- a/src/utils.h +++ b/src/utils.h @@ -55,10 +55,12 @@ void _anjay_zephyr_init_workqueue(void); /** * These functions should be used in place of basic Zephyr functions - * k_work_submit and k_work_schedule. + * k_work_submit, k_work_schedule and k_work_reschedule. * Their purpose is to avoid using the system workqueue in order to prevent * blocking other works from different modules. */ int _anjay_zephyr_k_work_submit(struct k_work *work); int _anjay_zephyr_k_work_schedule(struct k_work_delayable *dwork, k_timeout_t delay); +int _anjay_zephyr_k_work_reschedule(struct k_work_delayable *dwork, + k_timeout_t delay);