add initial support to process SNDCP control PDU data;

pull/61/head
Bryan Biedenkapp 2 years ago
parent d18b4c2b4e
commit f23d3931f4

@ -28,6 +28,7 @@ file(GLOB common_SRC
"src/common/p25/lc/tdulc/*.cpp"
"src/common/p25/lc/tsbk/*.cpp"
"src/common/p25/lc/tsbk/mbt/*.cpp"
"src/common/p25/sndcp/*.cpp"
"src/common/p25/lookups/*.cpp"
# NXDN module
@ -68,6 +69,7 @@ file(GLOB common_INCLUDE
"src/common/p25/lc/tdulc/*.h"
"src/common/p25/lc/tsbk/*.h"
"src/common/p25/lc/tsbk/mbt/*.h"
"src/common/p25/sndcp/*.h"
"src/common/p25/lookups/*.h"
# NXDN module

@ -356,8 +356,8 @@ namespace p25
/// </summary>
namespace PDURegType {
enum : uint8_t {
CNCT = 0x00U, // Connect
DISCNCT = 0x01U, // Disconnect
CONNECT = 0x00U, // Connect
DISCONNECT = 0x01U, // Disconnect
ACCPT = 0x04U, // Accept
DENY = 0x05U // Deny
};
@ -393,7 +393,18 @@ namespace p25
READY = 3U, // Ready - SU Activated and Rx/Tx Data
CLOSED = 4U, // Closed - SU not yet Registered or Deregistered
ILLEGAL = 255U, // Illegal/Unknown
ILLEGAL = 255U // Illegal/Unknown
};
}
/// <summary>
/// SNDCP Network Address Type
/// </summary>
namespace SNDCPNAT {
enum : uint8_t {
IPV4_STATIC_ADDR = 0U, // IPv4 Static Address
IPV4_DYN_ADDR = 1U, // IPv4 Dynamic Address
IPV4_NO_ADDRESS = 15U // No Address
};
}
@ -407,6 +418,7 @@ namespace p25
CONV_DATA_ONLY = 2U, // Conventional Data Only
ALTERNATING_CONV_DATA_VOICE = 3U, // Alternating Conventional Voice & Data
TRUNKED_CONV_DATA_ONLY = 4U, // Trunked and Conventional Data Only
ALT_T_AND_C_DATA_VOICE = 5U // Alternating Trunked and Conventional Voice & Data
};
}
@ -482,6 +494,16 @@ namespace p25
};
}
/// <summary>
/// SNDCP Deactivation Types
/// </summary>
namespace SNDCPDeactivationType {
enum : uint8_t {
DEACT_ALL = 0U, // Deactivate all NSAPIs
DEACT_THIS_PDU = 1U // Deactivate NSAPI in this PDU
};
}
const uint8_t LC_SVC_OPT_EMERGENCY = 0x80U;
const uint8_t LC_SVC_OPT_ENCRYPTION = 0x40U;

@ -0,0 +1,110 @@
// SPDX-License-Identifier: GPL-2.0-only
/**
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Common Library
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
#include "Defines.h"
#include "p25/P25Defines.h"
#include "p25/sndcp/SNDCPCtxActAccept.h"
#include "Log.h"
using namespace p25;
using namespace p25::defines;
using namespace p25::sndcp;
#include <cassert>
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the SNDCPCtxActAccept class.
/// </summary>
SNDCPCtxActAccept::SNDCPCtxActAccept() : SNDCPPacket(),
m_priority(4U),
m_readyTimer(SNDCPReadyTimer::TEN_SECONDS),
m_standbyTimer(SNDCPStandbyTimer::ONE_MINUTE),
m_nat(SNDCPNAT::IPV4_STATIC_ADDR),
m_ipAddress(0U),
m_mtu(SNDCP_MTU_510)
{
m_pduType = SNDCP_PDUType::ACT_TDS_CTX;
}
/// <summary>
/// Decode a SNDCP context activation response.
/// </summary>
/// <param name="data"></param>
/// <returns>True, if packet was decoded, otherwise false.</returns>
bool SNDCPCtxActAccept::decode(const uint8_t* data)
{
assert(data != nullptr);
SNDCPPacket::decodeHeader(data, false);
m_priority = (uint8_t)((data[1U] >> 4) & 0x0FU); // Priority
m_readyTimer = (uint8_t)(data[1U] & 0x0FU); // Ready Timer
m_standbyTimer = (uint8_t)((data[2U] >> 4) & 0x0FU); // Standby Timer
m_nat = (uint8_t)(data[2U] & 0x0FU); // NAT
m_ipAddress = 0U; // IP Address
m_ipAddress = data[3U];
m_ipAddress = (m_ipAddress << 8) + data[4U];
m_ipAddress = (m_ipAddress << 8) + data[5U];
m_ipAddress = (m_ipAddress << 8) + data[6U];
m_mtu = (uint8_t)((data[9U] >> 4) & 0x0FU); // MTU
return true;
}
/// <summary>
/// Encode a SNDCP context activation response.
/// </summary>
/// <param name="data"></param>
void SNDCPCtxActAccept::encode(uint8_t* data)
{
assert(data != nullptr);
SNDCPPacket::encodeHeader(data, true);
data[1U] = ((m_priority << 4U) & 0xF0U) + // Priority
(m_readyTimer & 0x0FU); // Ready Timer
data[2U] = ((m_standbyTimer << 4U) & 0xF0U) + // Standby Timer
(m_nat & 0x0FU); // NAT
data[3U] = (uint8_t)((m_ipAddress >> 24) & 0xFFU); // IP Address
data[4U] = (uint8_t)((m_ipAddress >> 16) & 0xFFU);
data[5U] = (uint8_t)((m_ipAddress >> 8) & 0xFFU);
data[6U] = (uint8_t)((m_ipAddress >> 0) & 0xFFU);
data[9U] = ((m_mtu << 4U) & 0xF0U); // MTU
}
// ---------------------------------------------------------------------------
// Protected Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Internal helper to copy the the class.
/// </summary>
/// <param name="data"></param>
void SNDCPCtxActAccept::copy(const SNDCPCtxActAccept& data)
{
m_priority = data.m_priority;
m_readyTimer = data.m_readyTimer;
m_standbyTimer = data.m_standbyTimer;
m_nat = data.m_nat;
m_ipAddress = data.m_ipAddress;
m_mtu = data.m_mtu;
}

@ -0,0 +1,62 @@
// SPDX-License-Identifier: GPL-2.0-only
/**
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Common Library
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
#if !defined(__P25_SNDCP__SNDCPCTXACTACCEPT_H__)
#define __P25_SNDCP__SNDCPCTXACTACCEPT_H__
#include "common/Defines.h"
#include "common/p25/sndcp/SNDCPPacket.h"
#include "common/Utils.h"
#include <string>
namespace p25
{
namespace sndcp
{
// ---------------------------------------------------------------------------
// Class Declaration
// Represents a SNDCP PDU context accept response.
// ---------------------------------------------------------------------------
class HOST_SW_API SNDCPCtxActAccept : public SNDCPPacket {
public:
/// <summary>Initializes a new instance of the SNDCPCtxActAccept class.</summary>
SNDCPCtxActAccept();
/// <summary>Decode a SNDCP context activation response.</summary>
bool decode(const uint8_t* data);
/// <summary>Encode a SNDCP context activation response.</summary>
void encode(uint8_t* data);
public:
/// <summary>Priority</summary>
__PROPERTY(uint8_t, priority, Priority);
/// <summary>Ready Timer</summary>
__PROPERTY(uint8_t, readyTimer, ReadyTimer);
/// <summary>Ready Timer</summary>
__PROPERTY(uint8_t, standbyTimer, StandbyTimer);
/// <summary>Network Address Type</summary>
__PROPERTY(uint8_t, nat, NAT);
/// <summary>IP Address</summary>
__PROPERTY(ulong64_t, ipAddress, IPAddress);
/// <summary>MTU</summary>
__PROPERTY(uint8_t, mtu, MTU);
__COPY(SNDCPCtxActAccept);
};
} // namespace sndcp
} // namespace p25
#endif // __P25_SNDCP__SNDCPCTXACTACCEPT_H__

@ -0,0 +1,73 @@
// SPDX-License-Identifier: GPL-2.0-only
/**
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Common Library
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
#include "Defines.h"
#include "p25/P25Defines.h"
#include "p25/sndcp/SNDCPCtxActReject.h"
#include "Log.h"
using namespace p25;
using namespace p25::defines;
using namespace p25::sndcp;
#include <cassert>
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the SNDCPCtxActReject class.
/// </summary>
SNDCPCtxActReject::SNDCPCtxActReject() : SNDCPPacket(),
m_rejectCode(SNDCPRejectReason::ANY_REASON)
{
m_pduType = SNDCP_PDUType::ACT_TDS_CTX_REJECT;
}
/// <summary>
/// Decode a SNDCP context activation reject packet.
/// </summary>
/// <param name="data"></param>
/// <returns>True, if packet was decoded, otherwise false.</returns>
bool SNDCPCtxActReject::decode(const uint8_t* data)
{
assert(data != nullptr);
SNDCPPacket::decodeHeader(data, false);
m_rejectCode = data[1U]; // Reject Code
return true;
}
/// <summary>
/// Encode a SNDCP context activation reject packet.
/// </summary>
/// <param name="data"></param>
void SNDCPCtxActReject::encode(uint8_t* data)
{
assert(data != nullptr);
SNDCPPacket::encodeHeader(data, true);
data[1U] = m_rejectCode; // Reject Code
}
/// <summary>
/// Internal helper to copy the the class.
/// </summary>
/// <param name="data"></param>
void SNDCPCtxActReject::copy(const SNDCPCtxActReject& data)
{
m_rejectCode = data.m_rejectCode;
}

@ -0,0 +1,50 @@
// SPDX-License-Identifier: GPL-2.0-only
/**
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Common Library
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
#if !defined(__P25_SNDCP__SNDCPCTXACTREJECT_H__)
#define __P25_SNDCP__SNDCPCTXACTREJECT_H__
#include "common/Defines.h"
#include "common/p25/sndcp/SNDCPPacket.h"
#include "common/Utils.h"
#include <string>
namespace p25
{
namespace sndcp
{
// ---------------------------------------------------------------------------
// Class Declaration
// Represents a SNDCP PDU context activation reject response.
// ---------------------------------------------------------------------------
class HOST_SW_API SNDCPCtxActReject : public SNDCPPacket {
public:
/// <summary>Initializes a new instance of the SNDCPCtxActReject class.</summary>
SNDCPCtxActReject();
/// <summary>Decode a SNDCP context activation reject packet.</summary>
bool decode(const uint8_t* data);
/// <summary>Encode a SNDCP context activation reject packet.</summary>
void encode(uint8_t* data);
public:
/// <summary>Reject Code</summary>
__PROPERTY(uint8_t, rejectCode, RejectCode);
__COPY(SNDCPCtxActReject);
};
} // namespace sndcp
} // namespace p25
#endif // __P25_SNDCP__SNDCPCTXACTREJECT_H__

@ -0,0 +1,97 @@
// SPDX-License-Identifier: GPL-2.0-only
/**
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Common Library
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
#include "Defines.h"
#include "p25/P25Defines.h"
#include "p25/sndcp/SNDCPCtxActRequest.h"
#include "Log.h"
using namespace p25;
using namespace p25::defines;
using namespace p25::sndcp;
#include <cassert>
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the SNDCPCtxActRequest class.
/// </summary>
SNDCPCtxActRequest::SNDCPCtxActRequest() : SNDCPPacket(),
m_nat(SNDCPNAT::IPV4_NO_ADDRESS),
m_ipAddress(0U),
m_dsut(SNDCP_DSUT::ALT_T_AND_C_DATA_VOICE)
{
m_pduType = SNDCP_PDUType::ACT_TDS_CTX;
}
/// <summary>
/// Decode a SNDCP context activation request.
/// </summary>
/// <param name="data"></param>
/// <returns>True, if packet was decoded, otherwise false.</returns>
bool SNDCPCtxActRequest::decode(const uint8_t* data)
{
assert(data != nullptr);
SNDCPPacket::decodeHeader(data, false);
m_nsapi = (uint8_t)((data[1U] >> 4) & 0x0FU); // NSAPI
m_nat = (uint8_t)((data[1U]) & 0x0FU); // NAT
m_ipAddress = 0U; // IP Address
m_ipAddress = data[2U];
m_ipAddress = (m_ipAddress << 8) + data[3U];
m_ipAddress = (m_ipAddress << 8) + data[4U];
m_ipAddress = (m_ipAddress << 8) + data[5U];
m_dsut = (uint8_t)((data[6U] >> 4) & 0x0FU); // Data Subscriber Unit Type
return true;
}
/// <summary>
/// Encode a SNDCP context activation request.
/// </summary>
/// <param name="data"></param>
void SNDCPCtxActRequest::encode(uint8_t* data)
{
assert(data != nullptr);
SNDCPPacket::encodeHeader(data, true);
data[1U] = ((m_nsapi << 4U) & 0xF0U) + // NSAPI
(m_nat & 0x0FU); // NAT
data[2U] = (uint8_t)((m_ipAddress >> 24) & 0xFFU); // IP Address
data[3U] = (uint8_t)((m_ipAddress >> 16) & 0xFFU);
data[4U] = (uint8_t)((m_ipAddress >> 8) & 0xFFU);
data[5U] = (uint8_t)((m_ipAddress >> 0) & 0xFFU);
data[6U] = ((m_dsut << 4U) & 0xF0U); // Data Subscriber Unit Type
}
/// <summary>
/// Internal helper to copy the the class.
/// </summary>
/// <param name="data"></param>
void SNDCPCtxActRequest::copy(const SNDCPCtxActRequest& data)
{
m_nsapi = data.m_nsapi;
m_nat = data.m_nat;
m_ipAddress = data.m_ipAddress;
m_dsut = data.m_dsut;
}

@ -0,0 +1,56 @@
// SPDX-License-Identifier: GPL-2.0-only
/**
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Common Library
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
#if !defined(__P25_SNDCP__SNDCPCTXACTREQUEST_H__)
#define __P25_SNDCP__SNDCPCTXACTREQUEST_H__
#include "common/Defines.h"
#include "common/p25/sndcp/SNDCPPacket.h"
#include "common/Utils.h"
#include <string>
namespace p25
{
namespace sndcp
{
// ---------------------------------------------------------------------------
// Class Declaration
// Represents a SNDCP PDU context activation request.
// ---------------------------------------------------------------------------
class HOST_SW_API SNDCPCtxActRequest : public SNDCPPacket {
public:
/// <summary>Initializes a new instance of the SNDCPCtxActRequest class.</summary>
SNDCPCtxActRequest();
/// <summary>Decode a SNDCP context activation request.</summary>
bool decode(const uint8_t* data);
/// <summary>Encode a SNDCP context activation request.</summary>
void encode(uint8_t* data);
public:
/// <summary>Network Address Type</summary>
__PROPERTY(uint8_t, nat, NAT);
/// <summary>IP Address</summary>
__PROPERTY(ulong64_t, ipAddress, IPAddress);
/// <summary>Data Subscriber Unit Type</summary>
__PROPERTY(uint8_t, dsut, DSUT);
__COPY(SNDCPCtxActRequest);
};
} // namespace sndcp
} // namespace p25
#endif // __P25_SNDCP__SNDCPCTXACTREQUEST_H__

@ -0,0 +1,73 @@
// SPDX-License-Identifier: GPL-2.0-only
/**
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Common Library
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
#include "Defines.h"
#include "p25/P25Defines.h"
#include "p25/sndcp/SNDCPCtxDeactivation.h"
#include "Log.h"
using namespace p25;
using namespace p25::defines;
using namespace p25::sndcp;
#include <cassert>
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the SNDCPCtxDeactivation class.
/// </summary>
SNDCPCtxDeactivation::SNDCPCtxDeactivation() : SNDCPPacket(),
m_deactType(SNDCPDeactivationType::DEACT_ALL)
{
m_pduType = SNDCP_PDUType::DEACT_TDS_CTX_REQ;
}
/// <summary>
/// Decode a SNDCP context deactivation packet.
/// </summary>
/// <param name="data"></param>
/// <returns>True, if packet was decoded, otherwise false.</returns>
bool SNDCPCtxDeactivation::decode(const uint8_t* data)
{
assert(data != nullptr);
SNDCPPacket::decodeHeader(data, false);
m_deactType = data[1U]; // Deactivation Type
return true;
}
/// <summary>
/// Encode a SNDCP context deactivation packet.
/// </summary>
/// <param name="data"></param>
void SNDCPCtxDeactivation::encode(uint8_t* data)
{
assert(data != nullptr);
SNDCPPacket::encodeHeader(data, true);
data[1U] = m_deactType; // Deactivation Type
}
/// <summary>
/// Internal helper to copy the the class.
/// </summary>
/// <param name="data"></param>
void SNDCPCtxDeactivation::copy(const SNDCPCtxDeactivation& data)
{
m_deactType = data.m_deactType;
}

@ -0,0 +1,51 @@
// SPDX-License-Identifier: GPL-2.0-only
/**
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Common Library
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
#if !defined(__P25_SNDCP__SNDCPCTXDEACTIVATION_H__)
#define __P25_SNDCP__SNDCPCTXDEACTIVATION_H__
#include "common/Defines.h"
#include "common/p25/sndcp/SNDCPPacket.h"
#include "common/Utils.h"
#include <string>
namespace p25
{
namespace sndcp
{
// ---------------------------------------------------------------------------
// Class Declaration
// Represents a SNDCP PDU context deactivation.
// ---------------------------------------------------------------------------
class HOST_SW_API SNDCPCtxDeactivation : public SNDCPPacket {
public:
/// <summary>Initializes a new instance of the SNDCPCtxDeactivation class.</summary>
SNDCPCtxDeactivation();
/// <summary>Decode a SNDCP context deactivation packet.</summary>
bool decode(const uint8_t* data);
/// <summary>Encode a SNDCP context deactivation packet.</summary>
void encode(uint8_t* data);
public:
/** Common Data */
/// <summary>Deactivation Type</summary>
__PROPERTY(uint8_t, deactType, DeactType);
__COPY(SNDCPCtxDeactivation);
};
} // namespace sndcp
} // namespace p25
#endif // __P25_SNDCP__SNDCPCTXDEACTIVATION_H__

@ -0,0 +1,90 @@
// SPDX-License-Identifier: GPL-2.0-only
/**
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Common Library
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
#include "Defines.h"
#include "p25/P25Defines.h"
#include "p25/sndcp/SNDCPFactory.h"
#include "Log.h"
#include "Utils.h"
using namespace p25;
using namespace p25::defines;
using namespace p25::sndcp;
#include <cassert>
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the SNDCPFactory class.
/// </summary>
SNDCPFactory::SNDCPFactory() = default;
/// <summary>
/// Finalizes a instance of SNDCPFactory class.
/// </summary>
SNDCPFactory::~SNDCPFactory() = default;
/// <summary>
/// Create an instance of a SNDCPPacket.
/// </summary>
/// <param name="data"></param>
/// <returns>True, if packet was decoded, otherwise false.</returns>
std::unique_ptr<SNDCPPacket> SNDCPFactory::create(const uint8_t* data)
{
assert(data != nullptr);
uint8_t pduType = (uint8_t)((data[0U] >> 4) & 0x0FU); // SNDCP PDU Message Type
switch (pduType) {
case SNDCP_PDUType::ACT_TDS_CTX:
return decode(new SNDCPCtxActRequest(), data);
break;
case SNDCP_PDUType::DEACT_TDS_CTX_REQ:
return decode(new SNDCPCtxDeactivation(), data);
break;
case SNDCP_PDUType::RF_CONFIRMED:
break;
case SNDCP_PDUType::RF_UNCONFIRMED:
break;
default:
LogError(LOG_P25, "SNDCPFactory::create(), unknown SNDCP PDU value, pduType = $%02X", pduType);
break;
}
return nullptr;
}
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
/// <summary>
///
/// </summary>
/// <param name="tsbk"></param>
/// <param name="data"></param>
/// <param name="rawTSBK"></param>
/// <returns></returns>
std::unique_ptr<SNDCPPacket> SNDCPFactory::decode(SNDCPPacket* packet, const uint8_t* data)
{
assert(packet != nullptr);
assert(data != nullptr);
if (!packet->decode(data)) {
return nullptr;
}
return std::unique_ptr<SNDCPPacket>(packet);
}

@ -0,0 +1,50 @@
// SPDX-License-Identifier: GPL-2.0-only
/**
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Common Library
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
#if !defined(__P25_SNDCP__SNDCP_FACTORY_H__)
#define __P25_SNDCP__SNDCP_FACTORY_H__
#include "common/Defines.h"
#include "common/p25/sndcp/SNDCPPacket.h"
#include "common/p25/sndcp/SNDCPCtxActAccept.h"
#include "common/p25/sndcp/SNDCPCtxActReject.h"
#include "common/p25/sndcp/SNDCPCtxActRequest.h"
#include "common/p25/sndcp/SNDCPCtxDeactivation.h"
namespace p25
{
namespace sndcp
{
// ---------------------------------------------------------------------------
// Class Declaration
// Helper class to instantiate an instance of a SNDCP packet.
// ---------------------------------------------------------------------------
class HOST_SW_API SNDCPFactory {
public:
/// <summary>Initializes a new instance of the SNDCPFactory class.</summary>
SNDCPFactory();
/// <summary>Finalizes a instance of the SNDCPFactory class.</summary>
~SNDCPFactory();
/// <summary>Create an instance of a SNDCPPacket.</summary>
static std::unique_ptr<SNDCPPacket> create(const uint8_t* data);
private:
/// <summary></summary>
static std::unique_ptr<SNDCPPacket> decode(SNDCPPacket* packet, const uint8_t* data);
};
} // namespace sndcp
} // namespace p25
#endif // __P25_SNDCP__SNDCP_FACTORY_H__

@ -0,0 +1,104 @@
// SPDX-License-Identifier: GPL-2.0-only
/**
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Common Library
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
#include "Defines.h"
#include "p25/P25Defines.h"
#include "p25/sndcp/SNDCPPacket.h"
#include "Log.h"
using namespace p25;
using namespace p25::defines;
using namespace p25::sndcp;
#include <cassert>
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a copy instance of the SNDCPPacket class.
/// </summary>
/// <param name="data"></param>
SNDCPPacket::SNDCPPacket(const SNDCPPacket& data) : SNDCPPacket()
{
copy(data);
}
/// <summary>
/// Initializes a new instance of the SNDCPPacket class.
/// </summary>
SNDCPPacket::SNDCPPacket() :
m_pduType(SNDCP_PDUType::ACT_TDS_CTX),
m_sndcpVersion(SNDCP_VERSION_1),
m_nsapi(0U)
{
/* stub */
}
/// <summary>
/// Finalizes a instance of SNDCPPacket class.
/// </summary>
SNDCPPacket::~SNDCPPacket() = default;
// ---------------------------------------------------------------------------
// Protected Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Internal helper to decode a SNDCP header.
/// </summary>
/// <param name="data"></param>
/// <param name="outbound"></param>
/// <returns>True, if header was decoded, otherwise false.</returns>
bool SNDCPPacket::decodeHeader(const uint8_t* data, bool outbound)
{
assert(data != nullptr);
m_pduType = (uint8_t)((data[0U] >> 4) & 0x0FU); // SNDCP PDU Message Type
if (m_pduType == SNDCP_PDUType::ACT_TDS_CTX && !outbound) {
m_sndcpVersion = (uint8_t)((data[0U]) & 0x0FU); // SNDCP Version
} else {
m_nsapi = (uint8_t)((data[0U]) & 0x0FU); // NSAPI
}
return true;
}
/// <summary>
/// Internal helper to encode a SNDCP header.
/// </summary>
/// <param name="data"></param>
/// <param name="outbound"></param>
void SNDCPPacket::encodeHeader(uint8_t* data, bool outbound)
{
assert(data != nullptr);
data[0U] = ((m_pduType << 4U) & 0xF0U); // SNDCP PDU Message Type
if (m_pduType == SNDCP_PDUType::ACT_TDS_CTX && !outbound) {
data[0U] += (m_sndcpVersion & 0x0FU); // SNDCP Version
} else {
data[0U] += (m_nsapi & 0x0FU); // NSAPI
}
}
/// <summary>
/// Internal helper to copy the the class.
/// </summary>
/// <param name="data"></param>
void SNDCPPacket::copy(const SNDCPPacket& data)
{
m_pduType = data.m_pduType;
m_sndcpVersion = data.m_sndcpVersion;
}

@ -0,0 +1,64 @@
// SPDX-License-Identifier: GPL-2.0-only
/**
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Common Library
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
#if !defined(__P25_SNDCP__SNDCPHEADER_H__)
#define __P25_SNDCP__SNDCPHEADER_H__
#include "common/Defines.h"
#include "common/Utils.h"
#include <string>
namespace p25
{
namespace sndcp
{
// ---------------------------------------------------------------------------
// Class Declaration
// Represents a SNDCP PDU packet header.
// ---------------------------------------------------------------------------
class HOST_SW_API SNDCPPacket {
public:
/// <summary>Initializes a copy instance of the SNDCPPacket class.</summary>
SNDCPPacket(const SNDCPPacket& data);
/// <summary>Initializes a new instance of the SNDCPPacket class.</summary>
SNDCPPacket();
/// <summary>Finalizes a instance of the SNDCPPacket class.</summary>
~SNDCPPacket();
/// <summary>Decode a SNDCP packet.</summary>
virtual bool decode(const uint8_t* data) = 0;
/// <summary>Encode a SNDCP packet.</summary>
virtual void encode(uint8_t* data) = 0;
public:
/** Common Data */
/// <summary>SNDCP PDU Type.</summary>
__PROTECTED_PROPERTY(uint8_t, pduType, PDUType);
/// <summary>Link control opcode.</summary>
__READONLY_PROPERTY(uint8_t, sndcpVersion, SNDCPVersion);
/// <summary>Network Service Access Point Identifier</summary>
__PROTECTED_PROPERTY(uint8_t, nsapi, NSAPI);
protected:
/// <summary>Internal helper to decode a SNDCP header.</summary>
bool decodeHeader(const uint8_t* data, bool outbound = false);
/// <summary>Internal helper to encode a SNDCP header.</summary>
void encodeHeader(uint8_t* data, bool outbound = false);
__PROTECTED_COPY(SNDCPPacket);
};
} // namespace sndcp
} // namespace p25
#endif // __P25_SNDCP__SNDCPHEADER_H__

@ -16,6 +16,7 @@
#include "common/p25/P25Defines.h"
#include "common/p25/acl/AccessControl.h"
#include "common/p25/lc/tdulc/TDULCFactory.h"
#include "common/p25/sndcp/SNDCPFactory.h"
#include "common/p25/P25Utils.h"
#include "common/p25/Sync.h"
#include "common/edac/CRC.h"
@ -24,10 +25,11 @@
#include "p25/packet/Data.h"
#include "ActivityLog.h"
using namespace p25::packet;
using namespace p25::data;
using namespace p25::defines;
using namespace p25;
using namespace p25::defines;
using namespace p25::data;
using namespace p25::sndcp;
using namespace p25::packet;
#include <cassert>
#include <cstring>
@ -38,6 +40,7 @@ using namespace p25;
const uint32_t CONN_WAIT_TIMEOUT = 1U;
const uint32_t SNDCP_READY_TIMEOUT = 10U;
const uint32_t SNDCP_STANDBY_TIMEOUT = 60U;
// ---------------------------------------------------------------------------
// Public Class Members
@ -224,6 +227,12 @@ bool Data::process(uint8_t* data, uint32_t len)
// process all blocks in the data stream
uint32_t dataOffset = 0U;
// if the primary header has a header offset ensure data if offset by that amount
if (m_rfDataHeader.getHeaderOffset() > 0U) {
offset += m_rfDataHeader.getHeaderOffset() * 8;
m_pduUserDataLength -= m_rfDataHeader.getHeaderOffset();
}
// if we are using a secondary header place it in the PDU user data buffer
if (m_rfUseSecondHeader) {
m_rfSecondHeader.getData(m_pduUserData + dataOffset);
@ -349,6 +358,36 @@ bool Data::process(uint8_t* data, uint32_t len)
LogMessage(LOG_RF, P25_PDU_STR ", ISP, response, fmt = $%02X, rspClass = $%02X, rspType = $%02X, rspStatus = $%02X, llId = %u, srcLlId = %u",
m_rfDataHeader.getFormat(), m_rfDataHeader.getResponseClass(), m_rfDataHeader.getResponseType(), m_rfDataHeader.getResponseStatus(),
m_rfDataHeader.getLLId(), m_rfDataHeader.getSrcLLId());
if (m_rfDataHeader.getResponseClass() == PDUAckClass::ACK && m_rfDataHeader.getResponseType() == PDUAckType::ACK) {
LogMessage(LOG_RF, P25_PDU_STR ", ISP, response, OSP ACK, llId = %u",
m_rfDataHeader.getLLId());
} else {
if (m_rfDataHeader.getResponseClass() == PDUAckClass::NACK) {
switch (m_rfDataHeader.getResponseType()) {
case PDUAckType::NACK_ILLEGAL:
LogMessage(LOG_RF, P25_PDU_STR ", ISP, response, OSP NACK, illegal format, llId = %u",
m_rfDataHeader.getLLId());
break;
case PDUAckType::NACK_PACKET_CRC:
LogMessage(LOG_RF, P25_PDU_STR ", ISP, response, OSP NACK, packet CRC error, llId = %u",
m_rfDataHeader.getLLId());
break;
case PDUAckType::NACK_SEQ:
case PDUAckType::NACK_OUT_OF_SEQ:
LogMessage(LOG_RF, P25_PDU_STR ", ISP, response, OSP NACK, packet out of sequence, llId = %u",
m_rfDataHeader.getLLId());
break;
case PDUAckType::NACK_UNDELIVERABLE:
LogMessage(LOG_RF, P25_PDU_STR ", ISP, response, OSP NACK, packet undeliverable, llId = %u",
m_rfDataHeader.getLLId());
break;
default:
break;
}
}
}
}
if (m_repeatPDU) {
@ -367,14 +406,14 @@ bool Data::process(uint8_t* data, uint32_t len)
{
uint8_t regType = (m_pduUserData[0] >> 4) & 0x0F;
switch (regType) {
case PDURegType::CNCT:
case PDURegType::CONNECT:
{
uint32_t llId = (m_pduUserData[1U] << 16) + (m_pduUserData[2U] << 8) + m_pduUserData[3U];
ulong64_t ipAddr = (m_pduUserData[8U] << 24) + (m_pduUserData[9U] << 16) +
(m_pduUserData[10U] << 8) + m_pduUserData[11U];
if (m_verbose) {
LogMessage(LOG_RF, P25_PDU_STR ", CNCT (Registration Request Connect), llId = %u, ipAddr = %s", llId, __IP_FROM_ULONG(ipAddr).c_str());
LogMessage(LOG_RF, P25_PDU_STR ", CONNECT (Registration Request Connect), llId = %u, ipAddr = %s", llId, __IP_FROM_ULONG(ipAddr).c_str());
}
m_connQueueTable[llId] = std::make_tuple(m_rfDataHeader.getMFId(), ipAddr);
@ -383,12 +422,12 @@ bool Data::process(uint8_t* data, uint32_t len)
m_connTimerTable[llId].start();
}
break;
case PDURegType::DISCNCT:
case PDURegType::DISCONNECT:
{
uint32_t llId = (m_pduUserData[1U] << 16) + (m_pduUserData[2U] << 8) + m_pduUserData[3U];
if (m_verbose) {
LogMessage(LOG_RF, P25_PDU_STR ", DISCNCT (Registration Request Disconnect), llId = %u", llId);
LogMessage(LOG_RF, P25_PDU_STR ", DISCONNECT (Registration Request Disconnect), llId = %u", llId);
}
if (hasLLIdFNEReg(llId)) {
@ -416,7 +455,7 @@ bool Data::process(uint8_t* data, uint32_t len)
m_rfDataHeader.getAMBTOpcode(), m_rfDataHeader.getBlocksToFollow());
}
processSNDCP();
processSNDCPControl();
}
break;
case PDUSAP::TRUNK_CTRL:
@ -939,7 +978,7 @@ void Data::sndcpInitialize(uint32_t llId)
if (!isSNDCPInitialized(llId)) {
m_sndcpStateTable[llId] = SNDCPState::IDLE;
m_sndcpReadyTimers[llId] = Timer(1000U, SNDCP_READY_TIMEOUT);
m_sndcpStandyTimers[llId] = Timer(1000U, 1000U);
m_sndcpStandbyTimers[llId] = Timer(1000U, SNDCP_STANDBY_TIMEOUT);
if (m_verbose) {
LogMessage(LOG_RF, P25_PDU_STR ", SNDCP, first initialize, llId = %u, state = %u", llId, (uint8_t)SNDCPState::IDLE);
@ -962,6 +1001,37 @@ bool Data::isSNDCPInitialized(uint32_t llId) const
return false;
}
/// <summary>
/// Helper to reset the SNDCP state for a logical link ID.
/// </summary>
/// <param name="llId"></param>
/// <param name="callTerm"></param>
void Data::sndcpReset(uint32_t llId, bool callTerm)
{
if (isSNDCPInitialized(llId)) {
if (m_verbose) {
LogMessage(LOG_RF, P25_PDU_STR ", SNDCP, reset, llId = %u, state = %u", llId, (uint8_t)m_sndcpStateTable[llId]);
}
m_sndcpStateTable[llId] = SNDCPState::CLOSED;
m_sndcpReadyTimers[llId].stop();
m_sndcpStandbyTimers[llId].stop();
if (callTerm) {
if (m_verbose) {
LogMessage(LOG_RF, P25_TDULC_STR ", CALL_TERM (Call Termination), llId = %u", llId);
}
std::unique_ptr<lc::TDULC> lc = std::make_unique<lc::tdulc::LC_CALL_TERM>();
m_p25->m_control->writeRF_TDULC(lc.get(), true);
if (m_p25->m_notifyCC) {
m_p25->notifyCC_ReleaseGrant(llId);
}
}
}
}
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
@ -1002,7 +1072,7 @@ Data::Data(Control* p25, bool dumpPDUData, bool repeatPDU, bool debug, bool verb
m_connTimerTable(),
m_sndcpStateTable(),
m_sndcpReadyTimers(),
m_sndcpStandyTimers(),
m_sndcpStandbyTimers(),
m_dumpPDUData(dumpPDUData),
m_repeatPDU(repeatPDU),
m_verbose(verbose),
@ -1027,7 +1097,7 @@ Data::Data(Control* p25, bool dumpPDUData, bool repeatPDU, bool debug, bool verb
m_sndcpStateTable.clear();
m_sndcpReadyTimers.clear();
m_sndcpStandyTimers.clear();
m_sndcpStandbyTimers.clear();
}
/// <summary>
@ -1045,14 +1115,110 @@ Data::~Data()
/// <summary>
/// Helper used to process SNDCP control data from PDU data.
/// </summary>
/// <param name="dataHeader"></param>
/// <param name="dataBlock"></param>
bool Data::processSNDCP()
bool Data::processSNDCPControl()
{
if (!m_p25->m_sndcpSupport) {
return false;
}
uint8_t data[P25_PDU_UNCONFIRMED_LENGTH_BYTES];
::memset(data, 0x00U, P25_PDU_UNCONFIRMED_LENGTH_BYTES);
std::unique_ptr<sndcp::SNDCPPacket> packet = SNDCPFactory::create(m_pduUserData);
if (packet == nullptr) {
LogWarning(LOG_RF, P25_PDU_STR ", undecodable SNDCP packet");
return false;
}
uint32_t llId = m_rfDataHeader.getLLId();
switch (packet->getPDUType()) {
case SNDCP_PDUType::ACT_TDS_CTX:
{
SNDCPCtxActRequest* isp = static_cast<SNDCPCtxActRequest*>(packet.get());
if (m_verbose) {
LogMessage(LOG_RF, P25_PDU_STR ", SNDCP context activation request, llId = %u, ipAddr = %08lX, nat = $%02X, dsut = $%02X", llId,
isp->getIPAddress(), isp->getNAT(), isp->getDSUT());
}
DataHeader rspHeader = DataHeader();
rspHeader.setFormat(PDUFormatType::CONFIRMED);
rspHeader.setMFId(MFG_STANDARD);
rspHeader.setAckNeeded(true);
rspHeader.setOutbound(true);
rspHeader.setSAP(PDUSAP::SNDCP_CTRL_DATA);
rspHeader.setLLId(llId);
rspHeader.setBlocksToFollow(1U);
// which network address type is this?
switch (isp->getNAT()) {
case SNDCPNAT::IPV4_STATIC_ADDR:
{
std::unique_ptr<SNDCPCtxActReject> osp = std::make_unique<SNDCPCtxActReject>();
osp->setNSAPI(packet->getNSAPI());
osp->setRejectCode(SNDCPRejectReason::DYN_IP_ALLOCATION_UNSUPPORTED);
osp->encode(data);
writeRF_PDU_User(rspHeader, rspHeader, false, data);
sndcpReset(llId, true);
// TODO TODO TODO
}
break;
case SNDCPNAT::IPV4_DYN_ADDR:
{
std::unique_ptr<SNDCPCtxActReject> osp = std::make_unique<SNDCPCtxActReject>();
osp->setNSAPI(packet->getNSAPI());
osp->setRejectCode(SNDCPRejectReason::STATIC_IP_ALLOCATION_UNSUPPORTED);
osp->encode(data);
writeRF_PDU_User(rspHeader, rspHeader, false, data);
sndcpReset(llId, true);
// TODO TODO TODO
}
break;
default:
{
std::unique_ptr<SNDCPCtxActReject> osp = std::make_unique<SNDCPCtxActReject>();
osp->setNSAPI(packet->getNSAPI());
osp->setRejectCode(SNDCPRejectReason::ANY_REASON);
osp->encode(data);
writeRF_PDU_User(rspHeader, rspHeader, false, data);
sndcpReset(llId, true);
}
break;
}
}
break;
case SNDCP_PDUType::DEACT_TDS_CTX_REQ:
{
SNDCPCtxDeactivation* isp = static_cast<SNDCPCtxDeactivation*>(packet.get());
if (m_verbose) {
LogMessage(LOG_RF, P25_PDU_STR ", SNDCP context deactivation request, llId = %u, deactType = %02X", llId,
isp->getDeactType());
}
writeRF_PDU_Ack_Response(PDUAckClass::ACK, PDUAckType::ACK, 0U, llId);
sndcpReset(llId, true);
}
break;
default:
{
LogError(LOG_RF, P25_PDU_STR ", unhandled SNDCP PDU Type, pduType = $%02X", packet->getPDUType());
sndcpReset(llId, true);
}
break;
} // switch (packet->getPDUType())
return true;
}

@ -64,9 +64,11 @@ namespace p25
/** SNDCP */
/// <summary>Helper to initialize the SNDCP state for a logical link ID.</summary>
virtual void sndcpInitialize(uint32_t srcId);
void sndcpInitialize(uint32_t llId);
/// <summary>Helper to determine if the logical link ID has been SNDCP initialized.</summary>
virtual bool isSNDCPInitialized(uint32_t srcId) const;
bool isSNDCPInitialized(uint32_t llId) const;
/// <summary>Helper to reset the SNDCP state for a logical link ID.</summary>
void sndcpReset(uint32_t llId, bool callTerm = false);
private:
friend class p25::Control;
@ -104,7 +106,7 @@ namespace p25
std::unordered_map<uint32_t, defines::SNDCPState::E> m_sndcpStateTable;
std::unordered_map<uint32_t, Timer> m_sndcpReadyTimers;
std::unordered_map<uint32_t, Timer> m_sndcpStandyTimers;
std::unordered_map<uint32_t, Timer> m_sndcpStandbyTimers;
bool m_dumpPDUData;
bool m_repeatPDU;
@ -118,7 +120,7 @@ namespace p25
~Data();
/// <summary>Helper used to process SNDCP control data from PDU data.</summary>
bool processSNDCP();
bool processSNDCPControl();
/// <summary>Write data processed from RF to the network.</summary>
void writeNetwork(const uint8_t currentBlock, const uint8_t* data, uint32_t len, bool lastBlock);

@ -26,10 +26,10 @@
#include "p25/packet/Voice.h"
#include "ActivityLog.h"
using namespace p25::packet;
using namespace p25::dfsi::defines;
using namespace p25::defines;
using namespace p25;
using namespace p25::defines;
using namespace p25::dfsi::defines;
using namespace p25::packet;
#include <cassert>
#include <cstring>

Loading…
Cancel
Save

Powered by TurnKey Linux.