From e9b2e91e6feeaf594d45c5acfcbb571a20042fa2 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Mon, 5 Jul 2021 10:58:14 -0400 Subject: [PATCH 1/7] Fix #169: Fix #172: Fix #173: check size and tag of SessionChannelMask --- src/Arduino_LoRaWAN.h | 8 +- src/lib/arduino_lorawan_sessionstate.cpp | 197 +++++++++++++---------- 2 files changed, 117 insertions(+), 88 deletions(-) diff --git a/src/Arduino_LoRaWAN.h b/src/Arduino_LoRaWAN.h index feea68e..3dabfb6 100644 --- a/src/Arduino_LoRaWAN.h +++ b/src/Arduino_LoRaWAN.h @@ -194,10 +194,10 @@ class Arduino_LoRaWAN struct SessionChannelMask_Header { - enum eMaskKind : uint8_t { kEUlike = 0, kUSlike = 1 }; + enum eMaskKind : uint8_t { kEUlike = 0, kUSlike = 1, kCNlike = 2 }; uint8_t Tag; ///< discriminator, eMaskKind. - uint8_t Size; ///< size of SessionChannelMask, in bytes + uint8_t Size; ///< size of actual SessionChannelMask, in bytes }; template @@ -541,7 +541,9 @@ class Arduino_LoRaWAN { SessionStateHeader Header; SessionStateV1 V1; + bool isValid() const; } SessionState; + /* || the constructor. */ @@ -677,6 +679,8 @@ class Arduino_LoRaWAN uint8_t *pBuf ); + bool IsValidState(const SessionState &state) const; + // return true iff network seems to be provisioned. Make // it virtual so it can be overridden if needed. virtual bool IsProvisioned(void) diff --git a/src/lib/arduino_lorawan_sessionstate.cpp b/src/lib/arduino_lorawan_sessionstate.cpp index 9cbde19..098187b 100644 --- a/src/lib/arduino_lorawan_sessionstate.cpp +++ b/src/lib/arduino_lorawan_sessionstate.cpp @@ -257,6 +257,40 @@ Name: Arduino_LoRaWAN::ApplySessionState() */ +bool Arduino_LoRaWAN::IsValidState(const Arduino_LoRaWAN::SessionState &state) const + { + // do not apply the session state unless it roughly matches our configuration. + if (! state.isValid()) + return false; + + // make sure region and country match. TODO: make sure network matches. + if (! (Arduino_LoRaWAN::Region(state.V1.Region) == this->GetRegion() && + state.V1.Country == uint16_t(this->GetCountryCode()))) + return false; + + // it matches! + return true; + } + +bool Arduino_LoRaWAN::SessionState::isValid() const + { + if (! (this->Header.Tag == kSessionStateTag_V1 && + this->Header.Size == sizeof(*this))) + return false; + + switch (this->V1.Channels.Header.Tag) + { + case Arduino_LoRaWAN::SessionChannelMask_Header::eMaskKind::kEUlike: + return this->V1.Channels.Header.Size == sizeof(this->V1.Channels.EUlike); + + case Arduino_LoRaWAN::SessionChannelMask_Header::eMaskKind::kUSlike: + return this->V1.Channels.Header.Size == sizeof(this->V1.Channels.USlike); + + default: + return false; + } + } + #define FUNCTION "Arduino_LoRaWAN::ApplySessionState" bool @@ -265,124 +299,115 @@ Arduino_LoRaWAN::ApplySessionState( ) { // do not apply the session state unless it roughly matches our configuration. - if (State.Header.Tag == kSessionStateTag_V1 && - Arduino_LoRaWAN::Region(State.V1.Region) == this->GetRegion() && - State.V1.Country == uint16_t(this->GetCountryCode()) - ) - { - auto const tNow = os_getTime(); + if (! this->IsValidState(State)) + return false; - // record that we've done it. - this->m_savedSessionState = State; + auto const tNow = os_getTime(); + + // record that we've done it. + this->m_savedSessionState = State; - // set FcntUp, FcntDown, and session state - LMIC.datarate = State.V1.LinkDR; + // set FcntUp, FcntDown, and session state + LMIC.datarate = State.V1.LinkDR; - // set the uplink and downlink count - LMIC.seqnoDn = State.V1.FCntDown; - LMIC.seqnoUp = State.V1.FCntUp; + // set the uplink and downlink count + LMIC.seqnoDn = State.V1.FCntDown; + LMIC.seqnoUp = State.V1.FCntUp; - // - // TODO(tmm@mcci.com): State.V1.gpsTime can be used to tweak the saved cycle - // time and also as a fallback if the system clock is not robust. but right - // now we ignore it. - // + // + // TODO(tmm@mcci.com): State.V1.gpsTime can be used to tweak the saved cycle + // time and also as a fallback if the system clock is not robust. but right + // now we ignore it. + // - // conservatively set the global avail time. - LMIC.globalDutyAvail = tNow + State.V1.globalAvail; + // conservatively set the global avail time. + LMIC.globalDutyAvail = tNow + State.V1.globalAvail; - // set the Rx2 frequency - LMIC.dn2Freq = State.V1.Rx2Frequency; + // set the Rx2 frequency + LMIC.dn2Freq = State.V1.Rx2Frequency; #if !defined(DISABLE_PING) - // set the ping frequency - LMIC.ping.freq = State.V1.PingFrequency; + // set the ping frequency + LMIC.ping.freq = State.V1.PingFrequency; #endif - LMIC.adrAckReq = State.V1.LinkIntegrity; - LMIC.adrTxPow = State.V1.TxPower; - LMIC.upRepeat = State.V1.Redundancy; - LMIC.globalDutyRate = State.V1.DutyCycle; - LMIC.rx1DrOffset = State.V1.Rx1DRoffset; - LMIC.dn2Dr = State.V1.Rx2DataRate; - LMIC.rxDelay = State.V1.RxDelay; + LMIC.adrAckReq = State.V1.LinkIntegrity; + LMIC.adrTxPow = State.V1.TxPower; + LMIC.upRepeat = State.V1.Redundancy; + LMIC.globalDutyRate = State.V1.DutyCycle; + LMIC.rx1DrOffset = State.V1.Rx1DRoffset; + LMIC.dn2Dr = State.V1.Rx2DataRate; + LMIC.rxDelay = State.V1.RxDelay; #if LMIC_ENABLE_TxParamSetupReq - LMIC.txParam = State.V1.TxParam; + LMIC.txParam = State.V1.TxParam; #endif #if !defined(DISABLE_BEACONS) - LMIC.bcnChnl = State.V1.BeaconChannel; + LMIC.bcnChnl = State.V1.BeaconChannel; #endif #if !defined(DISABLE_PING) - LMIC.ping.dr = State.V1.PingDr; + LMIC.ping.dr = State.V1.PingDr; #endif - LMIC.dn2Ans = State.V1.MacRxParamAns; - LMIC.macDlChannelAns = State.V1.MacDlChannelAns; - LMIC.macRxTimingSetupAns = State.V1.MacRxTimingSetupAns; + LMIC.dn2Ans = State.V1.MacRxParamAns; + LMIC.macDlChannelAns = State.V1.MacDlChannelAns; + LMIC.macRxTimingSetupAns = State.V1.MacRxTimingSetupAns; #if CFG_LMIC_EU_like - // don't turn off bits: user can't fool us here. - // we can get the immutable channels from the - // channelMap value after reset. - auto const resetMap = LMIC.channelMap; - auto const & euLike = State.V1.Channels.EUlike; - LMIC.channelMap |= euLike.ChannelMap; + // don't turn off bits: user can't fool us here. + // we can get the immutable channels from the + // channelMap value after reset. + auto const resetMap = LMIC.channelMap; + auto const & euLike = State.V1.Channels.EUlike; + LMIC.channelMap |= euLike.ChannelMap; #if ARDUINO_LMIC_VERSION_COMPARE_GE(ARDUINO_LMIC_VERSION, ARDUINO_LMIC_VERSION_CALC(3,99,0,1)) - LMIC.channelShuffleMap = euLike.ChannelShuffleMap; + LMIC.channelShuffleMap = euLike.ChannelShuffleMap; #endif - for (unsigned ch = 0; ch < MAX_CHANNELS; ++ch) + for (unsigned ch = 0; ch < MAX_CHANNELS; ++ch) + { + if ((resetMap & (decltype(resetMap)(1) << ch)) == 0) { - if ((resetMap & (decltype(resetMap)(1) << ch)) == 0) - { - // copy data -- note that the saved band number is encoded - LMIC_setupChannel( - ch, - euLike.getFrequency(euLike.UplinkFreq, ch), - euLike.ChannelDrMap[ch], - euLike.getBand(ch) - ); + // copy data -- note that the saved band number is encoded + LMIC_setupChannel( + ch, + euLike.getFrequency(euLike.UplinkFreq, ch), + euLike.ChannelDrMap[ch], + euLike.getBand(ch) + ); #if !defined(DISABLE_MCMD_DlChannelReq) - LMIC.channelDlFreq[ch] = euLike.getFrequency(euLike.DownlinkFreq, ch); + LMIC.channelDlFreq[ch] = euLike.getFrequency(euLike.DownlinkFreq, ch); #endif - } } + } - for (unsigned band = 0; band < MAX_BANDS; ++band) - { - LMIC.bands[band].txcap = euLike.Bands[band].txDutyDenom; - LMIC.bands[band].txpow = euLike.Bands[band].txPower; - LMIC.bands[band].lastchnl = euLike.Bands[band].lastChannel; - // Heuristic; we don't know how long has passed since we saved - // this, because we don't currently have GPS time available. - // Conservatively reserve time from now. - LMIC.bands[band].avail = tNow + euLike.Bands[band].ostimeAvail; - } + for (unsigned band = 0; band < MAX_BANDS; ++band) + { + LMIC.bands[band].txcap = euLike.Bands[band].txDutyDenom; + LMIC.bands[band].txpow = euLike.Bands[band].txPower; + LMIC.bands[band].lastchnl = euLike.Bands[band].lastChannel; + // Heuristic; we don't know how long has passed since we saved + // this, because we don't currently have GPS time available. + // Conservatively reserve time from now. + LMIC.bands[band].avail = tNow + euLike.Bands[band].ostimeAvail; + } #elif CFG_LMIC_US_like # if ARDUINO_LMIC_VERSION_COMPARE_GE(ARDUINO_LMIC_VERSION, ARDUINO_LMIC_VERSION_CALC(3,99,0,1)) - static_assert(sizeof(LMIC.channelShuffleMap) == sizeof(State.V1.Channels.USlike.ChannelShuffleMap), - "shuffle map doesn't match"); - // copy the shuffle map bits - memcpy(LMIC.channelShuffleMap, State.V1.Channels.USlike.ChannelShuffleMap, sizeof(LMIC.channelShuffleMap)); + static_assert(sizeof(LMIC.channelShuffleMap) == sizeof(State.V1.Channels.USlike.ChannelShuffleMap), + "shuffle map doesn't match"); + // copy the shuffle map bits + memcpy(LMIC.channelShuffleMap, State.V1.Channels.USlike.ChannelShuffleMap, sizeof(LMIC.channelShuffleMap)); # endif - // copy the enabled states - for (unsigned ch = 0; ch < State.V1.Channels.USlike.nCh; ++ch) - { - const bool state = State.V1.Channels.USlike.isEnabled(ch); - - if (state) - LMIC_enableChannel(ch); - else - LMIC_disableChannel(ch); - } -#endif - - return true; - } - else + // copy the enabled states + for (unsigned ch = 0; ch < State.V1.Channels.USlike.nCh; ++ch) { - return false; + const bool state = State.V1.Channels.USlike.isEnabled(ch); + + if (state) + LMIC_enableChannel(ch); + else + LMIC_disableChannel(ch); } +#endif } #undef FUNCTION From 49f5e0fe54d48bfb188014ce00c8985151a1fa4b Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Mon, 5 Jul 2021 10:59:33 -0400 Subject: [PATCH 2/7] Fix #171: don't use ABP info for OTAA unless saved state is valid --- src/lib/arduino_lorawan_begin.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib/arduino_lorawan_begin.cpp b/src/lib/arduino_lorawan_begin.cpp index 30c0ff3..b24fb33 100644 --- a/src/lib/arduino_lorawan_begin.cpp +++ b/src/lib/arduino_lorawan_begin.cpp @@ -49,7 +49,8 @@ bool Arduino_LoRaWAN::begin( LMIC_reset(); // if we can get saved state, go on. - if (! this->RestoreSessionState()) + auto const fHaveSavedState = this->RestoreSessionState(); + if (! fHaveSavedState) { // Otherwise set data rate and transmit power, based on regional considerations. this->NetBeginRegionInit(); @@ -69,7 +70,8 @@ bool Arduino_LoRaWAN::begin( // AbpProvisioningInfo abpInfo; - if (this->GetAbpProvisioningInfo(&abpInfo)) + if ((fHaveSavedState || this->GetProvisioningStyle() == ProvisioningStyle::kABP) && + this->GetAbpProvisioningInfo(&abpInfo)) { LMIC_setSession( abpInfo.NetID, From 18d064e389a65020628dc3f2fce48db00c27873d Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Mon, 5 Jul 2021 11:00:25 -0400 Subject: [PATCH 3/7] Fix #170: properly set size and tag of SessionChannelMask --- src/lib/arduino_lorawan_sessionstate.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/arduino_lorawan_sessionstate.cpp b/src/lib/arduino_lorawan_sessionstate.cpp index 098187b..3b77e65 100644 --- a/src/lib/arduino_lorawan_sessionstate.cpp +++ b/src/lib/arduino_lorawan_sessionstate.cpp @@ -99,7 +99,7 @@ Arduino_LoRaWAN::BuildSessionState( // handle EU like regions #if CFG_LMIC_EU_like State.V1.Channels.Header.Tag = State.V1.Channels.Header.kEUlike; - State.V1.Channels.Header.Tag = sizeof(State.V1.Channels.EUlike); + State.V1.Channels.Header.Size = sizeof(State.V1.Channels.EUlike); State.V1.Channels.EUlike.clearAll(); constexpr unsigned maxCh = MAX_CHANNELS < State.V1.Channels.EUlike.nCh ? MAX_CHANNELS : State.V1.Channels.EUlike.nCh; State.V1.Channels.EUlike.ChannelMap = LMIC.channelMap; @@ -135,8 +135,8 @@ Arduino_LoRaWAN::BuildSessionState( } #elif CFG_LMIC_US_like - State.V1.Channels.Header.Tag = State.V1.Channels.Header.kEUlike; - State.V1.Channels.Header.Tag = sizeof(State.V1.Channels.USlike); + State.V1.Channels.Header.Tag = State.V1.Channels.Header.kUSlike; + State.V1.Channels.Header.Size = sizeof(State.V1.Channels.USlike); #if ARDUINO_LMIC_VERSION_COMPARE_GE(ARDUINO_LMIC_VERSION, ARDUINO_LMIC_VERSION_CALC(3,99,0,1)) static_assert( From f581d84889e621f8a860c6ec6abe5899ad8a3eef Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Mon, 5 Jul 2021 11:01:06 -0400 Subject: [PATCH 4/7] Fix #173: Allow validation of CNlike SessionCHannelMasks --- src/lib/arduino_lorawan_sessionstate.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/arduino_lorawan_sessionstate.cpp b/src/lib/arduino_lorawan_sessionstate.cpp index 3b77e65..12103ca 100644 --- a/src/lib/arduino_lorawan_sessionstate.cpp +++ b/src/lib/arduino_lorawan_sessionstate.cpp @@ -286,6 +286,9 @@ bool Arduino_LoRaWAN::SessionState::isValid() const case Arduino_LoRaWAN::SessionChannelMask_Header::eMaskKind::kUSlike: return this->V1.Channels.Header.Size == sizeof(this->V1.Channels.USlike); + case Arduino_LoRaWAN::SessionChannelMask_Header::eMaskKind::kCNlike: + return this->V1.Channels.Header.Size == sizeof(this->V1.Channels.CNlike); + default: return false; } From b426d433ec2cdc8479e2beefde47337b8e17ea1a Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Mon, 5 Jul 2021 11:01:30 -0400 Subject: [PATCH 5/7] This is v0.9.1-pre1 --- src/Arduino_LoRaWAN.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Arduino_LoRaWAN.h b/src/Arduino_LoRaWAN.h index 3dabfb6..602d265 100644 --- a/src/Arduino_LoRaWAN.h +++ b/src/Arduino_LoRaWAN.h @@ -34,7 +34,7 @@ Copyright notice: /// \ref ARDUINO_LORAWAN_VERSION_COMPARE_LT() to compare relative versions. /// #define ARDUINO_LORAWAN_VERSION \ - ARDUINO_LORAWAN_VERSION_CALC(0, 9, 0, 0) /* v0.9.0 */ + ARDUINO_LORAWAN_VERSION_CALC(0, 9, 1, 1) /* v0.9.1-pre1 */ #define ARDUINO_LORAWAN_VERSION_GET_MAJOR(v) \ (((v) >> 24u) & 0xFFu) From f79de9207ad35aa341dfb336ed4c805906caa6f2 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Mon, 5 Jul 2021 17:30:23 -0400 Subject: [PATCH 6/7] Fix #176: on EV_JOINING, call client methods to re-initialize session --- src/lib/arduino_lorawan_begin.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/lib/arduino_lorawan_begin.cpp b/src/lib/arduino_lorawan_begin.cpp index b24fb33..ac26d9e 100644 --- a/src/lib/arduino_lorawan_begin.cpp +++ b/src/lib/arduino_lorawan_begin.cpp @@ -224,6 +224,18 @@ void Arduino_LoRaWAN::StandardEventProcessor( case EV_BEACON_TRACKED: break; case EV_JOINING: + { + // Set data rate and transmit power, based on regional considerations. + // Allows regions to change the channel mask (which otherwise will + // be reset to the region default -- possibly not what you want). + this->NetBeginRegionInit(); + + // Update the session info + this->SaveSessionInfo(); + + // save everything else of interest. + this->SaveSessionState(); + } break; case EV_JOINED: From ff209caa08a65d214f10c2aa316f97cd78659b20 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Mon, 5 Jul 2021 17:31:09 -0400 Subject: [PATCH 7/7] This is v0.9.1-pre2 --- src/Arduino_LoRaWAN.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Arduino_LoRaWAN.h b/src/Arduino_LoRaWAN.h index 602d265..3f1807f 100644 --- a/src/Arduino_LoRaWAN.h +++ b/src/Arduino_LoRaWAN.h @@ -34,7 +34,7 @@ Copyright notice: /// \ref ARDUINO_LORAWAN_VERSION_COMPARE_LT() to compare relative versions. /// #define ARDUINO_LORAWAN_VERSION \ - ARDUINO_LORAWAN_VERSION_CALC(0, 9, 1, 1) /* v0.9.1-pre1 */ + ARDUINO_LORAWAN_VERSION_CALC(0, 9, 1, 2) /* v0.9.1-pre2 */ #define ARDUINO_LORAWAN_VERSION_GET_MAJOR(v) \ (((v) >> 24u) & 0xFFu)