Skip to the content.

Protocol Integration Guide

Version: 1.0 Last Updated: 2025-11-06 Status: Active Development Guidelines


Table of Contents

  1. Overview
  2. Architecture: Where to Add Code
  3. Adding Input Format Handlers
  4. Adding Output Formatters
  5. Testing Requirements
  6. Performance Considerations
  7. Security Considerations
  8. Reference Implementations

Overview

This document provides guidelines for integrating new protocol adapters (input formats and output formatters) into the ASTERIX decoder.

Current Input Formats:

Current Output Formats:

Planned Protocols:


Architecture: Where to Add Code

Layer Separation

The ASTERIX decoder follows a three-layer architecture. Protocol adapters belong in different layers:

┌─────────────────────────────────────────────────────────────┐
│                   APPLICATION LAYER                         │
│  - CLI flags and argument parsing                           │
│  - User-facing output formatters                            │
│  - Language binding APIs                                    │
│                                                             │
│  Location: src/main/asterix.cpp                             │
│  Add: Output format selection logic                         │
└─────────────────────────┬───────────────────────────────────┘
                          │
┌─────────────────────────┴───────────────────────────────────┐
│                   ASTERIX LAYER                             │
│  - Protocol-specific format handlers                        │
│  - Input format parsers (PCAP, HDLC, FINAL, GPS)            │
│  - Output formatters (Text, JSON, XML)                      │
│  - ASTERIX data structures                                  │
│                                                             │
│  Location: src/asterix/*subformat*.{cxx,hxx}                │
│  Add: New protocol adapters HERE                            │
└─────────────────────────┬───────────────────────────────────┘
                          │
┌─────────────────────────┴───────────────────────────────────┐
│                   ENGINE LAYER                              │
│  - Generic device abstraction (file, network, serial)       │
│  - Transport-layer protocols (TCP, UDP, multicast)          │
│  - Base format/codec framework                              │
│                                                             │
│  Location: src/engine/                                      │
│  Add: New transport mechanisms (if needed)                  │
└─────────────────────────────────────────────────────────────┘

Decision Matrix: Where to Add Code?

Protocol Type Layer Example Files
Encapsulation format (wraps ASTERIX) ASTERIX Layer asterixpcapsubformat.{cxx,hxx}
Output format (serialize parsed data) ASTERIX Layer DataBlock::getText(), toJSON()
Transport mechanism (network/file I/O) Engine Layer UdpDevice.cpp, FileDevice.cpp
CLI flags (user interface) Application Layer src/main/asterix.cpp

Example Decision Tree:

Is it an ASTERIX encapsulation format (PCAP, HDLC, RTP)?
├─ YES → ASTERIX Layer (src/asterix/*subformat*)
└─ NO  → Is it a transport mechanism (TCP, WebSocket)?
         ├─ YES → Engine Layer (src/engine/*Device*)
         └─ NO  → Is it an output format (JSON, Protobuf)?
                  ├─ YES → ASTERIX Layer (DataBlock::to*)
                  └─ NO  → Application Layer (CLI)

Adding Input Format Handlers

Input format handlers extract ASTERIX payload from encapsulation protocols.

Step 1: Create Handler Files

Naming Convention: asterix<protocol>subformat.{cxx,hxx}

Example: Adding RTP encapsulation

cd src/asterix
touch asterixrtpsubformat.hxx
touch asterixrtpsubformat.cxx

Step 2: Define Header Interface

File: src/asterix/asterixrtpsubformat.hxx

#ifndef ASTERIXRTPSUBFORMAT_HXX__
#define ASTERIXRTPSUBFORMAT_HXX__

class CBaseDevice;
class CBaseFormatDescriptor;

/**
 * @class CAsterixRtpSubformat
 * @brief ASTERIX RTP (Real-time Transport Protocol) sub-format
 *
 * Extracts ASTERIX payload from RTP packets (RFC 3550).
 * RTP header format:
 *  0                   1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |V=2|P|X|  CC   |M|     PT      |       sequence number         |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                           timestamp                           |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |           synchronization source (SSRC) identifier            |
 * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
 * |            contributing source (CSRC) identifiers             |
 * |                             ....                              |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                    ASTERIX payload (variable)                 |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */
class CAsterixRtpSubformat {
public:
    /**
     * @brief Read and parse RTP packet, extract ASTERIX payload
     * @param formatDescriptor Format descriptor context
     * @param device Input device
     * @param discard Output flag - set to true if packet should be discarded
     * @return true on success, false on error
     */
    static bool ReadPacket(
        CBaseFormatDescriptor& formatDescriptor,
        CBaseDevice& device,
        bool& discard
    );

    /**
     * @brief Write ASTERIX data wrapped in RTP packet
     * @param formatDescriptor Format descriptor context
     * @param device Output device
     * @param discard Output flag - set to true if packet should be discarded
     * @return true on success, false on error
     */
    static bool WritePacket(
        CBaseFormatDescriptor& formatDescriptor,
        CBaseDevice& device,
        bool& discard
    );

    /**
     * @brief Process RTP packet (parse ASTERIX payload)
     * @param formatDescriptor Format descriptor context
     * @param device Device context
     * @param discard Output flag - set to true if packet should be discarded
     * @return true on success, false on error
     */
    static bool ProcessPacket(
        CBaseFormatDescriptor& formatDescriptor,
        CBaseDevice& device,
        bool& discard
    );

    /**
     * @brief Send heartbeat/keepalive packet
     * @param formatDescriptor Format descriptor context
     * @param device Device context
     * @return true on success, false on error
     */
    static bool Heartbeat(
        CBaseFormatDescriptor& formatDescriptor,
        CBaseDevice& device
    );

private:
    // RTP header structure (RFC 3550)
    typedef struct rtp_hdr_s {
        unsigned char  vpxcc;      // V(2) + P(1) + X(1) + CC(4)
        unsigned char  mpt;        // M(1) + PT(7)
        unsigned short seq_num;    // Sequence number
        unsigned int   timestamp;  // Timestamp
        unsigned int   ssrc;       // SSRC identifier
        // CSRC list follows (if CC > 0)
    } rtp_hdr_t;

    // Minimum RTP header size (no CSRC)
    static constexpr size_t RTP_MIN_HEADER_SIZE = 12;

    // Maximum CSRC count
    static constexpr size_t RTP_MAX_CSRC = 15;

    /**
     * @brief Get RTP header size (accounts for CSRC list)
     * @param header RTP header pointer
     * @return Total header size in bytes
     */
    static size_t GetHeaderSize(const rtp_hdr_t* header);

    /**
     * @brief Validate RTP header
     * @param header RTP header pointer
     * @param buffer_size Available buffer size
     * @return true if valid, false otherwise
     */
    static bool ValidateHeader(const rtp_hdr_t* header, size_t buffer_size);
};

#endif // ASTERIXRTPSUBFORMAT_HXX__

Step 3: Implement Handler Logic

File: src/asterix/asterixrtpsubformat.cxx

#include "asterixrtpsubformat.hxx"
#include "asterix.h"
#include "InputParser.h"
#include "AsterixData.h"
#include <cstring>

// Get RTP header size (12 bytes + 4*CC bytes for CSRC list)
size_t CAsterixRtpSubformat::GetHeaderSize(const rtp_hdr_t* header) {
    unsigned char cc = header->vpxcc & 0x0F;  // Extract CC field
    return RTP_MIN_HEADER_SIZE + (cc * 4);
}

// Validate RTP header
bool CAsterixRtpSubformat::ValidateHeader(const rtp_hdr_t* header, size_t buffer_size) {
    // Check buffer is large enough for minimum header
    if (buffer_size < RTP_MIN_HEADER_SIZE) {
        LOGERROR(1, "RTP header truncated: buffer size %zu < %zu",
                 buffer_size, RTP_MIN_HEADER_SIZE);
        return false;
    }

    // Check RTP version (must be 2)
    unsigned char version = (header->vpxcc >> 6) & 0x03;
    if (version != 2) {
        LOGERROR(1, "Invalid RTP version: %u (expected 2)", version);
        return false;
    }

    // Check CSRC count
    unsigned char cc = header->vpxcc & 0x0F;
    if (cc > RTP_MAX_CSRC) {
        LOGERROR(1, "Invalid CSRC count: %u (max %zu)", cc, RTP_MAX_CSRC);
        return false;
    }

    // Check buffer is large enough for full header
    size_t header_size = GetHeaderSize(header);
    if (buffer_size < header_size) {
        LOGERROR(1, "RTP header truncated: buffer size %zu < %zu",
                 buffer_size, header_size);
        return false;
    }

    return true;
}

bool CAsterixRtpSubformat::ReadPacket(
    CBaseFormatDescriptor& formatDescriptor,
    CBaseDevice& device,
    bool& discard)
{
    discard = false;

    // Read packet from device
    unsigned char buffer[65536];  // Max UDP packet size
    size_t bytes_read = 0;

    if (!device.Read(buffer, sizeof(buffer), bytes_read)) {
        LOGERROR(1, "Failed to read RTP packet from device");
        return false;
    }

    if (bytes_read == 0) {
        discard = true;
        return true;  // No data available
    }

    // Validate RTP header
    const rtp_hdr_t* rtp_header = reinterpret_cast<const rtp_hdr_t*>(buffer);

    if (!ValidateHeader(rtp_header, bytes_read)) {
        LOGERROR(1, "Invalid RTP header, discarding packet");
        discard = true;
        return true;
    }

    // Extract ASTERIX payload
    size_t header_size = GetHeaderSize(rtp_header);
    size_t payload_size = bytes_read - header_size;

    if (payload_size == 0) {
        LOGDEBUG(1, "RTP packet has no payload, discarding");
        discard = true;
        return true;
    }

    const unsigned char* asterix_data = buffer + header_size;

    // Store payload in format descriptor for processing
    // (Implementation depends on your descriptor structure)
    // formatDescriptor.SetData(asterix_data, payload_size);

    LOGDEBUG(1, "RTP packet: seq=%u, timestamp=%u, payload=%zu bytes",
             ntohs(rtp_header->seq_num),
             ntohl(rtp_header->timestamp),
             payload_size);

    return true;
}

bool CAsterixRtpSubformat::ProcessPacket(
    CBaseFormatDescriptor& formatDescriptor,
    CBaseDevice& device,
    bool& discard)
{
    // Process ASTERIX payload using InputParser
    // (This is typically handled by the framework after ReadPacket)
    return true;
}

bool CAsterixRtpSubformat::WritePacket(
    CBaseFormatDescriptor& formatDescriptor,
    CBaseDevice& device,
    bool& discard)
{
    // TODO: Implement RTP packet writing
    // 1. Build RTP header
    // 2. Append ASTERIX payload
    // 3. Write to device
    return false;
}

bool CAsterixRtpSubformat::Heartbeat(
    CBaseFormatDescriptor& formatDescriptor,
    CBaseDevice& device)
{
    // RTP doesn't require heartbeat (uses RTCP for that)
    return true;
}

Step 4: Integrate with Build System

File: src/asterix/CMakeLists.txt

Add the new source files:

set(ASTERIX_SOURCES
    # ... existing sources ...
    asterixrtpsubformat.cxx
    asterixrtpsubformat.hxx
)

Step 5: Add CLI Flag

File: src/main/asterix.cpp

Add command-line option:

// Add to argument parsing section
else if (!strcmp(argv[nArgPos], "-T") || !strcmp(argv[nArgPos], "--rtp"))
{
    // Enable RTP format
    use_rtp = true;
}

// Add to usage text
printf("  -T, --rtp           Use RTP encapsulation\n");

// Apply format in initialization
if (use_rtp) {
    // Set RTP format handler
    // (Implementation depends on your framework)
}

Step 6: Update Documentation

Files to Update:


Adding Output Formatters

Output formatters serialize parsed ASTERIX data to various formats.

Step 1: Add Formatter Method

Output formatters are methods on the DataBlock class:

File: src/asterix/DataBlock.h

class DataBlock {
public:
    // ... existing methods ...

    /**
     * @brief Export data block to Protocol Buffers format
     * @param formatType Output format type flags
     * @return Protocol Buffers serialized string
     */
    std::string toProtobuf(unsigned int formatType) const;

    /**
     * @brief Export data block to MessagePack format
     * @param formatType Output format type flags
     * @return MessagePack binary data
     */
    std::string toMessagePack(unsigned int formatType) const;
};

Step 2: Implement Formatter

File: src/asterix/DataBlock.cpp

#include "DataBlock.h"
#include "DataRecord.h"
#include <google/protobuf/message.h>  // Example for Protobuf
#include <msgpack.hpp>  // Example for MessagePack

std::string DataBlock::toProtobuf(unsigned int formatType) const {
    // Create Protobuf message (requires .proto definition)
    // This is a simplified example - you'll need to define the schema

    AsterixBlockProto proto_msg;

    // Set metadata
    proto_msg.set_category(m_nCategory);
    proto_msg.set_length(m_nLength);
    proto_msg.set_timestamp_ms(m_nTimestamp);

    // Add data records
    for (const auto& record : m_lDataRecords) {
        auto* proto_record = proto_msg.add_records();

        // Serialize each data item
        for (const auto& item : record->m_lDataItems) {
            auto* proto_item = proto_record->add_items();
            proto_item->set_id(item->m_strID);
            proto_item->set_value(item->toString());
        }
    }

    // Serialize to string
    std::string serialized;
    proto_msg.SerializeToString(&serialized);

    return serialized;
}

std::string DataBlock::toMessagePack(unsigned int formatType) const {
    msgpack::sbuffer buffer;
    msgpack::packer<msgpack::sbuffer> packer(&buffer);

    // Pack as map
    packer.pack_map(4);

    // Category
    packer.pack("category");
    packer.pack(m_nCategory);

    // Length
    packer.pack("length");
    packer.pack(m_nLength);

    // Timestamp
    packer.pack("timestamp_ms");
    packer.pack(m_nTimestamp);

    // Records
    packer.pack("records");
    packer.pack_array(m_lDataRecords.size());

    for (const auto& record : m_lDataRecords) {
        packer.pack_map(1);
        packer.pack("items");
        packer.pack_array(record->m_lDataItems.size());

        for (const auto& item : record->m_lDataItems) {
            packer.pack_map(2);
            packer.pack("id");
            packer.pack(item->m_strID);
            packer.pack("value");
            packer.pack(item->toString());
        }
    }

    return std::string(buffer.data(), buffer.size());
}

Step 3: Add CLI Flag

File: src/main/asterix.cpp

// Add format flag
else if (!strcmp(argv[nArgPos], "-pb") || !strcmp(argv[nArgPos], "--protobuf"))
{
    output_format = FORMAT_PROTOBUF;
}
else if (!strcmp(argv[nArgPos], "-mp") || !strcmp(argv[nArgPos], "--msgpack"))
{
    output_format = FORMAT_MSGPACK;
}

// Use format in output
switch (output_format) {
    case FORMAT_PROTOBUF:
        output = dataBlock->toProtobuf(formatType);
        break;
    case FORMAT_MSGPACK:
        output = dataBlock->toMessagePack(formatType);
        break;
    // ... other formats
}

Step 4: Update Language Bindings

Add output format support to Python/Rust bindings:

Python Example:

def to_protobuf(record: Dict) -> bytes:
    """Convert record to Protocol Buffers format"""
    # Call C++ formatter via FFI
    return _asterix.record_to_protobuf(record)

Rust Example:

impl AsterixRecord {
    pub fn to_protobuf(&self) -> Result<Vec<u8>> {
        // Call C++ formatter via CXX bridge
        unsafe { ffi::asterix_record_to_protobuf(self) }
    }
}

Testing Requirements

1. Unit Tests

Test format handler logic in isolation:

// tests/test_rtp_subformat.cpp
#include <gtest/gtest.h>
#include "asterixrtpsubformat.hxx"

TEST(RtpSubformat, ValidHeader) {
    // Valid RTP header (version 2, no CSRC)
    unsigned char packet[] = {
        0x80, 0x60,  // V=2, P=0, X=0, CC=0, M=0, PT=96
        0x12, 0x34,  // Sequence number
        0x00, 0x00, 0x00, 0x01,  // Timestamp
        0xAB, 0xCD, 0xEF, 0x12,  // SSRC
        // ASTERIX payload follows
    };

    const auto* header = reinterpret_cast<const CAsterixRtpSubformat::rtp_hdr_t*>(packet);

    EXPECT_TRUE(CAsterixRtpSubformat::ValidateHeader(header, sizeof(packet)));
    EXPECT_EQ(CAsterixRtpSubformat::GetHeaderSize(header), 12);
}

TEST(RtpSubformat, InvalidVersion) {
    // Invalid RTP version (V=1)
    unsigned char packet[] = {
        0x40, 0x60,  // V=1 (INVALID), P=0, X=0, CC=0, M=0, PT=96
        0x12, 0x34,
        0x00, 0x00, 0x00, 0x01,
        0xAB, 0xCD, 0xEF, 0x12,
    };

    const auto* header = reinterpret_cast<const CAsterixRtpSubformat::rtp_hdr_t*>(packet);

    EXPECT_FALSE(CAsterixRtpSubformat::ValidateHeader(header, sizeof(packet)));
}

TEST(RtpSubformat, TruncatedHeader) {
    unsigned char packet[] = {
        0x80, 0x60,  // Only 2 bytes (truncated)
    };

    const auto* header = reinterpret_cast<const CAsterixRtpSubformat::rtp_hdr_t*>(packet);

    EXPECT_FALSE(CAsterixRtpSubformat::ValidateHeader(header, sizeof(packet)));
}

2. Integration Tests

Test with real protocol data:

# tests/test_rtp_integration.sh
#!/bin/bash

# Test RTP encapsulated ASTERIX data
./install/asterix -T -f tests/data/asterix_rtp.pcap -j > output.json

# Validate output
if [ -s output.json ]; then
    echo "PASS: RTP parser produced output"
else
    echo "FAIL: RTP parser produced no output"
    exit 1
fi

# Check parsed data is valid JSON
if jq empty output.json 2>/dev/null; then
    echo "PASS: Output is valid JSON"
else
    echo "FAIL: Output is not valid JSON"
    exit 1
fi

3. Performance Tests

Measure parsing throughput:

// benches/bench_rtp.cpp
#include <benchmark/benchmark.h>
#include "asterixrtpsubformat.hxx"

static void BM_RtpParsing(benchmark::State& state) {
    // Load test data
    std::vector<unsigned char> rtp_data = load_test_data("rtp_sample.bin");

    for (auto _ : state) {
        // Parse RTP packet
        CAsterixRtpSubformat::ReadPacket(/* ... */);
    }

    state.SetBytesProcessed(state.iterations() * rtp_data.size());
}

BENCHMARK(BM_RtpParsing);
BENCHMARK_MAIN();

Run benchmarks:

cd build
cmake --build . --target bench_rtp
./bench_rtp --benchmark_format=json > rtp_bench.json

4. Fuzz Testing

Test with malformed protocol data:

// fuzz/fuzz_rtp.cpp
#include <stdint.h>
#include <stddef.h>
#include "asterixrtpsubformat.hxx"

extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
    // Fuzz RTP parser with random input
    if (size < 12) return 0;  // Too small for RTP header

    const auto* header = reinterpret_cast<const CAsterixRtpSubformat::rtp_hdr_t*>(data);

    // Should not crash or leak memory
    CAsterixRtpSubformat::ValidateHeader(header, size);

    return 0;
}

Run fuzzer:

clang++ -g -fsanitize=fuzzer,address fuzz_rtp.cpp -o fuzz_rtp
./fuzz_rtp -max_total_time=3600

Performance Considerations

1. Buffer Reuse

Reuse buffers to avoid allocations (example from PCAP handler):

// Performance Quick Win #5: Buffer reuse (15-20% speedup)
class CAsterixPcapSubformat {
private:
    static std::vector<unsigned char> s_packetBuffer;  // Reusable buffer

public:
    static bool ReadPacket(/* ... */) {
        // Reuse buffer instead of allocating each time
        if (s_packetBuffer.size() < required_size) {
            s_packetBuffer.resize(required_size);
        }

        // Read into reused buffer
        device.Read(s_packetBuffer.data(), required_size, bytes_read);
    }
};

// Initialize static buffer
std::vector<unsigned char> CAsterixPcapSubformat::s_packetBuffer(65536);

2. String Reserve

Reserve string capacity before concatenation:

// Performance Quick Win #1/#2: String reserve (15% speedup each)
std::string buildOutput() {
    std::string result;
    result.reserve(1024);  // Pre-allocate capacity

    result += "Header: ";
    result += data1;
    result += ", Payload: ";
    result += data2;

    return result;
}

3. Avoid Unnecessary Copies

Use std::string_view for read-only string operations:

// BAD: Copies string
void processField(std::string field) {
    if (field == "010") { /* ... */ }
}

// GOOD: Uses view (no copy)
void processField(std::string_view field) {
    if (field == "010") { /* ... */ }
}

4. Minimize Memory Allocations

Preallocate data structures:

class ProtocolHandler {
private:
    std::vector<DataRecord> m_records;

public:
    void parsePackets(size_t expected_count) {
        // Reserve space upfront
        m_records.clear();
        m_records.reserve(expected_count);

        // Parse without reallocation
        for (size_t i = 0; i < expected_count; i++) {
            m_records.emplace_back(parseRecord());
        }
    }
};

5. Benchmark Before Optimizing

Always measure before optimizing:

# Baseline
./install/asterix -f large_file.pcap -j > /dev/null
time: 2.34s

# After optimization
./install/asterix -f large_file.pcap -j > /dev/null
time: 1.87s

# Speedup: 25%

Security Considerations

1. Input Validation

Validate ALL protocol fields:

bool ValidateRtpHeader(const rtp_hdr_t* header, size_t buffer_size) {
    // Check buffer size
    if (buffer_size < RTP_MIN_HEADER_SIZE) {
        return false;
    }

    // Check RTP version
    unsigned char version = (header->vpxcc >> 6) & 0x03;
    if (version != 2) {
        return false;
    }

    // Check CSRC count (prevent integer overflow)
    unsigned char cc = header->vpxcc & 0x0F;
    if (cc > RTP_MAX_CSRC) {
        return false;
    }

    // Check total header size (prevent buffer overflow)
    size_t header_size = RTP_MIN_HEADER_SIZE + (cc * 4);
    if (header_size > buffer_size) {
        return false;
    }

    // Validate payload size (prevent underflow)
    size_t payload_size = buffer_size - header_size;
    if (payload_size > buffer_size) {  // Underflow check
        return false;
    }

    return true;
}

2. Bounds Checking

Always check array/buffer bounds:

// BAD: No bounds checking
unsigned char* payload = buffer + header_size;
unsigned char first_byte = payload[0];  // May be out of bounds!

// GOOD: Explicit bounds check
if (buffer_size > header_size) {
    unsigned char* payload = buffer + header_size;
    size_t payload_size = buffer_size - header_size;

    if (payload_size > 0) {
        unsigned char first_byte = payload[0];  // Safe
    }
}

3. Integer Overflow Prevention

Use checked arithmetic:

// BAD: Integer overflow possible
size_t total_size = header_size + payload_size;  // May overflow!
buffer.resize(total_size);

// GOOD: Overflow check
if (header_size > SIZE_MAX - payload_size) {
    LOGERROR(1, "Integer overflow in size calculation");
    return false;
}
size_t total_size = header_size + payload_size;  // Safe

4. Endianness Handling

Handle byte order correctly:

// Network byte order (big-endian) to host byte order
uint16_t seq_num = ntohs(rtp_header->seq_num);
uint32_t timestamp = ntohl(rtp_header->timestamp);

// Host byte order to network byte order
rtp_header->seq_num = htons(seq_num);
rtp_header->timestamp = htonl(timestamp);

5. Sanitize String Output

Prevent injection attacks in formatted output:

// BAD: Unsanitized user input
printf("Processing packet from: %s\n", user_input);  // Injection risk!

// GOOD: Sanitize or use safe formatting
std::string sanitized = sanitize_string(user_input);
std::cout << "Processing packet from: " << sanitized << std::endl;

Reference Implementations

PCAP Format Handler

Files: src/asterix/asterixpcapsubformat.{cxx,hxx}

Key Features:

Usage:

./install/asterix -P -f sample.pcap -j

Implementation Highlights:

// Buffer reuse pattern (15-20% speedup)
static unsigned char s_PacketBuffer[65536];

bool CAsterixPcapSubformat::ReadPacket(/* ... */) {
    // Read packet header
    pcaprec_hdr_t packet_header;
    device.Read(&packet_header, sizeof(packet_header), bytes_read);

    // Reuse static buffer (avoid allocation)
    size_t packet_size = packet_header.incl_len;
    if (packet_size > sizeof(s_PacketBuffer)) {
        LOGERROR(1, "Packet too large: %zu bytes", packet_size);
        return false;
    }

    device.Read(s_PacketBuffer, packet_size, bytes_read);

    // Strip Ethernet/IP/UDP headers, extract ASTERIX payload
    size_t header_size = 14 + 20 + 8;  // Ethernet + IP + UDP
    const unsigned char* asterix_data = s_PacketBuffer + header_size;
    size_t asterix_size = packet_size - header_size;

    // Process ASTERIX data
    return ProcessAsterixData(asterix_data, asterix_size);
}

HDLC Format Handler

Files: src/asterix/asterixhdlcsubformat.{cxx,hxx}

Key Features:

HDLC Frame Structure:

┌────┬─────────┬──────────────┬─────┬────┐
│Flag│ Address │   Payload    │ FCS │Flag│
│0x7E│ (1-2 B) │  (variable)  │(2 B)│0x7E│
└────┴─────────┴──────────────┴─────┴────┘

FINAL Format Handler

Files: src/asterix/asterixfinalsubformat.{cxx,hxx}

Key Features:

GPS Format Handler

Files: src/asterix/asterixgpssubformat.{cxx,hxx}

Key Features:


Checklist for New Protocol Adapters

Use this checklist when adding a new protocol handler:


Examples

Example 1: Adding WebSocket Transport

Layer: Engine (transport mechanism)

Files to Create:

Integration:

// src/engine/DeviceFactory.cpp
BaseDevice* DeviceFactory::CreateDevice(const std::string& type) {
    if (type == "websocket") {
        return new WebSocketDevice();
    }
    // ... other devices
}

Example 2: Adding YAML Output

Layer: ASTERIX (output formatter)

Files to Modify:

Implementation:

std::string DataBlock::toYAML(unsigned int formatType) const {
    std::ostringstream yaml;

    yaml << "category: " << (int)m_nCategory << "\n";
    yaml << "length: " << m_nLength << "\n";
    yaml << "timestamp_ms: " << m_nTimestamp << "\n";
    yaml << "records:\n";

    for (const auto& record : m_lDataRecords) {
        yaml << "  - items:\n";
        for (const auto& item : record->m_lDataItems) {
            yaml << "      " << item->m_strID << ": " << item->toString() << "\n";
        }
    }

    return yaml.str();
}

References


Document Version: 1.0 Last Updated: 2025-11-06 Maintainers: ASTERIX Contributors License: GPL-3.0-or-later