diff --git a/configs/fne-config.example.yml b/configs/fne-config.example.yml
index 4fc2f286..b2614b6f 100644
--- a/configs/fne-config.example.yml
+++ b/configs/fne-config.example.yml
@@ -88,6 +88,8 @@ master:
influxOrg: "dvm"
# Data Bucket Name on InfluxDB instance API.
influxBucket: "dvm"
+ # Flag indicating whether TSBK/CSBK/RCCH messages will be logged to InfluxDB.
+ influxLogRawData: false
#
# Talkgroup Rules Configuration
diff --git a/src/common/dmr/lc/CSBK.cpp b/src/common/dmr/lc/CSBK.cpp
index 50f68948..67655709 100644
--- a/src/common/dmr/lc/CSBK.cpp
+++ b/src/common/dmr/lc/CSBK.cpp
@@ -72,7 +72,8 @@ CSBK::CSBK() :
m_logicalCh1(DMR_CHNULL),
m_logicalCh2(DMR_CHNULL),
m_slotNo(0U),
- m_siteIdenEntry(::lookups::IdenTable())
+ m_siteIdenEntry(::lookups::IdenTable()),
+ m_raw(nullptr)
{
/* stub */
}
@@ -80,7 +81,11 @@ CSBK::CSBK() :
///
/// Finalizes a instance of the CSBK class.
///
-CSBK::~CSBK() = default;
+CSBK::~CSBK()
+{
+ if (m_raw != nullptr)
+ delete[] m_raw;
+}
///
/// Returns a string that represents the current CSBK.
@@ -91,6 +96,16 @@ std::string CSBK::toString()
return std::string("CSBKO_UNKWN (Unknown CSBK)");
}
+///
+/// Returns a copy of the raw decoded CSBK bytes.
+///
+/// This will only return data for a *decoded* CSBK, not a created or copied CSBK.
+///
+uint8_t* CSBK::getDecodedRaw() const
+{
+ return m_raw;
+}
+
///
/// Regenerate a DMR CSBK without decoding.
///
@@ -268,6 +283,9 @@ bool CSBK::decode(const uint8_t* data, uint8_t* payload)
Utils::dump(2U, "Decoded CSBK", csbk, DMR_CSBK_LENGTH_BYTES);
}
+ m_raw = new uint8_t[DMR_CSBK_LENGTH_BYTES];
+ ::memcpy(m_raw, csbk, DMR_CSBK_LENGTH_BYTES);
+
m_CSBKO = csbk[0U] & 0x3FU; // CSBKO
m_lastBlock = (csbk[0U] & 0x80U) == 0x80U; // Last Block Marker
m_FID = csbk[1U]; // Feature ID
diff --git a/src/common/dmr/lc/CSBK.h b/src/common/dmr/lc/CSBK.h
index 494864cd..6169b177 100644
--- a/src/common/dmr/lc/CSBK.h
+++ b/src/common/dmr/lc/CSBK.h
@@ -46,6 +46,10 @@ namespace dmr
/// Returns a string that represents the current CSBK.
virtual std::string toString();
+ /// Returns a copy of the raw decoded CSBK bytes.
+ /// This will only return data for a *decoded* CSBK, not a created or copied CSBK.
+ uint8_t* getDecodedRaw() const;
+
/// Regenerate a DMR CSBK without decoding.
/// This is because the DMR architecture allows fall-thru of unsupported CSBKs.
static bool regenerate(uint8_t* data, uint8_t dataType);
@@ -144,6 +148,9 @@ namespace dmr
void encode(uint8_t* data, const uint8_t* payload);
__PROTECTED_COPY(CSBK);
+
+ private:
+ uint8_t* m_raw;
};
} // namespace lc
} // namespace dmr
diff --git a/src/common/p25/lc/TSBK.cpp b/src/common/p25/lc/TSBK.cpp
index 5a1e3a80..bf89b3cd 100644
--- a/src/common/p25/lc/TSBK.cpp
+++ b/src/common/p25/lc/TSBK.cpp
@@ -95,7 +95,8 @@ TSBK::TSBK() :
m_group(true),
m_siteIdenEntry(lookups::IdenTable()),
m_rs(),
- m_trellis()
+ m_trellis(),
+ m_raw(nullptr)
{
if (m_siteCallsign == nullptr) {
m_siteCallsign = new uint8_t[P25_MOT_CALLSIGN_LENGTH_BYTES];
@@ -112,7 +113,8 @@ TSBK::TSBK() :
///
TSBK::~TSBK()
{
- /* stub */
+ if (m_raw != nullptr)
+ delete[] m_raw;
}
///
@@ -125,6 +127,16 @@ std::string TSBK::toString(bool isp)
return std::string("TSBK_IOSP_UNKWN (Unknown TSBK)");
}
+///
+/// Returns a copy of the raw decoded TSBK bytes.
+///
+/// This will only return data for a *decoded* TSBK, not a created or copied TSBK.
+///
+uint8_t* TSBK::getDecodedRaw() const
+{
+ return m_raw;
+}
+
///
/// Sets the callsign.
///
@@ -270,6 +282,9 @@ bool TSBK::decode(const uint8_t* data, uint8_t* payload, bool rawTSBK)
Utils::dump(2U, "TSBK::decode(), TSBK Value", tsbk, P25_TSBK_LENGTH_BYTES);
}
+ m_raw = new uint8_t[P25_TSBK_LENGTH_BYTES];
+ ::memcpy(m_raw, tsbk, P25_TSBK_LENGTH_BYTES);
+
m_lco = tsbk[0U] & 0x3F; // LCO
m_lastBlock = (tsbk[0U] & 0x80U) == 0x80U; // Last Block Marker
m_mfId = tsbk[1U]; // Mfg Id.
diff --git a/src/common/p25/lc/TSBK.h b/src/common/p25/lc/TSBK.h
index 04704bb7..f5bf3c47 100644
--- a/src/common/p25/lc/TSBK.h
+++ b/src/common/p25/lc/TSBK.h
@@ -68,6 +68,10 @@ namespace p25
/// Returns a string that represents the current TSBK.
virtual std::string toString(bool isp = false);
+ /// Returns a copy of the raw decoded TSBK bytes.
+ /// This will only return data for a *decoded* TSBK, not a created or copied TSBK.
+ uint8_t* getDecodedRaw() const;
+
/// Gets the flag indicating verbose log output.
static bool getVerbose() { return m_verbose; }
/// Sets the flag indicating verbose log output.
@@ -159,6 +163,9 @@ namespace p25
void encode(uint8_t* data, const uint8_t* payload, bool rawTSBK = false, bool noTrellis = false);
__PROTECTED_COPY(TSBK);
+
+ private:
+ uint8_t* m_raw;
};
} // namespace lc
} // namespace p25
diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp
index 6290d9cf..4987cca7 100644
--- a/src/fne/network/FNENetwork.cpp
+++ b/src/fne/network/FNENetwork.cpp
@@ -101,6 +101,7 @@ FNENetwork::FNENetwork(HostFNE* host, const std::string& address, uint16_t port,
m_influxServerToken(),
m_influxOrg("dvm"),
m_influxBucket("dvm"),
+ m_influxLogRawData(false),
m_reportPeerPing(reportPeerPing),
m_verbose(verbose)
{
@@ -151,6 +152,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions)
m_influxServerToken = conf["influxServerToken"].as();
m_influxOrg = conf["influxOrg"].as("dvm");
m_influxBucket = conf["influxBucket"].as("dvm");
+ m_influxLogRawData = conf["influxLogRawData"].as(false);
if (m_enableInfluxDB) {
m_influxServer = influxdb::ServerInfo(m_influxServerAddress, m_influxServerPort, m_influxOrg, m_influxServerToken, m_influxBucket);
}
@@ -168,6 +170,7 @@ void FNENetwork::setOptions(yaml::Node& conf, bool printOptions)
LogInfo(" InfluxDB Port: %u", m_influxServerPort);
LogInfo(" InfluxDB Organization: %s", m_influxOrg.c_str());
LogInfo(" InfluxDB Bucket: %s", m_influxBucket.c_str());
+ LogInfo(" InfluxDB Log Raw TSBK/CSBK/RCCH: %s", m_influxLogRawData ? "yes" : "no");
}
}
}
diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h
index 4d59d438..a2b90211 100644
--- a/src/fne/network/FNENetwork.h
+++ b/src/fne/network/FNENetwork.h
@@ -300,6 +300,7 @@ namespace network
std::string m_influxServerToken;
std::string m_influxOrg;
std::string m_influxBucket;
+ bool m_influxLogRawData;
influxdb::ServerInfo m_influxServer;
bool m_reportPeerPing;
diff --git a/src/fne/network/fne/TagDMRData.cpp b/src/fne/network/fne/TagDMRData.cpp
index 7b77d99e..211ea939 100644
--- a/src/fne/network/fne/TagDMRData.cpp
+++ b/src/fne/network/fne/TagDMRData.cpp
@@ -231,6 +231,11 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId
m_parrotFrames.push_back(std::make_tuple(copy, len, pktSeq, streamId));
}
+ // process CSBK from peer
+ if (!processCSBK(buffer, peerId, dmrData)) {
+ return false;
+ }
+
// repeat traffic to the connected peers
if (m_network->m_peers.size() > 0U) {
uint32_t i = 0U;
@@ -504,6 +509,49 @@ bool TagDMRData::peerRewrite(uint32_t peerId, uint32_t& dstId, uint32_t& slotNo,
return rewrote;
}
+///
+/// Helper to process CSBKs being passed from a peer.
+///
+///
+/// Peer ID
+///
+bool TagDMRData::processCSBK(uint8_t* buffer, uint32_t peerId, dmr::data::Data& dmrData)
+{
+ // are we receiving a CSBK?
+ if (dmrData.getDataType() == DT_CSBK) {
+ uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U];
+ dmrData.getData(data + 2U);
+
+ std::unique_ptr csbk = lc::csbk::CSBKFactory::createCSBK(data + 2U, DT_CSBK);
+ if (csbk != nullptr) {
+ // report csbk event to InfluxDB
+ if (m_network->m_enableInfluxDB && m_network->m_influxLogRawData) {
+ const uint8_t* raw = csbk->getDecodedRaw();
+ if (raw != nullptr) {
+ std::stringstream ss;
+ ss << std::hex <<
+ (int)raw[0] << (int)raw[1] << (int)raw[2] << (int)raw[4] <<
+ (int)raw[5] << (int)raw[6] << (int)raw[7] << (int)raw[8] <<
+ (int)raw[9] << (int)raw[10] << (int)raw[11];
+
+ influxdb::QueryBuilder()
+ .meas("csbk_event")
+ .tag("peerId", std::to_string(peerId))
+ .tag("lco", __INT_HEX_STR(csbk->getCSBKO()))
+ .tag("csbk", csbk->toString())
+ .field("raw", ss.str())
+ .timestamp(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count())
+ .request(m_network->m_influxServer);
+ }
+ }
+ } else {
+ LogWarning(LOG_NET, "PEER %u, passing CSBK that failed to decode? csbk == nullptr", peerId);
+ }
+ }
+
+ return true;
+}
+
///
/// Helper to determine if the peer is permitted for traffic.
///
diff --git a/src/fne/network/fne/TagDMRData.h b/src/fne/network/fne/TagDMRData.h
index d5faab71..30baaba8 100644
--- a/src/fne/network/fne/TagDMRData.h
+++ b/src/fne/network/fne/TagDMRData.h
@@ -77,6 +77,9 @@ namespace network
/// Helper to route rewrite destination ID and slot.
bool peerRewrite(uint32_t peerId, uint32_t& dstId, uint32_t& slotNo, bool outbound = true);
+ /// Helper to process CSBKs being passed from a peer.
+ bool processCSBK(uint8_t* buffer, uint32_t peerId, dmr::data::Data& dmrData);
+
/// Helper to determine if the peer is permitted for traffic.
bool isPeerPermitted(uint32_t peerId, dmr::data::Data& data, uint32_t streamId);
/// Helper to validate the DMR call stream.
diff --git a/src/fne/network/fne/TagP25Data.cpp b/src/fne/network/fne/TagP25Data.cpp
index 52af13e2..a57294aa 100644
--- a/src/fne/network/fne/TagP25Data.cpp
+++ b/src/fne/network/fne/TagP25Data.cpp
@@ -631,6 +631,27 @@ bool TagP25Data::processTSDU(uint8_t* buffer, uint32_t peerId, uint8_t duid)
std::unique_ptr tsbk = lc::tsbk::TSBKFactory::createTSBK(data.get());
if (tsbk != nullptr) {
+ // report tsbk event to InfluxDB
+ if (m_network->m_enableInfluxDB && m_network->m_influxLogRawData) {
+ const uint8_t* raw = tsbk->getDecodedRaw();
+ if (raw != nullptr) {
+ std::stringstream ss;
+ ss << std::hex <<
+ (int)raw[0] << (int)raw[1] << (int)raw[2] << (int)raw[4] <<
+ (int)raw[5] << (int)raw[6] << (int)raw[7] << (int)raw[8] <<
+ (int)raw[9] << (int)raw[10] << (int)raw[11];
+
+ influxdb::QueryBuilder()
+ .meas("tsbk_event")
+ .tag("peerId", std::to_string(peerId))
+ .tag("lco", __INT_HEX_STR(tsbk->getLCO()))
+ .tag("tsbk", tsbk->toString())
+ .field("raw", ss.str())
+ .timestamp(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count())
+ .request(m_network->m_influxServer);
+ }
+ }
+
// handle standard P25 reference opcodes
switch (tsbk->getLCO()) {
case TSBK_OSP_ADJ_STS_BCAST: