diff --git a/examples/gnrc_lorawan/Makefile b/examples/gnrc_lorawan/Makefile index b3ea8d2fab..fd41712596 100644 --- a/examples/gnrc_lorawan/Makefile +++ b/examples/gnrc_lorawan/Makefile @@ -14,6 +14,8 @@ USEMODULE += auto_init_gnrc_netif # Add support for GNRC LoRaWAN USEMODULE += gnrc_lorawan +# Add support for GNRC LoRaWAN 1.1 +# USEMODULE += gnrc_lorawan_1_1 # Use GNRC pktdump to print downlink messages USEMODULE += gnrc_pktdump @@ -55,7 +57,9 @@ ifndef CONFIG_KCONFIG_USEMODULE_LORAWAN CFLAGS += -DCONFIG_GNRC_NETIF_LORAWAN_NETIF_HDR CFLAGS += -DCONFIG_LORAMAC_APP_KEY_DEFAULT=\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\" + CFLAGS += -DCONFIG_LORAMAC_NWK_KEY_DEFAULT=\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\" CFLAGS += -DCONFIG_LORAMAC_APP_EUI_DEFAULT=\"BBBBBBBBBBBBBBBB\" + CFLAGS += -DCONFIG_LORAMAC_JOIN_EUI_DEFAULT=\"BBBBBBBBBBBBBBBB\" CFLAGS += -DCONFIG_LORAMAC_DEV_EUI_DEFAULT=\"CCCCCCCCCCCCCCCC\" # For TTN, It's necessary to set the RX2 DR to 3 in EU_868 region diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index aefc778d83..1b43208dff 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -89,6 +89,7 @@ PSEUDOMODULES += gnrc_ipv6_nib_dns PSEUDOMODULES += gnrc_ipv6_nib_rio PSEUDOMODULES += gnrc_ipv6_nib_router PSEUDOMODULES += gnrc_ipv6_nib_rtr_adv_pio_cb +PSEUDOMODULES += gnrc_lorawan_1_1 PSEUDOMODULES += gnrc_netdev_default PSEUDOMODULES += gnrc_neterr PSEUDOMODULES += gnrc_netapi_callbacks diff --git a/sys/include/net/gnrc/lorawan.h b/sys/include/net/gnrc/lorawan.h index a584a46214..acb2118f6b 100644 --- a/sys/include/net/gnrc/lorawan.h +++ b/sys/include/net/gnrc/lorawan.h @@ -199,10 +199,10 @@ void gnrc_lorawan_timeout_cb(gnrc_lorawan_t *mac); * @brief Init GNRC LoRaWAN * * @param[in] mac pointer to the MAC descriptor - * @param[in] nwkskey buffer to store the NwkSKey. Should be at least 16 bytes long - * @param[in] appskey buffer to store the AppsKey. Should be at least 16 bytes long + * @param[in] joineui pointer to Join EUI + * @param[in] ctx pointer to LoRaWAN context */ -void gnrc_lorawan_init(gnrc_lorawan_t *mac, uint8_t *nwkskey, uint8_t *appskey); +void gnrc_lorawan_init(gnrc_lorawan_t *mac, uint8_t *joineui, const gnrc_lorawan_key_ctx_t *ctx); /** * @brief Perform a MLME request diff --git a/sys/include/net/gnrc/netif/lorawan.h b/sys/include/net/gnrc/netif/lorawan.h index d544ed1da7..78d0760672 100644 --- a/sys/include/net/gnrc/netif/lorawan.h +++ b/sys/include/net/gnrc/netif/lorawan.h @@ -50,23 +50,133 @@ extern "C" { * @brief GNRC LoRaWAN interface descriptor */ typedef struct { - uint8_t nwkskey[LORAMAC_NWKSKEY_LEN]; /**< network SKey buffer */ - uint8_t appskey[LORAMAC_APPSKEY_LEN]; /**< App SKey buffer */ - uint8_t appkey[LORAMAC_APPKEY_LEN]; /**< App Key buffer */ - uint8_t deveui[LORAMAC_DEVEUI_LEN]; /**< Device EUI buffer */ - uint8_t appeui[LORAMAC_APPEUI_LEN]; /**< App EUI buffer */ - gnrc_lorawan_t mac; /**< gnrc lorawan mac descriptor */ - ztimer_t timer; /**< General purpose timer */ - ztimer_t backoff_timer; /**< Backoff timer */ - uint8_t flags; /**< flags for the LoRaWAN interface */ - uint8_t demod_margin; /**< value of last demodulation margin */ - uint8_t num_gateways; /**< number of gateways of last link check */ - uint8_t datarate; /**< LoRaWAN datarate for the next transmission */ - uint8_t port; /**< LoRaWAN port for the next transmission */ - uint8_t ack_req; /**< Request ACK in the next transmission */ - uint8_t otaa; /**< whether the next transmission is OTAA or not */ + uint8_t appskey[LORAMAC_APPSKEY_LEN]; /**< App SKey buffer */ + uint8_t fnwksintkey[LORAMAC_FNWKSINTKEY_LEN]; /**< Forwarding Network session integrity key buffer */ + uint8_t nwkkey[LORAMAC_NWKKEY_LEN]; /**< Network key buffer. Mapped to AppKey if LoRaWAN 1.0x */ +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) + uint8_t appkey[LORAMAC_APPKEY_LEN]; /**< App Key buffer. The AppKey used in LoRaWAN 1.0x has been mapped to the nwkkey in LoRaWAN 1.1x */ + uint8_t snwksintkey[LORAMAC_SNWKSINTKEY_LEN]; /**< Serving Network session integrity key buffer */ + uint8_t nwksenckey[LORAMAC_NWKSENCKEY_LEN]; /**< Network session encryption key buffer */ + uint8_t jsintkey[LORAMAC_JSINTKEY_LEN]; /**< Join session integrity key buffer */ + uint8_t jsenckey[LORAMAC_JSENCKEY_LEN]; /**< Join session encryption key buffer */ +#endif + uint8_t deveui[LORAMAC_DEVEUI_LEN]; /**< Device EUI buffer */ + uint8_t joineui[LORAMAC_JOINEUI_LEN]; /**< Join EUI buffer */ + gnrc_lorawan_t mac; /**< gnrc lorawan mac descriptor */ + ztimer_t timer; /**< General purpose timer */ + ztimer_t backoff_timer; /**< Backoff timer */ + uint8_t flags; /**< flags for the LoRaWAN interface */ + uint8_t demod_margin; /**< value of last demodulation margin */ + uint8_t num_gateways; /**< number of gateways of last link check */ + uint8_t datarate; /**< LoRaWAN datarate for the next transmission */ + uint8_t port; /**< LoRaWAN port for the next transmission */ + uint8_t ack_req; /**< Request ACK in the next transmission */ + uint8_t otaa; /**< whether the next transmission is OTAA or not */ } gnrc_netif_lorawan_t; +/** + * @brief Set the app key in the interface descriptor + * + * This getter function exists to allow if (IS_USED(...)) constructs in the + * LoRaWAN code in order to increase code coverage. + * + * @param[in] lw_netif pointer to the interface descriptor + * @param[in] key pointer to the app key + * @param[in] len length of the app key + * @return 0 on success + * @return <0 on failure + */ +static inline int gnrc_netif_lorawan_set_appkey(gnrc_netif_lorawan_t *lw_netif, const uint8_t *key, + size_t len) +{ + (void)lw_netif; + (void)key; + (void)len; + +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) + if (sizeof(lw_netif->appkey) < len) { + return -1; + } + memcpy(lw_netif->appkey, key, len); +#endif + return 0; +} + +/** + * @brief Get the app key from the interface descriptor + * + * This getter function exists to allow if (IS_USED(...)) constructs in the + * LoRaWAN code in order to increase code coverage. + * + * @param[in] lw_netif pointer to the interface descriptor + * @return pointer to the app key + */ +static inline uint8_t * gnrc_netif_lorawan_get_appkey(gnrc_netif_lorawan_t *lw_netif) +{ + (void)lw_netif; + +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) + return lw_netif->appkey; +#endif + return NULL; /* NO-OP */ +} + +/** + * @brief Set the serving network session integrity key in the interface descriptor + * + * This getter function exists to allow if (IS_USED(...)) constructs in the + * LoRaWAN code in order to increase code coverage. + * + * @param[in] lw_netif pointer to the interface descriptor + * @param[in] key pointer to the serving network session integrity key + * @param[in] len length of serving network session integrity key + * @return 0 on success + * @return <0 on failure + */ +static inline int gnrc_netif_lorawan_set_snwksintkey(gnrc_netif_lorawan_t *lw_netif, + const uint8_t *key, size_t len) +{ + (void)lw_netif; + (void)key; + (void)len; + +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) + if (sizeof(lw_netif->snwksintkey) < len) { + return -1; + } + memcpy(lw_netif->snwksintkey, key, len); +#endif + return 0; +} + +/** + * @brief Set the network session encryption key in the interface descriptor + * + * This getter function exists to allow if (IS_USED(...)) constructs in the + * LoRaWAN code in order to increase code coverage. + * + * @param[in] lw_netif pointer to the interface descriptor + * @param[in] key pointer to the network session encryption key + * @param[in] len length of network session encryption key + * @return 0 on success + * @return <0 on failure + */ +static inline int gnrc_netif_lorawan_set_nwksenckey(gnrc_netif_lorawan_t *lw_netif, + const uint8_t *key, size_t len) +{ + (void)lw_netif; + (void)key; + (void)len; + +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) + if (sizeof(lw_netif->nwksenckey) < len) { + return -1; + } + memcpy(lw_netif->nwksenckey, key, len); +#endif + return 0; +} + #ifdef __cplusplus } #endif diff --git a/sys/include/net/loramac.h b/sys/include/net/loramac.h index 440ba92d1f..52c94a1b7a 100644 --- a/sys/include/net/loramac.h +++ b/sys/include/net/loramac.h @@ -46,6 +46,15 @@ extern "C" { #define CONFIG_LORAMAC_DEV_EUI_DEFAULT "0000000000000000" #endif +/** + * @brief Default join EUI + * + * 8 bytes key, required for join procedure + */ +#ifndef CONFIG_LORAMAC_JOIN_EUI_DEFAULT +#define CONFIG_LORAMAC_JOIN_EUI_DEFAULT "0000000000000000" +#endif + /** * @brief Default application EUI * @@ -64,6 +73,15 @@ extern "C" { #define CONFIG_LORAMAC_APP_KEY_DEFAULT "00000000000000000000000000000000" #endif +/** + * @brief Default network key + * + * 16 bytes key, required for join procedure + */ +#ifndef CONFIG_LORAMAC_NWK_KEY_DEFAULT +#define CONFIG_LORAMAC_NWK_KEY_DEFAULT "00000000000000000000000000000000" +#endif + /** * @brief Default application session key * @@ -82,6 +100,33 @@ extern "C" { #define CONFIG_LORAMAC_NWK_SKEY_DEFAULT "00000000000000000000000000000000" #endif +/** + * @brief Default network session integrity key + * + * 16 bytes key, required for join procedure + */ +#ifndef CONFIG_LORAMAC_FNWKSINT_KEY_DEFAULT +#define CONFIG_LORAMAC_FNWKSINT_KEY_DEFAULT "00000000000000000000000000000000" +#endif + +/** + * @brief Default serving network session integrity key + * + * 16 bytes key, required for join procedure + */ +#ifndef CONFIG_LORAMAC_SNWKSINT_KEY_DEFAULT +#define CONFIG_LORAMAC_SNWKSINT_KEY_DEFAULT "00000000000000000000000000000000" +#endif + +/** + * @brief Default network session encryption key + * + * 16 bytes key, required for join procedure + */ +#ifndef CONFIG_LORAMAC_NWKSENC_KEY_DEFAULT +#define CONFIG_LORAMAC_NWKSENC_KEY_DEFAULT "00000000000000000000000000000000" +#endif + /** * @brief Default device address */ @@ -514,11 +559,21 @@ extern "C" { */ #define LORAMAC_APPEUI_LEN (8U) +/** + * @brief Join EUI length in bytes + */ +#define LORAMAC_JOINEUI_LEN (8U) + /** * @brief Application key length in bytes */ #define LORAMAC_APPKEY_LEN (16U) +/** + * @brief Network key length in bytes + */ +#define LORAMAC_NWKKEY_LEN (16U) + /** * @brief Application session key length in bytes */ @@ -529,6 +584,36 @@ extern "C" { */ #define LORAMAC_NWKSKEY_LEN (16U) +/** + * @brief Forwarding Network session integrity key length in bytes + */ +#define LORAMAC_FNWKSINTKEY_LEN (16U) + +/** + * @brief Serving Network session integrity key length in bytes + */ +#define LORAMAC_SNWKSINTKEY_LEN (16U) + +/** + * @brief Network session encryption key length in bytes + */ +#define LORAMAC_NWKSENCKEY_LEN (16U) + +/** + * @brief Join session integrity key length in bytes + */ +#define LORAMAC_JSINTKEY_LEN (16U) + +/** + * @brief Join session encryption key length in bytes + */ +#define LORAMAC_JSENCKEY_LEN (16U) + +/** + * @brief Network session encryption key length in bytes + */ +#define LORAMAC_JSINTKEY_LEN (16U) + /** * @brief Minimum port value */ @@ -544,6 +629,11 @@ extern "C" { */ #define LORAMAC_APP_NONCE_LEN (3U) +/** + * @brief Join nonce length in bytes + */ +#define LORAMAC_JOIN_NONCE_LEN (3U) + /** * @brief Network ID length in bytes */ diff --git a/sys/include/net/lorawan/hdr.h b/sys/include/net/lorawan/hdr.h index 877eafac5d..9444042ff8 100644 --- a/sys/include/net/lorawan/hdr.h +++ b/sys/include/net/lorawan/hdr.h @@ -44,6 +44,9 @@ #define LORAWAN_HDR_FOPTS_LEN_MASK (0x0F) /**< Frame options mask */ #define LORAWAN_HDR_FOPTS_LEN_POS (0U) /**< Frame options position */ +#define LORAWAN_JA_HDR_OPTNEG_MASK (0x80) /**< OptNeg bit mask */ +#define LORAWAN_JA_HDR_OPTNEG_POS (7U) /**< OptNeg bit position */ + #ifdef __cplusplus extern "C" { #endif @@ -92,7 +95,7 @@ typedef struct __attribute__((packed)) { */ typedef struct __attribute__((packed)) { uint8_t mt_maj; /**< mtype and major version holder */ - le_uint64_t app_eui; /**< application EUI */ + le_uint64_t join_eui; /**< join EUI. Mapped to app EUI if LoRaWAN 1.0x */ le_uint64_t dev_eui; /**< device EUI */ le_uint16_t dev_nonce; /**< device nonce */ le_uint32_t mic; /**< message integrity code */ @@ -103,7 +106,7 @@ typedef struct __attribute__((packed)) { */ typedef struct __attribute__((packed)) { uint8_t mt_maj; /**< mtype and major version holder */ - uint8_t app_nonce[LORAMAC_APP_NONCE_LEN]; /**< application nonce */ + uint8_t join_nonce[LORAMAC_JOIN_NONCE_LEN]; /**< join nonce. Mapped to application nonce if LoRaWAN 1.0x */ uint8_t net_id[LORAMAC_NETWORK_ID_LEN]; /**< network id */ uint8_t dev_addr[LORAMAC_DEVADDR_LEN]; /**< device address */ uint8_t dl_settings; /**< downlink settings */ @@ -278,6 +281,18 @@ static inline uint8_t lorawan_hdr_get_frame_opts_len(lorawan_hdr_t *hdr) return (hdr->fctrl & LORAWAN_HDR_FOPTS_LEN_MASK) >> LORAWAN_HDR_FOPTS_LEN_POS; } +/** + * @brief Get LoRaWAN join accept message OptNeg bit + * + * @param[in] ja_hdr Join accept message header + * + * @return value of the OptNeg bit + */ +static inline bool lorawan_ja_hdr_get_optneg(lorawan_join_accept_t *ja_hdr) +{ + return (ja_hdr->dl_settings & LORAWAN_JA_HDR_OPTNEG_MASK) >> LORAWAN_JA_HDR_OPTNEG_POS; +} + #ifdef __cplusplus } #endif diff --git a/sys/include/net/netopt.h b/sys/include/net/netopt.h index f2258ade82..561fe19ee6 100644 --- a/sys/include/net/netopt.h +++ b/sys/include/net/netopt.h @@ -584,20 +584,52 @@ typedef enum { */ NETOPT_LORAWAN_APPEUI, + /** + * @brief (uint8_t*) LoRaWAN join EUI (8 bytes length) + */ + NETOPT_LORAWAN_JOINEUI, + /** * @brief (uint8_t*) LoRaWAN application key (16 bytes length) */ NETOPT_LORAWAN_APPKEY, + /** + * @brief (uint8_t*) LoRaWAN network key (16 bytes length) + */ + NETOPT_LORAWAN_NWKKEY, + /** * @brief (uint8_t*) LoRaWAN network session key (16 bytes length) */ NETOPT_LORAWAN_NWKSKEY, + /** * @brief (uint8_t*) LoRaWAN application session key (16 bytes length) */ NETOPT_LORAWAN_APPSKEY, + /** + * @brief (uint8_t*) LoRaWAN forwarding network session integrity key (16 bytes length) + * + * This key is only valid when using LoRaWAN 1.1x. + */ + NETOPT_LORAWAN_FNWKSINTKEY, + + /** + * @brief (uint8_t*) LoRaWAN serving network session integrity key (16 bytes length) + * + * This key is only valid when using LoRaWAN 1.1x. + */ + NETOPT_LORAWAN_SNWKSINTKEY, + + /** + * @brief (uint8_t*) LoRaWAN network session encryption key buffer (16 bytes length) + * + * This key is only valid when using LoRaWAN 1.1x. + */ + NETOPT_LORAWAN_NWKSENCKEY, + /** * @brief (uint8_t) LoRaWAN device class (A, B, C) * - LoRaWAN: @ref loramac_class_t diff --git a/sys/net/crosslayer/netopt/netopt.c b/sys/net/crosslayer/netopt/netopt.c index 4bb66354d2..22ca26515f 100644 --- a/sys/net/crosslayer/netopt/netopt.c +++ b/sys/net/crosslayer/netopt/netopt.c @@ -94,9 +94,14 @@ static const char *_netopt_strmap[] = { [NETOPT_CHECKSUM] = "NETOPT_CHECKSUM", [NETOPT_PHY_BUSY] = "NETOPT_PHY_BUSY", [NETOPT_LORAWAN_APPEUI] = "NETOPT_LORAWAN_APPEUI", + [NETOPT_LORAWAN_JOINEUI] = "NETOPT_LORAWAN_JOINEUI", [NETOPT_LORAWAN_APPKEY] = "NETOPT_LORAWAN_APPKEY", + [NETOPT_LORAWAN_NWKKEY] = "NETOPT_LORAWAN_NWKKEY", [NETOPT_LORAWAN_APPSKEY] = "NETOPT_LORAWAN_APPSKEY", [NETOPT_LORAWAN_NWKSKEY] = "NETOPT_LORAWAN_NWKSKEY", + [NETOPT_LORAWAN_FNWKSINTKEY] = "NETOPT_LORAWAN_FNWKSINTKEY", + [NETOPT_LORAWAN_SNWKSINTKEY] = "NETOPT_LORAWAN_SNWKSINTKEY", + [NETOPT_LORAWAN_NWKSENCKEY] = "NETOPT_LORAWAN_NWKSENCKEY", [NETOPT_LORAWAN_DEVICE_CLASS] = "NETOPT_LORAWAN_DEVICE_CLASS", [NETOPT_LORAWAN_DR] = "NETOPT_LORAWAN_DR", [NETOPT_LORAWAN_ADR] = "NETOPT_LORAWAN_ADR", diff --git a/sys/net/gnrc/Makefile.dep b/sys/net/gnrc/Makefile.dep index dc82c11f8f..88b031caee 100644 --- a/sys/net/gnrc/Makefile.dep +++ b/sys/net/gnrc/Makefile.dep @@ -41,6 +41,11 @@ ifneq (,$(filter gnrc_lorawan,$(USEMODULE))) USEMODULE += crypto_aes_128 endif +ifneq (,$(filter gnrc_lorawan_1_1,$(USEMODULE))) + USEMODULE += gnrc_lorawan + FEATURES_REQUIRED += periph_flashpage +endif + ifneq (,$(filter gnrc_netdev_default,$(USEMODULE))) # enable default network devices on the platform USEMODULE += netdev_default @@ -78,7 +83,7 @@ ifneq (,$(filter gnrc_uhcpc,$(USEMODULE))) USEMODULE += fmt endif -ifneq (,$(filter gnrc_%,$(filter-out gnrc_lorawan gnrc_netapi gnrc_netreg gnrc_netif% gnrc_pkt%,$(USEMODULE)))) +ifneq (,$(filter gnrc_%,$(filter-out gnrc_lorawan gnrc_lorawan_1_1 gnrc_netapi gnrc_netreg gnrc_netif% gnrc_pkt%,$(USEMODULE)))) USEMODULE += gnrc endif diff --git a/sys/net/gnrc/link_layer/lorawan/gnrc_lorawan.c b/sys/net/gnrc/link_layer/lorawan/gnrc_lorawan.c index 9623e604f2..748b539693 100644 --- a/sys/net/gnrc/link_layer/lorawan/gnrc_lorawan.c +++ b/sys/net/gnrc/link_layer/lorawan/gnrc_lorawan.c @@ -29,6 +29,10 @@ #include "net/gnrc/lorawan/region.h" #include "timex.h" +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) +#include "periph/flashpage.h" +#endif + #define ENABLE_DEBUG 0 #include "debug.h" @@ -43,6 +47,7 @@ static inline void gnrc_lorawan_mlme_reset(gnrc_lorawan_t *mac) mac->mlme.pending_mlme_opts = 0; mac->rx_delay = (CONFIG_LORAMAC_DEFAULT_RX1_DELAY / MS_PER_SEC); mac->mlme.nid = CONFIG_LORAMAC_DEFAULT_NETID; + memset(mac->mlme.dev_nonce, 0x00, sizeof(mac->mlme.dev_nonce)); } static inline void gnrc_lorawan_mlme_backoff_init(gnrc_lorawan_t *mac) @@ -76,13 +81,43 @@ static void _sleep_radio(gnrc_lorawan_t *mac) dev->driver->set(dev, NETOPT_STATE, &state, sizeof(state)); } -void gnrc_lorawan_init(gnrc_lorawan_t *mac, uint8_t *nwkskey, uint8_t *appskey) +static void _load_persistent_state(gnrc_lorawan_t *mac) { - mac->nwkskey = nwkskey; - mac->appskey = appskey; + (void) mac; +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) + void *addr = flashpage_addr(GNRC_LORAWAN_STATE_FLASHPAGE_NUM); + gnrc_lorawan_persistent_state_t state = { 0 }; + + memcpy(&state, addr, sizeof(state)); + + if (state.initialized_marker != GNRC_LORAWAN_INITIALIZED_MARKER) { + memset(&state, 0x0, sizeof(state)); + state.initialized_marker = GNRC_LORAWAN_INITIALIZED_MARKER; + + flashpage_erase(GNRC_LORAWAN_STATE_FLASHPAGE_NUM); + /* ensure written length is multiple of FLASHPAGE_WRITE_BLOCK_SIZE */ + flashpage_write(addr, &state, (sizeof(state) / + FLASHPAGE_WRITE_BLOCK_SIZE + 0x1) * + FLASHPAGE_WRITE_BLOCK_SIZE); + } + memcpy(mac->mlme.dev_nonce, state.dev_nonce, sizeof(mac->mlme.dev_nonce)); +#endif +} + +void gnrc_lorawan_init(gnrc_lorawan_t *mac, uint8_t *joineui, const gnrc_lorawan_key_ctx_t *ctx) +{ + DEBUG("Lorawan init !\n"); + mac->joineui = joineui; + + memcpy(&mac->ctx, ctx, sizeof(gnrc_lorawan_key_ctx_t)); + mac->busy = false; gnrc_lorawan_mlme_backoff_init(mac); gnrc_lorawan_reset(mac); + + if (IS_USED(MODULE_GNRC_LORAWAN_1_1)) { + _load_persistent_state(mac); + } } void gnrc_lorawan_reset(gnrc_lorawan_t *mac) @@ -110,6 +145,24 @@ void gnrc_lorawan_reset(gnrc_lorawan_t *mac) gnrc_lorawan_channels_init(mac); } +void gnrc_lorawan_store_dev_nonce(uint8_t *dev_nonce) +{ + (void) dev_nonce; +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) + void *addr = flashpage_addr(GNRC_LORAWAN_STATE_FLASHPAGE_NUM); + gnrc_lorawan_persistent_state_t state = { 0 }; + memcpy(&state, addr, sizeof(state)); + + memcpy(state.dev_nonce, dev_nonce, sizeof(state.dev_nonce)); + + flashpage_erase(GNRC_LORAWAN_STATE_FLASHPAGE_NUM); + /* ensure written length is multiple of FLASHPAGE_WRITE_BLOCK_SIZE */ + flashpage_write(addr, &state, (sizeof(state) / + FLASHPAGE_WRITE_BLOCK_SIZE + 0x1) * + FLASHPAGE_WRITE_BLOCK_SIZE); +#endif +} + static void _config_radio(gnrc_lorawan_t *mac, uint32_t channel_freq, uint8_t dr, int rx) { @@ -157,20 +210,20 @@ void gnrc_lorawan_open_rx_window(gnrc_lorawan_t *mac) void gnrc_lorawan_timeout_cb(gnrc_lorawan_t *mac) { - switch(mac->state) { - case LORAWAN_STATE_RX_1: - case LORAWAN_STATE_RX_2: - gnrc_lorawan_open_rx_window(mac); - break; - case LORAWAN_STATE_JOIN: - gnrc_lorawan_trigger_join(mac); - break; - case LORAWAN_STATE_IDLE: - gnrc_lorawan_event_retrans_timeout(mac); - break; - default: - assert(false); - break; + switch (mac->state) { + case LORAWAN_STATE_RX_1: + case LORAWAN_STATE_RX_2: + gnrc_lorawan_open_rx_window(mac); + break; + case LORAWAN_STATE_JOIN: + gnrc_lorawan_trigger_join(mac); + break; + case LORAWAN_STATE_IDLE: + gnrc_lorawan_event_retrans_timeout(mac); + break; + default: + assert(false); + break; } } @@ -200,33 +253,32 @@ void gnrc_lorawan_radio_rx_timeout_cb(gnrc_lorawan_t *mac) { (void)mac; switch (mac->state) { - case LORAWAN_STATE_RX_1: - DEBUG("gnrc_lorawan: RX1 timeout.\n"); - _configure_rx_window(mac, CONFIG_LORAMAC_DEFAULT_RX2_FREQ, - mac->dl_settings & - GNRC_LORAWAN_DL_RX2_DR_MASK); - mac->state = LORAWAN_STATE_RX_2; - break; - case LORAWAN_STATE_RX_2: - DEBUG("gnrc_lorawan: RX2 timeout.\n"); - gnrc_lorawan_event_no_rx(mac); - mac->state = LORAWAN_STATE_IDLE; - break; - default: - assert(false); - break; + case LORAWAN_STATE_RX_1: + DEBUG("gnrc_lorawan: RX1 timeout.\n"); + _configure_rx_window(mac, CONFIG_LORAMAC_DEFAULT_RX2_FREQ, + mac->dl_settings & + GNRC_LORAWAN_DL_RX2_DR_MASK); + mac->state = LORAWAN_STATE_RX_2; + break; + case LORAWAN_STATE_RX_2: + DEBUG("gnrc_lorawan: RX2 timeout.\n"); + gnrc_lorawan_event_no_rx(mac); + mac->state = LORAWAN_STATE_IDLE; + break; + default: + assert(false); + break; } _sleep_radio(mac); } -void gnrc_lorawan_send_pkt(gnrc_lorawan_t *mac, iolist_t *psdu, uint8_t dr) +void gnrc_lorawan_send_pkt(gnrc_lorawan_t *mac, iolist_t *psdu, uint8_t dr, + uint32_t chan) { netdev_t *dev = gnrc_lorawan_get_netdev(mac); mac->state = LORAWAN_STATE_TX; - uint32_t chan = gnrc_lorawan_pick_channel(mac); - DEBUG("gnrc_lorawan: Channel: %" PRIu32 "Hz \n", chan); _config_radio(mac, chan, dr, false); @@ -254,14 +306,14 @@ void gnrc_lorawan_radio_rx_done_cb(gnrc_lorawan_t *mac, uint8_t *psdu, uint8_t mtype = (*psdu & MTYPE_MASK) >> 5; switch (mtype) { - case MTYPE_JOIN_ACCEPT: - gnrc_lorawan_mlme_process_join(mac, psdu, size); - break; - case MTYPE_CNF_DOWNLINK: - case MTYPE_UNCNF_DOWNLINK: - gnrc_lorawan_mcps_process_downlink(mac, psdu, size); - break; - default: - break; + case MTYPE_JOIN_ACCEPT: + gnrc_lorawan_mlme_process_join(mac, psdu, size); + break; + case MTYPE_CNF_DOWNLINK: + case MTYPE_UNCNF_DOWNLINK: + gnrc_lorawan_mcps_process_downlink(mac, psdu, size); + break; + default: + break; } } diff --git a/sys/net/gnrc/link_layer/lorawan/gnrc_lorawan_crypto.c b/sys/net/gnrc/link_layer/lorawan/gnrc_lorawan_crypto.c index 9cb5ae015c..18fc6e29b9 100644 --- a/sys/net/gnrc/link_layer/lorawan/gnrc_lorawan_crypto.c +++ b/sys/net/gnrc/link_layer/lorawan/gnrc_lorawan_crypto.c @@ -23,13 +23,23 @@ #include "byteorder.h" #include "net/lorawan/hdr.h" +#include "fmt.h" +#define ENABLE_DEBUG 0 +#include "debug.h" + #define MIC_B0_START (0x49) +#define MIC_B1_START (0x49) #define CRYPT_B0_START (0x01) #define DIR_MASK (0x1) #define SBIT_MASK (0xF) -#define APP_SKEY_B0_START (0x1) -#define NWK_SKEY_B0_START (0x2) +#define APP_SKEY_B0_START (0x2) +#define NWK_SKEY_B0_START (0x2) +#define FNWKSINT_KEY_B0_START (0x1) +#define SNWKSINT_KEY_B0_START (0x3) +#define NWKSENC_KEY_B0_START (0x4) +#define JSENC_KEY_B0_START (0x5) +#define JSINT_KEY_B0_START (0x6) static aes128_cmac_context_t CmacContext; static uint8_t digest[LORAMAC_APPKEY_LEN]; @@ -37,16 +47,31 @@ static cipher_t AesContext; typedef struct __attribute__((packed)) { uint8_t fb; - uint32_t u8_pad; + union { + uint32_t u8_pad; + le_uint16_t conf_fcnt; + }; uint8_t dir; le_uint32_t dev_addr; le_uint32_t fcnt; uint8_t u32_pad; uint8_t len; -} lorawan_block_t; +} lorawan_block0_t; -void gnrc_lorawan_calculate_join_mic(const uint8_t *buf, size_t len, - const uint8_t *key, le_uint32_t *out) +typedef struct __attribute__((packed)) { + uint8_t fb; + le_uint16_t conf_fcnt; + uint8_t tx_dr; + uint8_t tx_ch; + uint8_t dir; + le_uint32_t dev_addr; + le_uint32_t fcnt; + uint8_t u32_pad; + uint8_t len; +} lorawan_block1_t; + +void gnrc_lorawan_calculate_join_req_mic(const uint8_t *buf, size_t len, + uint8_t *key, le_uint32_t *out) { aes128_cmac_init(&CmacContext, key, LORAMAC_APPKEY_LEN); aes128_cmac_update(&CmacContext, buf, len); @@ -55,25 +80,113 @@ void gnrc_lorawan_calculate_join_mic(const uint8_t *buf, size_t len, memcpy(out, digest, sizeof(le_uint32_t)); } -void gnrc_lorawan_calculate_mic(const le_uint32_t *dev_addr, uint32_t fcnt, - uint8_t dir, iolist_t *frame, - const uint8_t *nwkskey, le_uint32_t *out) +void gnrc_lorawan_calculate_join_acpt_mic(const uint8_t *buf, size_t len, + gnrc_lorawan_t *mac, le_uint32_t *out) { - lorawan_block_t block; + uint8_t *key; + + if (IS_USED(MODULE_GNRC_LORAWAN_1_1) && gnrc_lorawan_optneg_is_set(mac)) { + key = gnrc_lorawan_get_jsintkey(mac); + } + else { + key = mac->ctx.nwksenckey; + } + + aes128_cmac_init(&CmacContext, key, LORAMAC_JSINTKEY_LEN); + + if (IS_USED(MODULE_GNRC_LORAWAN_1_1) && gnrc_lorawan_optneg_is_set(mac)) { + /** + * TODO: JoinReqType hardcoded for now. Will probably move into + * gnrc_lorawan_t struct once ReJoin requests are implemented. + */ + mlme_join_req_type_t type = JOIN_REQ; + aes128_cmac_update(&CmacContext, &type, 0x1); + aes128_cmac_update(&CmacContext, mac->joineui, LORAMAC_JOINEUI_LEN); + aes128_cmac_update(&CmacContext, mac->mlme.dev_nonce, GNRC_LORAWAN_DEV_NONCE_SIZE); + } + + /* buf = HDR | JoinNonce | NetID | DevAddr | DLSettings | RxDelay | CFList */ + aes128_cmac_update(&CmacContext, buf, len); + aes128_cmac_final(&CmacContext, digest); + + memcpy(out, digest, sizeof(le_uint32_t)); +} + +void gnrc_lorawan_calculate_mic_uplink(iolist_t *frame, uint16_t conf_fcnt, + gnrc_lorawan_t *mac, le_uint32_t *out) +{ + lorawan_block0_t block0 = { 0 }; + + block0.fb = MIC_B0_START; + block0.dir = 0x00; + + memcpy(&block0.dev_addr, &mac->dev_addr, sizeof(le_uint32_t)); + + block0.fcnt = byteorder_htoll(mac->mcps.fcnt); + block0.len = iolist_size(frame); + + /* cmacF = aes128_cmac(FNwkSIntKey, B0 | msg) */ + aes128_cmac_init(&CmacContext, mac->ctx.fnwksintkey, LORAMAC_FNWKSINTKEY_LEN); + aes128_cmac_update(&CmacContext, &block0, sizeof(block0)); + for (iolist_t *io = frame; io != NULL; io = io->iol_next) { + aes128_cmac_update(&CmacContext, io->iol_base, io->iol_len); + } + aes128_cmac_final(&CmacContext, digest); + + if (IS_USED(MODULE_GNRC_LORAWAN_1_1) && gnrc_lorawan_optneg_is_set(mac)) { + /* cmacF[0..1] (MIC = cmacS[0..1] | cmacF[0..1]) */ + memcpy((uint8_t *)out + 0x2, digest, 0x2); + } + else { + /* MIC = cmacF[0..3] */ + memcpy(out, digest, sizeof(le_uint32_t)); + } + + if (IS_USED(MODULE_GNRC_LORAWAN_1_1) && gnrc_lorawan_optneg_is_set(mac)) { + lorawan_block1_t block1 = { 0 }; + + block1.fb = MIC_B1_START; + block1.conf_fcnt = byteorder_htols(conf_fcnt); + block1.tx_dr = mac->last_dr; + block1.tx_ch = mac->last_chan_idx; + block1.dir = 0x00; + + memcpy(&block1.dev_addr, &mac->dev_addr, sizeof(le_uint32_t)); + + block1.fcnt = byteorder_htoll(mac->mcps.fcnt); + block1.len = iolist_size(frame); + + /* cmacS = aes128_cmac(SNwkSIntKey, B1 | msg) */ + aes128_cmac_init(&CmacContext, mac->ctx.snwksintkey, LORAMAC_SNWKSINTKEY_LEN); + aes128_cmac_update(&CmacContext, &block1, sizeof(block1)); + for (iolist_t *io = frame; io != NULL; io = io->iol_next) { + aes128_cmac_update(&CmacContext, io->iol_base, io->iol_len); + } + aes128_cmac_final(&CmacContext, digest); + + /* cmacS[0..1] (MIC = cmacS[0..1] | cmacF[0..1]) */ + memcpy(out, digest, 0x2); + } +} + +void gnrc_lorawan_calculate_mic_downlink(const le_uint32_t *dev_addr, + uint32_t fcnt, uint16_t conf_fcnt, + iolist_t *frame, + const uint8_t *snwksintkey, + le_uint32_t *out) +{ + lorawan_block0_t block = { 0 }; block.fb = MIC_B0_START; - block.u8_pad = 0; - block.dir = dir & DIR_MASK; + block.conf_fcnt = byteorder_htols(conf_fcnt); + block.dir = 0x1; memcpy(&block.dev_addr, dev_addr, sizeof(le_uint32_t)); block.fcnt = byteorder_htoll(fcnt); - - block.u32_pad = 0; - block.len = iolist_size(frame); - aes128_cmac_init(&CmacContext, nwkskey, LORAMAC_APPKEY_LEN); + aes128_cmac_init(&CmacContext, snwksintkey, LORAMAC_SNWKSINTKEY_LEN); aes128_cmac_update(&CmacContext, &block, sizeof(block)); for (iolist_t *io = frame; io != NULL; io = io->iol_next) { aes128_cmac_update(&CmacContext, io->iol_base, io->iol_len); @@ -83,6 +196,41 @@ void gnrc_lorawan_calculate_mic(const le_uint32_t *dev_addr, uint32_t fcnt, memcpy(out, digest, sizeof(le_uint32_t)); } +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) +void gnrc_lorawan_encrypt_fopts(uint8_t *fopts, size_t len, + const le_uint32_t *dev_addr, uint32_t fcnt, + bool afcnt, uint8_t dir, const uint8_t *key) +{ + uint8_t s_block[16] = { 0 }; + uint8_t a_block[16] = { 0 }; + + lorawan_block0_t *block = (lorawan_block0_t *)a_block; + + cipher_init(&AesContext, CIPHER_AES, key, LORAMAC_APPKEY_LEN); + + block->fb = CRYPT_B0_START; + + if (afcnt) { + a_block[0x4] = 0x02; + } + else { + a_block[0x4] = 0x01; + } + + block->dir = dir & DIR_MASK; + block->dev_addr = *dev_addr; + block->fcnt = byteorder_htoll(fcnt); + + block->len = 0x01; + + cipher_encrypt(&AesContext, a_block, s_block); + + for (size_t i = 0; i < len; i++) { + fopts[i] ^= s_block[i]; + } +} +#endif /* IS_USED(MODULE_GNRC_LORAWAN_1_1) */ + void gnrc_lorawan_encrypt_payload(iolist_t *iolist, const le_uint32_t *dev_addr, uint32_t fcnt, uint8_t dir, const uint8_t *appskey) @@ -93,7 +241,7 @@ void gnrc_lorawan_encrypt_payload(iolist_t *iolist, const le_uint32_t *dev_addr, memset(s_block, 0, sizeof(s_block)); memset(a_block, 0, sizeof(a_block)); - lorawan_block_t *block = (lorawan_block_t *)a_block; + lorawan_block0_t *block = (lorawan_block0_t *)a_block; cipher_init(&AesContext, CIPHER_AES, appskey, LORAMAC_APPKEY_LEN); @@ -136,30 +284,82 @@ void gnrc_lorawan_decrypt_join_accept(const uint8_t *key, uint8_t *pkt, } } -void gnrc_lorawan_generate_session_keys(const uint8_t *app_nonce, - const uint8_t *dev_nonce, - const uint8_t *appkey, uint8_t *nwkskey, - uint8_t *appskey) +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) +void gnrc_lorawan_generate_lifetime_session_keys(const uint8_t *deveui, + const uint8_t *nwkkey, + uint8_t *jsintkey, + uint8_t *jsenckey) { - uint8_t buf[LORAMAC_APPSKEY_LEN]; + uint8_t buf[LORAMAC_JSINTKEY_LEN] = { 0 }; - memset(buf, 0, sizeof(buf)); + cipher_init(&AesContext, CIPHER_AES, nwkkey, LORAMAC_NWKKEY_LEN); - cipher_init(&AesContext, CIPHER_AES, appkey, LORAMAC_APPSKEY_LEN); + memcpy(buf + 1, deveui, LORAMAC_DEVEUI_LEN); - /* net_id comes right after app_nonce */ - memcpy(buf + 1, app_nonce, - GNRC_LORAWAN_APP_NONCE_SIZE + GNRC_LORAWAN_NET_ID_SIZE); - memcpy(buf + 1 + GNRC_LORAWAN_APP_NONCE_SIZE + GNRC_LORAWAN_NET_ID_SIZE, - dev_nonce, GNRC_LORAWAN_DEV_NONCE_SIZE); + buf[0] = JSENC_KEY_B0_START; + cipher_encrypt(&AesContext, buf, jsenckey); - /* Calculate Application Session Key */ - buf[0] = APP_SKEY_B0_START; - cipher_encrypt(&AesContext, buf, nwkskey); - - /* Calculate Network Session Key */ - buf[0] = NWK_SKEY_B0_START; - cipher_encrypt(&AesContext, buf, appskey); + buf[0] = JSINT_KEY_B0_START; + cipher_encrypt(&AesContext, buf, jsintkey); } +#endif /* IS_USED(MODULE_GNRC_LORAWAN_1_1) */ +void gnrc_lorawan_generate_session_keys(const uint8_t *join_nonce, + const uint8_t *dev_nonce, + const uint8_t *joineui, + gnrc_lorawan_t *mac) +{ + uint8_t buf[LORAMAC_APPSKEY_LEN] = { 0 }; + uint8_t *nwkkey = mac->ctx.nwksenckey; + uint8_t *appkey = mac->ctx.appskey; + + if (IS_USED(MODULE_GNRC_LORAWAN_1_1) && gnrc_lorawan_optneg_is_set(mac)) { + /* JoinNonce | JoinEUI | DevNonce */ + memcpy(buf + 1, join_nonce, GNRC_LORAWAN_JOIN_NONCE_SIZE); + memcpy(buf + 1 + GNRC_LORAWAN_JOIN_NONCE_SIZE, joineui, + LORAMAC_JOINEUI_LEN); + memcpy(buf + 1 + GNRC_LORAWAN_JOIN_NONCE_SIZE + LORAMAC_JOINEUI_LEN, + dev_nonce, GNRC_LORAWAN_DEV_NONCE_SIZE); + } + else { + /* AppNonce | NetID | DevNonce */ + /* net_id comes right after join_nonce */ + memcpy(buf + 1, join_nonce, + GNRC_LORAWAN_APP_NONCE_SIZE + GNRC_LORAWAN_NET_ID_SIZE); + memcpy(buf + 1 + GNRC_LORAWAN_APP_NONCE_SIZE + GNRC_LORAWAN_NET_ID_SIZE, + dev_nonce, GNRC_LORAWAN_DEV_NONCE_SIZE); + } + + if (IS_USED(MODULE_GNRC_LORAWAN_1_1) && gnrc_lorawan_optneg_is_set(mac)) { + cipher_init(&AesContext, CIPHER_AES, appkey, LORAMAC_APPKEY_LEN); + + /* derive application session key */ + buf[0] = APP_SKEY_B0_START; + cipher_encrypt(&AesContext, buf, mac->ctx.appskey); + } + + cipher_init(&AesContext, CIPHER_AES, nwkkey, LORAMAC_NWKKEY_LEN); + + /* derive forwarding Network session integrity key */ + buf[0] = FNWKSINT_KEY_B0_START; + cipher_encrypt(&AesContext, buf, mac->ctx.fnwksintkey); + + if (IS_USED(MODULE_GNRC_LORAWAN_1_1) && gnrc_lorawan_optneg_is_set(mac)) { + /* derive serving network session integrity key */ + buf[0] = SNWKSINT_KEY_B0_START; + cipher_encrypt(&AesContext, buf, mac->ctx.snwksintkey); + + /* derive Network session encryption key */ + buf[0] = NWKSENC_KEY_B0_START; + cipher_encrypt(&AesContext, buf, mac->ctx.nwksenckey); + } + else { + /* derive application session key */ + buf[0] = APP_SKEY_B0_START; + cipher_encrypt(&AesContext, buf, mac->ctx.appskey); + + mac->ctx.snwksintkey = mac->ctx.fnwksintkey; + mac->ctx.nwksenckey = mac->ctx.fnwksintkey; + } +} /** @} */ diff --git a/sys/net/gnrc/link_layer/lorawan/gnrc_lorawan_mcps.c b/sys/net/gnrc/link_layer/lorawan/gnrc_lorawan_mcps.c index 13cd9ef75f..5184eb08cc 100644 --- a/sys/net/gnrc/link_layer/lorawan/gnrc_lorawan_mcps.c +++ b/sys/net/gnrc/link_layer/lorawan/gnrc_lorawan_mcps.c @@ -34,8 +34,10 @@ static void _end_of_tx(gnrc_lorawan_t *mac, int type, int status); -static int gnrc_lorawan_mic_is_valid(uint8_t *buf, size_t len, uint8_t *nwkskey) +static int gnrc_lorawan_mic_is_valid(uint8_t *buf, size_t len, uint8_t *key, + uint32_t fcnt_up, bool optneg) { + (void)fcnt_up; le_uint32_t calc_mic; lorawan_hdr_t *lw_hdr = (lorawan_hdr_t *)buf; @@ -44,8 +46,25 @@ static int gnrc_lorawan_mic_is_valid(uint8_t *buf, size_t len, uint8_t *nwkskey) iolist_t iol = { .iol_base = buf, .iol_len = len - MIC_SIZE, .iol_next = NULL }; - gnrc_lorawan_calculate_mic(&lw_hdr->addr, fcnt, GNRC_LORAWAN_DIR_DOWNLINK, - &iol, nwkskey, &calc_mic); + /* for LoRaWAN 1.0 conf_fcnt is hardcoded to be 0 */ + uint16_t conf_fnct = 0x00; + + if (IS_USED(MODULE_GNRC_LORAWAN_1_1) && optneg) { + /** + * If the device is connected to a LoRaWAN 1.1 Network Server and the + * ACK bit of the downlink frame is set, meaning this frame is acknowledging + * an uplink “confirmed” frame,then ConfFCnt is the frame counter value + * modulo 2^16 of the “confirmed” uplink frame that is being acknowledged. + * In all other cases ConfFCnt = 0x0000. + */ + if (lorawan_hdr_get_ack(lw_hdr)) { + conf_fnct = fcnt_up; + } + } + + gnrc_lorawan_calculate_mic_downlink(&lw_hdr->addr, fcnt, conf_fnct, &iol, + key, &calc_mic); + return calc_mic.u32 == ((le_uint32_t *)(buf + len - MIC_SIZE))->u32; } @@ -91,17 +110,6 @@ int gnrc_lorawan_parse_dl(gnrc_lorawan_t *mac, uint8_t *buf, size_t len, return -1; } - uint32_t _fcnt = - gnrc_lorawan_fcnt_stol(mac->mcps.fcnt_down, _hdr->fcnt.u16); - - if (mac->mcps.fcnt_down > _fcnt || mac->mcps.fcnt_down + - CONFIG_LORAMAC_DEFAULT_MAX_FCNT_GAP < _fcnt) { - DEBUG("gnrc_lorawan: wrong frame counter\n"); - return -1; - } - - pkt->fcnt_down = _fcnt; - int fopts_length = lorawan_hdr_get_frame_opts_len(_hdr); if (fopts_length) { @@ -129,6 +137,22 @@ int gnrc_lorawan_parse_dl(gnrc_lorawan_t *mac, uint8_t *buf, size_t len, } } + uint32_t _fcnt = + gnrc_lorawan_fcnt_stol(mac->mcps.fcnt_down, _hdr->fcnt.u16); + + uint32_t fcnt_down = mac->mcps.fcnt_down; + + if (IS_USED(MODULE_GNRC_LORAWAN_1_1) && pkt->port) { + fcnt_down = gnrc_lorawan_get_afcnt_down(mac); + } + + if (fcnt_down > _fcnt || fcnt_down + CONFIG_LORAMAC_DEFAULT_MAX_FCNT_GAP < _fcnt) { + DEBUG("gnrc_lorawan: wrong frame counter\n"); + return -1; + } + + pkt->fcnt_down = _fcnt; + pkt->ack_req = lorawan_hdr_get_mtype(_hdr) == MTYPE_CNF_DOWNLINK; pkt->ack = lorawan_hdr_get_ack(_hdr); pkt->frame_pending = lorawan_hdr_get_frame_pending(_hdr); @@ -142,7 +166,8 @@ void gnrc_lorawan_mcps_process_downlink(gnrc_lorawan_t *mac, uint8_t *psdu, struct parsed_packet _pkt; /* NOTE: MIC is in pkt */ - if (!gnrc_lorawan_mic_is_valid(psdu, size, mac->nwkskey)) { + if (!gnrc_lorawan_mic_is_valid(psdu, size, mac->ctx.snwksintkey, + mac->mcps.fcnt, gnrc_lorawan_optneg_is_set(mac))) { DEBUG("gnrc_lorawan: invalid MIC\n"); gnrc_lorawan_event_no_rx(mac); return; @@ -163,10 +188,10 @@ void gnrc_lorawan_mcps_process_downlink(gnrc_lorawan_t *mac, uint8_t *psdu, if (_pkt.enc_payload.iol_base) { uint8_t *key; if (_pkt.port) { - key = mac->appskey; + key = mac->ctx.appskey; } else { - key = mac->nwkskey; + key = mac->ctx.nwksenckey; fopts = &_pkt.enc_payload; } gnrc_lorawan_encrypt_payload(&_pkt.enc_payload, &_pkt.hdr->addr, @@ -175,7 +200,17 @@ void gnrc_lorawan_mcps_process_downlink(gnrc_lorawan_t *mac, uint8_t *psdu, key); } - mac->mcps.fcnt_down = _pkt.fcnt_down; + if (IS_USED(MODULE_GNRC_LORAWAN_1_1) && _pkt.port) { + gnrc_lorawan_set_afcnt_down(mac, _pkt.fcnt_down); + } + else { + mac->mcps.fcnt_down = _pkt.fcnt_down; + } + + if (IS_USED(MODULE_GNRC_LORAWAN_1_1)) { + gnrc_lorawan_set_last_fcnt_down(mac, _pkt.fcnt_down); + } + if (mac->mcps.waiting_for_ack && !_pkt.ack) { DEBUG("gnrc_lorawan: expected ACK packet\n"); gnrc_lorawan_event_no_rx(mac); @@ -188,6 +223,22 @@ void gnrc_lorawan_mcps_process_downlink(gnrc_lorawan_t *mac, uint8_t *psdu, /* if there are fopts, it's either an empty packet or application payload */ if (fopts) { + if (IS_USED(MODULE_GNRC_LORAWAN_1_1) && gnrc_lorawan_optneg_is_set(mac)) { + if (_pkt.port) { + gnrc_lorawan_encrypt_fopts(fopts->iol_base, fopts->iol_len, + &mac->dev_addr, + gnrc_lorawan_get_afcnt_down(mac), true, + GNRC_LORAWAN_DIR_DOWNLINK, + mac->ctx.nwksenckey); + } + else { + gnrc_lorawan_encrypt_fopts(fopts->iol_base, fopts->iol_len, + &mac->dev_addr, + mac->mcps.fcnt_down, false, + GNRC_LORAWAN_DIR_DOWNLINK, + mac->ctx.nwksenckey); + } + } DEBUG("gnrc_lorawan: processing fopts\n"); gnrc_lorawan_process_fopts(mac, fopts->iol_base, fopts->iol_len); } @@ -257,24 +308,32 @@ size_t gnrc_lorawan_build_uplink(gnrc_lorawan_t *mac, iolist_t *payload, buf.index += sizeof(lorawan_hdr_t); - int fopts_length = gnrc_lorawan_build_options(mac, &buf); + size_t fopts_length = gnrc_lorawan_build_options(mac, &buf); assert(fopts_length < 16); lorawan_hdr_set_frame_opts_len(lw_hdr, fopts_length); buf.data[buf.index++] = port; + uint8_t *key; + + if (port) { + key = mac->ctx.appskey; + } + else { + key = mac->ctx.nwksenckey; + } + gnrc_lorawan_encrypt_payload(payload, &mac->dev_addr, mac->mcps.fcnt, - GNRC_LORAWAN_DIR_UPLINK, - port ? mac->appskey : mac->nwkskey); + GNRC_LORAWAN_DIR_UPLINK, key); - iolist_t iol = - { .iol_base = buf.data, .iol_len = buf.index, .iol_next = payload }; + if (IS_USED(MODULE_GNRC_LORAWAN_1_1) && gnrc_lorawan_optneg_is_set(mac)) { + key = mac->ctx.nwksenckey; - gnrc_lorawan_calculate_mic(&mac->dev_addr, mac->mcps.fcnt, - GNRC_LORAWAN_DIR_UPLINK, - &iol, mac->nwkskey, - (le_uint32_t *)&buf.data[buf.index]); + gnrc_lorawan_encrypt_fopts(&buf.data[sizeof(lorawan_hdr_t)], fopts_length, + &mac->dev_addr, mac->mcps.fcnt, false, + GNRC_LORAWAN_DIR_UPLINK, key); + } return buf.index; } @@ -321,8 +380,28 @@ static void _transmit_pkt(gnrc_lorawan_t *mac) last_snip = last_snip->iol_next; } + mac->last_chan_idx = gnrc_lorawan_pick_channel(mac); + + uint16_t conf_fcnt = 0; + + if (IS_USED(MODULE_GNRC_LORAWAN_1_1)) { + /** + * If the ACK bit of the uplink frame is set, meaning this frame is + * acknowledging a downlink “confirmed” frame, then ConfFCnt is the frame + * counter value modulo 2^16 of the “confirmed” downlink frame that is being + * acknowledged. In all other cases ConfFCnt = 0x0000 + */ + lorawan_hdr_t *lw_hdr = (lorawan_hdr_t *)header.iol_base; + if (lorawan_hdr_get_ack(lw_hdr)) { + conf_fcnt = gnrc_lorawan_get_last_fcnt_down(mac); + } + } + + gnrc_lorawan_calculate_mic_uplink(&header, conf_fcnt, mac, footer.iol_base); + last_snip->iol_next = &footer; - gnrc_lorawan_send_pkt(mac, &header, mac->last_dr); + gnrc_lorawan_send_pkt(mac, &header, mac->last_dr, + mac->channel[mac->last_chan_idx]); /* cppcheck-suppress redundantAssignment * (reason: cppcheck bug. The pointer is temporally modified to add a footer. diff --git a/sys/net/gnrc/link_layer/lorawan/gnrc_lorawan_mlme.c b/sys/net/gnrc/link_layer/lorawan/gnrc_lorawan_mlme.c index 774837c867..f37698c82f 100644 --- a/sys/net/gnrc/link_layer/lorawan/gnrc_lorawan_mlme.c +++ b/sys/net/gnrc/link_layer/lorawan/gnrc_lorawan_mlme.c @@ -30,8 +30,8 @@ #define ENABLE_DEBUG 0 #include "debug.h" -static void _build_join_req_pkt(uint8_t *appeui, uint8_t *deveui, - uint8_t *appkey, uint8_t *dev_nonce, +static void _build_join_req_pkt(uint8_t *joineui, uint8_t *deveui, + uint8_t *key, uint8_t *dev_nonce, uint8_t *psdu) { lorawan_join_request_t *hdr = (lorawan_join_request_t *)psdu; @@ -40,46 +40,63 @@ static void _build_join_req_pkt(uint8_t *appeui, uint8_t *deveui, lorawan_hdr_set_mtype((lorawan_hdr_t *)hdr, MTYPE_JOIN_REQUEST); lorawan_hdr_set_maj((lorawan_hdr_t *)hdr, MAJOR_LRWAN_R1); - le_uint64_t l_appeui = *((le_uint64_t *)appeui); + le_uint64_t l_joineui = *((le_uint64_t *)joineui); le_uint64_t l_deveui = *((le_uint64_t *)deveui); - hdr->app_eui = l_appeui; + hdr->join_eui = l_joineui; hdr->dev_eui = l_deveui; le_uint16_t l_dev_nonce = *((le_uint16_t *)dev_nonce); hdr->dev_nonce = l_dev_nonce; - gnrc_lorawan_calculate_join_mic(psdu, JOIN_REQUEST_SIZE - MIC_SIZE, appkey, - &hdr->mic); + gnrc_lorawan_calculate_join_req_mic(psdu, JOIN_REQUEST_SIZE - MIC_SIZE, key, + &hdr->mic); } void gnrc_lorawan_trigger_join(gnrc_lorawan_t *mac) { - iolist_t pkt = {.iol_base = mac->mcps.mhdr_mic, .iol_len = - sizeof(lorawan_join_request_t), .iol_next = NULL}; - gnrc_lorawan_send_pkt(mac, &pkt, mac->last_dr); + iolist_t pkt = { .iol_base = mac->mcps.mhdr_mic, .iol_len = + sizeof(lorawan_join_request_t), .iol_next = NULL }; + + mac->last_chan_idx = gnrc_lorawan_pick_channel(mac); + gnrc_lorawan_send_pkt(mac, &pkt, mac->last_dr, + mac->channel[mac->last_chan_idx]); } static int gnrc_lorawan_send_join_request(gnrc_lorawan_t *mac, uint8_t *deveui, - uint8_t *appeui, uint8_t *appkey, - uint8_t dr) + uint8_t *eui, uint8_t *key, uint8_t dr) { - netdev_t *dev = gnrc_lorawan_get_netdev(mac); + if (IS_USED(MODULE_GNRC_LORAWAN_1_1)) { + /** + * DevNonce starting at 0 when device is powered up and incremented with + * every Join-request. + */ + uint16_t dev_nonce = byteorder_lebuftohs(mac->mlme.dev_nonce); + byteorder_htolebufs(mac->mlme.dev_nonce, ++dev_nonce); + gnrc_lorawan_store_dev_nonce(mac->mlme.dev_nonce); - /* Dev Nonce */ - uint32_t random_number; + gnrc_lorawan_generate_lifetime_session_keys(deveui, mac->ctx.nwksenckey, + gnrc_lorawan_get_jsintkey(mac), + gnrc_lorawan_get_jsenckey(mac)); + } + else { + netdev_t *dev = gnrc_lorawan_get_netdev(mac); - dev->driver->get(dev, NETOPT_RANDOM, &random_number, sizeof(random_number)); + /* Dev Nonce */ + uint32_t random_number; - mac->mlme.dev_nonce[0] = random_number & 0xFF; - mac->mlme.dev_nonce[1] = (random_number >> 8) & 0xFF; + dev->driver->get(dev, NETOPT_RANDOM, &random_number, sizeof(random_number)); + + mac->mlme.dev_nonce[0] = random_number & 0xFF; + mac->mlme.dev_nonce[1] = (random_number >> 8) & 0xFF; + } mac->last_dr = dr; mac->state = LORAWAN_STATE_JOIN; /* Use the buffer for MHDR */ - _build_join_req_pkt(appeui, deveui, appkey, mac->mlme.dev_nonce, (uint8_t*) mac->mcps.mhdr_mic); + _build_join_req_pkt(eui, deveui, key, mac->mlme.dev_nonce, (uint8_t *)mac->mcps.mhdr_mic); /* We need a random delay for join request. Otherwise there might be * network congestion if a group of nodes start at the same time */ @@ -111,25 +128,31 @@ void gnrc_lorawan_mlme_process_join(gnrc_lorawan_t *mac, uint8_t *data, uint8_t out[GNRC_LORAWAN_JOIN_ACCEPT_MAX_SIZE - 1]; uint8_t has_cflist = (size - 1) > CFLIST_SIZE; - gnrc_lorawan_decrypt_join_accept(mac->appskey, data + 1, - has_cflist, out); + gnrc_lorawan_decrypt_join_accept(mac->ctx.nwksenckey, data + 1, has_cflist, out); + memcpy(data + 1, out, size - 1); le_uint32_t mic; le_uint32_t *expected_mic = (le_uint32_t *)(data + size - MIC_SIZE); - gnrc_lorawan_calculate_join_mic(data, size - MIC_SIZE, mac->appskey, &mic); + lorawan_join_accept_t *ja_hdr = (lorawan_join_accept_t *)data; + + if (IS_USED(MODULE_GNRC_LORAWAN_1_1)) { + gnrc_lorawan_set_optneg(mac, lorawan_ja_hdr_get_optneg(ja_hdr)); + } + + gnrc_lorawan_calculate_join_acpt_mic(data, size - MIC_SIZE, mac, &mic); + if (mic.u32 != expected_mic->u32) { DEBUG("gnrc_lorawan_mlme: wrong MIC.\n"); status = -EBADMSG; goto out; } - lorawan_join_accept_t *ja_hdr = (lorawan_join_accept_t *)data; + void *joineui = IS_USED(MODULE_GNRC_LORAWAN_1_1) ? mac->joineui : NULL; - gnrc_lorawan_generate_session_keys(ja_hdr->app_nonce, mac->mlme.dev_nonce, - mac->appskey, mac->nwkskey, - mac->appskey); + gnrc_lorawan_generate_session_keys(ja_hdr->join_nonce, + mac->mlme.dev_nonce, joineui, mac); le_uint32_t le_nid; @@ -152,6 +175,11 @@ void gnrc_lorawan_mlme_process_join(gnrc_lorawan_t *mac, uint8_t *data, mac->mlme.activation = MLME_ACTIVATION_OTAA; status = GNRC_LORAWAN_REQ_STATUS_SUCCESS; + /* schedule rekey indication command */ + if (IS_USED(MODULE_GNRC_LORAWAN_1_1) && gnrc_lorawan_optneg_is_set(mac)) { + mac->mlme.pending_mlme_opts |= GNRC_LORAWAN_MLME_OPTS_REKEY_IND_REQ; + } + out: mlme_confirm.type = MLME_JOIN; mlme_confirm.status = status; @@ -167,21 +195,21 @@ void gnrc_lorawan_mlme_backoff_expire_cb(gnrc_lorawan_t *mac) if (counter == 0) { switch (state) { - case GNRC_LORAWAN_BACKOFF_STATE_1: - counter = GNRC_LORAWAN_BACKOFF_TIME_1; - state = GNRC_LORAWAN_BACKOFF_STATE_2; - mac->mlme.backoff_budget = GNRC_LORAWAN_BACKOFF_BUDGET_1; - break; - case GNRC_LORAWAN_BACKOFF_STATE_2: - counter = GNRC_LORAWAN_BACKOFF_TIME_2; - state = GNRC_LORAWAN_BACKOFF_STATE_3; - mac->mlme.backoff_budget = GNRC_LORAWAN_BACKOFF_BUDGET_2; - break; - case GNRC_LORAWAN_BACKOFF_STATE_3: - default: - counter = GNRC_LORAWAN_BACKOFF_TIME_3; - mac->mlme.backoff_budget = GNRC_LORAWAN_BACKOFF_BUDGET_3; - break; + case GNRC_LORAWAN_BACKOFF_STATE_1: + counter = GNRC_LORAWAN_BACKOFF_TIME_1; + state = GNRC_LORAWAN_BACKOFF_STATE_2; + mac->mlme.backoff_budget = GNRC_LORAWAN_BACKOFF_BUDGET_1; + break; + case GNRC_LORAWAN_BACKOFF_STATE_2: + counter = GNRC_LORAWAN_BACKOFF_TIME_2; + state = GNRC_LORAWAN_BACKOFF_STATE_3; + mac->mlme.backoff_budget = GNRC_LORAWAN_BACKOFF_BUDGET_2; + break; + case GNRC_LORAWAN_BACKOFF_STATE_3: + default: + counter = GNRC_LORAWAN_BACKOFF_TIME_3; + mac->mlme.backoff_budget = GNRC_LORAWAN_BACKOFF_BUDGET_3; + break; } } @@ -194,23 +222,23 @@ static void _mlme_set(gnrc_lorawan_t *mac, const mlme_request_t *mlme_request, { mlme_confirm->status = -EINVAL; switch (mlme_request->mib.type) { - case MIB_ACTIVATION_METHOD: - if (mlme_request->mib.activation != MLME_ACTIVATION_OTAA) { - mlme_confirm->status = GNRC_LORAWAN_REQ_STATUS_SUCCESS; - mac->mlme.activation = mlme_request->mib.activation; - } - break; - case MIB_DEV_ADDR: + case MIB_ACTIVATION_METHOD: + if (mlme_request->mib.activation != MLME_ACTIVATION_OTAA) { mlme_confirm->status = GNRC_LORAWAN_REQ_STATUS_SUCCESS; - memcpy(&mac->dev_addr, mlme_request->mib.dev_addr, - sizeof(uint32_t)); - break; - case MIB_RX2_DR: - mlme_confirm->status = GNRC_LORAWAN_REQ_STATUS_SUCCESS; - gnrc_lorawan_set_rx2_dr(mac, mlme_request->mib.rx2_dr); - break; - default: - break; + mac->mlme.activation = mlme_request->mib.activation; + } + break; + case MIB_DEV_ADDR: + mlme_confirm->status = GNRC_LORAWAN_REQ_STATUS_SUCCESS; + memcpy(&mac->dev_addr, mlme_request->mib.dev_addr, + sizeof(uint32_t)); + break; + case MIB_RX2_DR: + mlme_confirm->status = GNRC_LORAWAN_REQ_STATUS_SUCCESS; + gnrc_lorawan_set_rx2_dr(mac, mlme_request->mib.rx2_dr); + break; + default: + break; } } @@ -218,17 +246,17 @@ static void _mlme_get(gnrc_lorawan_t *mac, const mlme_request_t *mlme_request, mlme_confirm_t *mlme_confirm) { switch (mlme_request->mib.type) { - case MIB_ACTIVATION_METHOD: - mlme_confirm->status = GNRC_LORAWAN_REQ_STATUS_SUCCESS; - mlme_confirm->mib.activation = mac->mlme.activation; - break; - case MIB_DEV_ADDR: - mlme_confirm->status = GNRC_LORAWAN_REQ_STATUS_SUCCESS; - mlme_confirm->mib.dev_addr = &mac->dev_addr; - break; - default: - mlme_confirm->status = -EINVAL; - break; + case MIB_ACTIVATION_METHOD: + mlme_confirm->status = GNRC_LORAWAN_REQ_STATUS_SUCCESS; + mlme_confirm->mib.activation = mac->mlme.activation; + break; + case MIB_DEV_ADDR: + mlme_confirm->status = GNRC_LORAWAN_REQ_STATUS_SUCCESS; + mlme_confirm->mib.dev_addr = &mac->dev_addr; + break; + default: + mlme_confirm->status = -EINVAL; + break; } } @@ -237,47 +265,54 @@ void gnrc_lorawan_mlme_request(gnrc_lorawan_t *mac, mlme_confirm_t *mlme_confirm) { switch (mlme_request->type) { - case MLME_JOIN: - if (mac->mlme.activation != MLME_ACTIVATION_NONE) { - mlme_confirm->status = -EINVAL; - break; - } - if (!gnrc_lorawan_mac_acquire(mac)) { - mlme_confirm->status = -EBUSY; - break; - } + case MLME_JOIN: + if (mac->mlme.activation != MLME_ACTIVATION_NONE) { + mlme_confirm->status = -EINVAL; + break; + } + if (!gnrc_lorawan_mac_acquire(mac)) { + mlme_confirm->status = -EBUSY; + break; + } - if (mac->mlme.backoff_budget < 0) { - mlme_confirm->status = -EDQUOT; - break; - } - memcpy(mac->appskey, mlme_request->join.appkey, LORAMAC_APPKEY_LEN); - mlme_confirm->status = gnrc_lorawan_send_join_request(mac, - mlme_request->join.deveui, - mlme_request->join.appeui, mlme_request->join.appkey, - mlme_request->join.dr); - break; - case MLME_LINK_CHECK: - mac->mlme.pending_mlme_opts |= - GNRC_LORAWAN_MLME_OPTS_LINK_CHECK_REQ; - mlme_confirm->status = GNRC_LORAWAN_REQ_STATUS_DEFERRED; - break; - case MLME_SET: - _mlme_set(mac, mlme_request, mlme_confirm); - break; - case MLME_GET: - _mlme_get(mac, mlme_request, mlme_confirm); - break; - case MLME_RESET: - gnrc_lorawan_reset(mac); - mlme_confirm->status = GNRC_LORAWAN_REQ_STATUS_SUCCESS; - break; - default: + if (mac->mlme.backoff_budget < 0) { + mlme_confirm->status = -EDQUOT; break; + } + + if (IS_USED(MODULE_GNRC_LORAWAN_1_1)) { + memcpy(mac->ctx.appskey, gnrc_lorawan_mlme_join_get_appkey( + &mlme_request->join), LORAMAC_APPKEY_LEN); + } + + memcpy(mac->ctx.nwksenckey, mlme_request->join.nwkkey, LORAMAC_NWKKEY_LEN); + mlme_confirm->status = gnrc_lorawan_send_join_request(mac, + mlme_request->join.deveui, + mlme_request->join.joineui, + mlme_request->join.nwkkey, + mlme_request->join.dr); + break; + case MLME_LINK_CHECK: + mac->mlme.pending_mlme_opts |= + GNRC_LORAWAN_MLME_OPTS_LINK_CHECK_REQ; + mlme_confirm->status = GNRC_LORAWAN_REQ_STATUS_DEFERRED; + break; + case MLME_SET: + _mlme_set(mac, mlme_request, mlme_confirm); + break; + case MLME_GET: + _mlme_get(mac, mlme_request, mlme_confirm); + break; + case MLME_RESET: + gnrc_lorawan_reset(mac); + mlme_confirm->status = GNRC_LORAWAN_REQ_STATUS_SUCCESS; + break; + default: + break; } } -int _fopts_mlme_link_check_req(lorawan_buffer_t *buf) +static int _fopts_mlme_link_check_req(lorawan_buffer_t *buf) { if (buf) { assert(buf->index + GNRC_LORAWAN_CID_SIZE <= buf->size); @@ -301,6 +336,28 @@ static void _mlme_link_check_ans(gnrc_lorawan_t *mac, uint8_t *p) mac->mlme.pending_mlme_opts &= ~GNRC_LORAWAN_MLME_OPTS_LINK_CHECK_REQ; } +static int _fopts_mlme_link_rekey_ind(lorawan_buffer_t *buf) +{ + if (buf) { + assert(buf->index + GNRC_LORAWAN_CID_SIZE + + GNCR_LORAWAN_REKEY_IND_SIZE <= buf->size); + + buf->data[buf->index++] = GNCR_LORAWAN_CID_REKEY_CONF; + buf->data[buf->index++] = MINOR_LRWAN; + } + + return GNRC_LORAWAN_CID_SIZE + GNCR_LORAWAN_REKEY_IND_SIZE; +} + +static void _mlme_rekey_check_conf(gnrc_lorawan_t *mac, uint8_t *p) +{ + /* server version must by smaller or equal to device's LoRaWAN version */ + uint8_t server_minor = p[1]; + if (server_minor <= MINOR_LRWAN) { + mac->mlme.pending_mlme_opts &= ~GNRC_LORAWAN_MLME_OPTS_REKEY_IND_REQ; + } +} + void gnrc_lorawan_process_fopts(gnrc_lorawan_t *mac, uint8_t *fopts, size_t size) { @@ -314,12 +371,16 @@ void gnrc_lorawan_process_fopts(gnrc_lorawan_t *mac, uint8_t *fopts, for (uint8_t pos = 0; pos < size; pos += ret) { switch (fopts[pos]) { - case GNRC_LORAWAN_CID_LINK_CHECK_ANS: - ret += GNRC_LORAWAN_FOPT_LINK_CHECK_ANS_SIZE; - cb = _mlme_link_check_ans; - break; - default: - return; + case GNRC_LORAWAN_CID_LINK_CHECK_ANS: + ret += GNRC_LORAWAN_FOPT_LINK_CHECK_ANS_SIZE; + cb = _mlme_link_check_ans; + break; + case GNCR_LORAWAN_CID_REKEY_CONF: + ret += GNRC_LORAWAN_FOPT_REKEY_CONF_SIZE; + cb = _mlme_rekey_check_conf; + break; + default: + return; } if (pos + ret > size) { @@ -338,5 +399,9 @@ uint8_t gnrc_lorawan_build_options(gnrc_lorawan_t *mac, lorawan_buffer_t *buf) size += _fopts_mlme_link_check_req(buf); } + if (mac->mlme.pending_mlme_opts & GNRC_LORAWAN_MLME_OPTS_REKEY_IND_REQ) { + size += _fopts_mlme_link_rekey_ind(buf); + } + return size; } diff --git a/sys/net/gnrc/link_layer/lorawan/gnrc_lorawan_region.c b/sys/net/gnrc/link_layer/lorawan/gnrc_lorawan_region.c index b04e32cfbd..8edece9527 100644 --- a/sys/net/gnrc/link_layer/lorawan/gnrc_lorawan_region.c +++ b/sys/net/gnrc/link_layer/lorawan/gnrc_lorawan_region.c @@ -38,7 +38,7 @@ int gnrc_lorawan_set_dr(gnrc_lorawan_t *mac, uint8_t datarate) if (!gnrc_lorawan_validate_dr(datarate)) { return -EINVAL; } - DEBUG("gnrc_lorawan_region: Data Rate: DR%u \n",datarate); + DEBUG("gnrc_lorawan_region: Data Rate: DR%u \n", datarate); uint8_t bw = dr_bw[datarate]; uint8_t sf = dr_sf[datarate]; @@ -60,6 +60,7 @@ uint8_t gnrc_lorawan_rx1_get_dr_offset(uint8_t dr_up, uint8_t dr_offset) { DEBUG("gnrc_lorawan_region: RX1DRoffset: %u \n", dr_offset); int dr_eff = dr_offset > 5 ? 5 - dr_offset : dr_offset; + return MIN(5, MAX(0, dr_up - dr_eff)); } #endif @@ -72,7 +73,7 @@ int gnrc_lorawan_phy_set_channel_mask(gnrc_lorawan_t *mac, uint16_t channel_mask for (int n = channel_mask, i = 0; n; n = n >> 1, i++) { if ((n & 0x1) && !mac->channel[i]) { - return -EINVAL; + return -EINVAL; } } @@ -96,7 +97,7 @@ void gnrc_lorawan_channels_init(gnrc_lorawan_t *mac) } } -uint32_t gnrc_lorawan_pick_channel(gnrc_lorawan_t *mac) +uint8_t gnrc_lorawan_pick_channel(gnrc_lorawan_t *mac) { uint8_t index = 0; @@ -106,7 +107,8 @@ uint32_t gnrc_lorawan_pick_channel(gnrc_lorawan_t *mac) for (int i = 0; i < pos + 1; i++) { state = bitarithm_test_and_clear(state, &index); } - return mac->channel[index]; + + return index; } void gnrc_lorawan_process_cflist(gnrc_lorawan_t *mac, uint8_t *cflist) diff --git a/sys/net/gnrc/link_layer/lorawan/include/gnrc_lorawan_internal.h b/sys/net/gnrc/link_layer/lorawan/include/gnrc_lorawan_internal.h index 9990e17249..d220dce3e1 100644 --- a/sys/net/gnrc/link_layer/lorawan/include/gnrc_lorawan_internal.h +++ b/sys/net/gnrc/link_layer/lorawan/include/gnrc_lorawan_internal.h @@ -46,6 +46,8 @@ extern "C" { #define MAJOR_MASK 0x3 /**< Major mtype mask */ #define MAJOR_LRWAN_R1 0x0 /**< LoRaWAN R1 version type */ +#define MINOR_LRWAN 0x1 /**< Minor LoRaWAN version of device */ + #define JOIN_REQUEST_SIZE (23U) /**< Join Request size in bytes */ #define MIC_SIZE (4U) /**< MIC size in bytes */ #define CFLIST_SIZE (16U) /**< Channel Frequency list size in bytes */ @@ -68,11 +70,16 @@ extern "C" { #define GNRC_LORAWAN_BACKOFF_BUDGET_3 (8700000LL) /**< budget of time on air every 24 hours */ #define GNRC_LORAWAN_MLME_OPTS_LINK_CHECK_REQ (1 << 0) /**< Internal Link Check request flag */ +#define GNRC_LORAWAN_MLME_OPTS_REKEY_IND_REQ (1 << 1) /**< Internal Rekey Indication flag */ #define GNRC_LORAWAN_CID_SIZE (1U) /**< size of Command ID in FOps */ #define GNRC_LORAWAN_CID_LINK_CHECK_ANS (0x02) /**< Link Check CID */ +#define GNCR_LORAWAN_REKEY_IND_SIZE (1U) /**< RekeyInd MAC command size */ +#define GNCR_LORAWAN_CID_REKEY_CONF (0x0B) /**< Reykey Confirmation CID */ + #define GNRC_LORAWAN_FOPT_LINK_CHECK_ANS_SIZE (3U) /**< size of Link check answer */ +#define GNRC_LORAWAN_FOPT_REKEY_CONF_SIZE (2U) /**< size of Rekey confirmation */ #define GNRC_LORAWAN_JOIN_DELAY_U32_MASK (0x1FFFFF) /**< mask for detecting overflow in frame counter */ @@ -92,12 +99,21 @@ extern "C" { #define GNRC_LORAWAN_BACKOFF_TIME_3 (24U) /**< duration of third backoff state (in hours) */ #define GNRC_LORAWAN_APP_NONCE_SIZE (3U) /**< App Nonce size */ +#define GNRC_LORAWAN_JOIN_NONCE_SIZE (3U) /**< Join nonce size */ #define GNRC_LORAWAN_NET_ID_SIZE (3U) /**< Net ID size */ #define GNRC_LORAWAN_DEV_NONCE_SIZE (2U) /**< Dev Nonce size */ #define GNRC_LORAWAN_FOPTS_MAX_SIZE (15U) /**< Maximum size of Fopts field */ #define GNRC_LORAWAN_FPORT_SIZE (1U) /**< Size of the Fport field */ +#if defined(CPU_FAM_STM32F2) || defined(CPU_FAM_STM32F4) || \ + defined(CPU_FAM_STM32F7) +#define GNRC_LORAWAN_STATE_FLASHPAGE_NUM (0x7) /**< Flashpage number where to store state */ +#else +#define GNRC_LORAWAN_STATE_FLASHPAGE_NUM (FLASHPAGE_NUMOF - 0x4) /**< Flashpage number where to store state */ +#endif +#define GNRC_LORAWAN_INITIALIZED_MARKER (0x52f94f54U) /**< Persistent state initialized marker */ + /** * @brief Size of the internal MHDR-MIC buffer */ @@ -112,7 +128,7 @@ extern "C" { typedef struct { uint8_t *data; /**< pointer to the beginning of the buffer holding data */ uint8_t size; /**< size of the buffer */ - uint8_t index; /**< current inxed in the buffer */ + uint8_t index; /**< current index in the buffer */ } lorawan_buffer_t; /** @@ -120,11 +136,24 @@ typedef struct { */ typedef struct { void *deveui; /**< pointer to the Device EUI */ - void *appeui; /**< pointer to the Application EUI */ - void *appkey; /**< pointer to the Application Key */ + void *joineui; /**< pointer to the Join EUI */ + void *nwkkey; /**< pointer to the Network Key. Mapped to App key if LoRaWAN 1.0x */ +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) + void *appkey; /**< pointer to the App key */ +#endif uint8_t dr; /**< datarate for the Join Request */ } mlme_lorawan_join_t; +/** + * @brief MLME Join Request type + */ +typedef enum { + REJOIN_REQ_0, /**< Rejoin-request type 0 */ + REJOIN_REQ_1, /**< Rejoin-request type 1 */ + REJOIN_REQ_2, /**< Rejoin-request type 2 */ + JOIN_REQ = 0xFF /**< Join-request type */ +} mlme_join_req_type_t; + /** * @brief MLME Link Check confirmation data */ @@ -147,7 +176,11 @@ typedef struct { */ typedef struct { uint32_t fcnt; /**< uplink framecounter */ - uint32_t fcnt_down; /**< downlink frame counter */ + uint32_t fcnt_down; /**< downlink frame counter. Mapped to network downlink frame counter if using LoRaWAN 1.1 */ +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) + uint32_t afcnt_down; /**< application downlink frame counter */ + uint32_t last_fcnt_down; /**< last downlink frame counter used to decrypt frame. Needed to compute ConfFCnt */ +#endif iolist_t *msdu; /**< current MSDU */ int nb_trials; /**< holds the remaining number of retransmissions */ int ack_requested; /**< whether the network server requested an ACK */ @@ -168,6 +201,20 @@ typedef struct { uint8_t backoff_state; /**< state in the backoff state machine */ } gnrc_lorawan_mlme_t; +/** + * @brief GNRC LoRaWAN key context struct + */ +typedef struct { + uint8_t *appskey; /**< pointer to Application SKey buffer */ + uint8_t *fnwksintkey; /**< pointer to Forwarding Network session integrity key */ + uint8_t *snwksintkey; /**< pointer to Serving Network session integrity key */ + uint8_t *nwksenckey; /**< pointer to Network session encryption key */ +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) + uint8_t *jsintkey; /**< pointer to join session integrity key */ + uint8_t *jsenckey; /**< pointer to join session encryption key */ +#endif +} gnrc_lorawan_key_ctx_t; + /** * @brief GNRC LoRaWAN mac descriptor */ typedef struct { @@ -175,8 +222,11 @@ typedef struct { gnrc_lorawan_mlme_t mlme; /**< MLME descriptor */ void *mlme_buf; /**< pointer to MLME buffer */ void *mcps_buf; /**< pointer to MCPS buffer */ - uint8_t *nwkskey; /**< pointer to Network SKey buffer */ - uint8_t *appskey; /**< pointer to Application SKey buffer */ + uint8_t *joineui; /**< pointer to Join EUI */ + gnrc_lorawan_key_ctx_t ctx; /**< GNRC LoRaWAN key context struct */ +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) + bool optneg; /**< optneg bit */ +#endif uint32_t channel[GNRC_LORAWAN_MAX_CHANNELS]; /**< channel array */ uint16_t channel_mask; /**< channel mask */ uint32_t toa; /**< Time on Air of the last transmission */ @@ -188,8 +238,17 @@ typedef struct { uint8_t rx_delay; /**< Delay of first reception window */ uint8_t dr_range[GNRC_LORAWAN_MAX_CHANNELS]; /**< Datarate Range for all channels */ uint8_t last_dr; /**< datarate of the last transmission */ + uint8_t last_chan_idx; /**< index of channel used for last transmission */ } gnrc_lorawan_t; +/** + * @brief LoRaWAN state that needs to be preserved across reboots + */ +typedef struct { + uint32_t initialized_marker; /**< state initialized marker */ + uint8_t dev_nonce[2]; /**< Device Nonce */ +} gnrc_lorawan_persistent_state_t; + /** * @brief Encrypts LoRaWAN payload * @@ -205,6 +264,24 @@ void gnrc_lorawan_encrypt_payload(iolist_t *iolist, const le_uint32_t *dev_addr, uint32_t fcnt, uint8_t dir, const uint8_t *appskey); +/** + * @brief Encrypts FOpts field + * + * @note This function is also used for decrypting the FOpts field. The LoRaWAN server encrypts the packet using decryption, so the end device only needs to implement encryption + * @note This function is used only in LoRaWAN 1.1 + * + * @param[in] fopts pointer to fopts + * @param[in] len length of fopts + * @param[in] dev_addr device address + * @param[in] fcnt frame counter + * @param[in] afcnt flag indicating if aFCntDown is used + * @param[in] dir direction of the packet (0 if uplink, 1 if downlink) + * @param[in] key pointer to key + */ +void gnrc_lorawan_encrypt_fopts(uint8_t *fopts, size_t len, + const le_uint32_t *dev_addr, uint32_t fcnt, + bool afcnt, uint8_t dir, const uint8_t *key); + /** * @brief Decrypts join accept message * @@ -220,19 +297,34 @@ void gnrc_lorawan_decrypt_join_accept(const uint8_t *key, uint8_t *pkt, * @brief Generate LoRaWAN session keys * * Intended to be called after a successful Join Request in order to generate - * NwkSKey and AppSKey + * AppSKey, SNwkSIntKey, FNwkSIntKey and NwkSEncKey * - * @param[in] app_nonce pointer to the app_nonce of the Join Accept message - * @param[in] dev_nonce pointer to the dev_nonce buffer - * @param[in] appkey pointer to eh AppKey - * @param[out] nwkskey pointer to the NwkSKey - * @param[out] appskey pointer to the AppSKey + * @param[in] join_nonce pointer to the join_nonce of the Join Accept message + * @param[in] dev_nonce pointer to the dev_nonce buffer + * @param[in] joineui pointer to the Join EUI. NULL if LoRaWAN 1.0x + * @param[in, out] mac pointer to the LoRaWAN mac descriptor */ -void gnrc_lorawan_generate_session_keys(const uint8_t *app_nonce, +void gnrc_lorawan_generate_session_keys(const uint8_t *join_nonce, const uint8_t *dev_nonce, - const uint8_t *appkey, uint8_t *nwkskey, - uint8_t *appskey); + const uint8_t *joineui, + gnrc_lorawan_t *mac); +/** + * @brief Generate LoRaWAN 1.1x lifetime session keys + * + * Called in order to derive JSIntKey and JSEncKey. + * + * LoRaWAN specification 1.1 section 6.1.1.4 + * + * @param[in] deveui pointer to the join_nonce of the Join Accept message + * @param[in] nwkkey pointer to the Network key + * @param[out] jsintkey pointer to the Join session integrity key + * @param[out] jsenckey pointer to the Join session encryption key + */ +void gnrc_lorawan_generate_lifetime_session_keys(const uint8_t *deveui, + const uint8_t *nwkkey, + uint8_t *jsintkey, + uint8_t *jsenckey); /** * @brief Set datarate for the next transmission * @@ -263,9 +355,9 @@ size_t gnrc_lorawan_build_uplink(gnrc_lorawan_t *mac, iolist_t *payload, * * @param[in] mac pointer to the MAC descriptor * - * @return a free channel + * @return index of free channel inside channel array */ -uint32_t gnrc_lorawan_pick_channel(gnrc_lorawan_t *mac); +uint8_t gnrc_lorawan_pick_channel(gnrc_lorawan_t *mac); /** * @brief Build fopts header @@ -289,29 +381,55 @@ void gnrc_lorawan_process_fopts(gnrc_lorawan_t *mac, uint8_t *fopts, size_t size); /** - * @brief calculate join Message Integrity Code + * @brief Calculate join-request Message Integrity Code * * @param[in] buf pointer to the frame * @param[in] len length of the frame - * @param[in] key key used to calculate the MIC + * @param[in] key key to be used to calculate the MIC * @param[out] out calculated MIC */ -void gnrc_lorawan_calculate_join_mic(const uint8_t *buf, size_t len, - const uint8_t *key, le_uint32_t *out); +void gnrc_lorawan_calculate_join_req_mic(const uint8_t *buf, size_t len, + uint8_t *key, le_uint32_t *out); /** - * @brief Calculate Message Integrity Code for a MCPS message + * @brief Calculate join-accept Message Integrity Code + * + * @param[in] buf pointer to the frame + * @param[in] len length of the frame + * @param[in] mac pointer to MAC descriptor + * @param[out] out calculated MIC + */ +void gnrc_lorawan_calculate_join_acpt_mic(const uint8_t *buf, size_t len, + gnrc_lorawan_t *mac, le_uint32_t *out); + +/** + * @brief Calculate Message Integrity Code for an uplink MCPS message + * + * @param[in] frame pointer to the PSDU frame (without MIC) + * @param[in] conf_fcnt frame counter value of confirmed uplink frame. Always + * zero if using LoRaWAN 1.0x + * @param[in] mac pointer to MAC descriptor + * @param[out] out calculated MIC + */ +void gnrc_lorawan_calculate_mic_uplink(iolist_t *frame, uint16_t conf_fcnt, + gnrc_lorawan_t *mac, le_uint32_t *out); + +/** + * @brief Calculate Message Integrity Code for downlink MCPS message * * @param[in] dev_addr the Device Address * @param[in] fcnt frame counter - * @param[in] dir direction of the packet (0 is uplink, 1 is downlink) + * @param[in] conf_fcnt frame counter value of confirmed uplink frame * @param[in] frame pointer to the PSDU frame (without MIC) - * @param[in] nwkskey pointer to the Network Session Key + * @param[in] snwksintkey pointer to serving network session integrity key * @param[out] out calculated MIC */ -void gnrc_lorawan_calculate_mic(const le_uint32_t *dev_addr, uint32_t fcnt, - uint8_t dir, iolist_t *frame, - const uint8_t *nwkskey, le_uint32_t *out); +void gnrc_lorawan_calculate_mic_downlink(const le_uint32_t *dev_addr, + uint32_t fcnt, uint16_t conf_fcnt, + iolist_t *frame, + const uint8_t *snwksintkey, + le_uint32_t *out); + /** * @brief Build a MCPS LoRaWAN header * @@ -362,8 +480,10 @@ void gnrc_lorawan_reset(gnrc_lorawan_t *mac); * @param[in] mac pointer to the MAC descriptor * @param[in] psdu the psdu frame to be sent * @param[in] dr the datarate used for the transmission + * @param[in] chan the channel used for transmission */ -void gnrc_lorawan_send_pkt(gnrc_lorawan_t *mac, iolist_t *psdu, uint8_t dr); +void gnrc_lorawan_send_pkt(gnrc_lorawan_t *mac, iolist_t *psdu, uint8_t dr, + uint32_t chan); /** * @brief Process join accept message @@ -488,6 +608,209 @@ void gnrc_lorawan_set_rx2_dr(gnrc_lorawan_t *mac, uint8_t rx2_dr); */ void gnrc_lorawan_trigger_join(gnrc_lorawan_t *mac); +/** + * @brief Store DevNonce in flash memory + * + * @param[in] dev_nonce pointer to the DevNonce + */ +void gnrc_lorawan_store_dev_nonce(uint8_t *dev_nonce); + +/** + * @brief Check whether OptNeg bit is set + * + * When using LoRaWAN 1.1x the OptNeg bit indicates whether the Network Server + * implements the LoRaWAN1.0 protocol version (unset) or 1.1 and later (set). + * + * This getter function exists to allow if (IS_USED(...)) constructs in the + * LoRaWAN code in order to increase code coverage. + * + * @param[in] mac pointer to the MAC descriptor + * @return true if OptNeg bit is set + * @return false if OptNeg bit is unset + */ +static inline bool gnrc_lorawan_optneg_is_set(const gnrc_lorawan_t *mac) +{ + (void)mac; +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) + return mac->optneg; +#else + return 1; /* NO-OP */ +#endif +} + +/** + * @brief Set OptNeg bit in the MAC descriptor + * + * When using LoRaWAN 1.1x the OptNeg bit indicates whether the Network Server + * implements the LoRaWAN1.0 protocol version (unset) or 1.1 and later (set). + * + * This setter function exists to allow if (IS_USED(...)) constructs in the + * LoRaWAN code in order to increase code coverage. + * + * @param[in] mac pointer to the MAC descriptor + * @param[in] optneg OptNeg bit + */ +static inline void gnrc_lorawan_set_optneg(gnrc_lorawan_t *mac, uint8_t optneg) +{ + (void)mac; + (void)optneg; + +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) + mac->optneg = optneg; +#endif +} + +/** + * @brief Get the join session integrity key from the MAC descriptor + * + * This getter function exists to allow if (IS_USED(...)) constructs in the + * LoRaWAN code in order to increase code coverage. + * + * @param[in] mac pointer to the MAC descriptor + * @return pointer to join session integrity key + */ +static inline uint8_t *gnrc_lorawan_get_jsintkey(const gnrc_lorawan_t *mac) +{ + (void)mac; +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) + return mac->ctx.jsintkey; +#else + return NULL; /* NO-OP */ +#endif +} + +/** + * @brief Get the join session encryption key from the MAC descriptor + * + * This getter function exists to allow if (IS_USED(...)) constructs in the + * LoRaWAN code in order to increase code coverage. + * + * @param[in] mac pointer to the MAC descriptor + * @return pointer to join session encryption key + */ +static inline uint8_t *gnrc_lorawan_get_jsenckey(const gnrc_lorawan_t *mac) +{ + (void)mac; +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) + return mac->ctx.jsenckey; +#else + return NULL; /* NO-OP */ +#endif +} + +/** + * @brief Get the application downlink frame counter from the MAC descriptor + * + * This getter function exists to allow if (IS_USED(...)) constructs in the + * LoRaWAN code in order to increase code coverage. + * + * @param[in] mac pointer to the MAC descriptor + * @return application downlink frame counter + */ +static inline uint32_t gnrc_lorawan_get_afcnt_down(const gnrc_lorawan_t *mac) +{ + (void)mac; +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) + return mac->mcps.afcnt_down; +#else + return 0; /* NO-OP */ +#endif +} + +/** + * @brief Set the application downlink frame counter in the MAC descriptor + * + * This getter function exists to allow if (IS_USED(...)) constructs in the + * LoRaWAN code in order to increase code coverage. + * + * @param[in] mac pointer to the MAC descriptor + * @param[in] afcnt_down application downlink frame counter + */ +static inline void gnrc_lorawan_set_afcnt_down(gnrc_lorawan_t *mac, uint32_t afcnt_down) +{ + (void)mac; + (void)afcnt_down; + +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) + mac->mcps.afcnt_down = afcnt_down; +#endif +} + +/** + * @brief Get the last downlink frame counter from the MAC descriptor + * + * This getter function exists to allow if (IS_USED(...)) constructs in the + * LoRaWAN code in order to increase code coverage. + * + * @param[in] mac pointer to the MAC descriptor + */ +static inline uint32_t gnrc_lorawan_get_last_fcnt_down(const gnrc_lorawan_t *mac) +{ + (void)mac; +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) + return mac->mcps.last_fcnt_down; +#else + return 0; /* NO-OP */ +#endif +} + +/** + * @brief Set the last downlink frame counter in the MAC descriptor + * + * This getter function exists to allow if (IS_USED(...)) constructs in the + * LoRaWAN code in order to increase code coverage. + * + * @param[in] mac pointer to the MAC descriptor + * @param[in] last_fcnt_down last downlink frame counter + */ +static inline void gnrc_lorawan_set_last_fcnt_down(gnrc_lorawan_t *mac, uint32_t last_fcnt_down) +{ + (void)mac; + (void)last_fcnt_down; +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) + mac->mcps.last_fcnt_down = last_fcnt_down; +#endif +} + +/** + * @brief Get the app key from the MLME join request data + * + * This getter function exists to allow if (IS_USED(...)) constructs in the + * LoRaWAN code in order to increase code coverage. + * + * @param[in] mlme_join pointer to the MLME join request data + * @return pointer to the app key + */ +static inline uint8_t * gnrc_lorawan_mlme_join_get_appkey(const mlme_lorawan_join_t *mlme_join) +{ + (void)mlme_join; + +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) + return mlme_join->appkey; +#else + return NULL; /* NO-OP */ +#endif +} + +/** + * @brief Se the app key in the MLME join request data + * + * This getter function exists to allow if (IS_USED(...)) constructs in the + * LoRaWAN code in order to increase code coverage. + * + * @param[in] mlme_join pointer to the MLME join request data + * @param[in] key pointer to the app key + */ +static inline void gnrc_lorawan_mlme_join_set_appkey(mlme_lorawan_join_t *mlme_join, uint8_t *key) +{ + (void)mlme_join; + (void)key; + +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) + mlme_join->appkey = key; +#endif +} + #ifdef __cplusplus } #endif diff --git a/sys/net/gnrc/netif/lorawan/gnrc_netif_lorawan.c b/sys/net/gnrc/netif/lorawan/gnrc_netif_lorawan.c index 8bbdbf4882..a8d0c81668 100644 --- a/sys/net/gnrc/netif/lorawan/gnrc_netif_lorawan.c +++ b/sys/net/gnrc/netif/lorawan/gnrc_netif_lorawan.c @@ -33,15 +33,18 @@ #define MSG_TYPE_MLME_BACKOFF_EXPIRE (0x3458) /**< Backoff timer expiration message type */ -static uint8_t _nwkskey[LORAMAC_NWKSKEY_LEN]; static uint8_t _appskey[LORAMAC_APPSKEY_LEN]; static uint8_t _appkey[LORAMAC_APPKEY_LEN]; +static uint8_t _snwksintkey[LORAMAC_SNWKSINTKEY_LEN]; +static uint8_t _nwksenckey[LORAMAC_NWKSENCKEY_LEN]; +static uint8_t _nwkkey[LORAMAC_NWKKEY_LEN]; +static uint8_t _joineui[LORAMAC_JOINEUI_LEN]; +static uint8_t _fnwksintkey[LORAMAC_FNWKSINTKEY_LEN]; static uint8_t _deveui[LORAMAC_DEVEUI_LEN]; -static uint8_t _appeui[LORAMAC_APPEUI_LEN]; static uint8_t _devaddr[LORAMAC_DEVADDR_LEN]; -static msg_t timeout_msg = {.type = MSG_TYPE_TIMEOUT}; -static msg_t backoff_msg = {.type = MSG_TYPE_MLME_BACKOFF_EXPIRE}; +static msg_t timeout_msg = { .type = MSG_TYPE_TIMEOUT }; +static msg_t backoff_msg = { .type = MSG_TYPE_MLME_BACKOFF_EXPIRE }; static int _send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt); static gnrc_pktsnip_t *_recv(gnrc_netif_t *netif); @@ -82,12 +85,14 @@ void gnrc_lorawan_mlme_confirm(gnrc_lorawan_t *mac, mlme_confirm_t *confirm) void gnrc_lorawan_set_timer(gnrc_lorawan_t *mac, uint32_t us) { gnrc_netif_lorawan_t *lw_netif = container_of(mac, gnrc_netif_lorawan_t, mac); - ztimer_set_msg(ZTIMER_MSEC, &lw_netif->timer, us/1000, &timeout_msg, thread_getpid()); + + ztimer_set_msg(ZTIMER_MSEC, &lw_netif->timer, us / 1000, &timeout_msg, thread_getpid()); } void gnrc_lorawan_remove_timer(gnrc_lorawan_t *mac) { gnrc_netif_lorawan_t *lw_netif = container_of(mac, gnrc_netif_lorawan_t, mac); + ztimer_remove(ZTIMER_MSEC, &lw_netif->timer); } @@ -121,6 +126,7 @@ void gnrc_lorawan_mcps_indication(gnrc_lorawan_t *mac, mcps_indication_t *ind) gnrc_pktsnip_t *pkt = gnrc_pktbuf_add(NULL, ind->data.pkt->iol_base, ind->data.pkt->iol_len, nettype); + if (!pkt) { DEBUG("gnrc_lorawan: mcps_indication: couldn't allocate pktbuf\n"); return; @@ -205,18 +211,18 @@ static void _driver_cb(netdev_t *dev, netdev_event_t event) else { DEBUG("gnrc_netif: event triggered -> %i\n", event); switch (event) { - case NETDEV_EVENT_RX_COMPLETE: - _rx_done(mac); - break; - case NETDEV_EVENT_TX_COMPLETE: - gnrc_lorawan_radio_tx_done_cb(mac); - break; - case NETDEV_EVENT_RX_TIMEOUT: - gnrc_lorawan_radio_rx_timeout_cb(mac); - break; - default: - DEBUG("gnrc_netif: warning: unhandled event %u.\n", event); - break; + case NETDEV_EVENT_RX_COMPLETE: + _rx_done(mac); + break; + case NETDEV_EVENT_TX_COMPLETE: + gnrc_lorawan_radio_tx_done_cb(mac); + break; + case NETDEV_EVENT_RX_TIMEOUT: + gnrc_lorawan_radio_rx_timeout_cb(mac); + break; + default: + DEBUG("gnrc_netif: warning: unhandled event %u.\n", event); + break; } } } @@ -249,6 +255,7 @@ netdev_t *gnrc_lorawan_get_netdev(gnrc_lorawan_t *mac) static int _init(gnrc_netif_t *netif) { + DEBUG("netif init ! \n"); int res = gnrc_netif_default_init(netif); if (res < 0) { @@ -259,22 +266,54 @@ static int _init(gnrc_netif_t *netif) _reset(netif); /* Convert default keys, address and EUIs to hex */ - fmt_hex_bytes(_nwkskey, CONFIG_LORAMAC_NWK_SKEY_DEFAULT); fmt_hex_bytes(_appskey, CONFIG_LORAMAC_APP_SKEY_DEFAULT); - fmt_hex_bytes(_appkey, CONFIG_LORAMAC_APP_KEY_DEFAULT); + if (IS_USED(MODULE_GNRC_LORAWAN_1_1)) { + fmt_hex_bytes(_joineui, CONFIG_LORAMAC_JOIN_EUI_DEFAULT); + fmt_hex_bytes(_appkey, CONFIG_LORAMAC_APP_KEY_DEFAULT); + fmt_hex_bytes(_nwkkey, CONFIG_LORAMAC_NWK_KEY_DEFAULT); + fmt_hex_bytes(_fnwksintkey, CONFIG_LORAMAC_FNWKSINT_KEY_DEFAULT); + fmt_hex_bytes(_snwksintkey, CONFIG_LORAMAC_SNWKSINT_KEY_DEFAULT); + fmt_hex_bytes(_nwksenckey, CONFIG_LORAMAC_NWKSENC_KEY_DEFAULT); + } + else { + fmt_hex_bytes(_joineui, CONFIG_LORAMAC_APP_EUI_DEFAULT); + fmt_hex_bytes(_nwkkey, CONFIG_LORAMAC_APP_KEY_DEFAULT); + fmt_hex_bytes(_fnwksintkey, CONFIG_LORAMAC_NWK_SKEY_DEFAULT); + } + fmt_hex_bytes(_deveui, CONFIG_LORAMAC_DEV_EUI_DEFAULT); - fmt_hex_bytes(_appeui, CONFIG_LORAMAC_APP_EUI_DEFAULT); fmt_hex_bytes(_devaddr, CONFIG_LORAMAC_DEV_ADDR_DEFAULT); /* Initialize default keys, address and EUIs */ - memcpy(netif->lorawan.nwkskey, _nwkskey, sizeof(_nwkskey)); memcpy(netif->lorawan.appskey, _appskey, sizeof(_appskey)); _memcpy_reversed(netif->lorawan.deveui, _deveui, sizeof(_deveui)); - memcpy(netif->lorawan.appkey, _appkey, sizeof(_appkey)); - _memcpy_reversed(netif->lorawan.appeui, _appeui, sizeof(_appeui)); + _memcpy_reversed(netif->lorawan.joineui, _joineui, sizeof(_joineui)); + memcpy(netif->lorawan.nwkkey, _nwkkey, sizeof(_nwkkey)); + memcpy(netif->lorawan.fnwksintkey, _fnwksintkey, sizeof(_fnwksintkey)); + + if (IS_USED(MODULE_GNRC_LORAWAN_1_1)) { + gnrc_netif_lorawan_set_appkey(&netif->lorawan, _appkey, sizeof(_appkey)); + gnrc_netif_lorawan_set_snwksintkey(&netif->lorawan, _snwksintkey, sizeof(_snwksintkey)); + gnrc_netif_lorawan_set_nwksenckey(&netif->lorawan, _nwksenckey, sizeof(_nwksenckey)); + } _set_be_addr(&netif->lorawan.mac, _devaddr); - gnrc_lorawan_init(&netif->lorawan.mac, netif->lorawan.nwkskey, netif->lorawan.appskey); + + const gnrc_lorawan_key_ctx_t ctx = { + .appskey = netif->lorawan.appskey, + .fnwksintkey = netif->lorawan.fnwksintkey, +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) + .snwksintkey = netif->lorawan.snwksintkey, + .nwksenckey = netif->lorawan.nwksenckey, + .jsintkey = netif->lorawan.jsintkey, + .jsenckey = netif->lorawan.jsenckey +#else + .snwksintkey = netif->lorawan.fnwksintkey, + .nwksenckey = netif->lorawan.fnwksintkey +#endif + }; + + gnrc_lorawan_init(&netif->lorawan.mac, netif->lorawan.joineui, &ctx); ztimer_set_msg(ZTIMER_MSEC, &netif->lorawan.backoff_timer, GNRC_LORAWAN_BACKOFF_WINDOW_TICK / 1000, @@ -338,7 +377,7 @@ static int _send(gnrc_netif_t *netif, gnrc_pktsnip_t *payload) { .type = netif->lorawan.ack_req ? MCPS_CONFIRMED : MCPS_UNCONFIRMED, .data = { .pkt = (iolist_t *)payload, .port = port, - .dr = netif->lorawan.datarate } }; + .dr = netif->lorawan.datarate } }; mcps_confirm_t conf; gnrc_lorawan_mcps_request(&netif->lorawan.mac, &req, &conf); @@ -357,16 +396,16 @@ static void _msg_handler(gnrc_netif_t *netif, msg_t *msg) (void)netif; (void)msg; switch (msg->type) { - case MSG_TYPE_TIMEOUT: - gnrc_lorawan_timeout_cb(&netif->lorawan.mac); - break; - case MSG_TYPE_MLME_BACKOFF_EXPIRE: - gnrc_lorawan_mlme_backoff_expire_cb(&netif->lorawan.mac); - ztimer_set_msg(ZTIMER_MSEC, &netif->lorawan.backoff_timer, - GNRC_LORAWAN_BACKOFF_WINDOW_TICK / 1000, - &backoff_msg, thread_getpid()); - default: - break; + case MSG_TYPE_TIMEOUT: + gnrc_lorawan_timeout_cb(&netif->lorawan.mac); + break; + case MSG_TYPE_MLME_BACKOFF_EXPIRE: + gnrc_lorawan_mlme_backoff_expire_cb(&netif->lorawan.mac); + ztimer_set_msg(ZTIMER_MSEC, &netif->lorawan.backoff_timer, + GNRC_LORAWAN_BACKOFF_WINDOW_TICK / 1000, + &backoff_msg, thread_getpid()); + default: + break; } } @@ -379,47 +418,47 @@ static int _get(gnrc_netif_t *netif, gnrc_netapi_opt_t *opt) mlme_request_t mlme_request; switch (opt->opt) { - case NETOPT_OTAA: - assert(opt->data_len >= sizeof(netopt_enable_t)); - *((netopt_enable_t *)opt->data) = netif->lorawan.otaa; - break; - case NETOPT_LINK: - mlme_request.type = MLME_GET; - mlme_request.mib.type = MIB_ACTIVATION_METHOD; - gnrc_lorawan_mlme_request(&netif->lorawan.mac, &mlme_request, - &mlme_confirm); - *((netopt_enable_t *)opt->data) = mlme_confirm.mib.activation != - MLME_ACTIVATION_NONE; - break; - case NETOPT_LINK_CHECK: - assert(opt->data_len == sizeof(netopt_enable_t)); - *((netopt_enable_t *)opt->data) = - (netif->lorawan.flags & GNRC_NETIF_LORAWAN_FLAGS_LINK_CHECK) ? - NETOPT_ENABLE : NETOPT_DISABLE; - break; - case NETOPT_NUM_GATEWAYS: - assert(opt->data_len == sizeof(uint8_t)); - *((uint8_t *)opt->data) = netif->lorawan.num_gateways; - break; - case NETOPT_DEMOD_MARGIN: - assert(opt->data_len == sizeof(uint8_t)); - *((uint8_t *)opt->data) = netif->lorawan.demod_margin; - break; - case NETOPT_ADDRESS: - mlme_request.type = MLME_GET; - mlme_request.mib.type = MIB_DEV_ADDR; + case NETOPT_OTAA: + assert(opt->data_len >= sizeof(netopt_enable_t)); + *((netopt_enable_t *)opt->data) = netif->lorawan.otaa; + break; + case NETOPT_LINK: + mlme_request.type = MLME_GET; + mlme_request.mib.type = MIB_ACTIVATION_METHOD; + gnrc_lorawan_mlme_request(&netif->lorawan.mac, &mlme_request, + &mlme_confirm); + *((netopt_enable_t *)opt->data) = mlme_confirm.mib.activation != + MLME_ACTIVATION_NONE; + break; + case NETOPT_LINK_CHECK: + assert(opt->data_len == sizeof(netopt_enable_t)); + *((netopt_enable_t *)opt->data) = + (netif->lorawan.flags & GNRC_NETIF_LORAWAN_FLAGS_LINK_CHECK) ? + NETOPT_ENABLE : NETOPT_DISABLE; + break; + case NETOPT_NUM_GATEWAYS: + assert(opt->data_len == sizeof(uint8_t)); + *((uint8_t *)opt->data) = netif->lorawan.num_gateways; + break; + case NETOPT_DEMOD_MARGIN: + assert(opt->data_len == sizeof(uint8_t)); + *((uint8_t *)opt->data) = netif->lorawan.demod_margin; + break; + case NETOPT_ADDRESS: + mlme_request.type = MLME_GET; + mlme_request.mib.type = MIB_DEV_ADDR; - gnrc_lorawan_mlme_request(&netif->lorawan.mac, &mlme_request, - &mlme_confirm); - tmp = *((uint32_t *)mlme_confirm.mib.dev_addr); - tmp = byteorder_swapl(tmp); - memcpy(opt->data, &tmp, sizeof(uint32_t)); - res = sizeof(uint32_t); - break; - default: - res = netif->dev->driver->get(netif->dev, opt->opt, opt->data, - opt->data_len); - break; + gnrc_lorawan_mlme_request(&netif->lorawan.mac, &mlme_request, + &mlme_confirm); + tmp = *((uint32_t *)mlme_confirm.mib.dev_addr); + tmp = byteorder_swapl(tmp); + memcpy(opt->data, &tmp, sizeof(uint32_t)); + res = sizeof(uint32_t); + break; + default: + res = netif->dev->driver->get(netif->dev, opt->opt, opt->data, + opt->data_len); + break; } return res; } @@ -432,101 +471,138 @@ static int _set(gnrc_netif_t *netif, const gnrc_netapi_opt_t *opt) gnrc_netif_acquire(netif); switch (opt->opt) { - case NETOPT_LORAWAN_DR: - assert(opt->data_len == sizeof(uint8_t)); - if (!gnrc_lorawan_validate_dr(*((uint8_t *)opt->data))) { - DEBUG("gnrc_netif_lorawan: Invalid datarate\n"); - res = -EINVAL; - break; - } - netif->lorawan.datarate = *((uint8_t *)opt->data); - break; - case NETOPT_LORAWAN_TX_PORT: - assert(opt->data_len == sizeof(uint8_t)); - netif->lorawan.port = *((uint8_t *)opt->data); - break; - case NETOPT_ACK_REQ: - assert(opt->data_len == sizeof(netopt_enable_t)); - netif->lorawan.ack_req = *((netopt_enable_t *)opt->data); - break; - case NETOPT_LORAWAN_APPKEY: - assert(opt->data_len == LORAMAC_APPKEY_LEN); - memcpy(netif->lorawan.appkey, opt->data, LORAMAC_APPKEY_LEN); - break; - case NETOPT_ADDRESS_LONG: - assert(opt->data_len == LORAMAC_DEVEUI_LEN); - _memcpy_reversed(netif->lorawan.deveui, opt->data, - LORAMAC_DEVEUI_LEN); - break; - case NETOPT_LORAWAN_APPEUI: - assert(opt->data_len == LORAMAC_APPEUI_LEN); - _memcpy_reversed(netif->lorawan.appeui, opt->data, - LORAMAC_APPEUI_LEN); - break; - case NETOPT_OTAA: - assert(opt->data_len == sizeof(netopt_enable_t)); - netif->lorawan.otaa = *((netopt_enable_t *)opt->data); - break; - case NETOPT_LORAWAN_APPSKEY: - assert(opt->data_len >= LORAMAC_APPSKEY_LEN); - memcpy(netif->lorawan.appskey, opt->data, LORAMAC_APPSKEY_LEN); - break; - case NETOPT_LORAWAN_NWKSKEY: - assert(opt->data_len >= LORAMAC_NWKSKEY_LEN); - memcpy(netif->lorawan.nwkskey, opt->data, LORAMAC_NWKSKEY_LEN); - break; - case NETOPT_LINK: - { - netopt_enable_t en = *((netopt_enable_t *)opt->data); - if (en) { - if (netif->lorawan.otaa) { - mlme_request.type = MLME_JOIN; - mlme_request.join.deveui = netif->lorawan.deveui; - mlme_request.join.appeui = netif->lorawan.appeui; - mlme_request.join.appkey = netif->lorawan.appkey; - mlme_request.join.dr = netif->lorawan.datarate; - gnrc_lorawan_mlme_request(&netif->lorawan.mac, - &mlme_request, &mlme_confirm); - } - else { - mlme_request.type = MLME_SET; - mlme_request.mib.type = MIB_ACTIVATION_METHOD; - mlme_request.mib.activation = MLME_ACTIVATION_ABP; - gnrc_lorawan_mlme_request(&netif->lorawan.mac, - &mlme_request, &mlme_confirm); - } - } - else { - mlme_request.type = MLME_RESET; - gnrc_lorawan_mlme_request(&netif->lorawan.mac, &mlme_request, - &mlme_confirm); - res = mlme_confirm.status; - if (mlme_confirm.status == 0) { - /* reset netif as well */ - _reset(netif); - } - } + case NETOPT_LORAWAN_DR: + assert(opt->data_len == sizeof(uint8_t)); + if (!gnrc_lorawan_validate_dr(*((uint8_t *)opt->data))) { + DEBUG("gnrc_netif_lorawan: Invalid datarate\n"); + res = -EINVAL; break; } - case NETOPT_ADDRESS: - assert(opt->data_len == sizeof(uint32_t)); - _set_be_addr(&netif->lorawan.mac, opt->data); - break; - case NETOPT_LINK_CHECK: - netif->lorawan.flags |= GNRC_NETIF_LORAWAN_FLAGS_LINK_CHECK; - break; - case NETOPT_LORAWAN_RX2_DR: - assert(opt->data_len == sizeof(uint8_t)); - mlme_request.type = MLME_SET; - mlme_request.mib.type = MIB_RX2_DR; - mlme_request.mib.rx2_dr = *((uint8_t *)opt->data); + netif->lorawan.datarate = *((uint8_t *)opt->data); + break; + case NETOPT_LORAWAN_TX_PORT: + assert(opt->data_len == sizeof(uint8_t)); + netif->lorawan.port = *((uint8_t *)opt->data); + break; + case NETOPT_ACK_REQ: + assert(opt->data_len == sizeof(netopt_enable_t)); + netif->lorawan.ack_req = *((netopt_enable_t *)opt->data); + break; + case NETOPT_ADDRESS_LONG: + assert(opt->data_len == LORAMAC_DEVEUI_LEN); + _memcpy_reversed(netif->lorawan.deveui, opt->data, + LORAMAC_DEVEUI_LEN); + break; + case NETOPT_LORAWAN_APPEUI: + case NETOPT_LORAWAN_JOINEUI: + assert(opt->data_len == LORAMAC_JOINEUI_LEN); + _memcpy_reversed(netif->lorawan.joineui, opt->data, + LORAMAC_JOINEUI_LEN); + break; +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) + case NETOPT_LORAWAN_NWKKEY: + assert(opt->data_len == LORAMAC_NWKKEY_LEN); + memcpy(netif->lorawan.nwkkey, opt->data, LORAMAC_NWKKEY_LEN); + break; + case NETOPT_LORAWAN_APPKEY: + assert(opt->data_len == LORAMAC_APPKEY_LEN); + memcpy(netif->lorawan.appkey, opt->data, LORAMAC_APPKEY_LEN); + break; + case NETOPT_LORAWAN_FNWKSINTKEY: + assert(opt->data_len == LORAMAC_FNWKSINTKEY_LEN); + _memcpy_reversed(netif->lorawan.fnwksintkey, opt->data, + LORAMAC_FNWKSINTKEY_LEN); + break; + case NETOPT_LORAWAN_SNWKSINTKEY: + assert(opt->data_len == LORAMAC_SNWKSINTKEY_LEN); + _memcpy_reversed(netif->lorawan.snwksintkey, opt->data, + LORAMAC_SNWKSINTKEY_LEN); + break; + case NETOPT_LORAWAN_NWKSENCKEY: + assert(opt->data_len == LORAMAC_NWKSENCKEY_LEN); + _memcpy_reversed(netif->lorawan.nwksenckey, opt->data, + LORAMAC_NWKSKEY_LEN); + break; +#else + case NETOPT_LORAWAN_APPKEY: + assert(opt->data_len == LORAMAC_NWKKEY_LEN); + memcpy(netif->lorawan.nwkkey, opt->data, LORAMAC_NWKKEY_LEN); + break; + case NETOPT_LORAWAN_NWKSKEY: + case NETOPT_LORAWAN_FNWKSINTKEY: + case NETOPT_LORAWAN_SNWKSINTKEY: + case NETOPT_LORAWAN_NWKSENCKEY: + assert(opt->data_len == LORAMAC_FNWKSINTKEY_LEN); + _memcpy_reversed(netif->lorawan.fnwksintkey, opt->data, + LORAMAC_FNWKSINTKEY_LEN); + break; +#endif + case NETOPT_OTAA: + assert(opt->data_len == sizeof(netopt_enable_t)); + netif->lorawan.otaa = *((netopt_enable_t *)opt->data); + break; + case NETOPT_LORAWAN_APPSKEY: + assert(opt->data_len >= LORAMAC_APPSKEY_LEN); + memcpy(netif->lorawan.appskey, opt->data, LORAMAC_APPSKEY_LEN); + break; + case NETOPT_LINK: + { + netopt_enable_t en = *((netopt_enable_t *)opt->data); + if (en) { + if (netif->lorawan.otaa) { + mlme_request.type = MLME_JOIN; + mlme_request.join.deveui = netif->lorawan.deveui; + mlme_request.join.joineui = netif->lorawan.joineui; + mlme_request.join.nwkkey = netif->lorawan.nwkkey; + + if (IS_USED(MODULE_GNRC_LORAWAN_1_1)) { + gnrc_lorawan_mlme_join_set_appkey(&mlme_request.join, gnrc_netif_lorawan_get_appkey( + &netif->lorawan)); + } + + mlme_request.join.dr = netif->lorawan.datarate; + gnrc_lorawan_mlme_request(&netif->lorawan.mac, + &mlme_request, &mlme_confirm); + } + else { + mlme_request.type = MLME_SET; + mlme_request.mib.type = MIB_ACTIVATION_METHOD; + mlme_request.mib.activation = MLME_ACTIVATION_ABP; + gnrc_lorawan_mlme_request(&netif->lorawan.mac, + &mlme_request, &mlme_confirm); + } + } + else { + mlme_request.type = MLME_RESET; gnrc_lorawan_mlme_request(&netif->lorawan.mac, &mlme_request, &mlme_confirm); - break; - default: - res = netif->dev->driver->set(netif->dev, opt->opt, opt->data, - opt->data_len); - break; + res = mlme_confirm.status; + if (mlme_confirm.status == 0) { + /* reset netif as well */ + _reset(netif); + } + } + break; + } + case NETOPT_ADDRESS: + assert(opt->data_len == sizeof(uint32_t)); + _set_be_addr(&netif->lorawan.mac, opt->data); + break; + case NETOPT_LINK_CHECK: + netif->lorawan.flags |= GNRC_NETIF_LORAWAN_FLAGS_LINK_CHECK; + break; + case NETOPT_LORAWAN_RX2_DR: + assert(opt->data_len == sizeof(uint8_t)); + mlme_request.type = MLME_SET; + mlme_request.mib.type = MIB_RX2_DR; + mlme_request.mib.rx2_dr = *((uint8_t *)opt->data); + gnrc_lorawan_mlme_request(&netif->lorawan.mac, &mlme_request, + &mlme_confirm); + break; + default: + res = netif->dev->driver->set(netif->dev, opt->opt, opt->data, + opt->data_len); + break; } gnrc_netif_release(netif); return res; diff --git a/sys/net/link_layer/Kconfig.lorawan b/sys/net/link_layer/Kconfig.lorawan index 3853b413f4..fcf6c201c9 100644 --- a/sys/net/link_layer/Kconfig.lorawan +++ b/sys/net/link_layer/Kconfig.lorawan @@ -89,7 +89,7 @@ config LORAMAC_DEFAULT_JOIN_PROCEDURE_ABP endchoice -if USEMODULE_GNRC_LORAWAN || USEPKG_SEMTECH_LORAMAC +if (USEMODULE_GNRC_LORAWAN || USEPKG_SEMTECH_LORAMAC) menu "OTAA credentials" @@ -99,17 +99,37 @@ config LORAMAC_DEV_EUI_DEFAULT help Configure the default device EUI for join procedure. +config LORAMAC_APP_KEY_DEFAULT + string "Application key" + default "00000000000000000000000000000000" + help + Configure the default application key for join procedure. + +if USEMODULE_GNRC_LORAWAN_1_1 + +config LORAMAC_JOIN_EUI_DEFAULT + string "Join EUI" + default "0000000000000000" + help + Configure the default join EUI for join procedure. + +config LORAMAC_NWK_KEY_DEFAULT + string "Network key" + default "00000000000000000000000000000000" + help + Configure the default network key for join procedure. + +endif # GNRC_LORAWAN_1_1 + +if !USEMODULE_GNRC_LORAWAN_1_1 + config LORAMAC_APP_EUI_DEFAULT string "Application EUI" default "0000000000000000" help Configure the default application EUI for join procedure. -config LORAMAC_APP_KEY_DEFAULT - string "Application key" - default "00000000000000000000000000000000" - help - Configure the default application key for join procedure. +endif # !USEMODULE_GNRC_LORAWAN_1_1 endmenu # OTAA credentials @@ -125,18 +145,50 @@ config LORAMAC_DEV_ADDR_DEFAULT help Configure the default device address for ABP. -config LORAMAC_NWK_SKEY_DEFAULT - string "Network session key" - default "00000000000000000000000000000000" - help - Configure the default network session key for ABP. - config LORAMAC_APP_SKEY_DEFAULT string "Application session key" default "00000000000000000000000000000000" help Configure the default application session key for ABP. +if USEMODULE_GNRC_LORAWAN_1_1 + +config LORAMAC_FNWKSINT_KEY_DEFAULT + string "Forwarding network session integrity key" + default "00000000000000000000000000000000" + help + Configure the default forwarding network session integrity key for ABP. + +config LORAMAC_SNWKSINT_KEY_DEFAULT + string "Serving network session integrity key" + default "00000000000000000000000000000000" + help + Configure the default serving network session integrity key for ABP. + +config LORAMAC_NWKSENC_KEY_DEFAULT + string "Network session encryption key" + default "00000000000000000000000000000000" + help + Configure the default network session encryption key for ABP. + +config LORAMAC_NWK_KEY_DEFAULT + string "Network key" + default "00000000000000000000000000000000" + help + Configure the default network key for ABP. + +endif # USEMODULE_GNRC_LORAWAN_1_1 + +if !USEMODULE_GNRC_LORAWAN_1_1 + + config LORAMAC_NWK_SKEY_DEFAULT + string "Network session key" + default "00000000000000000000000000000000" + help + Configure the default network session key for ABP. + +endif # !USEMODULE_GNRC_LORAWAN_1_1 + endmenu # ABP credentials endif # USEMODULE_GNRC_LORAWAN diff --git a/sys/shell/cmds/gnrc_netif.c b/sys/shell/cmds/gnrc_netif.c index 94e28ccca8..f18438d081 100644 --- a/sys/shell/cmds/gnrc_netif.c +++ b/sys/shell/cmds/gnrc_netif.c @@ -217,13 +217,21 @@ static void _set_usage(char *cmd_name) " * \"bw\" - alias for channel bandwidth\n" " * \"sf\" - alias for spreading factor\n" " * \"cr\" - alias for coding rate\n" - " * \"appeui\" - sets Application EUI\n" " * \"appkey\" - sets Application key\n" " * \"appskey\" - sets Application session key\n" +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) + " * \"joineui\" - sets Join EUI\n" + " * \"nwkkey\" - sets Network key\n" + " * \"nwksenckey\" - sets Network session encryption key\n" + " * \"snwksintkey\" - sets Serving network session integrity key\n" + " * \"fnwksintkey\" - sets Forwarding network session integrity key\n" +#else + " * \"appeui\" - sets Application EUI\n" + " * \"nwkskey\" - sets Network Session Key\n" +#endif " * \"deveui\" - sets Device EUI\n" " * \"dr\" - sets datarate\n" " * \"rx2_dr\" - sets datarate of RX2 (lorawan)\n" - " * \"nwkskey\" - sets Network Session Key\n" #endif #ifdef MODULE_NETDEV_IEEE802154_MULTIMODE " * \"phy_mode\" - select PHY mode\n" @@ -300,13 +308,34 @@ static void _print_netopt(netopt_t opt) printf("AppSKey"); break; - case NETOPT_LORAWAN_NWKSKEY: - printf("NwkSKey"); +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) + case NETOPT_LORAWAN_JOINEUI: + printf("JoinEUI"); break; + case NETOPT_LORAWAN_NWKKEY: + printf("NwkKey"); + break; + + case NETOPT_LORAWAN_NWKSENCKEY: + printf("NwkSEncKey"); + break; + + case NETOPT_LORAWAN_SNWKSINTKEY: + printf("SNwkSIntKey"); + break; + + case NETOPT_LORAWAN_FNWKSINTKEY: + printf("FNwkSIntKey"); + break; +#else case NETOPT_LORAWAN_APPEUI: printf("AppEUI"); break; + case NETOPT_LORAWAN_NWKSKEY: + printf("NwkSKey"); + break; +#endif /* IS_USED(MODULE_GNRC_LORAWAN_1_1) */ case NETOPT_SRC_LEN: printf("source address length"); @@ -1228,6 +1257,10 @@ static int _netif_set_lw_key(netif_t *iface, netopt_t opt, char *key_str) case NETOPT_LORAWAN_APPKEY: case NETOPT_LORAWAN_APPSKEY: case NETOPT_LORAWAN_NWKSKEY: + case NETOPT_LORAWAN_NWKKEY: + case NETOPT_LORAWAN_SNWKSINTKEY: + case NETOPT_LORAWAN_FNWKSINTKEY: + case NETOPT_LORAWAN_NWKSENCKEY: /* All keys have the same length as the APP KEY */ expected_len = LORAMAC_APPKEY_LEN; break; @@ -1472,21 +1505,40 @@ static int _netif_set(char *cmd_name, netif_t *iface, char *key, char *value) else if ((strcmp("coding_rate", key) == 0) || (strcmp("cr", key) == 0)) { return _netif_set_coding_rate(iface, value); } +#if IS_USED(MODULE_GNRC_LORAWAN_1_1) + else if (strcmp("joineui", key) == 0) { + return _netif_set_lw_key(iface, NETOPT_LORAWAN_JOINEUI, value); + } + else if (strcmp("fnwksintkey", key) == 0) { + return _netif_set_lw_key(iface, NETOPT_LORAWAN_FNWKSINTKEY, value); + } + else if (strcmp("snwksintkey", key) == 0) { + return _netif_set_lw_key(iface, NETOPT_LORAWAN_SNWKSINTKEY, value); + } + else if (strcmp("nwksenckey", key) == 0) { + return _netif_set_lw_key(iface, NETOPT_LORAWAN_NWKSENCKEY, value); + } + else if (strcmp("nwkkey", key) == 0) { + return _netif_set_lw_key(iface, NETOPT_LORAWAN_NWKKEY, value); + } +#else else if (strcmp("appeui", key) == 0) { return _netif_set_lw_key(iface, NETOPT_LORAWAN_APPEUI, value); } + else if (strcmp("nwkskey", key) == 0) { + return _netif_set_addr(iface, NETOPT_LORAWAN_NWKSKEY, value); + } +#endif /* IS_USED(MODULE_GNRC_LORAWAN_1_1) */ + else if (strcmp("appskey", key) == 0) { + return _netif_set_addr(iface, NETOPT_LORAWAN_APPSKEY, value); + } else if (strcmp("appkey", key) == 0) { return _netif_set_lw_key(iface, NETOPT_LORAWAN_APPKEY, value); } else if (strcmp("deveui", key) == 0) { return _netif_set_addr(iface, NETOPT_ADDRESS_LONG, value); } - else if (strcmp("appskey", key) == 0) { - return _netif_set_addr(iface, NETOPT_LORAWAN_APPSKEY, value); - } - else if (strcmp("nwkskey", key) == 0) { - return _netif_set_addr(iface, NETOPT_LORAWAN_NWKSKEY, value); - } + else if (strcmp("dr", key) == 0) { return _netif_set_u8(iface, NETOPT_LORAWAN_DR, 0, value); } diff --git a/tests/gnrc_lorawan/main.c b/tests/gnrc_lorawan/main.c index b2fd5b1948..2af6ebce60 100644 --- a/tests/gnrc_lorawan/main.c +++ b/tests/gnrc_lorawan/main.c @@ -70,7 +70,12 @@ static void test_gnrc_lorawan__validate_mic(void) /* Uplink packet */ le_uint32_t calc_mic; - gnrc_lorawan_calculate_mic(&dev_addr, fcnt, 0, &pkt, nwkskey, &calc_mic); + gnrc_lorawan_t mac = {0}; + memcpy(&mac.dev_addr, &dev_addr, sizeof(dev_addr)); + mac.ctx.fnwksintkey = nwkskey; + mac.mcps.fcnt = fcnt; + + gnrc_lorawan_calculate_mic_uplink(&pkt, 0x00, &mac, &calc_mic); TEST_ASSERT(memcmp(&calc_mic, mic, sizeof(le_uint32_t)) == 0); } @@ -84,7 +89,12 @@ static void test_gnrc_lorawan__wrong_mic(void) /* Uplink packet */ le_uint32_t calc_mic; - gnrc_lorawan_calculate_mic(&dev_addr, fcnt, 0, &pkt, nwkskey, &calc_mic); + gnrc_lorawan_t mac = {0}; + memcpy(&mac.dev_addr, &dev_addr, sizeof(dev_addr)); + mac.ctx.fnwksintkey = nwkskey; + mac.mcps.fcnt = fcnt; + + gnrc_lorawan_calculate_mic_uplink(&pkt, 0x00, &mac, &calc_mic); TEST_ASSERT(memcmp(&calc_mic, mic, sizeof(le_uint32_t)) != 0); }