[LoRaWAN] Improve session restoration/activation behaviour

This commit is contained in:
StevenCellist 2024-05-09 18:08:07 +02:00
parent bb7fffe95d
commit 868afacecc
3 changed files with 174 additions and 210 deletions

View file

@ -307,11 +307,9 @@ getBufferNonces KEYWORD2
setBufferNonces KEYWORD2 setBufferNonces KEYWORD2
getBufferSession KEYWORD2 getBufferSession KEYWORD2
setBufferSession KEYWORD2 setBufferSession KEYWORD2
restore KEYWORD2
beginOTAA KEYWORD2 beginOTAA KEYWORD2
beginABP KEYWORD2 beginABP KEYWORD2
isJoined KEYWORD2 isJoined KEYWORD2
saveSession KEYWORD2
sendMacCommandReq KEYWORD2 sendMacCommandReq KEYWORD2
uplink KEYWORD2 uplink KEYWORD2
downlink KEYWORD2 downlink KEYWORD2

View file

@ -47,9 +47,17 @@ void LoRaWANNode::setCSMA(uint8_t backoffMax, uint8_t difsSlots, bool enableCSMA
void LoRaWANNode::wipe() { void LoRaWANNode::wipe() {
memset(this->bufferNonces, 0, RADIOLIB_LW_NONCES_BUF_SIZE); memset(this->bufferNonces, 0, RADIOLIB_LW_NONCES_BUF_SIZE);
memset(this->bufferSession, 0, RADIOLIB_LW_SESSION_BUF_SIZE); memset(this->bufferSession, 0, RADIOLIB_LW_SESSION_BUF_SIZE);
memset(&(this->commandsUp), 0, sizeof(LoRaWANMacCommandQueue_t));
memset(&(this->commandsDown), 0, sizeof(LoRaWANMacCommandQueue_t));
this->devNonce = 0;
this->joinNonce = 0;
} }
uint8_t* LoRaWANNode::getBufferNonces() { uint8_t* LoRaWANNode::getBufferNonces() {
// generate the signature of the Nonces buffer, and store it in the last two bytes of the Nonces buffer
uint16_t signature = LoRaWANNode::checkSum16(this->bufferNonces, RADIOLIB_LW_NONCES_BUF_SIZE - 2);
LoRaWANNode::hton<uint16_t>(&this->bufferNonces[RADIOLIB_LW_NONCES_SIGNATURE], signature);
return(this->bufferNonces); return(this->bufferNonces);
} }
@ -65,6 +73,9 @@ int16_t LoRaWANNode::setBufferNonces(uint8_t* persistentBuffer) {
// copy the whole buffer over // copy the whole buffer over
memcpy(this->bufferNonces, persistentBuffer, RADIOLIB_LW_NONCES_BUF_SIZE); memcpy(this->bufferNonces, persistentBuffer, RADIOLIB_LW_NONCES_BUF_SIZE);
this->devNonce = LoRaWANNode::ntoh<uint16_t>(&this->bufferNonces[RADIOLIB_LW_NONCES_DEV_NONCE]);
this->joinNonce = LoRaWANNode::ntoh<uint32_t>(&this->bufferNonces[RADIOLIB_LW_NONCES_JOIN_NONCE], 3);
// revert to inactive as long as no session is restored // revert to inactive as long as no session is restored
this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE] = (uint8_t)false; this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE] = (uint8_t)false;
@ -72,8 +83,20 @@ int16_t LoRaWANNode::setBufferNonces(uint8_t* persistentBuffer) {
} }
uint8_t* LoRaWANNode::getBufferSession() { uint8_t* LoRaWANNode::getBufferSession() {
// update buffer contents // store all frame counters
this->saveSession(); LoRaWANNode::hton<uint32_t>(&this->bufferSession[RADIOLIB_LW_SESSION_A_FCNT_DOWN], this->aFCntDown);
LoRaWANNode::hton<uint32_t>(&this->bufferSession[RADIOLIB_LW_SESSION_N_FCNT_DOWN], this->nFCntDown);
LoRaWANNode::hton<uint32_t>(&this->bufferSession[RADIOLIB_LW_SESSION_CONF_FCNT_UP], this->confFCntUp);
LoRaWANNode::hton<uint32_t>(&this->bufferSession[RADIOLIB_LW_SESSION_CONF_FCNT_DOWN], this->confFCntDown);
LoRaWANNode::hton<uint32_t>(&this->bufferSession[RADIOLIB_LW_SESSION_ADR_FCNT], this->adrFCnt);
LoRaWANNode::hton<uint32_t>(&this->bufferSession[RADIOLIB_LW_SESSION_FCNT_UP], this->fCntUp);
// save the current uplink MAC command queue
memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_MAC_QUEUE_UL], &this->commandsUp, sizeof(LoRaWANMacCommandQueue_t));
// generate the signature of the Session buffer, and store it in the last two bytes of the Session buffer
uint16_t signature = LoRaWANNode::checkSum16(this->bufferSession, RADIOLIB_LW_SESSION_BUF_SIZE - 2);
LoRaWANNode::hton<uint16_t>(&this->bufferSession[RADIOLIB_LW_SESSION_SIGNATURE], signature);
return(this->bufferSession); return(this->bufferSession);
} }
@ -98,73 +121,12 @@ int16_t LoRaWANNode::setBufferSession(uint8_t* persistentBuffer) {
// copy the whole buffer over // copy the whole buffer over
memcpy(this->bufferSession, persistentBuffer, RADIOLIB_LW_SESSION_BUF_SIZE); memcpy(this->bufferSession, persistentBuffer, RADIOLIB_LW_SESSION_BUF_SIZE);
// as both the Nonces and session are restored, revert to active session //// this code can be used in case breaking chances must be caught:
this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE] = (uint8_t)true;
return(state);
}
int16_t LoRaWANNode::checkBufferCommon(uint8_t *buffer, uint16_t size) {
// check if there are actually values in the buffer
size_t i = 0;
for(; i < size; i++) {
if(buffer[i]) {
break;
}
}
if(i == size) {
return(RADIOLIB_ERR_NETWORK_NOT_JOINED);
}
// check integrity of the whole buffer (compare checksum to included checksum)
uint16_t checkSum = LoRaWANNode::checkSum16(buffer, size - 2);
uint16_t signature = LoRaWANNode::ntoh<uint16_t>(&buffer[size - 2]);
if(signature != checkSum) {
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Calculated checksum: %04X, expected: %04X", checkSum, signature);
return(RADIOLIB_ERR_CHECKSUM_MISMATCH);
}
return(RADIOLIB_ERR_NONE);
}
int16_t LoRaWANNode::restore(uint16_t checkSum, uint16_t lwMode, uint8_t lwClass, uint8_t freqPlan) {
// if already joined, ignore
if(this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE]) {
return(RADIOLIB_ERR_NONE);
}
bool isSameKeys = LoRaWANNode::ntoh<uint16_t>(&this->bufferNonces[RADIOLIB_LW_NONCES_CHECKSUM]) == checkSum;
bool isSameMode = LoRaWANNode::ntoh<uint16_t>(&this->bufferNonces[RADIOLIB_LW_NONCES_MODE]) == lwMode;
bool isSameClass = LoRaWANNode::ntoh<uint8_t>(&this->bufferNonces[RADIOLIB_LW_NONCES_CLASS]) == lwClass;
bool isSamePlan = LoRaWANNode::ntoh<uint8_t>(&this->bufferNonces[RADIOLIB_LW_NONCES_PLAN]) == freqPlan;
// check if Nonces buffer matches the current configuration
if(!isSameKeys || !isSameMode || !isSameClass || !isSamePlan) {
// if configuration did not match, discard whatever is currently in the buffers and start fresh
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Configuration mismatch (checksum: %d, mode: %d, class: %d, plan: %d)", isSameKeys, isSameMode, isSameClass, isSamePlan);
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Nonces buffer:");
RADIOLIB_DEBUG_PROTOCOL_HEXDUMP(this->bufferNonces, RADIOLIB_LW_NONCES_BUF_SIZE);
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Clearing buffer and starting fresh");
this->wipe();
return(RADIOLIB_ERR_NETWORK_NOT_JOINED);
}
if(lwMode == RADIOLIB_LW_MODE_OTAA) {
// Nonces buffer is OK, so we can at least restore Nonces
this->devNonce = LoRaWANNode::ntoh<uint16_t>(&this->bufferNonces[RADIOLIB_LW_NONCES_DEV_NONCE]);
this->joinNonce = LoRaWANNode::ntoh<uint32_t>(&this->bufferNonces[RADIOLIB_LW_NONCES_JOIN_NONCE], 3);
}
// uint8_t nvm_table_version = this->bufferNonces[RADIOLIB_LW_NONCES_VERSION]; // uint8_t nvm_table_version = this->bufferNonces[RADIOLIB_LW_NONCES_VERSION];
// if (RADIOLIB_LW_NONCES_VERSION_VAL > nvm_table_version) { // if (RADIOLIB_LW_NONCES_VERSION_VAL > nvm_table_version) {
// // set default values for variables that are new or something // // set default values for variables that are new or something
// } // }
if(this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE] == 0) {
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("No active session in progress; please join the network");
RADIOLIB_DEBUG_PROTOCOL_HEXDUMP(this->bufferNonces, RADIOLIB_LW_NONCES_BUF_SIZE);
return(RADIOLIB_ERR_NETWORK_NOT_JOINED);
}
// pull all authentication keys from persistent storage // pull all authentication keys from persistent storage
this->devAddr = LoRaWANNode::ntoh<uint32_t>(&this->bufferSession[RADIOLIB_LW_SESSION_DEV_ADDR]); this->devAddr = LoRaWANNode::ntoh<uint32_t>(&this->bufferSession[RADIOLIB_LW_SESSION_DEV_ADDR]);
memcpy(this->appSKey, &this->bufferSession[RADIOLIB_LW_SESSION_APP_SKEY], RADIOLIB_AES128_BLOCK_SIZE); memcpy(this->appSKey, &this->bufferSession[RADIOLIB_LW_SESSION_APP_SKEY], RADIOLIB_AES128_BLOCK_SIZE);
@ -183,22 +145,52 @@ int16_t LoRaWANNode::restore(uint16_t checkSum, uint16_t lwMode, uint8_t lwClass
this->adrFCnt = LoRaWANNode::ntoh<uint32_t>(&this->bufferSession[RADIOLIB_LW_SESSION_ADR_FCNT]); this->adrFCnt = LoRaWANNode::ntoh<uint32_t>(&this->bufferSession[RADIOLIB_LW_SESSION_ADR_FCNT]);
this->fCntUp = LoRaWANNode::ntoh<uint32_t>(&this->bufferSession[RADIOLIB_LW_SESSION_FCNT_UP]); this->fCntUp = LoRaWANNode::ntoh<uint32_t>(&this->bufferSession[RADIOLIB_LW_SESSION_FCNT_UP]);
int16_t state = RADIOLIB_ERR_UNKNOWN;
// for dynamic bands, first restore the defined channels before restoring ADR
if(this->band->bandType == RADIOLIB_LW_BAND_DYNAMIC) {
// restore the defined channels
state = this->restoreChannels();
RADIOLIB_ASSERT(state);
}
// restore the complete MAC state // restore the complete MAC state
// all-zero buffer used for checking if MAC commands are set
uint8_t bufferZeroes[RADIOLIB_LW_MAX_MAC_COMMAND_LEN_DOWN] = { 0 };
LoRaWANMacCommand_t cmd = { LoRaWANMacCommand_t cmd = {
.cid = RADIOLIB_LW_MAC_TX_PARAM_SETUP, .cid = 0,
.payload = { 0 }, .payload = { 0 },
.len = MacTable[RADIOLIB_LW_MAC_TX_PARAM_SETUP].lenDn, .len = 0,
.repeat = 0, .repeat = 0,
}; };
// for dynamic bands, first restore the defined channels before restoring ADR
// this is because the ADR command acts as a mask for the enabled channels
if(this->band->bandType == RADIOLIB_LW_BAND_DYNAMIC) {
// setup the default channels
state = this->setupChannelsDyn();
RADIOLIB_ASSERT(state);
// restore the session channels
uint8_t *startChannelsUp = &this->bufferSession[RADIOLIB_LW_SESSION_UL_CHANNELS];
cmd.cid = RADIOLIB_LW_MAC_NEW_CHANNEL;
for(int i = 0; i < RADIOLIB_LW_NUM_AVAILABLE_CHANNELS; i++) {
cmd.len = MacTable[RADIOLIB_LW_MAC_NEW_CHANNEL].lenDn;
memcpy(cmd.payload, startChannelsUp + (i * cmd.len), cmd.len);
if(memcmp(cmd.payload, bufferZeroes, cmd.len) != 0) { // only execute if it is not all zeroes
cmd.repeat = 1;
(void)execMacCommand(&cmd);
}
}
uint8_t *startChannelsDown = &this->bufferSession[RADIOLIB_LW_SESSION_DL_CHANNELS];
cmd.cid = RADIOLIB_LW_MAC_DL_CHANNEL;
for(int i = 0; i < RADIOLIB_LW_NUM_AVAILABLE_CHANNELS; i++) {
cmd.len = MacTable[RADIOLIB_LW_MAC_DL_CHANNEL].lenDn;
memcpy(cmd.payload, startChannelsDown + (i * cmd.len), cmd.len);
if(memcmp(cmd.payload, bufferZeroes, cmd.len) != 0) { // only execute if it is not all zeroes
(void)execMacCommand(&cmd);
}
}
}
cmd.cid = RADIOLIB_LW_MAC_TX_PARAM_SETUP,
cmd.len = MacTable[RADIOLIB_LW_MAC_TX_PARAM_SETUP].lenDn,
memcpy(cmd.payload, &this->bufferSession[RADIOLIB_LW_SESSION_TX_PARAM_SETUP], cmd.len); memcpy(cmd.payload, &this->bufferSession[RADIOLIB_LW_SESSION_TX_PARAM_SETUP], cmd.len);
(void)execMacCommand(&cmd); (void)execMacCommand(&cmd);
@ -209,8 +201,26 @@ int16_t LoRaWANNode::restore(uint16_t checkSum, uint16_t lwMode, uint8_t lwClass
// for fixed bands, first restore ADR, then the defined channels // for fixed bands, first restore ADR, then the defined channels
if(this->band->bandType == RADIOLIB_LW_BAND_FIXED) { if(this->band->bandType == RADIOLIB_LW_BAND_FIXED) {
state = this->restoreChannels(); // setup the default channels
state = this->setupChannelsFix(this->subBand);
RADIOLIB_ASSERT(state); RADIOLIB_ASSERT(state);
// restore the session channels
uint8_t *startMACpayload = &this->bufferSession[RADIOLIB_LW_SESSION_UL_CHANNELS];
// there are at most 8 channel masks present
cmd.cid = RADIOLIB_LW_MAC_LINK_ADR;
for(int i = 0; i < 8; i++) {
cmd.len = MacTable[RADIOLIB_LW_MAC_LINK_ADR].lenDn;
memcpy(cmd.payload, startMACpayload + (i * cmd.len), cmd.len);
// there COULD, according to spec, be an all zeroes ADR command - meh
if(memcmp(cmd.payload, bufferZeroes, cmd.len) == 0) {
break;
}
cmd.repeat = (i+1);
(void)execMacCommand(&cmd);
}
} }
cmd.cid = RADIOLIB_LW_MAC_DUTY_CYCLE; cmd.cid = RADIOLIB_LW_MAC_DUTY_CYCLE;
@ -241,73 +251,54 @@ int16_t LoRaWANNode::restore(uint16_t checkSum, uint16_t lwMode, uint8_t lwClass
// copy uplink MAC command queue back in place // copy uplink MAC command queue back in place
memcpy(&this->commandsUp, &this->bufferSession[RADIOLIB_LW_SESSION_MAC_QUEUE_UL], sizeof(LoRaWANMacCommandQueue_t)); memcpy(&this->commandsUp, &this->bufferSession[RADIOLIB_LW_SESSION_MAC_QUEUE_UL], sizeof(LoRaWANMacCommandQueue_t));
// as both the Nonces and session are restored, revert to active session
this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE] = (uint8_t)true;
return(state); return(state);
} }
int16_t LoRaWANNode::restoreChannels() { int16_t LoRaWANNode::checkBufferCommon(uint8_t *buffer, uint16_t size) {
// first do the default channels, in case these are not covered by restored channels // check if there are actually values in the buffer
if(this->band->bandType == RADIOLIB_LW_BAND_DYNAMIC) { size_t i = 0;
this->setupChannelsDyn(false); for(; i < size; i++) {
} else { // RADIOLIB_LW_BAND_FIXED if(buffer[i]) {
this->setupChannelsFix(this->subBand); break;
}
}
if(i == size) {
return(RADIOLIB_ERR_NETWORK_NOT_JOINED);
} }
uint8_t bufferZeroes[5] = { 0 }; // check integrity of the whole buffer (compare checksum to included checksum)
if(this->band->bandType == RADIOLIB_LW_BAND_DYNAMIC) { uint16_t checkSum = LoRaWANNode::checkSum16(buffer, size - 2);
uint8_t *startChannelsUp = &this->bufferSession[RADIOLIB_LW_SESSION_UL_CHANNELS]; uint16_t signature = LoRaWANNode::ntoh<uint16_t>(&buffer[size - 2]);
if(signature != checkSum) {
LoRaWANMacCommand_t cmd = { .cid = RADIOLIB_LW_MAC_NEW_CHANNEL, .payload = { 0 }, .len = 0, .repeat = 0 }; RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Calculated checksum: %04X, expected: %04X", checkSum, signature);
for(int i = 0; i < RADIOLIB_LW_NUM_AVAILABLE_CHANNELS; i++) { return(RADIOLIB_ERR_CHECKSUM_MISMATCH);
cmd.len = MacTable[RADIOLIB_LW_MAC_NEW_CHANNEL].lenDn;
memcpy(cmd.payload, startChannelsUp + (i * cmd.len), cmd.len);
if(memcmp(cmd.payload, bufferZeroes, cmd.len) != 0) { // only execute if it is not all zeroes
cmd.repeat = 1;
(void)execMacCommand(&cmd);
}
}
uint8_t *startChannelsDown = &this->bufferSession[RADIOLIB_LW_SESSION_DL_CHANNELS];
cmd.cid = RADIOLIB_LW_MAC_DL_CHANNEL;
for(int i = 0; i < RADIOLIB_LW_NUM_AVAILABLE_CHANNELS; i++) {
cmd.len = MacTable[RADIOLIB_LW_MAC_DL_CHANNEL].lenDn;
memcpy(cmd.payload, startChannelsDown + (i * cmd.len), cmd.len);
if(memcmp(cmd.payload, bufferZeroes, cmd.len) != 0) { // only execute if it is not all zeroes
(void)execMacCommand(&cmd);
}
}
} else { // RADIOLIB_LW_BAND_FIXED
uint8_t *startMACpayload = &this->bufferSession[RADIOLIB_LW_SESSION_UL_CHANNELS];
LoRaWANMacCommand_t cmd = {
.cid = RADIOLIB_LW_MAC_LINK_ADR,
.payload = { 0 },
.len = 0,
.repeat = 0,
};
// there are at most 8 channel masks present
for(int i = 0; i < 8; i++) {
cmd.len = MacTable[RADIOLIB_LW_MAC_LINK_ADR].lenDn;
memcpy(cmd.payload, startMACpayload + (i * cmd.len), cmd.len);
// there COULD, according to spec, be an all zeroes ADR command - meh
if(memcmp(cmd.payload, bufferZeroes, cmd.len) == 0) {
break;
}
cmd.repeat = (i+1);
(void)execMacCommand(&cmd);
}
} }
return(RADIOLIB_ERR_NONE); return(RADIOLIB_ERR_NONE);
} }
void LoRaWANNode::beginCommon(uint8_t initialDr) { bool LoRaWANNode::verifyBuffers(uint16_t checkSum, uint16_t lwMode, uint8_t lwClass, uint8_t freqPlan) {
// in case a new session is started while there is an ongoing session bool isSameKeys = LoRaWANNode::ntoh<uint16_t>(&this->bufferNonces[RADIOLIB_LW_NONCES_CHECKSUM]) == checkSum;
// clear the MAC queues completely bool isSameMode = LoRaWANNode::ntoh<uint16_t>(&this->bufferNonces[RADIOLIB_LW_NONCES_MODE]) == lwMode;
memset(&(this->commandsUp), 0, sizeof(LoRaWANMacCommandQueue_t)); bool isSameClass = LoRaWANNode::ntoh<uint8_t>(&this->bufferNonces[RADIOLIB_LW_NONCES_CLASS]) == lwClass;
memset(&(this->commandsDown), 0, sizeof(LoRaWANMacCommandQueue_t)); bool isSamePlan = LoRaWANNode::ntoh<uint8_t>(&this->bufferNonces[RADIOLIB_LW_NONCES_PLAN]) == freqPlan;
// check if Nonces buffer matches the current configuration
if(!isSameKeys || !isSameMode || !isSameClass || !isSamePlan) {
// if configuration did not match, discard whatever is currently in the buffers and start fresh
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Configuration mismatch (checksum: %d, mode: %d, class: %d, plan: %d)", isSameKeys, isSameMode, isSameClass, isSamePlan);
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Nonces buffer:");
RADIOLIB_DEBUG_PROTOCOL_HEXDUMP(this->bufferNonces, RADIOLIB_LW_NONCES_BUF_SIZE);
return(false);
}
return(true);
}
void LoRaWANNode::beginCommon(uint8_t initialDr) {
uint8_t drUp = 0; uint8_t drUp = 0;
if(this->band->bandType == RADIOLIB_LW_BAND_DYNAMIC) { if(this->band->bandType == RADIOLIB_LW_BAND_DYNAMIC) {
// if join datarate is user-specified and valid, select that value // if join datarate is user-specified and valid, select that value
@ -447,14 +438,6 @@ void LoRaWANNode::beginCommon(uint8_t initialDr) {
} }
int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKey, uint8_t* appKey, bool force, uint8_t joinDr) { int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKey, uint8_t* appKey, bool force, uint8_t joinDr) {
// if not forced and already joined, don't do anything
if(!force && this->isJoined()) {
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("beginOTAA(): Did not rejoin: session already active");
return(RADIOLIB_ERR_NONE);
}
int16_t state = RADIOLIB_ERR_UNKNOWN;
// generate activation key checksum // generate activation key checksum
uint16_t checkSum = 0; uint16_t checkSum = 0;
checkSum ^= LoRaWANNode::checkSum16(reinterpret_cast<uint8_t*>(&joinEUI), 8); checkSum ^= LoRaWANNode::checkSum16(reinterpret_cast<uint8_t*>(&joinEUI), 8);
@ -462,19 +445,20 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
checkSum ^= LoRaWANNode::checkSum16(nwkKey, 16); checkSum ^= LoRaWANNode::checkSum16(nwkKey, 16);
checkSum ^= LoRaWANNode::checkSum16(appKey, 16); checkSum ^= LoRaWANNode::checkSum16(appKey, 16);
// if The Force is used, disable the active session; // if the supplied activation info doesn't match the restored buffers, discard all contents of previous session
// as a result, restore() will only restore Nonces if they are available, not the session if(!this->verifyBuffers(checkSum, RADIOLIB_LW_MODE_OTAA, RADIOLIB_LW_CLASS_A, this->band->bandNum)) {
if(force) { this->wipe();
this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE] = (uint8_t)false;
} }
state = this->restore(checkSum, RADIOLIB_LW_MODE_OTAA, RADIOLIB_LW_CLASS_A, this->band->bandNum); // if the device is activated with a valid session, and user didn't force a new session, return
if(this->isJoined() && !force) {
if(!force) { return(RADIOLIB_ERR_NONE);
return(state);
} }
Module* mod = this->phyLayer->getMod(); int16_t state = RADIOLIB_ERR_UNKNOWN;
// either no valid session was found or user forced a new session, so set active-bit to false
this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE] = (uint8_t)false;
// setup join-request uplink/downlink frequencies and datarates // setup join-request uplink/downlink frequencies and datarates
if(this->band->bandType == RADIOLIB_LW_BAND_DYNAMIC) { if(this->band->bandType == RADIOLIB_LW_BAND_DYNAMIC) {
@ -502,10 +486,6 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
// copy devNonce currently in use // copy devNonce currently in use
uint16_t devNonceUsed = this->devNonce; uint16_t devNonceUsed = this->devNonce;
// increment devNonce as we are sending another join-request
this->devNonce += 1;
LoRaWANNode::hton<uint16_t>(&this->bufferNonces[RADIOLIB_LW_NONCES_DEV_NONCE], this->devNonce);
// build the join-request message // build the join-request message
uint8_t joinRequestMsg[RADIOLIB_LW_JOIN_REQUEST_LEN]; uint8_t joinRequestMsg[RADIOLIB_LW_JOIN_REQUEST_LEN];
@ -521,10 +501,15 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
LoRaWANNode::hton<uint32_t>(&joinRequestMsg[RADIOLIB_LW_JOIN_REQUEST_LEN - sizeof(uint32_t)], mic); LoRaWANNode::hton<uint32_t>(&joinRequestMsg[RADIOLIB_LW_JOIN_REQUEST_LEN - sizeof(uint32_t)], mic);
// send it // send it
Module* mod = this->phyLayer->getMod();
state = this->phyLayer->transmit(joinRequestMsg, RADIOLIB_LW_JOIN_REQUEST_LEN); state = this->phyLayer->transmit(joinRequestMsg, RADIOLIB_LW_JOIN_REQUEST_LEN);
this->rxDelayStart = mod->hal->millis(); this->rxDelayStart = mod->hal->millis();
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Join-request sent <-- Rx Delay start");
RADIOLIB_ASSERT(state); RADIOLIB_ASSERT(state);
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Join-request sent <-- Rx Delay start");
// join-request successfully sent, so increase & save devNonce
this->devNonce += 1;
LoRaWANNode::hton<uint16_t>(&this->bufferNonces[RADIOLIB_LW_NONCES_DEV_NONCE], this->devNonce);
// configure Rx delay for join-accept message - these are re-configured once a valid join-request is received // configure Rx delay for join-accept message - these are re-configured once a valid join-request is received
this->rxDelays[0] = RADIOLIB_LW_JOIN_ACCEPT_DELAY_1_MS; this->rxDelays[0] = RADIOLIB_LW_JOIN_ACCEPT_DELAY_1_MS;
@ -718,19 +703,26 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
uint16_t signature = LoRaWANNode::checkSum16(this->bufferNonces, RADIOLIB_LW_NONCES_BUF_SIZE - 2); uint16_t signature = LoRaWANNode::checkSum16(this->bufferNonces, RADIOLIB_LW_NONCES_BUF_SIZE - 2);
LoRaWANNode::hton<uint16_t>(&this->bufferNonces[RADIOLIB_LW_NONCES_SIGNATURE], signature); LoRaWANNode::hton<uint16_t>(&this->bufferNonces[RADIOLIB_LW_NONCES_SIGNATURE], signature);
// store DevAddr and all keys
LoRaWANNode::hton<uint32_t>(&this->bufferSession[RADIOLIB_LW_SESSION_DEV_ADDR], this->devAddr);
memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_APP_SKEY], this->appSKey, RADIOLIB_AES128_BLOCK_SIZE);
memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_NWK_SENC_KEY], this->nwkSEncKey, RADIOLIB_AES128_BLOCK_SIZE);
memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_FNWK_SINT_KEY], this->fNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE);
memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_SNWK_SINT_KEY], this->sNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE);
// copy the signature of the Nonces buffer over to the Session buffer
uint16_t noncesSignature = LoRaWANNode::ntoh<uint16_t>(&this->bufferNonces[RADIOLIB_LW_NONCES_SIGNATURE]);
LoRaWANNode::hton<uint16_t>(&this->bufferSession[RADIOLIB_LW_SESSION_NONCES_SIGNATURE], noncesSignature);
// store network parameters
LoRaWANNode::hton<uint32_t>(&this->bufferSession[RADIOLIB_LW_SESSION_HOMENET_ID], this->homeNetId);
LoRaWANNode::hton<uint8_t>(&this->bufferSession[RADIOLIB_LW_SESSION_VERSION], this->rev);
return(RADIOLIB_ERR_NONE); return(RADIOLIB_ERR_NONE);
} }
int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* fNwkSIntKey, uint8_t* sNwkSIntKey, uint8_t* nwkSEncKey, uint8_t* appSKey, bool force, uint8_t initialDr) { int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* fNwkSIntKey, uint8_t* sNwkSIntKey, uint8_t* nwkSEncKey, uint8_t* appSKey, bool force, uint8_t initialDr) {
// if not forced and already joined, don't do anything // generate activation key checksum
if(!force && this->isJoined()) {
RADIOLIB_DEBUG_PROTOCOL_PRINTLN("beginABP(): Did not rejoin: session already active");
return(RADIOLIB_ERR_NONE);
}
int16_t state = RADIOLIB_ERR_UNKNOWN;
// check if we actually need to restart from a clean session
uint16_t checkSum = 0; uint16_t checkSum = 0;
checkSum ^= LoRaWANNode::checkSum16(reinterpret_cast<uint8_t*>(&addr), 4); checkSum ^= LoRaWANNode::checkSum16(reinterpret_cast<uint8_t*>(&addr), 4);
checkSum ^= LoRaWANNode::checkSum16(nwkSEncKey, 16); checkSum ^= LoRaWANNode::checkSum16(nwkSEncKey, 16);
@ -738,18 +730,19 @@ int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* fNwkSIntKey, uint8_t* sNwk
if(fNwkSIntKey) { checkSum ^= LoRaWANNode::checkSum16(fNwkSIntKey, 16); } if(fNwkSIntKey) { checkSum ^= LoRaWANNode::checkSum16(fNwkSIntKey, 16); }
if(sNwkSIntKey) { checkSum ^= LoRaWANNode::checkSum16(sNwkSIntKey, 16); } if(sNwkSIntKey) { checkSum ^= LoRaWANNode::checkSum16(sNwkSIntKey, 16); }
// if The Force is used, disable the active session; // if the supplied activation info doesn't match the restored buffers, discard all contents of previous session
// as a result, restore() will not restore the session (and there are no Nonces in ABP mode) if(!this->verifyBuffers(checkSum, RADIOLIB_LW_MODE_OTAA, RADIOLIB_LW_CLASS_A, this->band->bandNum)) {
if(force) { this->wipe();
this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE] = (uint8_t)false;
} }
state = this->restore(checkSum, RADIOLIB_LW_MODE_ABP, RADIOLIB_LW_CLASS_A, this->band->bandNum); // if the device is activated with a valid session, and user didn't force a new session, return
if(this->isJoined() && !force) {
if(!force) { return(RADIOLIB_ERR_NONE);
return(state);
} }
// either no valid session was found or user forced a new session, so set active-bit to false
this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE] = (uint8_t)false;
this->devAddr = addr; this->devAddr = addr;
memcpy(this->appSKey, appSKey, RADIOLIB_AES128_KEY_SIZE); memcpy(this->appSKey, appSKey, RADIOLIB_AES128_KEY_SIZE);
memcpy(this->nwkSEncKey, nwkSEncKey, RADIOLIB_AES128_KEY_SIZE); memcpy(this->nwkSEncKey, nwkSEncKey, RADIOLIB_AES128_KEY_SIZE);
@ -788,20 +781,13 @@ int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* fNwkSIntKey, uint8_t* sNwk
LoRaWANNode::hton<uint8_t>(&this->bufferNonces[RADIOLIB_LW_NONCES_PLAN], this->band->bandNum); LoRaWANNode::hton<uint8_t>(&this->bufferNonces[RADIOLIB_LW_NONCES_PLAN], this->band->bandNum);
LoRaWANNode::hton<uint16_t>(&this->bufferNonces[RADIOLIB_LW_NONCES_CHECKSUM], checkSum); LoRaWANNode::hton<uint16_t>(&this->bufferNonces[RADIOLIB_LW_NONCES_CHECKSUM], checkSum);
// new session all good, so set active-bit to true
this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE] = (uint8_t)true; this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE] = (uint8_t)true;
// generate the signature of the Nonces buffer, and store it in the last two bytes of the Nonces buffer // generate the signature of the Nonces buffer, and store it in the last two bytes of the Nonces buffer
uint16_t signature = LoRaWANNode::checkSum16(this->bufferNonces, RADIOLIB_LW_NONCES_BUF_SIZE - 2); uint16_t signature = LoRaWANNode::checkSum16(this->bufferNonces, RADIOLIB_LW_NONCES_BUF_SIZE - 2);
LoRaWANNode::hton<uint16_t>(&this->bufferNonces[RADIOLIB_LW_NONCES_SIGNATURE], signature); LoRaWANNode::hton<uint16_t>(&this->bufferNonces[RADIOLIB_LW_NONCES_SIGNATURE], signature);
return(RADIOLIB_ERR_NONE);
}
bool LoRaWANNode::isJoined() {
return(this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE]);
}
int16_t LoRaWANNode::saveSession() {
// store DevAddr and all keys // store DevAddr and all keys
LoRaWANNode::hton<uint32_t>(&this->bufferSession[RADIOLIB_LW_SESSION_DEV_ADDR], this->devAddr); LoRaWANNode::hton<uint32_t>(&this->bufferSession[RADIOLIB_LW_SESSION_DEV_ADDR], this->devAddr);
memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_APP_SKEY], this->appSKey, RADIOLIB_AES128_BLOCK_SIZE); memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_APP_SKEY], this->appSKey, RADIOLIB_AES128_BLOCK_SIZE);
@ -817,24 +803,13 @@ int16_t LoRaWANNode::saveSession() {
LoRaWANNode::hton<uint32_t>(&this->bufferSession[RADIOLIB_LW_SESSION_HOMENET_ID], this->homeNetId); LoRaWANNode::hton<uint32_t>(&this->bufferSession[RADIOLIB_LW_SESSION_HOMENET_ID], this->homeNetId);
LoRaWANNode::hton<uint8_t>(&this->bufferSession[RADIOLIB_LW_SESSION_VERSION], this->rev); LoRaWANNode::hton<uint8_t>(&this->bufferSession[RADIOLIB_LW_SESSION_VERSION], this->rev);
// store all frame counters
LoRaWANNode::hton<uint32_t>(&this->bufferSession[RADIOLIB_LW_SESSION_A_FCNT_DOWN], this->aFCntDown);
LoRaWANNode::hton<uint32_t>(&this->bufferSession[RADIOLIB_LW_SESSION_N_FCNT_DOWN], this->nFCntDown);
LoRaWANNode::hton<uint32_t>(&this->bufferSession[RADIOLIB_LW_SESSION_CONF_FCNT_UP], this->confFCntUp);
LoRaWANNode::hton<uint32_t>(&this->bufferSession[RADIOLIB_LW_SESSION_CONF_FCNT_DOWN], this->confFCntDown);
LoRaWANNode::hton<uint32_t>(&this->bufferSession[RADIOLIB_LW_SESSION_ADR_FCNT], this->adrFCnt);
LoRaWANNode::hton<uint32_t>(&this->bufferSession[RADIOLIB_LW_SESSION_FCNT_UP], this->fCntUp);
// save the current uplink MAC command queue
memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_MAC_QUEUE_UL], &this->commandsUp, sizeof(LoRaWANMacCommandQueue_t));
// generate the signature of the Session buffer, and store it in the last two bytes of the Session buffer
uint16_t signature = LoRaWANNode::checkSum16(this->bufferSession, RADIOLIB_LW_SESSION_BUF_SIZE - 2);
LoRaWANNode::hton<uint16_t>(&this->bufferSession[RADIOLIB_LW_SESSION_SIGNATURE], signature);
return(RADIOLIB_ERR_NONE); return(RADIOLIB_ERR_NONE);
} }
bool LoRaWANNode::isJoined() {
return(this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE]);
}
#if defined(RADIOLIB_BUILD_ARDUINO) #if defined(RADIOLIB_BUILD_ARDUINO)
int16_t LoRaWANNode::uplink(String& str, uint8_t fPort, bool isConfirmed, LoRaWANEvent_t* event) { int16_t LoRaWANNode::uplink(String& str, uint8_t fPort, bool isConfirmed, LoRaWANEvent_t* event) {
return(this->uplink(str.c_str(), fPort, isConfirmed, event)); return(this->uplink(str.c_str(), fPort, isConfirmed, event));

View file

@ -544,12 +544,6 @@ class LoRaWANNode {
*/ */
int16_t setBufferSession(uint8_t* persistentBuffer); int16_t setBufferSession(uint8_t* persistentBuffer);
/*!
\brief Restore session by loading information from persistent storage.
\returns \ref status_codes
*/
int16_t restore(uint16_t checkSum, uint16_t lwMode, uint8_t lwClass, uint8_t freqPlan);
/*! /*!
\brief Join network by performing over-the-air activation. By this procedure, \brief Join network by performing over-the-air activation. By this procedure,
the device will perform an exchange with the network server and set all necessary configuration. the device will perform an exchange with the network server and set all necessary configuration.
@ -581,12 +575,6 @@ class LoRaWANNode {
/*! \brief Whether there is an ongoing session active */ /*! \brief Whether there is an ongoing session active */
bool isJoined(); bool isJoined();
/*!
\brief Save the current state of the session to the session buffer.
\returns \ref status_codes
*/
int16_t saveSession();
/*! /*!
\brief Add a MAC command to the uplink queue. \brief Add a MAC command to the uplink queue.
Only LinkCheck and DeviceTime are available to the user. Only LinkCheck and DeviceTime are available to the user.
@ -956,6 +944,9 @@ class LoRaWANNode {
// save the selected sub-band in case this must be restored in ADR control // save the selected sub-band in case this must be restored in ADR control
uint8_t subBand = 0; uint8_t subBand = 0;
// check if restored buffers match the supplied activation info
bool verifyBuffers(uint16_t checkSum, uint16_t lwMode, uint8_t lwClass, uint8_t freqPlan);
// wait for, open and listen during Rx1 and Rx2 windows; only performs listening // wait for, open and listen during Rx1 and Rx2 windows; only performs listening
int16_t downlinkCommon(); int16_t downlinkCommon();