Skip to content

Commit 2849d02

Browse files
committed
Fix FCnt rollover regression introduced in 0.12.0.
See brocaar/chirpstack-application-server#6.
1 parent 7855dcf commit 2849d02

File tree

4 files changed

+93
-17
lines changed

4 files changed

+93
-17
lines changed

docs/changelog.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## 0.12.4
4+
5+
* Fix regression that caused a FCnt roll-over to result in an invalid MIC
6+
error. This was caused by validating the MIC before expanding the 16 bit
7+
FCnt to the full 32 bit value. (thanks @andrepferreira)
8+
39
## 0.12.3
410

511
* Relax frame-counter option.

internal/testsuite/uplink_test.go

+67-1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,15 @@ func TestUplinkScenarios(t *testing.T) {
7979
AppEUI: [8]byte{8, 7, 6, 5, 4, 3, 2, 1},
8080
}
8181

82+
nsFCntRollOver := session.NodeSession{
83+
DevAddr: [4]byte{1, 2, 3, 4},
84+
DevEUI: [8]byte{1, 2, 3, 4, 5, 6, 7, 8},
85+
NwkSKey: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
86+
FCntUp: 65535,
87+
FCntDown: 5,
88+
AppEUI: [8]byte{8, 7, 6, 5, 4, 3, 2, 1},
89+
}
90+
8291
nsRelaxFCnt := session.NodeSession{
8392
DevAddr: [4]byte{1, 2, 3, 4},
8493
DevEUI: [8]byte{1, 2, 3, 4, 5, 6, 7, 8},
@@ -171,6 +180,31 @@ func TestUplinkScenarios(t *testing.T) {
171180
},
172181
}
173182

183+
expectedApplicationPushDataUpFCntRollOver := &as.HandleDataUpRequest{
184+
AppEUI: ns.AppEUI[:],
185+
DevEUI: ns.DevEUI[:],
186+
FCnt: 65536,
187+
FPort: 1,
188+
Data: []byte{1, 2, 3, 4},
189+
TxInfo: &as.TXInfo{
190+
Frequency: int64(rxInfo.Frequency),
191+
DataRate: &as.DataRate{
192+
Modulation: string(rxInfo.DataRate.Modulation),
193+
BandWidth: uint32(rxInfo.DataRate.Bandwidth),
194+
SpreadFactor: uint32(rxInfo.DataRate.SpreadFactor),
195+
Bitrate: uint32(rxInfo.DataRate.BitRate),
196+
},
197+
},
198+
RxInfo: []*as.RXInfo{
199+
{
200+
Mac: rxInfo.MAC[:],
201+
Time: rxInfo.Time.Format(time.RFC3339Nano),
202+
Rssi: int32(rxInfo.RSSI),
203+
LoRaSNR: rxInfo.LoRaSNR,
204+
},
205+
},
206+
}
207+
174208
expectedApplicationPushDataUpNoData := &as.HandleDataUpRequest{
175209
AppEUI: ns.AppEUI[:],
176210
DevEUI: ns.DevEUI[:],
@@ -732,6 +766,31 @@ func TestUplinkScenarios(t *testing.T) {
732766
ExpectedFCntUp: 10,
733767
ExpectedFCntDown: 5,
734768
},
769+
{
770+
Name: "unconfirmed uplink with FCnt rollover",
771+
NodeSession: nsFCntRollOver,
772+
RXInfo: rxInfo,
773+
SetMICKey: ns.NwkSKey,
774+
PHYPayload: lorawan.PHYPayload{
775+
MHDR: lorawan.MHDR{
776+
MType: lorawan.UnconfirmedDataUp,
777+
Major: lorawan.LoRaWANR1,
778+
},
779+
MACPayload: &lorawan.MACPayload{
780+
FHDR: lorawan.FHDR{
781+
DevAddr: ns.DevAddr,
782+
FCnt: 65536,
783+
},
784+
FPort: &fPortOne,
785+
FRMPayload: []lorawan.Payload{&lorawan.DataPayload{Bytes: []byte{1, 2, 3, 4}}},
786+
},
787+
},
788+
ExpectedControllerHandleRXInfo: expectedControllerHandleRXInfo,
789+
ExpectedApplicationHandleDataUp: expectedApplicationPushDataUpFCntRollOver,
790+
ExpectedApplicationGetDataDown: expectedGetDataDown,
791+
ExpectedFCntUp: 65536,
792+
ExpectedFCntDown: 5,
793+
},
735794
}
736795

737796
runUplinkTests(ctx, tests)
@@ -1319,10 +1378,17 @@ func runUplinkTests(ctx common.Context, tests []uplinkTestCase) {
13191378
}
13201379
So(t.PHYPayload.SetMIC(t.SetMICKey), ShouldBeNil)
13211380

1381+
// marshal and unmarshal the PHYPayload to make sure the FCnt gets
1382+
// truncated to to 16 bit
1383+
var phy lorawan.PHYPayload
1384+
b, err := t.PHYPayload.MarshalBinary()
1385+
So(err, ShouldBeNil)
1386+
So(phy.UnmarshalBinary(b), ShouldBeNil)
1387+
13221388
// create RXPacket and call HandleRXPacket
13231389
rxPacket := gw.RXPacket{
13241390
RXInfo: t.RXInfo,
1325-
PHYPayload: t.PHYPayload,
1391+
PHYPayload: phy,
13261392
}
13271393
So(uplink.HandleRXPacket(ctx, rxPacket), ShouldResemble, t.ExpectedHandleRXPacketError)
13281394

internal/uplink/data.go

+19-15
Original file line numberDiff line numberDiff line change
@@ -32,21 +32,6 @@ func validateAndCollectDataUpRXPacket(ctx common.Context, rxPacket gw.RXPacket)
3232
return err
3333
}
3434

35-
// validate MIC
36-
micOK, err := rxPacket.PHYPayload.ValidateMIC(ns.NwkSKey)
37-
if err != nil {
38-
return fmt.Errorf("validate MIC error: %s", err)
39-
}
40-
if !micOK {
41-
ctx.Application.HandleError(context.Background(), &as.HandleErrorRequest{
42-
AppEUI: ns.AppEUI[:],
43-
DevEUI: ns.DevEUI[:],
44-
Type: as.ErrorType_DATA_UP_MIC,
45-
Error: "invalid MIC",
46-
})
47-
return errors.New("invalid MIC")
48-
}
49-
5035
// validate and get the full int32 FCnt
5136
fullFCnt, ok := session.ValidateAndGetFullFCntUp(ns, macPL.FHDR.FCnt)
5237
if !ok {
@@ -81,6 +66,21 @@ func validateAndCollectDataUpRXPacket(ctx common.Context, rxPacket gw.RXPacket)
8166
}
8267
macPL.FHDR.FCnt = fullFCnt
8368

69+
// validate MIC
70+
micOK, err := rxPacket.PHYPayload.ValidateMIC(ns.NwkSKey)
71+
if err != nil {
72+
return fmt.Errorf("validate MIC error: %s", err)
73+
}
74+
if !micOK {
75+
ctx.Application.HandleError(context.Background(), &as.HandleErrorRequest{
76+
AppEUI: ns.AppEUI[:],
77+
DevEUI: ns.DevEUI[:],
78+
Type: as.ErrorType_DATA_UP_MIC,
79+
Error: "invalid MIC",
80+
})
81+
return errors.New("invalid MIC")
82+
}
83+
8484
if macPL.FPort != nil {
8585
if *macPL.FPort == 0 {
8686
// decrypt FRMPayload with NwkSKey when FPort == 0
@@ -111,6 +111,10 @@ func handleCollectedDataUpPackets(ctx common.Context, rxPacket models.RXPacket)
111111
return err
112112
}
113113

114+
// expand the FCnt, the value itself has already been validated during the
115+
// collection, so there is no need to handle the ok value
116+
macPL.FHDR.FCnt, _ = session.ValidateAndGetFullFCntUp(ns, macPL.FHDR.FCnt)
117+
114118
log.WithFields(log.Fields{
115119
"dev_eui": ns.DevEUI,
116120
"gw_count": len(macs),

mkdocs.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pages:
1515
- changelog.md
1616

1717
extra:
18-
version: '0.12.3'
18+
version: '0.12.4'
1919
github:
2020
download_release: true
2121

0 commit comments

Comments
 (0)