private async Task <byte[]> ProcessJoinRequest(LoRaMessageWrapper loraMessage) { byte[] udpMsgForPktForwarder = new Byte[0]; LoraDeviceInfo joinLoraDeviceInfo; var joinReq = (LoRaPayloadJoinRequest)loraMessage.LoRaPayloadMessage; joinReq.DevEUI.Span.Reverse(); joinReq.AppEUI.Span.Reverse(); string devEui = ConversionHelper.ByteArrayToString(joinReq.DevEUI.ToArray()); string devNonce = ConversionHelper.ByteArrayToString(joinReq.DevNonce.ToArray()); Logger.Log(devEui, $"join request received", Logger.LoggingLevel.Info); //checking if this devnonce was already processed or the deveui was already refused Cache.TryGetValue(devEui, out joinLoraDeviceInfo); //check if join request is valid. //we have a join request in the cache if (joinLoraDeviceInfo != null) { //we query every time in case the device is turned one while not yet in the registry //it is not our device so ingore the join //if (!joinLoraDeviceInfo.IsOurDevice) //{ // Logger.Log(devEui, $"join request refused the device is not ours", Logger.LoggingLevel.Info); // return null; //} //is our device but the join was not valid if (!joinLoraDeviceInfo.IsJoinValid) { //if the devNonce is equal to the current it is a potential replay attck if (joinLoraDeviceInfo.DevNonce == devNonce) { Logger.Log(devEui, $"join request refused devNonce already used", Logger.LoggingLevel.Info); return(null); } //Check if the device is trying to join through the wrong gateway if (!String.IsNullOrEmpty(joinLoraDeviceInfo.GatewayID) && joinLoraDeviceInfo.GatewayID.ToUpper() != GatewayID.ToUpper()) { Logger.Log(devEui, $"trying to join not through its linked gateway, ignoring join request", Logger.LoggingLevel.Info); return(null); } } } joinLoraDeviceInfo = await LoraDeviceInfoManager.PerformOTAAAsync(GatewayID, devEui, ConversionHelper.ByteArrayToString(joinReq.AppEUI.ToArray()), devNonce, joinLoraDeviceInfo); if (joinLoraDeviceInfo != null && joinLoraDeviceInfo.IsJoinValid) { //TODO to be fixed by Mik //if (!loraMessage.LoRaPayloadMessage.CheckMic(joinLoraDeviceInfo.AppKey)) //{ // Logger.Log(devEui, $"join request MIC invalid", Logger.LoggingLevel.Info); // return null; //} //join request resets the frame counters joinLoraDeviceInfo.FCntUp = 0; joinLoraDeviceInfo.FCntDown = 0; //in this case it's too late, we need to break and awoid saving twins if ((DateTime.UtcNow - startTimeProcessing) > TimeSpan.FromMilliseconds(RegionFactory.CurrentRegion.join_accept_delay2 * 1000)) { Logger.Log(devEui, $"processing of the join request took too long, sending no message", Logger.LoggingLevel.Info); var physicalResponse = new PhysicalPayload(loraMessage.PhysicalPayload.token, PhysicalIdentifier.PUSH_ACK, null); return(physicalResponse.GetMessage()); } //update reported properties and frame Counter await joinLoraDeviceInfo.HubSender.UpdateReportedPropertiesOTAAasync(joinLoraDeviceInfo); byte[] appNonce = ConversionHelper.StringToByteArray(joinLoraDeviceInfo.AppNonce); byte[] netId = ConversionHelper.StringToByteArray(joinLoraDeviceInfo.NetId); byte[] devAddr = ConversionHelper.StringToByteArray(joinLoraDeviceInfo.DevAddr); string appKey = joinLoraDeviceInfo.AppKey; Array.Reverse(netId); Array.Reverse(appNonce); LoRaPayloadJoinAccept loRaPayloadJoinAccept = new LoRaPayloadJoinAccept( //NETID 0 / 1 is default test ConversionHelper.ByteArrayToString(netId), //todo add app key management appKey, //todo add device address management devAddr, appNonce, new byte[] { 0 }, new byte[] { 0 }, null ); var datr = RegionFactory.CurrentRegion.GetDownstreamDR(loraMessage.PktFwdPayload.GetPktFwdMessage().Rxpks[0]); uint rfch = loraMessage.PktFwdPayload.GetPktFwdMessage().Rxpks[0].rfch; double freq = RegionFactory.CurrentRegion.GetDownstreamChannel(loraMessage.PktFwdPayload.GetPktFwdMessage().Rxpks[0]); //set tmst for the normal case long tmst = loraMessage.PktFwdPayload.GetPktFwdMessage().Rxpks[0].tmst + RegionFactory.CurrentRegion.join_accept_delay1 * 1000000; ////in this case it's too late, we need to break //if ((DateTime.UtcNow - startTimeProcessing) > TimeSpan.FromMilliseconds(RegionFactory.CurrentRegion.join_accept_delay2 * 1000)) //{ // Logger.Log(devEui, $"processing of the join request took too long, sending no message", Logger.LoggingLevel.Info); // var physicalResponse = new PhysicalPayload(loraMessage.PhysicalPayload.token, PhysicalIdentifier.PULL_RESP, null); // Logger.Log(devEui, $"processing time: {DateTime.UtcNow - startTimeProcessing}", Logger.LoggingLevel.Info); // return physicalResponse.GetMessage(); //} //in this case the second join windows must be used if ((DateTime.UtcNow - startTimeProcessing) > TimeSpan.FromMilliseconds(RegionFactory.CurrentRegion.join_accept_delay1 * 1000 - 100)) { Logger.Log(devEui, $"processing of the join request took too long, using second join accept receive window", Logger.LoggingLevel.Info); tmst = loraMessage.PktFwdPayload.GetPktFwdMessage().Rxpks[0].tmst + RegionFactory.CurrentRegion.join_accept_delay2 * 1000000; if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("RX2_DATR"))) { Logger.Log(devEui, $"using standard second receive windows for join request", Logger.LoggingLevel.Info); //using EU fix DR for RX2 freq = RegionFactory.CurrentRegion.RX2DefaultReceiveWindows.frequency; datr = RegionFactory.CurrentRegion.DRtoConfiguration[RegionFactory.CurrentRegion.RX2DefaultReceiveWindows.dr].configuration; } //if specific twins are set, specify second channel to be as specified else { Logger.Log(devEui, $"using custom second receive windows for join request", Logger.LoggingLevel.Info); freq = double.Parse(Environment.GetEnvironmentVariable("RX2_FREQ")); datr = Environment.GetEnvironmentVariable("RX2_DATR"); } } LoRaMessageWrapper joinAcceptMessage = new LoRaMessageWrapper(loRaPayloadJoinAccept, LoRaMessageType.JoinAccept, loraMessage.PhysicalPayload.token, datr, 0, freq, tmst); udpMsgForPktForwarder = joinAcceptMessage.PhysicalPayload.GetMessage(); //add to cache for processing normal messages. This awoids one additional call to the server. Cache.AddToCache(joinLoraDeviceInfo.DevAddr, joinLoraDeviceInfo); Logger.Log(devEui, $"join accept sent", Logger.LoggingLevel.Info); } else { Logger.Log(devEui, $"join request refused", Logger.LoggingLevel.Info); } Logger.Log(devEui, $"processing time: {DateTime.UtcNow - startTimeProcessing}", Logger.LoggingLevel.Info); return(udpMsgForPktForwarder); }
private async Task <byte[]> ProcessJoinRequest(LoRaMessage loraMessage) { byte[] udpMsgForPktForwarder = new Byte[0]; LoraDeviceInfo joinLoraDeviceInfo; var joinReq = (LoRaPayloadJoinRequest)loraMessage.payloadMessage; Array.Reverse(joinReq.devEUI); Array.Reverse(joinReq.appEUI); string devEui = BitConverter.ToString(joinReq.devEUI).Replace("-", ""); string devNonce = BitConverter.ToString(joinReq.devNonce).Replace("-", ""); Logger.Log(devEui, $"join request received", Logger.LoggingLevel.Info); //checking if this devnonce was already processed or the deveui was already refused Cache.TryGetValue(devEui, out joinLoraDeviceInfo); //we have a join request in the cache if (joinLoraDeviceInfo != null) { //it is not our device so ingore the join if (!joinLoraDeviceInfo.IsOurDevice) { Logger.Log(devEui, $"join request refused the device is not ours", Logger.LoggingLevel.Info); return(null); } //is our device but the join was not valid else if (!joinLoraDeviceInfo.IsJoinValid) { //if the devNonce is equal to the current it is a potential replay attck if (joinLoraDeviceInfo.DevNonce == devNonce) { Logger.Log(devEui, $"join request refused devNonce already used", Logger.LoggingLevel.Info); return(null); } //Check if the device is trying to join through the wrong gateway if (!String.IsNullOrEmpty(joinLoraDeviceInfo.GatewayID) && joinLoraDeviceInfo.GatewayID.ToUpper() != GatewayID.ToUpper()) { Logger.Log(devEui, $"trying to join not through its linked gateway, ignoring join request", Logger.LoggingLevel.Info); return(null); } } } joinLoraDeviceInfo = await LoraDeviceInfoManager.PerformOTAAAsync(GatewayID, devEui, BitConverter.ToString(joinReq.appEUI).Replace("-", ""), devNonce); if (joinLoraDeviceInfo.IsJoinValid) { byte[] appNonce = StringToByteArray(joinLoraDeviceInfo.AppNonce); byte[] netId = StringToByteArray(joinLoraDeviceInfo.NetId); byte[] devAddr = StringToByteArray(joinLoraDeviceInfo.DevAddr); string appKey = joinLoraDeviceInfo.AppKey; Array.Reverse(netId); Array.Reverse(appNonce); LoRaPayloadJoinAccept loRaPayloadJoinAccept = new LoRaPayloadJoinAccept( //NETID 0 / 1 is default test BitConverter.ToString(netId).Replace("-", ""), //todo add app key management appKey, //todo add device address management devAddr, appNonce ); var _datr = ((UplinkPktFwdMessage)loraMessage.loraMetadata.fullPayload).rxpk[0].datr; uint _rfch = ((UplinkPktFwdMessage)loraMessage.loraMetadata.fullPayload).rxpk[0].rfch; double _freq = ((UplinkPktFwdMessage)loraMessage.loraMetadata.fullPayload).rxpk[0].freq; //set tmst for the normal case long _tmst = ((UplinkPktFwdMessage)loraMessage.loraMetadata.fullPayload).rxpk[0].tmst + 5000000; //uncomment to force second windows usage //Thread.Sleep(4600-(int)(DateTime.Now - startTimeProcessing).TotalMilliseconds); //in this case it's too late, we need to break if ((DateTime.UtcNow - startTimeProcessing) > TimeSpan.FromMilliseconds(6000)) { Logger.Log(devEui, $"processing of the join request took too long, sending no message", Logger.LoggingLevel.Info); var physicalResponse = new PhysicalPayload(loraMessage.physicalPayload.token, PhysicalIdentifier.PULL_RESP, null); return(physicalResponse.GetMessage()); } //in this case the second join windows must be used else if ((DateTime.UtcNow - startTimeProcessing) > TimeSpan.FromMilliseconds(4500)) { Logger.Log(devEui, $"processing of the join request took too long, using second join accept receive window", Logger.LoggingLevel.Info); _tmst = ((UplinkPktFwdMessage)loraMessage.loraMetadata.fullPayload).rxpk[0].tmst + 6000000; if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("RX2_DATR"))) { Logger.Log(devEui, $"using standard second receive windows for join request", Logger.LoggingLevel.Info); //using EU fix DR for RX2 _freq = 869.525; _datr = "SF12BW125"; } //if specific twins are set, specify second channel to be as specified else { Logger.Log(devEui, $"using custom DR second receive windows for join request", Logger.LoggingLevel.Info); _freq = double.Parse(Environment.GetEnvironmentVariable("RX2_FREQ")); _datr = Environment.GetEnvironmentVariable("RX2_DATR"); } } LoRaMessage joinAcceptMessage = new LoRaMessage(loRaPayloadJoinAccept, LoRaMessageType.JoinAccept, loraMessage.physicalPayload.token, _datr, 0, _freq, _tmst); udpMsgForPktForwarder = joinAcceptMessage.physicalPayload.GetMessage(); joinLoraDeviceInfo.HubSender = new IoTHubSender(joinLoraDeviceInfo.DevEUI, joinLoraDeviceInfo.PrimaryKey); //open the connection to iot hub without waiting for perf optimization in case of the device start sending a msg just after join request _ = joinLoraDeviceInfo.HubSender.OpenAsync(); //join request resets the frame counters joinLoraDeviceInfo.FCntUp = 0; joinLoraDeviceInfo.FCntDown = 0; //update the frame counter on the server _ = joinLoraDeviceInfo.HubSender.UpdateFcntAsync(joinLoraDeviceInfo.FCntUp, joinLoraDeviceInfo.FCntDown); //add to cache for processing normal messages. This awoids one additional call to the server. Cache.AddToCache(joinLoraDeviceInfo.DevAddr, joinLoraDeviceInfo); Logger.Log(devEui, $"join accept sent", Logger.LoggingLevel.Info); } //add to cache to avoid replay attack, btw server side does the check too. Cache.AddToCache(devEui, joinLoraDeviceInfo); return(udpMsgForPktForwarder); }