diff --git a/Constants.cs b/Constants.cs index 35ea341..e7e5252 100644 --- a/Constants.cs +++ b/Constants.cs @@ -66,7 +66,58 @@ namespace fnecore } // public enum ConnectionState /// - /// + /// Peer Connection NAK Messages + /// + public enum ConnectionMSTNAK + { + /// + /// General Failure + /// + GENERAL_FAILURE, + + /// + /// Mode Not Enabled + /// + MODE_NOT_ENABLED, + /// + /// Illegal Packet + /// + ILLEGAL_PACKET, + + /// + /// FNE Unauthorized + /// + FNE_UNAUTHORIZED, + /// + /// Bad Connection State + /// + BAD_CONN_STATE, + /// + /// Invalid Configuration Data + /// + INVALID_CONFIG_DATA, + /// + /// Peer Reset + /// + PEER_RESET, + /// + /// Peer ACL + /// + PEER_ACL, + + /// + /// FNE Maximum Connections + /// + FNE_MAX_CONN, + + /// + /// Invalid + /// + INVALID = 0xFFFF + } // public enum ConnectionMSTNAK + + /// + /// Call Type /// public enum CallType : byte { @@ -81,7 +132,7 @@ namespace fnecore } // public enum CallType : byte /// - /// + /// Frame Type /// public enum FrameType : byte { @@ -105,7 +156,7 @@ namespace fnecore } // public enum FrameType : byte /// - /// + /// DVM State /// public enum DVMState : byte { diff --git a/FneBase.cs b/FneBase.cs index df3af49..cbd2629 100644 --- a/FneBase.cs +++ b/FneBase.cs @@ -163,11 +163,6 @@ namespace fnecore /// public ConnectionState State; - /// - /// Flag indicating peer is "connected". - /// - public bool Connection; - /// /// Number of pings received. /// diff --git a/FnePeer.cs b/FnePeer.cs index 549e5af..0a60b09 100644 --- a/FnePeer.cs +++ b/FnePeer.cs @@ -27,6 +27,7 @@ using fnecore.DMR; using fnecore.P25; using fnecore.NXDN; using fnecore.P25.KMM; +using System.Net.NetworkInformation; namespace fnecore { @@ -142,7 +143,7 @@ namespace fnecore info = new PeerInformation(); info.PeerID = peerId; - info.Connection = false; + info.State = ConnectionState.WAITING_LOGIN; PingsAcked = 0; } @@ -557,8 +558,67 @@ namespace fnecore { if (this.peerId == peerId) { - info.Connection = false; - Log(LogLevel.DEBUG, $"({systemName}) PEER {this.peerId} MSTNAK received"); + // DVM 3.6 adds support to respond with a NAK reason, as such we just check if the NAK response is greater + // then 10 bytes and process the reason value + ConnectionMSTNAK reason = ConnectionMSTNAK.INVALID; + if (message.Length > 10) + { + reason = (ConnectionMSTNAK)FneUtils.ToUInt16(message, 10); + switch (reason) + { + case ConnectionMSTNAK.MODE_NOT_ENABLED: + Log(LogLevel.WARNING, $"({systemName}) PEER {this.peerId} master NAK; digital mode not enabled on FNE"); + break; + case ConnectionMSTNAK.ILLEGAL_PACKET: + Log(LogLevel.WARNING, $"({systemName}) PEER {this.peerId} master NAK; illegal/unknown packet"); + break; + case ConnectionMSTNAK.FNE_UNAUTHORIZED: + Log(LogLevel.WARNING, $"({systemName}) PEER {this.peerId} master NAK; unauthorized"); + break; + case ConnectionMSTNAK.BAD_CONN_STATE: + Log(LogLevel.WARNING, $"({systemName}) PEER {this.peerId} master NAK; bad connection state"); + break; + case ConnectionMSTNAK.INVALID_CONFIG_DATA: + Log(LogLevel.WARNING, $"({systemName}) PEER {this.peerId} master NAK; invalid configuration data"); + break; + case ConnectionMSTNAK.FNE_MAX_CONN: + Log(LogLevel.WARNING, $"({systemName}) PEER {this.peerId} master NAK; FNE has reached maximum permitted connections"); + break; + case ConnectionMSTNAK.PEER_RESET: + Log(LogLevel.WARNING, $"({systemName}) PEER {this.peerId} master NAK; FNE demanded connection reset"); + break; + case ConnectionMSTNAK.PEER_ACL: + Log(LogLevel.ERROR, $"({systemName}) PEER {this.peerId} master NAK; ACL rejection, network disabled"); + info.State = ConnectionState.WAITING_LOGIN; + Stop(); + break; + + case ConnectionMSTNAK.GENERAL_FAILURE: + default: + Log(LogLevel.WARNING, $"({systemName}) PEER {this.peerId} master NAK; general failure"); + break; + } + } + + if (info.State == ConnectionState.RUNNING && (reason == ConnectionMSTNAK.FNE_MAX_CONN)) + { + Log(LogLevel.WARNING, $"({systemName}) PEER {this.peerId} master NAK; attemping to relogin"); + + // reset states + PingsSent = 0; + PingsAcked = 0; + info.State = ConnectionState.WAITING_LOGIN; + } + else + { + Log(LogLevel.ERROR, $"({systemName}) PEER {this.peerId} master NAK; network reconnect"); + + // reset states + PingsSent = 0; + PingsAcked = 0; + info.State = ConnectionState.WAITING_LOGIN; + return; + } } } break; @@ -664,7 +724,6 @@ namespace fnecore else { info.State = ConnectionState.WAITING_LOGIN; - info.Connection = false; Log(LogLevel.ERROR, $"({systemName}) PEER {this.peerId} master ACK contained wrong ID - connection reset"); } } @@ -672,7 +731,6 @@ namespace fnecore { if (this.peerId == peerId) { - info.Connection = true; info.State = ConnectionState.RUNNING; Log(LogLevel.INFO, $"({systemName}) PEER {this.peerId} connection to MASTER completed"); @@ -682,7 +740,6 @@ namespace fnecore else { info.State = ConnectionState.WAITING_LOGIN; - info.Connection = false; Log(LogLevel.ERROR, $"({systemName}) PEER {this.peerId} master ACK contained wrong ID - connection reset"); } } @@ -692,7 +749,7 @@ namespace fnecore { if (this.peerId == peerId) { - info.Connection = false; + info.State = ConnectionState.WAITING_LOGIN; Log(LogLevel.DEBUG, $"({systemName}) PEER {this.peerId} MSTCL received"); // userland actions @@ -740,6 +797,12 @@ namespace fnecore catch (InvalidOperationException) { Log(LogLevel.ERROR, $"({systemName}) Not connected or lost connection to {masterEndpoint}; reconnecting..."); + + // reset states + PingsSent = 0; + PingsAcked = 0; + info.State = ConnectionState.WAITING_LOGIN; + client.Connect(masterEndpoint); } catch (SocketException se) @@ -752,6 +815,12 @@ namespace fnecore case SocketError.ConnectionAborted: case SocketError.ConnectionRefused: Log(LogLevel.ERROR, $"({systemName}) Not connected or lost connection to {masterEndpoint}; reconnecting..."); + + // reset states + PingsSent = 0; + PingsAcked = 0; + info.State = ConnectionState.WAITING_LOGIN; + client.Connect(masterEndpoint); break; default: @@ -776,8 +845,9 @@ namespace fnecore try { // if we're not connected, zero out the connection stats and send a login request to the master - if (!info.Connection || info.State == ConnectionState.WAITING_LOGIN) + if (info.State == ConnectionState.WAITING_LOGIN) { + // reset states PingsSent = 0; PingsAcked = 0; info.State = ConnectionState.WAITING_LOGIN; @@ -792,12 +862,13 @@ namespace fnecore } // if we are connected, sent a ping to the master and increment the counter - if (info.Connection && info.State == ConnectionState.RUNNING) + if (info.State == ConnectionState.RUNNING) { if (PingsSent > (PingsAcked + MAX_MISSED_PEER_PINGS)) { Log(LogLevel.WARNING, $"({systemName} Peer connection lost to {masterEndpoint}; reconnecting..."); + // reset states PingsSent = 0; PingsAcked = 0; info.State = ConnectionState.WAITING_LOGIN; @@ -818,6 +889,12 @@ namespace fnecore catch (InvalidOperationException) { Log(LogLevel.ERROR, $"({systemName}) Not connected or lost connection to {masterEndpoint}; reconnecting..."); + + // reset states + PingsSent = 0; + PingsAcked = 0; + info.State = ConnectionState.WAITING_LOGIN; + client.Connect(masterEndpoint); } catch (SocketException se) @@ -830,6 +907,12 @@ namespace fnecore case SocketError.ConnectionAborted: case SocketError.ConnectionRefused: Log(LogLevel.ERROR, $"({systemName}) Not connected or lost connection to {masterEndpoint}; reconnecting..."); + + // reset states + PingsSent = 0; + PingsAcked = 0; + info.State = ConnectionState.WAITING_LOGIN; + client.Connect(masterEndpoint); break; default: