[LoRaWAN] Implement full session persistence & more v1.1 specification (#835)
* Implement session persistence & more 1.1 protocol * [LoRaW] Improve session persistence, check frame counters & Nonces, multiple MAC commands * [LoRaWAN] fix popping MAC command from queue I just realized that the method popMacCommand did not correctly remove items from the queue - this should solve the problem * [LoRaWAN] implement improvements from #835 * [LoRaWAN] String --> uint8_t[] * [LoRaWAN] Fix typo
This commit is contained in:
parent
29c891e017
commit
556f37f608
6 changed files with 404 additions and 82 deletions
|
@ -291,8 +291,9 @@ setModem KEYWORD2
|
|||
|
||||
# LoRaWAN
|
||||
wipe KEYWORD2
|
||||
restoreOTAA KEYWORD2
|
||||
beginOTAA KEYWORD2
|
||||
beginAPB KEYWORD2
|
||||
beginABP KEYWORD2
|
||||
uplink KEYWORD2
|
||||
downlink KEYWORD2
|
||||
configureChannel KEYWORD2
|
||||
|
@ -401,5 +402,10 @@ RADIOLIB_ERR_INVALID_PORT LITERAL1
|
|||
RADIOLIB_ERR_NO_RX_WINDOW LITERAL1
|
||||
RADIOLIB_ERR_INVALID_CHANNEL LITERAL1
|
||||
RADIOLIB_ERR_INVALID_CID LITERAL1
|
||||
RADIOLIB_ERR_UPLINK_UNAVAILABLE LITERAL1
|
||||
RADIOLIB_ERR_COMMAND_QUEUE_FULL LITERAL1
|
||||
RADIOLIB_ERR_COMMAND_QUEUE_EMPTY LITERAL1
|
||||
RADIOLIB_ERR_COMMAND_QUEUE_ITEM_NOT_FOUND LITERAL1
|
||||
RADIOLIB_ERR_JOIN_NONCE_INVALID LITERAL1
|
||||
RADIOLIB_ERR_N_FCNT_DOWN_INVALID LITERAL1
|
||||
RADIOLIB_ERR_A_FCNT_DOWN_INVALID LITERAL1
|
|
@ -442,7 +442,7 @@
|
|||
|
||||
// the amount of space allocated to the persistent storage
|
||||
#if !defined(RADIOLIB_HAL_PERSISTENT_STORAGE_SIZE)
|
||||
#define RADIOLIB_HAL_PERSISTENT_STORAGE_SIZE (0x60)
|
||||
#define RADIOLIB_HAL_PERSISTENT_STORAGE_SIZE (0xD0)
|
||||
#endif
|
||||
|
||||
// This only compiles on STM32 boards with SUBGHZ module, but also
|
||||
|
|
48
src/Hal.h
48
src/Hal.h
|
@ -7,25 +7,47 @@
|
|||
#include "BuildOpt.h"
|
||||
|
||||
// list of persistent parameters
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID (0)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_ADDR_ID (1)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID (2)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID (3)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_APP_S_KEY_ID (4)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_FNWK_SINT_KEY_ID (5)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_SNWK_SINT_KEY_ID (6)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_NWK_SENC_KEY_ID (7)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION_ID (0) // this is NOT the LoRaWAN version, but version of this table
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID (1)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_ADDR_ID (2)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_APP_S_KEY_ID (3)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_FNWK_SINT_KEY_ID (4)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_SNWK_SINT_KEY_ID (5)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_NWK_SENC_KEY_ID (6)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID (7)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_JOIN_NONCE_ID (8)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_HOME_NET_ID (9)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_DL_SETTINGS_ID (10)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_CF_LIST_ID (11)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX_DELAY_ID (12)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID (13)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_A_FCNT_DOWN_ID (14)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_N_FCNT_DOWN_ID (15)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_CONF_FCNT_ID (16)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_ID (17)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_FOPTS_ID (18)
|
||||
|
||||
static const uint32_t RadioLibPersistentParamTable[] = {
|
||||
0x00, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID
|
||||
0x04, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_ADDR_ID
|
||||
0x08, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID
|
||||
0x0C, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID
|
||||
0x00, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION
|
||||
0x08, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID
|
||||
0x0C, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_ADDR_ID
|
||||
0x10, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_APP_S_KEY_ID
|
||||
0x20, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_FNWK_SINT_KEY_ID
|
||||
0x30, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_SNWK_SINT_KEY_ID
|
||||
0x40, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_NWK_SENC_KEY_ID
|
||||
0x50, // end
|
||||
0x50, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID
|
||||
0x54, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_JOIN_NONCE_ID
|
||||
0x58, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_HOME_NET_ID
|
||||
0x5C, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_DL_SETTINGS_ID
|
||||
0x60, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_CF_LIST
|
||||
0x70, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX_DELAY_ID
|
||||
0x74, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID
|
||||
0x78, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_AFCNT_DOWN_ID
|
||||
0x7C, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_NFCNT_DOWN_ID
|
||||
0x80, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_CONF_FCNT_ID
|
||||
0x84, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_ID
|
||||
0x88, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_FOPTS_ID
|
||||
0xD0, // end
|
||||
};
|
||||
|
||||
/*!
|
||||
|
|
|
@ -533,6 +533,26 @@
|
|||
*/
|
||||
#define RADIOLIB_ERR_COMMAND_QUEUE_EMPTY (-1110)
|
||||
|
||||
/*!
|
||||
\brief Unable to delete MAC command because it was not found in the queue.
|
||||
*/
|
||||
#define RADIOLIB_ERR_COMMAND_QUEUE_ITEM_NOT_FOUND (-1111)
|
||||
|
||||
/*!
|
||||
\brief Unable to join network because JoinNonce is not higher than saved value.
|
||||
*/
|
||||
#define RADIOLIB_ERR_JOIN_NONCE_INVALID (-1112)
|
||||
|
||||
/*!
|
||||
\brief Received downlink Network frame counter is invalid (lower than last heard value).
|
||||
*/
|
||||
#define RADIOLIB_ERR_N_FCNT_DOWN_INVALID (-1113)
|
||||
|
||||
/*!
|
||||
\brief Received downlink Application frame counter is invalid (lower than last heard value).
|
||||
*/
|
||||
#define RADIOLIB_ERR_A_FCNT_DOWN_INVALID (-1114)
|
||||
|
||||
/*!
|
||||
\}
|
||||
*/
|
||||
|
|
|
@ -44,7 +44,7 @@ void LoRaWANNode::wipe() {
|
|||
mod->hal->wipePersistentStorage();
|
||||
}
|
||||
|
||||
int16_t LoRaWANNode::begin() {
|
||||
int16_t LoRaWANNode::restoreOTAA() {
|
||||
int16_t state = this->setPhyProperties();
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
|
@ -55,12 +55,99 @@ int16_t LoRaWANNode::begin() {
|
|||
return(RADIOLIB_ERR_NETWORK_NOT_JOINED);
|
||||
}
|
||||
|
||||
// in case of future revisions of NVM, use a version parameter to allow transitioning from one version to another while keeping session alive
|
||||
uint16_t nvm_table_version = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION_ID);
|
||||
// if (RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION > nvm_table_version) {
|
||||
// // set default values for variables that are new or something
|
||||
// }
|
||||
(void)nvm_table_version;
|
||||
|
||||
// pull all needed information from persistent storage
|
||||
this->devAddr = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_ADDR_ID);
|
||||
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_APP_S_KEY_ID), this->appSKey, RADIOLIB_AES128_BLOCK_SIZE);
|
||||
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FNWK_SINT_KEY_ID), this->fNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE);
|
||||
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_SNWK_SINT_KEY_ID), this->sNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE);
|
||||
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_NWK_SENC_KEY_ID), this->nwkSEncKey, RADIOLIB_AES128_BLOCK_SIZE);
|
||||
RADIOLIB_DEBUG_PRINTLN("appSKey:");
|
||||
RADIOLIB_DEBUG_HEXDUMP(this->appSKey, RADIOLIB_AES128_BLOCK_SIZE);
|
||||
|
||||
uint32_t dlSettings = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DL_SETTINGS_ID);
|
||||
this->rev = (dlSettings & RADIOLIB_LORAWAN_JOIN_ACCEPT_R_1_1) >> 7;
|
||||
uint8_t rx1DrOffset = (dlSettings & 0x70) >> 4;
|
||||
uint8_t rx2DataRate = dlSettings & 0x0F;
|
||||
RADIOLIB_DEBUG_PRINTLN("LoRaWAN revision: %d", this->rev);
|
||||
|
||||
// TODO process the RX2 data rate
|
||||
(void)rx2DataRate;
|
||||
|
||||
// TODO process the data rate offset
|
||||
(void)rx1DrOffset;
|
||||
|
||||
// parse Rx1 delay (and subsequently Rx2)
|
||||
this->rxDelays[0] = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX_DELAY_ID);
|
||||
if(this->rxDelays[0] == 0) {
|
||||
this->rxDelays[0] = RADIOLIB_LORAWAN_RECEIVE_DELAY_1_MS;
|
||||
}
|
||||
this->rxDelays[1] = this->rxDelays[0] + 1000;
|
||||
|
||||
// process CFlist if any bit is non-zero
|
||||
uint8_t cfList[RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN] = { 0 };
|
||||
uint8_t allZero[RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN] = { 0 };
|
||||
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_CF_LIST_ID), cfList, RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN);
|
||||
RADIOLIB_DEBUG_PRINTLN("cfList:");
|
||||
RADIOLIB_DEBUG_HEXDUMP(cfList, RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN);
|
||||
if(memcmp(cfList, allZero, RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN)) {
|
||||
if(this->band->cfListType == RADIOLIB_LORAWAN_CFLIST_TYPE_FREQUENCIES) {
|
||||
// list of frequencies
|
||||
for(uint8_t i = 0; i < 5; i++) {
|
||||
uint32_t freq = LoRaWANNode::ntoh<uint32_t>(&cfList[3*i], 3);
|
||||
availableChannelsFreq[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i] = (float)freq/10000.0;
|
||||
availableChannelsFreq[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i] = availableChannelsFreq[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i];
|
||||
RADIOLIB_DEBUG_PRINTLN("Channel UL/DL %d frequency = %f MHz", i, availableChannelsFreq[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i]);
|
||||
}
|
||||
|
||||
} else {
|
||||
// frequency mask, we need to find out which frequencies are actually being used
|
||||
uint8_t channelId = 0;
|
||||
uint8_t chSpan = 0;
|
||||
uint8_t chNum = 0;
|
||||
for(uint8_t i = 0; i < 5; i++) {
|
||||
uint16_t mask = LoRaWANNode::ntoh<uint16_t>(&cfList[2*i]);
|
||||
RADIOLIB_DEBUG_PRINTLN("mask[%d] = 0x%04x", i, mask);
|
||||
for(uint8_t j = 0; j < 16; j++) {
|
||||
if(chNum >= this->band->defaultChannels[chSpan].numChannels) {
|
||||
chNum -= this->band->defaultChannels[chSpan].numChannels;
|
||||
chSpan++;
|
||||
|
||||
if(chSpan >= this->band->numChannelSpans) {
|
||||
RADIOLIB_DEBUG_PRINTLN("channel bitmask overrun!");
|
||||
return(RADIOLIB_ERR_UNKNOWN);
|
||||
}
|
||||
}
|
||||
|
||||
if(mask & (1UL << j)) {
|
||||
RADIOLIB_DEBUG_PRINTLN("chNum = %d, chSpan = %d", chNum, chSpan);
|
||||
uint8_t dir = this->band->defaultChannels[chSpan].direction;
|
||||
float freq = this->band->defaultChannels[chSpan].freqStart + chNum*this->band->defaultChannels[chSpan].freqStep;
|
||||
availableChannelsFreq[dir][channelId] = freq;
|
||||
RADIOLIB_DEBUG_PRINTLN("Channel %cL %d frequency = %f MHz", dir ? 'U': 'D', channelId, availableChannelsFreq[dir][channelId]);
|
||||
channelId++;
|
||||
}
|
||||
|
||||
chNum++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t queueBuff[sizeof(LoRaWANMacCommandQueue_t)] = { 0 };
|
||||
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FOPTS_ID), queueBuff, sizeof(LoRaWANMacCommandQueue_t));
|
||||
memcpy(&queueBuff, &this->commandsUp, sizeof(LoRaWANMacCommandQueue_t));
|
||||
|
||||
state = this->setupChannels();
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
|
@ -69,7 +156,7 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
|||
Module* mod = this->phyLayer->getMod();
|
||||
if(!force && (mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID) == RADIOLIB_LORAWAN_MAGIC)) {
|
||||
// the device has joined already, we can just pull the data from persistent storage
|
||||
return(this->begin());
|
||||
return(this->restoreOTAA());
|
||||
}
|
||||
|
||||
// set the physical layer configuration
|
||||
|
@ -175,9 +262,23 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
|||
RADIOLIB_DEBUG_PRINTLN("joinAcceptMsg:");
|
||||
RADIOLIB_DEBUG_HEXDUMP(joinAcceptMsg, lenRx);
|
||||
|
||||
// get current JoinNonce from downlink and previous JoinNonce from NVM
|
||||
uint32_t joinNonce = LoRaWANNode::ntoh<uint32_t>(&joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_JOIN_NONCE_POS], 3);
|
||||
uint32_t joinNoncePrev = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_JOIN_NONCE_ID);
|
||||
RADIOLIB_DEBUG_PRINTLN("JoinNoncePrev: %d, JoinNonce: %d", joinNoncePrev, joinNonce);
|
||||
|
||||
// JoinNonce received must be greater than the last JoinNonce heard, else error
|
||||
if(joinNonce <= joinNoncePrev) {
|
||||
return(RADIOLIB_ERR_JOIN_NONCE_INVALID);
|
||||
}
|
||||
|
||||
// check LoRaWAN revision (the MIC verification depends on this)
|
||||
uint8_t dlSettings = joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_DL_SETTINGS_POS];
|
||||
if(dlSettings & RADIOLIB_LORAWAN_JOIN_ACCEPT_R_1_1) {
|
||||
this->rev = (dlSettings & RADIOLIB_LORAWAN_JOIN_ACCEPT_R_1_1) >> 7;
|
||||
RADIOLIB_DEBUG_PRINTLN("LoRaWAN revision: 1.%d", this->rev);
|
||||
|
||||
// verify MIC
|
||||
if(this->rev == 1) {
|
||||
// 1.1 version, first we need to derive the join accept integrity key
|
||||
uint8_t keyDerivationBuff[RADIOLIB_AES128_BLOCK_SIZE] = { 0 };
|
||||
keyDerivationBuff[0] = RADIOLIB_LORAWAN_JOIN_ACCEPT_JS_INT_KEY;
|
||||
|
@ -203,11 +304,20 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
|||
}
|
||||
|
||||
}
|
||||
uint8_t rx1DrOffset = (dlSettings & 0x70) >> 4;
|
||||
uint8_t rx2DataRate = dlSettings & 0x0F;
|
||||
|
||||
// TODO process the RX2 data rate
|
||||
(void)rx2DataRate;
|
||||
|
||||
// parse the contents
|
||||
uint32_t joinNonce = LoRaWANNode::ntoh<uint32_t>(&joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_JOIN_NONCE_POS], 3);
|
||||
// TODO process the data rate offset
|
||||
(void)rx1DrOffset;
|
||||
|
||||
// parse other contents
|
||||
uint32_t homeNetId = LoRaWANNode::ntoh<uint32_t>(&joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_HOME_NET_ID_POS], 3);
|
||||
this->devAddr = LoRaWANNode::ntoh<uint32_t>(&joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_DEV_ADDR_POS]);
|
||||
|
||||
// parse Rx1 delay (and subsequently Rx2)
|
||||
this->rxDelays[0] = joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_RX_DELAY_POS]*1000;
|
||||
if(this->rxDelays[0] == 0) {
|
||||
this->rxDelays[0] = RADIOLIB_LORAWAN_RECEIVE_DELAY_1_MS;
|
||||
|
@ -215,7 +325,9 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
|||
this->rxDelays[1] = this->rxDelays[0] + 1000;
|
||||
|
||||
// process CFlist if present
|
||||
uint8_t cfList[RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN] = { 0 };
|
||||
if(lenRx == RADIOLIB_LORAWAN_JOIN_ACCEPT_MAX_LEN) {
|
||||
memcpy(&cfList[0], &joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_POS], RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN);
|
||||
if(this->band->cfListType == RADIOLIB_LORAWAN_CFLIST_TYPE_FREQUENCIES) {
|
||||
// list of frequencies
|
||||
for(uint8_t i = 0; i < 5; i++) {
|
||||
|
@ -262,15 +374,14 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// prepare buffer for key derivation
|
||||
uint8_t keyDerivationBuff[RADIOLIB_AES128_BLOCK_SIZE] = { 0 };
|
||||
LoRaWANNode::hton<uint32_t>(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_JOIN_NONCE_POS], joinNonce, 3);
|
||||
|
||||
// check protocol version (1.0 vs 1.1)
|
||||
if(dlSettings & RADIOLIB_LORAWAN_JOIN_ACCEPT_R_1_1) {
|
||||
if(this->rev == 1) {
|
||||
// 1.1 version, derive the keys
|
||||
LoRaWANNode::hton<uint64_t>(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_JOIN_EUI_POS], joinEUI);
|
||||
LoRaWANNode::hton<uint16_t>(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_DEV_NONCE_POS], devNonce);
|
||||
|
@ -292,7 +403,6 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
|||
RadioLibAES128Instance.encryptECB(keyDerivationBuff, RADIOLIB_AES128_BLOCK_SIZE, this->nwkSEncKey);
|
||||
|
||||
// enqueue the RekeyInd MAC command to be sent in the next uplink
|
||||
this->rev = 1;
|
||||
LoRaWANMacCommand_t cmd = {
|
||||
.cid = RADIOLIB_LORAWAN_MAC_CMD_REKEY,
|
||||
.len = sizeof(uint8_t),
|
||||
|
@ -304,7 +414,6 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
|||
|
||||
} else {
|
||||
// 1.0 version, just derive the keys
|
||||
this->rev = 0;
|
||||
LoRaWANNode::hton<uint32_t>(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_HOME_NET_ID_POS], homeNetId, 3);
|
||||
LoRaWANNode::hton<uint16_t>(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_DEV_ADDR_POS], devNonce);
|
||||
keyDerivationBuff[0] = RADIOLIB_LORAWAN_JOIN_ACCEPT_APP_S_KEY;
|
||||
|
@ -329,13 +438,27 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
|||
mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_SNWK_SINT_KEY_ID), this->sNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE);
|
||||
mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_NWK_SENC_KEY_ID), this->nwkSEncKey, RADIOLIB_AES128_BLOCK_SIZE);
|
||||
|
||||
// save uplink parameters
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_JOIN_NONCE_ID, joinNonce);
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_HOME_NET_ID, homeNetId);
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX_DELAY_ID, this->rxDelays[0]);
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DL_SETTINGS_ID, (uint32_t)dlSettings);
|
||||
|
||||
// save cfList (all 0 if none is present)
|
||||
mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_CF_LIST_ID), cfList, RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN);
|
||||
|
||||
// all complete, reset device counters and set the magic number
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID, 0);
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_N_FCNT_DOWN_ID, 0);
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_A_FCNT_DOWN_ID, 0);
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID, RADIOLIB_LORAWAN_MAGIC);
|
||||
|
||||
// everything written to NVM, write current version to NVM
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION_ID, RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION);
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
int16_t LoRaWANNode::beginAPB(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, uint8_t* fNwkSIntKey, uint8_t* sNwkSIntKey) {
|
||||
int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, uint8_t* fNwkSIntKey, uint8_t* sNwkSIntKey) {
|
||||
this->devAddr = addr;
|
||||
memcpy(this->appSKey, appSKey, RADIOLIB_AES128_KEY_SIZE);
|
||||
memcpy(this->nwkSEncKey, nwkSKey, RADIOLIB_AES128_KEY_SIZE);
|
||||
|
@ -355,7 +478,13 @@ int16_t LoRaWANNode::beginAPB(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey,
|
|||
|
||||
// setup uplink/downlink frequencies and datarates
|
||||
state = this->setupChannels();
|
||||
return(state);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// everything written to NVM, write current version to NVM
|
||||
Module* mod = this->phyLayer->getMod();
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION_ID, RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION);
|
||||
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
#if defined(RADIOLIB_BUILD_ARDUINO)
|
||||
|
@ -373,12 +502,24 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port) {
|
|||
if(port > 0xDF) {
|
||||
return(RADIOLIB_ERR_INVALID_PORT);
|
||||
}
|
||||
// port 0 is only allowed for MAC-only payloads
|
||||
if(port == RADIOLIB_LORAWAN_FPORT_MAC_COMMAND) {
|
||||
if (!isMACPayload) {
|
||||
return(RADIOLIB_ERR_INVALID_PORT);
|
||||
}
|
||||
// if this is MAC only payload, continue and reset for next uplink
|
||||
isMACPayload = false;
|
||||
}
|
||||
|
||||
Module* mod = this->phyLayer->getMod();
|
||||
|
||||
// check if there are some MAC commands to piggyback
|
||||
size_t foptsLen = 0;
|
||||
if(this->commandsUp.numCommands > 0) {
|
||||
// check if there are some MAC commands to piggyback (only when piggybacking onto a application-frame)
|
||||
uint8_t foptsLen = 0;
|
||||
size_t foptsBufSize = 0;
|
||||
if(this->commandsUp.numCommands > 0 && port != RADIOLIB_LORAWAN_FPORT_MAC_COMMAND) {
|
||||
// there are, assume the maximum possible FOpts len for buffer allocation
|
||||
foptsLen = 15;
|
||||
foptsLen = this->commandsUp.len;
|
||||
foptsBufSize = 15;
|
||||
}
|
||||
|
||||
// check maximum payload len as defined in phy
|
||||
|
@ -392,7 +533,6 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port) {
|
|||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// check if sufficient time has elapsed since the last uplink
|
||||
Module* mod = this->phyLayer->getMod();
|
||||
if(mod->hal->millis() - this->rxDelayStart < rxDelays[1]) {
|
||||
// not enough time elapsed since the last uplink, we may still be in an RX window
|
||||
return(RADIOLIB_ERR_UPLINK_UNAVAILABLE);
|
||||
|
@ -400,7 +540,7 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port) {
|
|||
|
||||
// build the uplink message
|
||||
// the first 16 bytes are reserved for MIC calculation blocks
|
||||
size_t uplinkMsgLen = RADIOLIB_LORAWAN_FRAME_LEN(len, foptsLen);
|
||||
size_t uplinkMsgLen = RADIOLIB_LORAWAN_FRAME_LEN(len, foptsBufSize);
|
||||
#if defined(RADIOLIB_STATIC_ONLY)
|
||||
uint8_t uplinkMsg[RADIOLIB_STATIC_ARRAY_SIZE];
|
||||
#else
|
||||
|
@ -420,22 +560,37 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port) {
|
|||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID, fcnt);
|
||||
LoRaWANNode::hton<uint16_t>(&uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCNT_POS], (uint16_t)fcnt);
|
||||
|
||||
// check if we have some MAC command to append
|
||||
// TODO implement appending multiple MAC commands
|
||||
LoRaWANMacCommand_t cmd = { .cid = 0, .len = 0, .payload = { 0 }, .repeat = 0, };
|
||||
if(popMacCommand(&cmd, &this->commandsUp) == RADIOLIB_ERR_NONE) {
|
||||
// we do, add it to fopts
|
||||
uint8_t foptsBuff[RADIOLIB_AES128_BLOCK_SIZE];
|
||||
foptsBuff[0] = cmd.cid;
|
||||
for(size_t i = 1; i < cmd.len; i++) {
|
||||
foptsBuff[i] = cmd.payload[i];
|
||||
// check if we have some MAC commands to append
|
||||
if(foptsLen > 0) {
|
||||
uint8_t foptsNum = this->commandsUp.numCommands;
|
||||
uint8_t foptsBuff[foptsBufSize];
|
||||
size_t idx = 0;
|
||||
for (size_t i = 0; i < foptsNum; i++) {
|
||||
LoRaWANMacCommand_t cmd = { .cid = 0, .len = 0, .payload = { 0 }, .repeat = 0, };
|
||||
popMacCommand(&cmd, &this->commandsUp, i);
|
||||
if (cmd.cid == 0) {
|
||||
break;
|
||||
}
|
||||
foptsBuff[idx] = cmd.cid;
|
||||
for(size_t i = 1; i < cmd.len; i++) {
|
||||
foptsBuff[idx + i] = cmd.payload[i];
|
||||
}
|
||||
idx += cmd.len + 1;
|
||||
}
|
||||
foptsLen = 1 + cmd.len;
|
||||
|
||||
RADIOLIB_DEBUG_PRINTLN("Uplink MAC payload (%d commands):", foptsNum);
|
||||
RADIOLIB_DEBUG_HEXDUMP(foptsBuff, foptsBufSize);
|
||||
|
||||
uplinkMsgLen = RADIOLIB_LORAWAN_FRAME_LEN(len, foptsLen);
|
||||
uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] |= foptsLen;
|
||||
|
||||
// encrypt it
|
||||
processAES(foptsBuff, foptsLen, this->nwkSEncKey, &uplinkMsg[RADIOLIB_LORAWAN_FHDR_FOPTS_POS], fcnt, RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK, 0x01, true);
|
||||
|
||||
// write the current MAC command queue to nvm for next uplink
|
||||
uint8_t queueBuff[sizeof(LoRaWANMacCommandQueue_t)];
|
||||
memcpy(&queueBuff, &this->commandsUp, sizeof(LoRaWANMacCommandQueue_t));
|
||||
mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FOPTS_ID), queueBuff, sizeof(LoRaWANMacCommandQueue_t));
|
||||
}
|
||||
|
||||
// set the port
|
||||
|
@ -448,6 +603,7 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port) {
|
|||
}
|
||||
|
||||
// encrypt the frame payload
|
||||
// TODO check ctrId --> erratum says it should be 0x01?
|
||||
processAES(data, len, encKey, &uplinkMsg[RADIOLIB_LORAWAN_FRAME_PAYLOAD_POS(foptsLen)], fcnt, RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK, 0x00, true);
|
||||
|
||||
// create blocks for MIC calculation
|
||||
|
@ -685,15 +841,6 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
|
|||
if(state == RADIOLIB_ERR_LORA_HEADER_DAMAGED) {
|
||||
state = RADIOLIB_ERR_NONE;
|
||||
}
|
||||
|
||||
// get the frame counter and set it to the MIC calculation block
|
||||
// TODO this will not handle overflow into 32-bits!
|
||||
// TODO cache the ADR bit?
|
||||
uint16_t fcnt = LoRaWANNode::ntoh<uint16_t>(&downlinkMsg[RADIOLIB_LORAWAN_FHDR_FCNT_POS]);
|
||||
LoRaWANNode::hton<uint16_t>(&downlinkMsg[RADIOLIB_LORAWAN_BLOCK_FCNT_POS], fcnt);
|
||||
|
||||
RADIOLIB_DEBUG_PRINTLN("downlinkMsg:");
|
||||
RADIOLIB_DEBUG_HEXDUMP(downlinkMsg, RADIOLIB_AES128_BLOCK_SIZE + downlinkMsgLen);
|
||||
|
||||
if(state != RADIOLIB_ERR_NONE) {
|
||||
#if !defined(RADIOLIB_STATIC_ONLY)
|
||||
|
@ -702,6 +849,55 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
|
|||
return(state);
|
||||
}
|
||||
|
||||
// get the frame counter and set it to the MIC calculation block
|
||||
// TODO cache the ADR bit?
|
||||
uint16_t fcnt16 = LoRaWANNode::ntoh<uint16_t>(&downlinkMsg[RADIOLIB_LORAWAN_FHDR_FCNT_POS]);
|
||||
LoRaWANNode::hton<uint16_t>(&downlinkMsg[RADIOLIB_LORAWAN_BLOCK_FCNT_POS], fcnt16);
|
||||
uint32_t fcnt32 = fcnt16; // calculate possible rollover once decided if this is network downlink or application downlink
|
||||
|
||||
RADIOLIB_DEBUG_PRINTLN("downlinkMsg:");
|
||||
RADIOLIB_DEBUG_HEXDUMP(downlinkMsg, RADIOLIB_AES128_BLOCK_SIZE + downlinkMsgLen);
|
||||
|
||||
// calculate length of FOpts and payload
|
||||
uint8_t foptsLen = downlinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] & RADIOLIB_LORAWAN_FHDR_FOPTS_LEN_MASK;
|
||||
int payLen = downlinkMsgLen - 8 - foptsLen - sizeof(uint32_t);
|
||||
|
||||
bool isAppDownlink = true;
|
||||
if (payLen <= 0 && this->rev == 1) { // no payload => MAC commands only => Network frame (LoRaWAN v1.1 only)
|
||||
isAppDownlink = false;
|
||||
}
|
||||
|
||||
// check the FcntDown value (Network or Application)
|
||||
uint32_t fcntDownPrev = 0;
|
||||
if (isAppDownlink) {
|
||||
fcntDownPrev = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_A_FCNT_DOWN_ID);
|
||||
} else {
|
||||
fcntDownPrev = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_N_FCNT_DOWN_ID);
|
||||
}
|
||||
|
||||
// assume a 16-bit to 32-bit rollover when difference in LSB is smaller than MAX_FCNT_GAP
|
||||
// if that isn't the case and the received fcnt is smaller or equal to the last heard fcnt, then error
|
||||
if ((fcnt16 <= fcntDownPrev) && ((0xFFFF - (uint16_t)fcntDownPrev + fcnt16) > RADIOLIB_LORAWAN_MAX_FCNT_GAP)) {
|
||||
#if !defined(RADIOLIB_STATIC_ONLY)
|
||||
delete[] downlinkMsg;
|
||||
#endif
|
||||
if (isAppDownlink) {
|
||||
return(RADIOLIB_ERR_A_FCNT_DOWN_INVALID);
|
||||
} else {
|
||||
return(RADIOLIB_ERR_N_FCNT_DOWN_INVALID);
|
||||
}
|
||||
} else if (fcnt16 <= fcntDownPrev) {
|
||||
uint16_t msb = (fcntDownPrev >> 16) + 1; // assume a rollover
|
||||
fcnt32 |= (msb << 16); // add back the MSB part
|
||||
}
|
||||
|
||||
// save current fcnt to NVM
|
||||
if (isAppDownlink) {
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_A_FCNT_DOWN_ID, fcnt32);
|
||||
} else {
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_N_FCNT_DOWN_ID, fcnt32);
|
||||
}
|
||||
|
||||
// check the MIC
|
||||
if(!verifyMIC(downlinkMsg, RADIOLIB_AES128_BLOCK_SIZE + downlinkMsgLen, this->sNwkSIntKey)) {
|
||||
#if !defined(RADIOLIB_STATIC_ONLY)
|
||||
|
@ -720,15 +916,14 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
|
|||
return(RADIOLIB_ERR_DOWNLINK_MALFORMED);
|
||||
}
|
||||
|
||||
// check fopts len
|
||||
uint8_t foptsLen = downlinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] & RADIOLIB_LORAWAN_FHDR_FOPTS_LEN_MASK;
|
||||
// process FOpts (if there are any)
|
||||
if(foptsLen > 0) {
|
||||
// there are some Fopts, decrypt them
|
||||
uint8_t fopts[RADIOLIB_LORAWAN_FHDR_FOPTS_LEN_MASK];
|
||||
|
||||
// according to the specification, the last two arguments should be 0x00 and false,
|
||||
// but that will fail even for LoRaWAN 1.1.0 server
|
||||
processAES(&downlinkMsg[RADIOLIB_LORAWAN_FHDR_FOPTS_POS], (size_t)foptsLen, this->nwkSEncKey, fopts, fcnt, RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK, 0x01, true);
|
||||
// TODO it COULD be the case that the assumed rollover is incorrect, if possible figure out a way to catch this and retry with just fcnt16
|
||||
uint8_t ctrId = 0x01 + isAppDownlink; // see LoRaWAN v1.1 errata
|
||||
processAES(&downlinkMsg[RADIOLIB_LORAWAN_FHDR_FOPTS_POS], (size_t)foptsLen, this->nwkSEncKey, fopts, fcnt32, RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK, ctrId, true);
|
||||
|
||||
RADIOLIB_DEBUG_PRINTLN("fopts:");
|
||||
RADIOLIB_DEBUG_HEXDUMP(fopts, foptsLen);
|
||||
|
@ -753,23 +948,59 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
|
|||
remLen -= processedLen;
|
||||
foptsPtr += processedLen;
|
||||
}
|
||||
|
||||
// if FOptsLen for the next uplink is larger than can be piggybacked onto an uplink, send separate uplink
|
||||
if(this->commandsUp.len > 15) {
|
||||
uint8_t foptsNum = this->commandsUp.numCommands;
|
||||
size_t foptsBufSize = this->commandsUp.len;
|
||||
uint8_t foptsBuff[foptsBufSize];
|
||||
size_t idx = 0;
|
||||
for(size_t i = 0; i < foptsNum; i++) {
|
||||
LoRaWANMacCommand_t cmd = { .cid = 0, .len = 0, .payload = { 0 }, .repeat = 0, };
|
||||
popMacCommand(&cmd, &this->commandsUp, i);
|
||||
if(cmd.cid == 0) {
|
||||
break;
|
||||
}
|
||||
foptsBuff[idx] = cmd.cid;
|
||||
for(size_t i = 1; i < cmd.len; i++) {
|
||||
foptsBuff[idx + i] = cmd.payload[i];
|
||||
}
|
||||
idx += cmd.len + 1;
|
||||
}
|
||||
RADIOLIB_DEBUG_PRINTLN("Uplink MAC payload (%d commands):", foptsNum);
|
||||
RADIOLIB_DEBUG_HEXDUMP(foptsBuff, foptsBufSize);
|
||||
|
||||
isMACPayload = true;
|
||||
this->uplink(foptsBuff, foptsBufSize, RADIOLIB_LORAWAN_FPORT_MAC_COMMAND);
|
||||
uint8_t strDown[this->band->payloadLenMax[this->dataRate[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK]]];
|
||||
size_t lenDown = 0;
|
||||
state = this->downlink(strDown, &lenDown);
|
||||
RADIOLIB_ASSERT(state);
|
||||
}
|
||||
|
||||
// write the MAC command queue to nvm for next uplink
|
||||
uint8_t queueBuff[sizeof(LoRaWANMacCommandQueue_t)];
|
||||
memcpy(&queueBuff, &this->commandsUp, sizeof(LoRaWANMacCommandQueue_t));
|
||||
mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FOPTS_ID), queueBuff, sizeof(LoRaWANMacCommandQueue_t));
|
||||
}
|
||||
|
||||
// fopts are processed or not present, check if there is payload
|
||||
int payLen = downlinkMsgLen - 8 - foptsLen - sizeof(uint32_t);
|
||||
// process payload (if there is any)
|
||||
if(payLen <= 0) {
|
||||
// no payload
|
||||
*len = 0;
|
||||
#if !defined(RADIOLIB_STATIC_ONLY)
|
||||
delete[] downlinkMsg;
|
||||
#endif
|
||||
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
// there is payload, and so there should be a port too
|
||||
// TODO pass the port?
|
||||
*len = payLen - 1;
|
||||
processAES(&downlinkMsg[RADIOLIB_LORAWAN_FHDR_FOPTS_POS], downlinkMsgLen, this->appSKey, data, fcnt, RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK, 0x00, true);
|
||||
// TODO it COULD be the case that the assumed rollover is incorrect, then figure out a way to catch this and retry with just fcnt16
|
||||
// TODO does the erratum hold here as well?
|
||||
processAES(&downlinkMsg[RADIOLIB_LORAWAN_FHDR_FOPTS_POS], downlinkMsgLen, this->appSKey, data, fcnt32, RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK, 0x00, true);
|
||||
|
||||
#if !defined(RADIOLIB_STATIC_ONLY)
|
||||
delete[] downlinkMsg;
|
||||
|
@ -1124,38 +1355,69 @@ int16_t LoRaWANNode::pushMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQ
|
|||
queue->commands[queue->numCommands - 1].payload[4],
|
||||
queue->commands[queue->numCommands - 1].repeat);*/
|
||||
queue->numCommands++;
|
||||
queue->len += 1 + cmd->len; // 1 byte for command ID, len bytes for payload
|
||||
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
int16_t LoRaWANNode::popMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQueue_t* queue, bool force) {
|
||||
int16_t LoRaWANNode::popMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQueue_t* queue, size_t index) {
|
||||
if(queue->numCommands == 0) {
|
||||
return(RADIOLIB_ERR_COMMAND_QUEUE_EMPTY);
|
||||
}
|
||||
|
||||
if(cmd) {
|
||||
/*RADIOLIB_DEBUG_PRINTLN("pop MAC CID = %02x, len = %d, payload = %02x %02x %02x %02x %02x, repeat = %d ",
|
||||
queue->commands[queue->numCommands - 1].cid,
|
||||
queue->commands[queue->numCommands - 1].len,
|
||||
queue->commands[queue->numCommands - 1].payload[0],
|
||||
queue->commands[queue->numCommands - 1].payload[1],
|
||||
queue->commands[queue->numCommands - 1].payload[2],
|
||||
queue->commands[queue->numCommands - 1].payload[3],
|
||||
queue->commands[queue->numCommands - 1].payload[4],
|
||||
queue->commands[queue->numCommands - 1].repeat);*/
|
||||
memcpy(cmd, &queue->commands[queue->numCommands - 1], sizeof(LoRaWANMacCommand_t));
|
||||
// RADIOLIB_DEBUG_PRINTLN("pop MAC CID = %02x, len = %d, payload = %02x %02x %02x %02x %02x, repeat = %d ",
|
||||
// queue->commands[index].cid,
|
||||
// queue->commands[index].len,
|
||||
// queue->commands[index].payload[0],
|
||||
// queue->commands[index].payload[1],
|
||||
// queue->commands[index].payload[2],
|
||||
// queue->commands[index].payload[3],
|
||||
// queue->commands[index].payload[4],
|
||||
// queue->commands[index].repeat);
|
||||
memcpy(cmd, &queue->commands[index], sizeof(LoRaWANMacCommand_t));
|
||||
}
|
||||
|
||||
if((!force) && (queue->commands[queue->numCommands - 1].repeat > 0)) {
|
||||
queue->commands[queue->numCommands - 1].repeat--;
|
||||
if(queue->commands[index].repeat > 0) {
|
||||
queue->commands[index].repeat--;
|
||||
} else {
|
||||
queue->commands[queue->numCommands - 1].repeat = 0;
|
||||
queue->numCommands--;
|
||||
deleteMacCommand(queue->commands[index].cid, queue);
|
||||
}
|
||||
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
int16_t LoRaWANNode::deleteMacCommand(uint8_t cid, LoRaWANMacCommandQueue_t* queue) {
|
||||
if(queue->numCommands == 0) {
|
||||
return(RADIOLIB_ERR_COMMAND_QUEUE_EMPTY);
|
||||
}
|
||||
|
||||
for(size_t index = 0; index < queue->numCommands; index++) {
|
||||
if(queue->commands[index].cid == cid) {
|
||||
// RADIOLIB_DEBUG_PRINTLN("delete MAC CID = %02x, len = %d, payload = %02x %02x %02x %02x %02x, repeat = %d ",
|
||||
// queue->commands[index].cid,
|
||||
// queue->commands[index].len,
|
||||
// queue->commands[index].payload[0],
|
||||
// queue->commands[index].payload[1],
|
||||
// queue->commands[index].payload[2],
|
||||
// queue->commands[index].payload[3],
|
||||
// queue->commands[index].payload[4],
|
||||
// queue->commands[index].repeat);
|
||||
queue->len -= (1 + queue->commands[index].len); // 1 byte for command ID, len for payload
|
||||
// move all subsequent commands one forward in the queue
|
||||
if(index < RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE - 1) {
|
||||
memmove(&queue->commands[index], &queue->commands[index + 1], (RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE - index) * sizeof(LoRaWANMacCommand_t));
|
||||
}
|
||||
// set the latest element to all 0
|
||||
memset(&queue->commands[RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE - 1], 0x00, sizeof(LoRaWANMacCommand_t));
|
||||
queue->numCommands--;
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
return(RADIOLIB_ERR_COMMAND_QUEUE_ITEM_NOT_FOUND);
|
||||
}
|
||||
|
||||
size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
||||
RADIOLIB_DEBUG_PRINTLN("exe MAC CID = %02x, len = %d", cmd->cid, cmd->len);
|
||||
|
||||
|
@ -1171,7 +1433,7 @@ size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
|||
RADIOLIB_DEBUG_PRINTLN("Server version: 1.%d", srvVersion);
|
||||
if(srvVersion == this->rev) {
|
||||
// valid server version, stop sending the ResetInd MAC command
|
||||
popMacCommand(NULL, &this->commandsUp, true);
|
||||
deleteMacCommand(RADIOLIB_LORAWAN_MAC_CMD_RESET, &this->commandsUp);
|
||||
}
|
||||
return(1);
|
||||
} break;
|
||||
|
@ -1378,7 +1640,7 @@ size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
|||
RADIOLIB_DEBUG_PRINTLN("Server version: 1.%d", srvVersion);
|
||||
if((srvVersion > 0) && (srvVersion <= this->rev)) {
|
||||
// valid server version, stop sending the ReKey MAC command
|
||||
popMacCommand(NULL, &this->commandsUp, true);
|
||||
deleteMacCommand(RADIOLIB_LORAWAN_MAC_CMD_REKEY, &this->commandsUp);
|
||||
}
|
||||
return(1);
|
||||
} break;
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
#include "../PhysicalLayer/PhysicalLayer.h"
|
||||
#include "../../utils/Cryptography.h"
|
||||
|
||||
// version of NVM table layout (NOT the LoRaWAN version)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION (0x01)
|
||||
|
||||
// preamble format
|
||||
#define RADIOLIB_LORAWAN_LORA_SYNC_WORD (0x34)
|
||||
#define RADIOLIB_LORAWAN_LORA_PREAMBLE_LEN (8)
|
||||
|
@ -257,7 +260,7 @@ struct LoRaWANMacCommand_t {
|
|||
uint8_t cid;
|
||||
|
||||
/*! \brief Length of the payload */
|
||||
size_t len;
|
||||
uint8_t len;
|
||||
|
||||
/*! \brief Payload buffer (5 bytes is the longest possible) */
|
||||
uint8_t payload[5];
|
||||
|
@ -269,6 +272,7 @@ struct LoRaWANMacCommand_t {
|
|||
struct LoRaWANMacCommandQueue_t {
|
||||
LoRaWANMacCommand_t commands[RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE];
|
||||
size_t numCommands;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -306,10 +310,10 @@ class LoRaWANNode {
|
|||
void wipe();
|
||||
|
||||
/*!
|
||||
\brief Join network by loading information from persistent storage.
|
||||
\brief Restore OTAA session by loading information from persistent storage.
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t begin();
|
||||
int16_t restoreOTAA();
|
||||
|
||||
/*!
|
||||
\brief Join network by performing over-the-air activation. By this procedure,
|
||||
|
@ -333,7 +337,7 @@ class LoRaWANNode {
|
|||
\param sNwkSIntKey Pointer to the network session S key (LoRaWAN 1.1), unused for LoRaWAN 1.0.
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t beginAPB(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, uint8_t* fNwkSIntKey = NULL, uint8_t* sNwkSIntKey = NULL);
|
||||
int16_t beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, uint8_t* fNwkSIntKey = NULL, uint8_t* sNwkSIntKey = NULL);
|
||||
|
||||
#if defined(RADIOLIB_BUILD_ARDUINO)
|
||||
/*!
|
||||
|
@ -395,10 +399,12 @@ class LoRaWANNode {
|
|||
LoRaWANMacCommandQueue_t commandsUp = {
|
||||
.commands = { { .cid = 0, .len = 0, .payload = { 0 }, .repeat = 0, } },
|
||||
.numCommands = 0,
|
||||
.len = 0,
|
||||
};
|
||||
LoRaWANMacCommandQueue_t commandsDown = {
|
||||
.commands = { { .cid = 0, .len = 0, .payload = { 0 }, .repeat = 0, } },
|
||||
.numCommands = 0,
|
||||
.len = 0,
|
||||
};
|
||||
|
||||
// the following is either provided by the network server (OTAA)
|
||||
|
@ -438,6 +444,9 @@ class LoRaWANNode {
|
|||
// device status - battery level
|
||||
uint8_t battLevel = 0xFF;
|
||||
|
||||
// indicates whether an uplink has MAC commands as payload
|
||||
bool isMACPayload = false;
|
||||
|
||||
// method to generate message integrity code
|
||||
uint32_t generateMIC(uint8_t* msg, size_t len, uint8_t* key);
|
||||
|
||||
|
@ -475,7 +484,10 @@ class LoRaWANNode {
|
|||
int16_t pushMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQueue_t* queue);
|
||||
|
||||
// pop MAC command from queue, done by copy unless CMD is NULL
|
||||
int16_t popMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQueue_t* queue, bool force = false);
|
||||
int16_t popMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQueue_t* queue, size_t index);
|
||||
|
||||
// delete a specific MAC command from queue, indicated by the command ID
|
||||
int16_t deleteMacCommand(uint8_t cid, LoRaWANMacCommandQueue_t* queue);
|
||||
|
||||
// execute mac command, return the number of processed bytes for sequential processing
|
||||
size_t execMacCommand(LoRaWANMacCommand_t* cmd);
|
||||
|
|
Loading…
Add table
Reference in a new issue