// Create creates the given node-session. The DevAddr must contain the same NwkID as the configured NetID. Node-sessions will expire automatically after the configured TTL. func (a *NodeSessionAPI) Create(ctx context.Context, req *pb.CreateNodeSessionRequest) (*pb.CreateNodeSessionResponse, error) { var appEUI, devEUI lorawan.EUI64 var appSKey, nwkSKey lorawan.AES128Key var devAddr lorawan.DevAddr if err := appEUI.UnmarshalText([]byte(req.AppEUI)); err != nil { return nil, err } if err := devEUI.UnmarshalText([]byte(req.DevEUI)); err != nil { return nil, err } if err := appSKey.UnmarshalText([]byte(req.AppSKey)); err != nil { return nil, err } if err := nwkSKey.UnmarshalText([]byte(req.NwkSKey)); err != nil { return nil, err } if err := devAddr.UnmarshalText([]byte(req.DevAddr)); err != nil { return nil, err } var cFList *lorawan.CFList if len(req.CFList) > len(cFList) { return nil, fmt.Errorf("max CFList channels is %d", len(cFList)) } if len(req.CFList) > 0 { cFList = &lorawan.CFList{} for i, f := range req.CFList { cFList[i] = f } } ns := models.NodeSession{ DevAddr: devAddr, AppEUI: appEUI, DevEUI: devEUI, AppSKey: appSKey, NwkSKey: nwkSKey, FCntUp: req.FCntUp, FCntDown: req.FCntDown, RXDelay: uint8(req.RxDelay), RX1DROffset: uint8(req.Rx1DROffset), CFList: cFList, } if err := a.validateNodeSession(ns); err != nil { return nil, err } if err := storage.CreateNodeSession(a.ctx.RedisPool, ns); err != nil { return nil, err } return &pb.CreateNodeSessionResponse{}, nil }
func TestHandleDataUpScenarios(t *testing.T) { conf := common.GetTestConfig() Convey("Given a clean state, test backends and a node-session", t, func() { app := &testApplicationBackend{ rxPayloadChan: make(chan models.RXPayload, 1), txPayloadChan: make(chan models.TXPayload, 1), notificationPayloadChan: make(chan interface{}, 10), } gw := &testGatewayBackend{ rxPacketChan: make(chan models.RXPacket, 1), txPacketChan: make(chan models.TXPacket, 1), } ctrl := &testControllerBackend{ rxMACPayloadChan: make(chan models.MACPayload, 2), txMACPayloadChan: make(chan models.MACPayload, 1), rxInfoPayloadChan: make(chan models.RXInfoPayload, 1), errorPayloadChan: make(chan models.ErrorPayload, 1), } p := storage.NewRedisPool(conf.RedisURL) common.MustFlushRedis(p) ctx := Context{ RedisPool: p, Gateway: gw, Application: app, Controller: ctrl, } ns := models.NodeSession{ DevAddr: [4]byte{1, 2, 3, 4}, DevEUI: [8]byte{1, 2, 3, 4, 5, 6, 7, 8}, AppSKey: [16]byte{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, NwkSKey: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, FCntUp: 8, FCntDown: 5, AppEUI: [8]byte{8, 7, 6, 5, 4, 3, 2, 1}, } So(storage.CreateNodeSession(ctx.RedisPool, ns), ShouldBeNil) rxInfo := models.RXInfo{ Frequency: common.Band.UplinkChannels[0].Frequency, DataRate: common.Band.DataRates[common.Band.UplinkChannels[0].DataRates[0]], } var fPortZero uint8 var fPortOne uint8 = 1 Convey("Given a set of test-scenarios for basic flows (nothing in queue)", func() { tests := []dataUpTestCase{ // errors { Name: "the application backend returns an error", RXInfo: rxInfo, EncryptFRMPayloadKey: ns.AppSKey, SetMICKey: ns.NwkSKey, ApplicationBackendError: errors.New("BOOM!"), PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataUp, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 10, }, FPort: &fPortOne, }, }, ExpectedControllerRXInfoPayloads: []models.RXInfoPayload{ {DevEUI: ns.DevEUI, FCnt: 10, RXInfo: []models.RXInfo{rxInfo}}, }, ExpectedFCntUp: 8, ExpectedFCntDown: 5, ExpectedHandleRXPacketError: errors.New("send rx payload to application error: BOOM!"), }, { Name: "the frame-counter is invalid", RXInfo: rxInfo, EncryptFRMPayloadKey: ns.AppSKey, SetMICKey: ns.NwkSKey, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataUp, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 7, }, FPort: &fPortOne, }, }, ExpectedFCntUp: 8, ExpectedFCntDown: 5, ExpectedHandleRXPacketError: errors.New("invalid FCnt or too many dropped frames"), }, { Name: "the mic is invalid", RXInfo: rxInfo, EncryptFRMPayloadKey: ns.AppSKey, SetMICKey: ns.AppSKey, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataUp, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 10, }, FPort: &fPortOne, }, }, ExpectedFCntUp: 8, ExpectedFCntDown: 5, ExpectedHandleRXPacketError: errors.New("invalid MIC"), }, // basic flows { Name: "unconfirmed uplink data with payload", RXInfo: rxInfo, EncryptFRMPayloadKey: ns.AppSKey, SetMICKey: ns.NwkSKey, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataUp, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 10, }, FPort: &fPortOne, FRMPayload: []lorawan.Payload{&lorawan.DataPayload{Bytes: []byte{1, 2, 3, 4}}}, }, }, ExpectedControllerRXInfoPayloads: []models.RXInfoPayload{ {DevEUI: ns.DevEUI, FCnt: 10, RXInfo: []models.RXInfo{rxInfo}}, }, ExpectedApplicationRXPayloads: []models.RXPayload{ {DevEUI: ns.DevEUI, FPort: 1, GatewayCount: 1, Data: []byte{1, 2, 3, 4}}, }, ExpectedFCntUp: 10, ExpectedFCntDown: 5, }, { Name: "unconfirmed uplink data without payload (just a FPort)", RXInfo: rxInfo, EncryptFRMPayloadKey: ns.AppSKey, SetMICKey: ns.NwkSKey, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataUp, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 10, }, FPort: &fPortOne, }, }, ExpectedControllerRXInfoPayloads: []models.RXInfoPayload{ {DevEUI: ns.DevEUI, FCnt: 10, RXInfo: []models.RXInfo{rxInfo}}, }, ExpectedApplicationRXPayloads: []models.RXPayload{ {DevEUI: ns.DevEUI, FPort: 1, GatewayCount: 1}, }, ExpectedFCntUp: 10, ExpectedFCntDown: 5, }, { Name: "confirmed uplink data with payload", RXInfo: rxInfo, EncryptFRMPayloadKey: ns.AppSKey, SetMICKey: ns.NwkSKey, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.ConfirmedDataUp, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 10, }, FPort: &fPortOne, FRMPayload: []lorawan.Payload{&lorawan.DataPayload{Bytes: []byte{1, 2, 3, 4}}}, }, }, ExpectedControllerRXInfoPayloads: []models.RXInfoPayload{ {DevEUI: ns.DevEUI, FCnt: 10, RXInfo: []models.RXInfo{rxInfo}}, }, ExpectedApplicationRXPayloads: []models.RXPayload{ {DevEUI: ns.DevEUI, FPort: 1, GatewayCount: 1, Data: []byte{1, 2, 3, 4}}, }, ExpectedGatewayTXPackets: []models.TXPacket{ { TXInfo: models.TXInfo{ Timestamp: rxInfo.Timestamp + 1000000, Frequency: rxInfo.Frequency, Power: 14, DataRate: rxInfo.DataRate, }, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataDown, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 5, FCtrl: lorawan.FCtrl{ ACK: true, }, }, }, }, }, }, ExpectedFCntUp: 10, ExpectedFCntDown: 6, }, { Name: "confirmed uplink data without payload", RXInfo: rxInfo, EncryptFRMPayloadKey: ns.AppSKey, SetMICKey: ns.NwkSKey, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.ConfirmedDataUp, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 10, }, FPort: &fPortOne, }, }, ExpectedControllerRXInfoPayloads: []models.RXInfoPayload{ {DevEUI: ns.DevEUI, FCnt: 10, RXInfo: []models.RXInfo{rxInfo}}, }, ExpectedApplicationRXPayloads: []models.RXPayload{ {DevEUI: ns.DevEUI, FPort: 1, GatewayCount: 1}, }, ExpectedGatewayTXPackets: []models.TXPacket{ { TXInfo: models.TXInfo{ Timestamp: rxInfo.Timestamp + 1000000, Frequency: rxInfo.Frequency, Power: 14, DataRate: rxInfo.DataRate, }, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataDown, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 5, FCtrl: lorawan.FCtrl{ ACK: true, }, }, }, }, }, }, ExpectedFCntUp: 10, ExpectedFCntDown: 6, }, { Name: "two uplink mac commands (FOpts)", RXInfo: rxInfo, EncryptFRMPayloadKey: ns.NwkSKey, SetMICKey: ns.NwkSKey, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataUp, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 10, FOpts: []lorawan.MACCommand{ {CID: lorawan.LinkCheckReq}, {CID: lorawan.LinkADRAns, Payload: &lorawan.LinkADRAnsPayload{ChannelMaskACK: true}}, }, }, }, }, ExpectedControllerRXInfoPayloads: []models.RXInfoPayload{ {DevEUI: ns.DevEUI, FCnt: 10, RXInfo: []models.RXInfo{rxInfo}}, }, ExpectedControllerRXMACPayloads: []models.MACPayload{ {DevEUI: ns.DevEUI, MACCommand: []byte{2}}, {DevEUI: ns.DevEUI, MACCommand: []byte{3, 1}}, }, ExpectedFCntUp: 10, ExpectedFCntDown: 5, }, { Name: "two uplink mac commands (FRMPayload)", RXInfo: rxInfo, EncryptFRMPayloadKey: ns.NwkSKey, SetMICKey: ns.NwkSKey, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataUp, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 10, }, FPort: &fPortZero, FRMPayload: []lorawan.Payload{ &lorawan.MACCommand{CID: lorawan.LinkCheckReq}, &lorawan.MACCommand{CID: lorawan.LinkADRAns, Payload: &lorawan.LinkADRAnsPayload{ChannelMaskACK: true}}, }, }, }, ExpectedControllerRXInfoPayloads: []models.RXInfoPayload{ {DevEUI: ns.DevEUI, FCnt: 10, RXInfo: []models.RXInfo{rxInfo}}, }, ExpectedControllerRXMACPayloads: []models.MACPayload{ {DevEUI: ns.DevEUI, FRMPayload: true, MACCommand: []byte{2}}, {DevEUI: ns.DevEUI, FRMPayload: true, MACCommand: []byte{3, 1}}, }, ExpectedFCntUp: 10, ExpectedFCntDown: 5, }, } runDataUpTests(ctx, ns.DevEUI, ns.DevAddr, tests) }) Convey("Given a set of test-scenarios for mac-command queue", func() { var fPortThree uint8 = 3 tests := []dataUpTestCase{ { Name: "unconfirmed uplink data + two downlink mac commands in queue (FOpts)", RXInfo: rxInfo, EncryptFRMPayloadKey: ns.AppSKey, SetMICKey: ns.NwkSKey, TXMACPayloadQueue: []models.MACPayload{ {DevEUI: ns.DevEUI, MACCommand: []byte{6}}, {DevEUI: ns.DevEUI, MACCommand: []byte{8, 3}}, }, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataUp, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 10, }, FPort: &fPortOne, FRMPayload: []lorawan.Payload{&lorawan.DataPayload{Bytes: []byte{1, 2, 3, 4}}}, }, }, ExpectedControllerRXInfoPayloads: []models.RXInfoPayload{ {DevEUI: ns.DevEUI, FCnt: 10, RXInfo: []models.RXInfo{rxInfo}}, }, ExpectedApplicationRXPayloads: []models.RXPayload{ {DevEUI: ns.DevEUI, FPort: 1, GatewayCount: 1, Data: []byte{1, 2, 3, 4}}, }, ExpectedGatewayTXPackets: []models.TXPacket{ { TXInfo: models.TXInfo{ Timestamp: rxInfo.Timestamp + 1000000, Frequency: rxInfo.Frequency, Power: 14, DataRate: rxInfo.DataRate, }, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataDown, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 5, FOpts: []lorawan.MACCommand{ {CID: lorawan.CID(6)}, {CID: lorawan.CID(8), Payload: &lorawan.RXTimingSetupReqPayload{Delay: 3}}, }, }, }, }, }, }, ExpectedFCntUp: 10, ExpectedFCntDown: 6, }, { Name: "unconfirmed uplink data + two downlink mac commands in queue (FOpts) + unconfirmed tx-payload in queue", RXInfo: rxInfo, EncryptFRMPayloadKey: ns.AppSKey, DecryptExpectedFRMPayloadKey: ns.AppSKey, SetMICKey: ns.NwkSKey, TXPayloadQueue: []models.TXPayload{ {DevEUI: ns.DevEUI, FPort: 3, Data: []byte{4, 5, 6}}, }, TXMACPayloadQueue: []models.MACPayload{ {DevEUI: ns.DevEUI, MACCommand: []byte{6}}, {DevEUI: ns.DevEUI, MACCommand: []byte{8, 3}}, }, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataUp, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 10, }, FPort: &fPortOne, FRMPayload: []lorawan.Payload{&lorawan.DataPayload{Bytes: []byte{1, 2, 3, 4}}}, }, }, ExpectedControllerRXInfoPayloads: []models.RXInfoPayload{ {DevEUI: ns.DevEUI, FCnt: 10, RXInfo: []models.RXInfo{rxInfo}}, }, ExpectedApplicationRXPayloads: []models.RXPayload{ {DevEUI: ns.DevEUI, FPort: 1, GatewayCount: 1, Data: []byte{1, 2, 3, 4}}, }, ExpectedGatewayTXPackets: []models.TXPacket{ { TXInfo: models.TXInfo{ Timestamp: rxInfo.Timestamp + 1000000, Frequency: rxInfo.Frequency, Power: 14, DataRate: rxInfo.DataRate, }, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataDown, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 5, FOpts: []lorawan.MACCommand{ {CID: lorawan.CID(6)}, {CID: lorawan.CID(8), Payload: &lorawan.RXTimingSetupReqPayload{Delay: 3}}, }, }, FPort: &fPortThree, FRMPayload: []lorawan.Payload{ &lorawan.DataPayload{Bytes: []byte{4, 5, 6}}, }, }, }, }, }, ExpectedFCntUp: 10, ExpectedFCntDown: 6, }, { Name: "unconfirmed uplink data + two downlink mac commands in queue (FRMPayload)", RXInfo: rxInfo, EncryptFRMPayloadKey: ns.AppSKey, DecryptExpectedFRMPayloadKey: ns.NwkSKey, SetMICKey: ns.NwkSKey, TXMACPayloadQueue: []models.MACPayload{ {DevEUI: ns.DevEUI, FRMPayload: true, MACCommand: []byte{6}}, {DevEUI: ns.DevEUI, FRMPayload: true, MACCommand: []byte{8, 3}}, }, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataUp, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 10, }, FPort: &fPortOne, FRMPayload: []lorawan.Payload{&lorawan.DataPayload{Bytes: []byte{1, 2, 3, 4}}}, }, }, ExpectedControllerRXInfoPayloads: []models.RXInfoPayload{ {DevEUI: ns.DevEUI, FCnt: 10, RXInfo: []models.RXInfo{rxInfo}}, }, ExpectedApplicationRXPayloads: []models.RXPayload{ {DevEUI: ns.DevEUI, FPort: 1, GatewayCount: 1, Data: []byte{1, 2, 3, 4}}, }, ExpectedGatewayTXPackets: []models.TXPacket{ { TXInfo: models.TXInfo{ Timestamp: rxInfo.Timestamp + 1000000, Frequency: rxInfo.Frequency, Power: 14, DataRate: rxInfo.DataRate, }, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataDown, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 5, }, FPort: &fPortZero, FRMPayload: []lorawan.Payload{ &lorawan.MACCommand{CID: lorawan.CID(6)}, &lorawan.MACCommand{CID: lorawan.CID(8), Payload: &lorawan.RXTimingSetupReqPayload{Delay: 3}}, }, }, }, }, }, ExpectedFCntUp: 10, ExpectedFCntDown: 6, }, { Name: "unconfirmed uplink data + two downlink mac commands in queue (FRMPayload) + unconfirmed tx-payload in queue", RXInfo: rxInfo, EncryptFRMPayloadKey: ns.AppSKey, DecryptExpectedFRMPayloadKey: ns.NwkSKey, SetMICKey: ns.NwkSKey, TXPayloadQueue: []models.TXPayload{ {DevEUI: ns.DevEUI, FPort: 3, Data: []byte{4, 5, 6}}, }, TXMACPayloadQueue: []models.MACPayload{ {DevEUI: ns.DevEUI, FRMPayload: true, MACCommand: []byte{6}}, {DevEUI: ns.DevEUI, FRMPayload: true, MACCommand: []byte{8, 3}}, }, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataUp, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 10, }, FPort: &fPortOne, FRMPayload: []lorawan.Payload{&lorawan.DataPayload{Bytes: []byte{1, 2, 3, 4}}}, }, }, ExpectedControllerRXInfoPayloads: []models.RXInfoPayload{ {DevEUI: ns.DevEUI, FCnt: 10, RXInfo: []models.RXInfo{rxInfo}}, }, ExpectedApplicationRXPayloads: []models.RXPayload{ {DevEUI: ns.DevEUI, FPort: 1, GatewayCount: 1, Data: []byte{1, 2, 3, 4}}, }, ExpectedGatewayTXPackets: []models.TXPacket{ { TXInfo: models.TXInfo{ Timestamp: rxInfo.Timestamp + 1000000, Frequency: rxInfo.Frequency, Power: 14, DataRate: rxInfo.DataRate, }, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataDown, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 5, FCtrl: lorawan.FCtrl{ FPending: true, }, }, FPort: &fPortZero, FRMPayload: []lorawan.Payload{ &lorawan.MACCommand{CID: lorawan.CID(6)}, &lorawan.MACCommand{CID: lorawan.CID(8), Payload: &lorawan.RXTimingSetupReqPayload{Delay: 3}}, }, }, }, }, }, ExpectedGetTXPayloadFromQueue: &models.TXPayload{DevEUI: ns.DevEUI, FPort: 3, Data: []byte{4, 5, 6}}, ExpectedFCntUp: 10, ExpectedFCntDown: 6, }, { Name: "unconfirmed uplink data + 18 bytes of MAC commands (FOpts) of which one is invalid", RXInfo: rxInfo, EncryptFRMPayloadKey: ns.AppSKey, SetMICKey: ns.NwkSKey, TXMACPayloadQueue: []models.MACPayload{ {Reference: "a", DevEUI: ns.DevEUI, MACCommand: []byte{2, 10, 5}}, {Reference: "b", DevEUI: ns.DevEUI, MACCommand: []byte{6}}, {Reference: "c", DevEUI: ns.DevEUI, MACCommand: []byte{4, 15}}, {Reference: "d", DevEUI: ns.DevEUI, MACCommand: []byte{4, 15, 16}}, // invalid payload, should be discarded + error notification {Reference: "e", DevEUI: ns.DevEUI, MACCommand: []byte{2, 10, 5}}, {Reference: "f", DevEUI: ns.DevEUI, MACCommand: []byte{2, 10, 5}}, {Reference: "g", DevEUI: ns.DevEUI, MACCommand: []byte{2, 10, 6}}, // this payload should stay in the queue }, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataUp, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 10, }, FPort: &fPortOne, FRMPayload: []lorawan.Payload{&lorawan.DataPayload{Bytes: []byte{1, 2, 3, 4}}}, }, }, ExpectedControllerRXInfoPayloads: []models.RXInfoPayload{ {DevEUI: ns.DevEUI, FCnt: 10, RXInfo: []models.RXInfo{rxInfo}}, }, ExpectedControllerErrorPayloads: []models.ErrorPayload{ {Reference: "d", DevEUI: ns.DevEUI, Message: "unmarshal mac command error: lorawan: 1 byte of data is expected"}, }, ExpectedApplicationRXPayloads: []models.RXPayload{ {DevEUI: ns.DevEUI, FPort: 1, GatewayCount: 1, Data: []byte{1, 2, 3, 4}}, }, ExpectedGatewayTXPackets: []models.TXPacket{ { TXInfo: models.TXInfo{ Timestamp: rxInfo.Timestamp + 1000000, Frequency: rxInfo.Frequency, Power: 14, DataRate: rxInfo.DataRate, }, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataDown, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 5, FCtrl: lorawan.FCtrl{ FPending: true, }, FOpts: []lorawan.MACCommand{ {CID: lorawan.LinkCheckAns, Payload: &lorawan.LinkCheckAnsPayload{Margin: 10, GwCnt: 5}}, {CID: lorawan.DevStatusReq}, {CID: lorawan.DutyCycleReq, Payload: &lorawan.DutyCycleReqPayload{MaxDCCycle: 15}}, {CID: lorawan.LinkCheckAns, Payload: &lorawan.LinkCheckAnsPayload{Margin: 10, GwCnt: 5}}, {CID: lorawan.LinkCheckAns, Payload: &lorawan.LinkCheckAnsPayload{Margin: 10, GwCnt: 5}}, }, }, }, }, }, }, ExpectedTXMACPayloadQueue: []models.MACPayload{ {Reference: "g", DevEUI: ns.DevEUI, MACCommand: []byte{2, 10, 6}}, }, ExpectedFCntUp: 10, ExpectedFCntDown: 6, }, } runDataUpTests(ctx, ns.DevEUI, ns.DevAddr, tests) }) Convey("Given a set of test-scenarios for tx-payload queue", func() { var fPortTen uint8 = 10 tests := []dataUpTestCase{ { Name: "unconfirmed uplink data + one unconfirmed downlink payload in queue", RXInfo: rxInfo, EncryptFRMPayloadKey: ns.AppSKey, DecryptExpectedFRMPayloadKey: ns.AppSKey, SetMICKey: ns.NwkSKey, TXPayloadQueue: []models.TXPayload{ {Reference: "a", DevEUI: ns.DevEUI, FPort: 10, Data: []byte{1, 2, 3, 4}}, }, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataUp, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 10, }, FPort: &fPortOne, }, }, ExpectedControllerRXInfoPayloads: []models.RXInfoPayload{ {DevEUI: ns.DevEUI, FCnt: 10, RXInfo: []models.RXInfo{rxInfo}}, }, ExpectedApplicationRXPayloads: []models.RXPayload{ {DevEUI: ns.DevEUI, FPort: 1, GatewayCount: 1}, }, ExpectedGatewayTXPackets: []models.TXPacket{ { TXInfo: models.TXInfo{ Timestamp: rxInfo.Timestamp + 1000000, Frequency: rxInfo.Frequency, Power: 14, DataRate: rxInfo.DataRate, }, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataDown, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 5, }, FPort: &fPortTen, FRMPayload: []lorawan.Payload{ &lorawan.DataPayload{Bytes: []byte{1, 2, 3, 4}}, }, }, }, }, }, ExpectedFCntUp: 10, ExpectedFCntDown: 6, }, { Name: "unconfirmed uplink data + two unconfirmed downlink payloads in queue", RXInfo: rxInfo, EncryptFRMPayloadKey: ns.AppSKey, DecryptExpectedFRMPayloadKey: ns.AppSKey, SetMICKey: ns.NwkSKey, TXPayloadQueue: []models.TXPayload{ {Reference: "a", DevEUI: ns.DevEUI, FPort: 10, Data: []byte{1, 2, 3, 4}}, {Reference: "b", DevEUI: ns.DevEUI, FPort: 10, Data: []byte{4, 3, 2, 1}}, }, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataUp, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 10, }, FPort: &fPortOne, }, }, ExpectedControllerRXInfoPayloads: []models.RXInfoPayload{ {DevEUI: ns.DevEUI, FCnt: 10, RXInfo: []models.RXInfo{rxInfo}}, }, ExpectedApplicationRXPayloads: []models.RXPayload{ {DevEUI: ns.DevEUI, FPort: 1, GatewayCount: 1}, }, ExpectedGatewayTXPackets: []models.TXPacket{ { TXInfo: models.TXInfo{ Timestamp: rxInfo.Timestamp + 1000000, Frequency: rxInfo.Frequency, Power: 14, DataRate: rxInfo.DataRate, }, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataDown, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 5, FCtrl: lorawan.FCtrl{ FPending: true, }, }, FPort: &fPortTen, FRMPayload: []lorawan.Payload{ &lorawan.DataPayload{Bytes: []byte{1, 2, 3, 4}}, }, }, }, }, }, ExpectedFCntUp: 10, ExpectedFCntDown: 6, ExpectedGetTXPayloadFromQueue: &models.TXPayload{Reference: "b", DevEUI: ns.DevEUI, FPort: 10, Data: []byte{4, 3, 2, 1}}, }, { Name: "unconfirmed uplink data + one confirmed downlink payload in queue", RXInfo: rxInfo, EncryptFRMPayloadKey: ns.AppSKey, DecryptExpectedFRMPayloadKey: ns.AppSKey, SetMICKey: ns.NwkSKey, TXPayloadQueue: []models.TXPayload{ {Reference: "a", Confirmed: true, DevEUI: ns.DevEUI, FPort: 10, Data: []byte{1, 2, 3, 4}}, }, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataUp, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 10, }, FPort: &fPortOne, }, }, ExpectedControllerRXInfoPayloads: []models.RXInfoPayload{ {DevEUI: ns.DevEUI, FCnt: 10, RXInfo: []models.RXInfo{rxInfo}}, }, ExpectedApplicationRXPayloads: []models.RXPayload{ {DevEUI: ns.DevEUI, FPort: 1, GatewayCount: 1}, }, ExpectedGatewayTXPackets: []models.TXPacket{ { TXInfo: models.TXInfo{ Timestamp: rxInfo.Timestamp + 1000000, Frequency: rxInfo.Frequency, Power: 14, DataRate: rxInfo.DataRate, }, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.ConfirmedDataDown, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 5, }, FPort: &fPortTen, FRMPayload: []lorawan.Payload{ &lorawan.DataPayload{Bytes: []byte{1, 2, 3, 4}}, }, }, }, }, }, ExpectedFCntUp: 10, ExpectedFCntDown: 5, // will be incremented after the node ACKs the frame ExpectedGetTXPayloadFromQueue: &models.TXPayload{Reference: "a", Confirmed: true, DevEUI: ns.DevEUI, FPort: 10, Data: []byte{1, 2, 3, 4}}, }, { Name: "unconfirmed uplink data with ACK + one confirmed downlink payload in in-process queue", RXInfo: rxInfo, EncryptFRMPayloadKey: ns.AppSKey, DecryptExpectedFRMPayloadKey: ns.AppSKey, SetMICKey: ns.NwkSKey, TXMACPayloadInProcess: &models.TXPayload{Reference: "a", Confirmed: true, DevEUI: ns.DevEUI, FPort: 10, Data: []byte{1, 2, 3, 4}}, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataUp, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 10, FCtrl: lorawan.FCtrl{ ACK: true, }, }, FPort: &fPortOne, }, }, ExpectedControllerRXInfoPayloads: []models.RXInfoPayload{ {DevEUI: ns.DevEUI, FCnt: 10, RXInfo: []models.RXInfo{rxInfo}}, }, ExpectedApplicationRXPayloads: []models.RXPayload{ {DevEUI: ns.DevEUI, FPort: 1, GatewayCount: 1}, }, ExpectedApplicationNotifications: []interface{}{ models.ACKNotification{Reference: "a", DevEUI: ns.DevEUI}, }, ExpectedFCntUp: 10, ExpectedFCntDown: 6, // has been updated because of the ACK }, { Name: "unconfirmed uplink data + two unconfirmed downlink payload in queue of which the first exceeds the max payload size (for dr 0)", RXInfo: rxInfo, EncryptFRMPayloadKey: ns.AppSKey, DecryptExpectedFRMPayloadKey: ns.AppSKey, SetMICKey: ns.NwkSKey, TXPayloadQueue: []models.TXPayload{ {Reference: "a", DevEUI: ns.DevEUI, FPort: 10, Data: make([]byte, 52)}, {Reference: "b", DevEUI: ns.DevEUI, FPort: 10, Data: []byte{1, 2, 3, 4}}, }, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataUp, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 10, }, FPort: &fPortOne, }, }, ExpectedControllerRXInfoPayloads: []models.RXInfoPayload{ {DevEUI: ns.DevEUI, FCnt: 10, RXInfo: []models.RXInfo{rxInfo}}, }, ExpectedApplicationRXPayloads: []models.RXPayload{ {DevEUI: ns.DevEUI, FPort: 1, GatewayCount: 1}, }, ExpectedApplicationNotifications: []interface{}{ models.ErrorPayload{Reference: "a", DevEUI: ns.DevEUI, Message: "downlink payload max size exceeded (dr: 0, allowed: 51, got: 52)"}, }, ExpectedGatewayTXPackets: []models.TXPacket{ { TXInfo: models.TXInfo{ Timestamp: rxInfo.Timestamp + 1000000, Frequency: rxInfo.Frequency, Power: 14, DataRate: rxInfo.DataRate, }, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataDown, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 5, }, FPort: &fPortTen, FRMPayload: []lorawan.Payload{ &lorawan.DataPayload{Bytes: []byte{1, 2, 3, 4}}, }, }, }, }, }, ExpectedFCntUp: 10, ExpectedFCntDown: 6, }, { Name: "unconfirmed uplink data + one unconfirmed downlink payload in queue (exactly max size for dr 0) + one mac command", RXInfo: rxInfo, EncryptFRMPayloadKey: ns.AppSKey, DecryptExpectedFRMPayloadKey: ns.AppSKey, SetMICKey: ns.NwkSKey, TXPayloadQueue: []models.TXPayload{ {Reference: "a", DevEUI: ns.DevEUI, FPort: 10, Data: make([]byte, 51)}, }, TXMACPayloadQueue: []models.MACPayload{ {DevEUI: ns.DevEUI, MACCommand: []byte{6}}, }, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataUp, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 10, }, FPort: &fPortOne, }, }, ExpectedControllerRXInfoPayloads: []models.RXInfoPayload{ {DevEUI: ns.DevEUI, FCnt: 10, RXInfo: []models.RXInfo{rxInfo}}, }, ExpectedApplicationRXPayloads: []models.RXPayload{ {DevEUI: ns.DevEUI, FPort: 1, GatewayCount: 1}, }, ExpectedGatewayTXPackets: []models.TXPacket{ { TXInfo: models.TXInfo{ Timestamp: rxInfo.Timestamp + 1000000, Frequency: rxInfo.Frequency, Power: 14, DataRate: rxInfo.DataRate, }, PHYPayload: lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataDown, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: ns.DevAddr, FCnt: 5, FCtrl: lorawan.FCtrl{ FPending: true, }, FOpts: []lorawan.MACCommand{ {CID: lorawan.CID(6)}, }, }, }, }, }, }, ExpectedFCntUp: 10, ExpectedFCntDown: 6, ExpectedGetTXPayloadFromQueue: &models.TXPayload{Reference: "a", DevEUI: ns.DevEUI, FPort: 10, Data: make([]byte, 51)}, }, } runDataUpTests(ctx, ns.DevEUI, ns.DevAddr, tests) }) }) }