From ec3c027e97e35f54121df5d5e598c2b0127e84f2 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sat, 28 Mar 2026 11:40:44 -0400 Subject: [PATCH] in an attempt to prevent the FNE upstream peer connection from becoming brain dead, use the new mechanism to handle NAKs and properly reset our connection on reception of one; --- src/common/network/Network.cpp | 36 +++++++++++++++++++++------------ src/common/network/Network.h | 10 ++++++++- src/fne/network/PeerNetwork.cpp | 26 ++++++++++++++++++++++++ src/fne/network/PeerNetwork.h | 8 ++++++++ 4 files changed, 66 insertions(+), 14 deletions(-) diff --git a/src/common/network/Network.cpp b/src/common/network/Network.cpp index b14dafc1..5a8d618d 100644 --- a/src/common/network/Network.cpp +++ b/src/common/network/Network.cpp @@ -1101,7 +1101,7 @@ void Network::clock(uint32_t ms) { // 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 - uint16_t reason = 0U; + uint16_t reason = NET_CONN_NAK_GENERAL_FAILURE; if (length > 10) { reason = GET_UINT16(buffer, 10U); switch (reason) { @@ -1150,19 +1150,22 @@ void Network::clock(uint32_t ms) } } - if (m_status == NET_STAT_RUNNING && (reason == NET_CONN_NAK_FNE_MAX_CONN)) { - LogWarning(LOG_NET, "PEER %u master NAK; attemping to relogin, remotePeerId = %u", m_peerId, rtpHeader.getSSRC()); - m_status = NET_STAT_WAITING_LOGIN; - m_timeoutTimer.start(); - m_retryTimer.start(); - } - else { - if (m_enabled) { - LogError(LOG_NET, "PEER %u master NAK; network reconnect, remotePeerId = %u", m_peerId, rtpHeader.getSSRC()); - close(); - open(); + // if the NAK reason is unhandled by the user code, perform default handling + if (!userNakHandler(m_peerId, reason, fneHeader, rtpHeader)) { + if (m_status == NET_STAT_RUNNING && (reason == NET_CONN_NAK_FNE_MAX_CONN)) { + LogWarning(LOG_NET, "PEER %u master NAK; attemping to relogin, remotePeerId = %u", m_peerId, rtpHeader.getSSRC()); + m_status = NET_STAT_WAITING_LOGIN; + m_timeoutTimer.start(); + m_retryTimer.start(); + } + else { + if (m_enabled) { + LogError(LOG_NET, "PEER %u master NAK; network reconnect, remotePeerId = %u", m_peerId, rtpHeader.getSSRC()); + close(); + open(); + } + return; } - return; } } break; @@ -1433,6 +1436,13 @@ void Network::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opcode, Utils::dump("Unknown opcode from the master", data, length); } +/* User overrideable handler that allows user code to process NAKs received from the master. */ + +bool Network::userNakHandler(uint32_t peerId, uint16_t reason, const frame::RTPFNEHeader& fneHeader, const frame::RTPHeader& rtpHeader) +{ + return false; // return false to perform default handling of the NAK +} + /* Writes login request to the network. */ bool Network::writeLogin() diff --git a/src/common/network/Network.h b/src/common/network/Network.h index 185daeaf..3c55f95f 100644 --- a/src/common/network/Network.h +++ b/src/common/network/Network.h @@ -4,7 +4,7 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * Copyright (C) 2017-2025 Bryan Biedenkapp, N2PLL + * Copyright (C) 2017-2026 Bryan Biedenkapp, N2PLL * */ /** @@ -421,6 +421,14 @@ namespace network */ virtual void userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opcode, const uint8_t* data = nullptr, uint32_t length = 0U, uint32_t streamId = 0U, const frame::RTPFNEHeader& fneHeader = frame::RTPFNEHeader(), const frame::RTPHeader& rtpHeader = frame::RTPHeader()); + /** + * @brief User overrideable handler that allows user code to process NAKs received from the master. + * @param peerId Peer ID. + * @param reason Reason code. + * @param fneHeader RTP FNE Header. + * @param rtpHeader RTP Header. + */ + virtual bool userNakHandler(uint32_t peerId, uint16_t reason, const frame::RTPFNEHeader& fneHeader = frame::RTPFNEHeader(), const frame::RTPHeader& rtpHeader = frame::RTPHeader()); /** * @brief Writes login request to the network. diff --git a/src/fne/network/PeerNetwork.cpp b/src/fne/network/PeerNetwork.cpp index 58984a57..fa16e301 100644 --- a/src/fne/network/PeerNetwork.cpp +++ b/src/fne/network/PeerNetwork.cpp @@ -479,6 +479,32 @@ void PeerNetwork::userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opco } } +/* User overrideable handler that allows user code to process NAKs received from the master. */ + +bool Network::userNakHandler(uint32_t peerId, uint16_t reason, const frame::RTPFNEHeader& fneHeader, const frame::RTPHeader& rtpHeader) +{ + switch (reason) { + case NET_CONN_NAK_FNE_UNAUTHORIZED: + { + // if we have an FNE unauthorized, lets force a connection reset, FNE's should very rarely receive these + // packets + LogWarning(LOG_PEER, "PEER %u received unauthorized NAK from master, resetting connection", peerId); + + m_enabled = false; + close(); + + m_enabled = true; + open(); + } + return true; + + default: + break; + } + + return false; // return false to perform default handling of the NAK +} + /* Writes configuration to the network. */ bool PeerNetwork::writeConfig() diff --git a/src/fne/network/PeerNetwork.h b/src/fne/network/PeerNetwork.h index 10101fc8..fbc948f9 100644 --- a/src/fne/network/PeerNetwork.h +++ b/src/fne/network/PeerNetwork.h @@ -303,6 +303,14 @@ namespace network */ void userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opcode, const uint8_t* data = nullptr, uint32_t length = 0U, uint32_t streamId = 0U, const frame::RTPFNEHeader& fneHeader = frame::RTPFNEHeader(), const frame::RTPHeader& rtpHeader = frame::RTPHeader()) override; + /** + * @brief User overrideable handler that allows user code to process NAKs received from the master. + * @param peerId Peer ID. + * @param reason Reason code. + * @param fneHeader RTP FNE Header. + * @param rtpHeader RTP Header. + */ + bool userNakHandler(uint32_t peerId, uint16_t reason, const frame::RTPFNEHeader& fneHeader = frame::RTPFNEHeader(), const frame::RTPHeader& rtpHeader = frame::RTPHeader()) override; /** * @brief Writes configuration to the network.