// newReferenceUplink returns a default uplink message func newReferenceUplink() *pb.UplinkMessage { gtwID := "eui-0102030405060708" phy := lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataUp, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: lorawan.DevAddr([4]byte{1, 2, 3, 4}), }, }, } bytes, _ := phy.MarshalBinary() up := &pb.UplinkMessage{ Payload: bytes, ProtocolMetadata: &pb_protocol.RxMetadata{Protocol: &pb_protocol.RxMetadata_Lorawan{Lorawan: &pb_lorawan.Metadata{ CodingRate: "4/5", DataRate: "SF7BW125", Modulation: pb_lorawan.Modulation_LORA, }}}, GatewayMetadata: &pb_gateway.RxMetadata{ GatewayId: gtwID, Timestamp: 100, Frequency: 868100000, Rssi: -25.0, Snr: 5.0, }, } return up }
func (n *networkServer) HandleDownlink(message *pb_broker.DownlinkMessage) (*pb_broker.DownlinkMessage, error) { // Get Device dev, err := n.devices.Get(*message.AppEui, *message.DevEui) if err != nil { return nil, err } n.status.downlink.Mark(1) dev.StartUpdate() if dev.AppID != message.AppId || dev.DevID != message.DevId { return nil, errors.NewErrInvalidArgument("Downlink", "AppID and DevID do not match AppEUI and DevEUI") } // Unmarshal LoRaWAN Payload var phyPayload lorawan.PHYPayload err = phyPayload.UnmarshalBinary(message.Payload) if err != nil { return nil, err } macPayload, ok := phyPayload.MACPayload.(*lorawan.MACPayload) if !ok { return nil, errors.NewErrInvalidArgument("Downlink", "does not contain a MAC payload") } // Set DevAddr macPayload.FHDR.DevAddr = lorawan.DevAddr(dev.DevAddr) // FIRST set and THEN increment FCntDown // TODO: For confirmed downlink, FCntDown should be incremented AFTER ACK macPayload.FHDR.FCnt = dev.FCntDown dev.FCntDown++ err = n.devices.Set(dev) if err != nil { return nil, err } // Sign MIC phyPayload.SetMIC(lorawan.AES128Key(dev.NwkSKey)) // Update message bytes, err := phyPayload.MarshalBinary() if err != nil { return nil, err } message.Payload = bytes return message, nil }
func doTestHandleActivation(h *handler, appEUI types.AppEUI, devEUI types.DevEUI, devNonce [2]byte, appKey types.AppKey) (*pb.DeviceActivationResponse, error) { devAddr := types.DevAddr{1, 2, 3, 4} requestPHY := lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.JoinRequest, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.JoinRequestPayload{ AppEUI: lorawan.EUI64(appEUI), DevEUI: lorawan.EUI64(devEUI), DevNonce: devNonce, }, } requestPHY.SetMIC(lorawan.AES128Key(appKey)) requestBytes, _ := requestPHY.MarshalBinary() responsePHY := lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.JoinAccept, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.JoinAcceptPayload{}, } templateBytes, _ := responsePHY.MarshalBinary() return h.HandleActivation(&pb_broker.DeduplicatedDeviceActivationRequest{ Payload: requestBytes, AppEui: &appEUI, AppId: appEUI.String(), DevEui: &devEUI, DevId: devEUI.String(), ActivationMetadata: &pb_protocol.ActivationMetadata{Protocol: &pb_protocol.ActivationMetadata_Lorawan{ Lorawan: &pb_lorawan.ActivationMetadata{ DevAddr: &devAddr, }, }}, ResponseTemplate: &pb_broker.DeviceActivationResponse{ Payload: templateBytes, }, }) }
func TestHandleDownlink(t *testing.T) { a := New(t) ns := &networkServer{ devices: device.NewRedisDeviceStore(GetRedisClient(), "test-handle-downlink"), } ns.InitStatus() appEUI := types.AppEUI(getEUI(1, 2, 3, 4, 5, 6, 7, 8)) devEUI := types.DevEUI(getEUI(1, 2, 3, 4, 5, 6, 7, 8)) devAddr := getDevAddr(1, 2, 3, 4) // Device Not Found message := &pb_broker.DownlinkMessage{ AppEui: &appEUI, DevEui: &devEUI, Payload: []byte{}, } _, err := ns.HandleDownlink(message) a.So(err, ShouldNotBeNil) ns.devices.Set(&device.Device{ DevAddr: devAddr, AppEUI: appEUI, DevEUI: devEUI, }) defer func() { ns.devices.Delete(appEUI, devEUI) }() // Invalid Payload message = &pb_broker.DownlinkMessage{ AppEui: &appEUI, DevEui: &devEUI, Payload: []byte{}, } _, err = ns.HandleDownlink(message) a.So(err, ShouldNotBeNil) fPort := uint8(3) phy := lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataDown, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FPort: &fPort, FHDR: lorawan.FHDR{ FCtrl: lorawan.FCtrl{ ACK: true, }, }, }, } bytes, _ := phy.MarshalBinary() message = &pb_broker.DownlinkMessage{ AppEui: &appEUI, DevEui: &devEUI, Payload: bytes, } res, err := ns.HandleDownlink(message) a.So(err, ShouldBeNil) var phyPayload lorawan.PHYPayload phyPayload.UnmarshalBinary(res.Payload) macPayload, _ := phyPayload.MACPayload.(*lorawan.MACPayload) a.So(*macPayload.FPort, ShouldEqual, 3) a.So(macPayload.FHDR.DevAddr, ShouldEqual, lorawan.DevAddr{1, 2, 3, 4}) a.So(macPayload.FHDR.FCnt, ShouldEqual, 0) // The first Frame counter is zero a.So(phyPayload.MIC, ShouldNotEqual, [4]byte{0, 0, 0, 0}) // MIC should be set, we'll check it with actual examples in the integration test dev, _ := ns.devices.Get(appEUI, devEUI) a.So(dev.FCntDown, ShouldEqual, 1) }
func TestHandleUplink(t *testing.T) { a := New(t) ns := &networkServer{ devices: device.NewRedisDeviceStore(GetRedisClient(), "ns-test-handle-uplink"), } ns.InitStatus() appEUI := types.AppEUI(getEUI(1, 2, 3, 4, 5, 6, 7, 8)) devEUI := types.DevEUI(getEUI(1, 2, 3, 4, 5, 6, 7, 8)) devAddr := getDevAddr(1, 2, 3, 4) // Device Not Found message := &pb_broker.DeduplicatedUplinkMessage{ AppEui: &appEUI, DevEui: &devEUI, Payload: []byte{}, } _, err := ns.HandleUplink(message) a.So(err, ShouldNotBeNil) ns.devices.Set(&device.Device{ DevAddr: devAddr, AppEUI: appEUI, DevEUI: devEUI, }) defer func() { ns.devices.Delete(appEUI, devEUI) }() // Invalid Payload message = &pb_broker.DeduplicatedUplinkMessage{ AppEui: &appEUI, DevEui: &devEUI, Payload: []byte{}, } _, err = ns.HandleUplink(message) a.So(err, ShouldNotBeNil) phy := lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataUp, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: lorawan.DevAddr([4]byte{1, 2, 3, 4}), FCnt: 1, FCtrl: lorawan.FCtrl{ ADR: true, ADRACKReq: true, }, FOpts: []lorawan.MACCommand{ lorawan.MACCommand{CID: lorawan.LinkCheckReq}, }, }, }, } bytes, _ := phy.MarshalBinary() // Valid Uplink message = &pb_broker.DeduplicatedUplinkMessage{ AppEui: &appEUI, DevEui: &devEUI, Payload: bytes, ResponseTemplate: &pb_broker.DownlinkMessage{}, GatewayMetadata: []*pb_gateway.RxMetadata{ &pb_gateway.RxMetadata{}, }, ProtocolMetadata: &pb_protocol.RxMetadata{Protocol: &pb_protocol.RxMetadata_Lorawan{ Lorawan: &pb_lorawan.Metadata{ DataRate: "SF7BW125", }, }}, } res, err := ns.HandleUplink(message) a.So(err, ShouldBeNil) a.So(res.ResponseTemplate, ShouldNotBeNil) // LoRaWAN: Unmarshal var phyPayload lorawan.PHYPayload phyPayload.UnmarshalBinary(res.ResponseTemplate.Payload) macPayload, _ := phyPayload.MACPayload.(*lorawan.MACPayload) // ResponseTemplate DevAddr should match a.So([4]byte(macPayload.FHDR.DevAddr), ShouldEqual, [4]byte(devAddr)) // ResponseTemplate should ACK the ADRACKReq a.So(macPayload.FHDR.FCtrl.ACK, ShouldBeTrue) a.So(macPayload.FHDR.FOpts, ShouldHaveLength, 1) a.So(macPayload.FHDR.FOpts[0].Payload, ShouldResemble, &lorawan.LinkCheckAnsPayload{GwCnt: 1, Margin: 7}) // Frame Counter should have been updated dev, _ := ns.devices.Get(appEUI, devEUI) a.So(dev.FCntUp, ShouldEqual, 1) a.So(time.Now().Sub(dev.LastSeen), ShouldBeLessThan, 1*time.Second) }
func (b *broker) HandleActivation(activation *pb.DeviceActivationRequest) (res *pb.DeviceActivationResponse, err error) { ctx := b.Ctx.WithFields(log.Fields{ "GatewayID": activation.GatewayMetadata.GatewayId, "AppEUI": *activation.AppEui, "DevEUI": *activation.DevEui, }) start := time.Now() defer func() { if err != nil { ctx.WithError(err).Warn("Could not handle activation") } else { ctx.WithField("Duration", time.Now().Sub(start)).Info("Handled activation") } }() time := time.Now() b.status.activations.Mark(1) // De-duplicate uplink messages duplicates := b.deduplicateActivation(activation) if len(duplicates) == 0 { return nil, errDuplicateActivation } b.status.activationsUnique.Mark(1) base := duplicates[0] // Collect GatewayMetadata and DownlinkOptions var gatewayMetadata []*gateway.RxMetadata var downlinkOptions []*pb.DownlinkOption var deviceActivationResponse *pb.DeviceActivationResponse for _, duplicate := range duplicates { gatewayMetadata = append(gatewayMetadata, duplicate.GatewayMetadata) downlinkOptions = append(downlinkOptions, duplicate.DownlinkOptions...) } // Select best DownlinkOption if len(downlinkOptions) > 0 { deviceActivationResponse = &pb.DeviceActivationResponse{ DownlinkOption: selectBestDownlink(downlinkOptions), } } // Build Uplink deduplicatedActivationRequest := &pb.DeduplicatedDeviceActivationRequest{ Payload: base.Payload, DevEui: base.DevEui, AppEui: base.AppEui, ProtocolMetadata: base.ProtocolMetadata, GatewayMetadata: gatewayMetadata, ActivationMetadata: base.ActivationMetadata, ServerTime: time.UnixNano(), ResponseTemplate: deviceActivationResponse, } // Send Activate to NS deduplicatedActivationRequest, err = b.ns.PrepareActivation(b.Component.GetContext(b.nsToken), deduplicatedActivationRequest) if err != nil { return nil, errors.Wrap(errors.FromGRPCError(err), "NetworkServer refused to prepare activation") } ctx = ctx.WithFields(log.Fields{ "AppID": deduplicatedActivationRequest.AppId, "DevID": deduplicatedActivationRequest.DevId, }) // Find Handler (based on AppEUI) var announcements []*pb_discovery.Announcement announcements, err = b.Discovery.GetAllHandlersForAppID(deduplicatedActivationRequest.AppId) if err != nil { return nil, err } if len(announcements) == 0 { return nil, errors.NewErrNotFound(fmt.Sprintf("Handler for AppID %s", deduplicatedActivationRequest.AppId)) } ctx = ctx.WithField("NumHandlers", len(announcements)) // LoRaWAN: Unmarshal and prepare version without MIC var phyPayload lorawan.PHYPayload err = phyPayload.UnmarshalBinary(deduplicatedActivationRequest.Payload) if err != nil { return nil, err } correctMIC := phyPayload.MIC phyPayload.MIC = [4]byte{0, 0, 0, 0} phyPayloadWithoutMIC, err := phyPayload.MarshalBinary() if err != nil { return nil, err } // Build Challenge challenge := &pb.ActivationChallengeRequest{ Payload: phyPayloadWithoutMIC, AppId: deduplicatedActivationRequest.AppId, DevId: deduplicatedActivationRequest.DevId, AppEui: deduplicatedActivationRequest.AppEui, DevEui: deduplicatedActivationRequest.DevEui, } // Send Challenge to all handlers and collect responses var wg sync.WaitGroup responses := make(chan *challengeResponseWithHandler, len(announcements)) for _, announcement := range announcements { conn, err := b.getHandlerConn(announcement.Id) if err != nil { ctx.WithError(err).Warn("Could not dial handler for Activation") continue } client := pb_handler.NewHandlerClient(conn) // Do async request wg.Add(1) go func(announcement *pb_discovery.Announcement) { res, err := client.ActivationChallenge(b.Component.GetContext(""), challenge) if err == nil && res != nil { responses <- &challengeResponseWithHandler{ handler: announcement, client: client, response: res, } } wg.Done() }(announcement) } // Make sure to close channel when all requests are done go func() { wg.Wait() close(responses) }() var gotFirst bool var joinHandler *pb_discovery.Announcement var joinHandlerClient pb_handler.HandlerClient for res := range responses { var phyPayload lorawan.PHYPayload err = phyPayload.UnmarshalBinary(res.response.Payload) if err != nil { continue } if phyPayload.MIC != correctMIC { continue } if gotFirst { ctx.Warn("Duplicate Activation Response") } else { gotFirst = true joinHandler = res.handler joinHandlerClient = res.client } } // Activation not accepted by any broker if !gotFirst { ctx.Debug("Activation not accepted by any Handler") return nil, errors.New("Activation not accepted by any Handler") } ctx.WithField("HandlerID", joinHandler.Id).Debug("Forward Activation") handlerResponse, err := joinHandlerClient.Activate(b.Component.GetContext(""), deduplicatedActivationRequest) if err != nil { return nil, errors.Wrap(errors.FromGRPCError(err), "Handler refused activation") } handlerResponse, err = b.ns.Activate(b.Component.GetContext(b.nsToken), handlerResponse) if err != nil { return nil, errors.Wrap(errors.FromGRPCError(err), "NetworkServer refused activation") } res = &pb.DeviceActivationResponse{ Payload: handlerResponse.Payload, Message: handlerResponse.Message, DownlinkOption: handlerResponse.DownlinkOption, } return res, nil }
func (n *networkServer) HandlePrepareActivation(activation *pb_broker.DeduplicatedDeviceActivationRequest) (*pb_broker.DeduplicatedDeviceActivationRequest, error) { if activation.AppEui == nil || activation.DevEui == nil { return nil, errors.NewErrInvalidArgument("Activation", "missing AppEUI or DevEUI") } dev, err := n.devices.Get(*activation.AppEui, *activation.DevEui) if err != nil { return nil, err } activation.AppId = dev.AppID activation.DevId = dev.DevID // Get activation constraints (for DevAddr prefix selection) activationConstraints := strings.Split(dev.Options.ActivationConstraints, ",") if len(activationConstraints) == 1 && activationConstraints[0] == "" { activationConstraints = []string{} } activationConstraints = append(activationConstraints, "otaa") // Build activation metadata if not present if meta := activation.GetActivationMetadata(); meta == nil { activation.ActivationMetadata = &pb_protocol.ActivationMetadata{} } // Build lorawan metadata if not present if lorawan := activation.ActivationMetadata.GetLorawan(); lorawan == nil { return nil, errors.NewErrInvalidArgument("Activation", "missing LoRaWAN metadata") } // Build response template if not present if pld := activation.GetResponseTemplate(); pld == nil { return nil, errors.NewErrInvalidArgument("Activation", "missing response template") } lorawanMeta := activation.ActivationMetadata.GetLorawan() // Get a random device address devAddr, err := n.getDevAddr(activationConstraints...) if err != nil { return nil, err } // Set the DevAddr in the Activation Metadata lorawanMeta.DevAddr = &devAddr // Build JoinAccept Payload phy := lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.JoinAccept, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.JoinAcceptPayload{ NetID: n.netID, DLSettings: lorawan.DLSettings{RX2DataRate: uint8(lorawanMeta.Rx2Dr), RX1DROffset: uint8(lorawanMeta.Rx1DrOffset)}, RXDelay: uint8(lorawanMeta.RxDelay), DevAddr: lorawan.DevAddr(devAddr), }, } if lorawanMeta.CfList != nil { var cfList lorawan.CFList for i, cfListItem := range lorawanMeta.CfList.Freq { cfList[i] = cfListItem } phy.MACPayload.(*lorawan.JoinAcceptPayload).CFList = &cfList } // Set the Payload phyBytes, err := phy.MarshalBinary() if err != nil { return nil, err } activation.ResponseTemplate.Payload = phyBytes return activation, nil }
func TestHandleUplink(t *testing.T) { a := New(t) b := getTestBroker(t) gtwID := "eui-0102030405060708" // Invalid Payload err := b.HandleUplink(&pb.UplinkMessage{ Payload: []byte{0x01, 0x02, 0x03}, GatewayMetadata: &gateway.RxMetadata{Snr: 1.2, GatewayId: gtwID}, ProtocolMetadata: &protocol.RxMetadata{}, }) a.So(err, ShouldNotBeNil) // Valid Payload phy := lorawan.PHYPayload{ MHDR: lorawan.MHDR{ MType: lorawan.UnconfirmedDataUp, Major: lorawan.LoRaWANR1, }, MACPayload: &lorawan.MACPayload{ FHDR: lorawan.FHDR{ DevAddr: lorawan.DevAddr([4]byte{1, 2, 3, 4}), FCnt: 1, }, }, } bytes, _ := phy.MarshalBinary() // Device not found b.uplinkDeduplicator = NewDeduplicator(10 * time.Millisecond) b.ns.EXPECT().GetDevices(gomock.Any(), gomock.Any()).Return(&pb_networkserver.DevicesResponse{ Results: []*pb_lorawan.Device{}, }, nil) err = b.HandleUplink(&pb.UplinkMessage{ Payload: bytes, GatewayMetadata: &gateway.RxMetadata{Snr: 1.2, GatewayId: gtwID}, ProtocolMetadata: &protocol.RxMetadata{Protocol: &protocol.RxMetadata_Lorawan{Lorawan: &pb_lorawan.Metadata{}}}, }) a.So(err, ShouldHaveSameTypeAs, &errors.ErrNotFound{}) devEUI := types.DevEUI{1, 2, 3, 4, 5, 6, 7, 8} wrongDevEUI := types.DevEUI{1, 2, 3, 4, 5, 6, 7, 9} appEUI := types.AppEUI{1, 2, 3, 4, 5, 6, 7, 8} appID := "appid-1" nwkSKey := types.NwkSKey{1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8} // Add devices b = getTestBroker(t) nsResponse := &pb_networkserver.DevicesResponse{ Results: []*pb_lorawan.Device{ &pb_lorawan.Device{ DevEui: &wrongDevEUI, AppEui: &appEUI, AppId: appID, NwkSKey: &nwkSKey, FCntUp: 4, }, &pb_lorawan.Device{ DevEui: &devEUI, AppEui: &appEUI, AppId: appID, NwkSKey: &nwkSKey, FCntUp: 3, }, }, } b.handlers["handlerID"] = &handler{uplink: make(chan *pb.DeduplicatedUplinkMessage, 10)} // Device doesn't match b.uplinkDeduplicator = NewDeduplicator(10 * time.Millisecond) b.ns.EXPECT().GetDevices(gomock.Any(), gomock.Any()).Return(nsResponse, nil) err = b.HandleUplink(&pb.UplinkMessage{ Payload: bytes, GatewayMetadata: &gateway.RxMetadata{Snr: 1.2, GatewayId: gtwID}, ProtocolMetadata: &protocol.RxMetadata{Protocol: &protocol.RxMetadata_Lorawan{Lorawan: &pb_lorawan.Metadata{}}}, }) a.So(err, ShouldHaveSameTypeAs, &errors.ErrNotFound{}) phy.SetMIC(lorawan.AES128Key{1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8}) bytes, _ = phy.MarshalBinary() // Wrong FCnt b.uplinkDeduplicator = NewDeduplicator(10 * time.Millisecond) b.ns.EXPECT().GetDevices(gomock.Any(), gomock.Any()).Return(nsResponse, nil) err = b.HandleUplink(&pb.UplinkMessage{ Payload: bytes, GatewayMetadata: &gateway.RxMetadata{Snr: 1.2, GatewayId: gtwID}, ProtocolMetadata: &protocol.RxMetadata{Protocol: &protocol.RxMetadata_Lorawan{Lorawan: &pb_lorawan.Metadata{}}}, }) a.So(err, ShouldHaveSameTypeAs, &errors.ErrNotFound{}) // Disable FCnt Check b.uplinkDeduplicator = NewDeduplicator(10 * time.Millisecond) nsResponse.Results[0].DisableFCntCheck = true b.ns.EXPECT().GetDevices(gomock.Any(), gomock.Any()).Return(nsResponse, nil) b.ns.EXPECT().Uplink(gomock.Any(), gomock.Any()) b.discovery.EXPECT().GetAllHandlersForAppID("appid-1").Return([]*pb_discovery.Announcement{ &pb_discovery.Announcement{ Id: "handlerID", }, }, nil) err = b.HandleUplink(&pb.UplinkMessage{ Payload: bytes, GatewayMetadata: &gateway.RxMetadata{Snr: 1.2, GatewayId: gtwID}, ProtocolMetadata: &protocol.RxMetadata{Protocol: &protocol.RxMetadata_Lorawan{Lorawan: &pb_lorawan.Metadata{}}}, }) a.So(err, ShouldBeNil) // OK FCnt b.uplinkDeduplicator = NewDeduplicator(10 * time.Millisecond) nsResponse.Results[0].FCntUp = 0 nsResponse.Results[0].DisableFCntCheck = false b.ns.EXPECT().GetDevices(gomock.Any(), gomock.Any()).Return(nsResponse, nil) b.ns.EXPECT().Uplink(gomock.Any(), gomock.Any()) b.discovery.EXPECT().GetAllHandlersForAppID("appid-1").Return([]*pb_discovery.Announcement{ &pb_discovery.Announcement{ Id: "handlerID", }, }, nil) err = b.HandleUplink(&pb.UplinkMessage{ Payload: bytes, GatewayMetadata: &gateway.RxMetadata{Snr: 1.2, GatewayId: gtwID}, ProtocolMetadata: &protocol.RxMetadata{Protocol: &protocol.RxMetadata_Lorawan{Lorawan: &pb_lorawan.Metadata{}}}, }) a.So(err, ShouldBeNil) }
func (h *handler) ConvertToLoRaWAN(ctx log.Interface, appDown *types.DownlinkMessage, ttnDown *pb_broker.DownlinkMessage) error { // Find Device dev, err := h.devices.Get(appDown.AppID, appDown.DevID) if err != nil { return err } // LoRaWAN: Unmarshal Downlink var phyPayload lorawan.PHYPayload err = phyPayload.UnmarshalBinary(ttnDown.Payload) if err != nil { return err } macPayload, ok := phyPayload.MACPayload.(*lorawan.MACPayload) if !ok { return errors.NewErrInvalidArgument("Downlink", "does not contain a MAC payload") } if ttnDown.DownlinkOption != nil && ttnDown.DownlinkOption.ProtocolConfig.GetLorawan() != nil { macPayload.FHDR.FCnt = ttnDown.DownlinkOption.ProtocolConfig.GetLorawan().FCnt } // Abort when downlink not needed if len(appDown.PayloadRaw) == 0 && !macPayload.FHDR.FCtrl.ACK && len(macPayload.FHDR.FOpts) == 0 { return ErrNotNeeded } // Set FPort if appDown.FPort != 0 { macPayload.FPort = &appDown.FPort } // Set Payload if len(appDown.PayloadRaw) > 0 { macPayload.FRMPayload = []lorawan.Payload{&lorawan.DataPayload{Bytes: appDown.PayloadRaw}} if macPayload.FPort == nil || *macPayload.FPort == 0 { macPayload.FPort = pointer.Uint8(1) } } else { macPayload.FRMPayload = []lorawan.Payload{} } // Encrypt err = phyPayload.EncryptFRMPayload(lorawan.AES128Key(dev.AppSKey)) if err != nil { return err } // Set MIC err = phyPayload.SetMIC(lorawan.AES128Key(dev.NwkSKey)) if err != nil { return err } // Marshal phyPayloadBytes, err := phyPayload.MarshalBinary() if err != nil { return err } ttnDown.Payload = phyPayloadBytes return nil }