Skip to the content.

Zenoh Integration Guide

This guide covers using Zenoh with ASTERIX for edge-to-cloud pub/sub data distribution in ATM/ATC environments.

Overview

Zenoh is a zero-overhead pub/sub/query protocol designed for edge-to-cloud data flows. It provides:

Zenoh is particularly suited for:

Building with Zenoh Support (Rust)

The Zenoh integration is implemented in the Rust asterix-rs crate, leveraging Zenoh’s native Rust implementation for optimal performance.

Prerequisites

Build Configuration

cd asterix-rs

# Build with Zenoh feature enabled
cargo build --features zenoh

# Run tests
cargo test --features zenoh

# Build release version
cargo build --release --features zenoh

Cargo.toml Configuration

Add the Zenoh feature to your Cargo.toml:

[dependencies]
asterix = { version = "0.1", features = ["zenoh"] }

API Overview

Key Components

Component Description
ZenohPublisher Publishes ASTERIX records to Zenoh network
ZenohSubscriber Subscribes to ASTERIX data from Zenoh network
ZenohConfig Configuration for Zenoh sessions
AsterixSample Received ASTERIX sample with metadata

Key Expression Convention

ASTERIX data is published using hierarchical key expressions:

asterix/{category}/{sac}/{sic}
Component Description Example
category ASTERIX category number 48, 62, 65
sac System Area Code 1, 10, 255
sic System Identification Code 2, 20, 128

Examples:

Usage Examples

Publishing ASTERIX Data

use asterix::{parse, ParseOptions, init_default};
use asterix::transport::zenoh::{ZenohPublisher, ZenohConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize ASTERIX parser
    init_default()?;

    // Read ASTERIX data
    let data = std::fs::read("recording.asterix")?;
    let records = parse(&data, ParseOptions::default())?;

    // Create Zenoh publisher (uses multicast discovery by default)
    let publisher = ZenohPublisher::new(ZenohConfig::default()).await?;

    // Publish each record
    for record in &records {
        publisher.publish(record).await?;
        println!("Published CAT{} record", record.category);
    }

    // Clean shutdown
    publisher.close().await?;

    Ok(())
}

Subscribing to ASTERIX Data

use asterix::transport::zenoh::{ZenohSubscriber, ZenohConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Subscribe to all CAT048 data
    let mut subscriber = ZenohSubscriber::new(
        ZenohConfig::default(),
        "asterix/48/**"
    ).await?;

    println!("Waiting for ASTERIX data...");

    // Receive samples
    while let Some(sample) = subscriber.recv().await {
        println!(
            "Received: CAT{} from SAC={:?} SIC={:?} ({} bytes)",
            sample.category,
            sample.sac,
            sample.sic,
            sample.data.len()
        );
    }

    subscriber.close().await?;

    Ok(())
}

Publishing Raw Bytes

For scenarios where you have raw ASTERIX data without parsing:

use asterix::transport::zenoh::{ZenohPublisher, ZenohConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let publisher = ZenohPublisher::new(ZenohConfig::default()).await?;

    // Raw ASTERIX bytes
    let raw_data = vec![0x30, 0x00, 0x10, /* ... */];

    // Publish with category only
    publisher.publish_raw(48, &raw_data).await?;

    // Or publish with full routing info
    publisher.publish_raw_with_routing(48, 1, 2, &raw_data).await?;

    publisher.close().await?;

    Ok(())
}

Connecting to a Zenoh Router

For larger deployments with a central router:

use asterix::transport::zenoh::{ZenohPublisher, ZenohConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Connect to specific router
    let config = ZenohConfig::with_router("tcp/192.168.1.100:7447");

    let publisher = ZenohPublisher::new(config).await?;
    // ... publish data

    Ok(())
}

Multiple Endpoints

Connect to multiple routers for redundancy:

use asterix::transport::zenoh::ZenohConfig;

let config = ZenohConfig::with_endpoints(vec![
    "tcp/router1.atc.local:7447".to_string(),
    "tcp/router2.atc.local:7447".to_string(),
]);

Configuration Options

ZenohConfig

Field Type Default Description
endpoints Vec<String> [] Router endpoints to connect to
key_prefix String "asterix" Prefix for key expressions
include_raw_bytes bool true Include raw bytes in published data
congestion_control CongestionControl Block How to handle network congestion
priority Priority Data Message priority level

Congestion Control

Mode Description Use Case
Block Wait until data can be sent Critical data, guaranteed delivery
Drop Drop data if network congested High-frequency updates, latest matters most

Priority Levels

Priority Description
RealTime Highest priority (safety-critical)
Interactive User-facing data
Data Default for bulk data
Background Lowest priority

Network Topologies

Peer-to-Peer (Default)

Uses multicast discovery for automatic peer detection:

let config = ZenohConfig::peer_to_peer();
// or simply
let config = ZenohConfig::default();

Suitable for:

Client-Router

Connect to central router(s):

let config = ZenohConfig::with_router("tcp/router.atc.local:7447");

Suitable for:

Mesh with Routers

Multiple interconnected routers:

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   Site A    │     │   Site B    │     │   Cloud     │
│  Publisher  │────▶│   Router    │────▶│  Subscriber │
│  (CAT048)   │     │  (Bridge)   │     │  (Archive)  │
└─────────────┘     └─────────────┘     └─────────────┘

Running the Examples

The crate includes ready-to-run examples:

Publisher Example

# Parse a file and publish all records
cargo run --example zenoh_publisher --features zenoh -- sample.asterix

# Connect to specific router
cargo run --example zenoh_publisher --features zenoh -- \
    --router tcp/192.168.1.1:7447 sample.asterix

Subscriber Example

# Subscribe to all ASTERIX data (multicast discovery)
cargo run --example zenoh_subscriber --features zenoh

# Subscribe to specific category
cargo run --example zenoh_subscriber --features zenoh -- \
    --filter "asterix/48/**"

# Receive limited samples
cargo run --example zenoh_subscriber --features zenoh -- \
    --max 100

Performance Considerations

Latency Optimization

  1. Use peer-to-peer mode for lowest latency in local networks
  2. Set appropriate priority for time-critical data:
    let mut config = ZenohConfig::default();
    config.priority = Priority::RealTime;
    
  3. Use congestion control wisely:
    • Block for critical data
    • Drop for high-frequency updates

Throughput Optimization

  1. Batch publications when possible
  2. Use raw byte publishing to avoid serialization overhead:
    publisher.publish_raw(category, &data).await?;
    
  3. Configure Zenoh for high throughput (advanced):
    • Adjust buffer sizes
    • Enable batching
    • Use shared memory for local subscribers

Memory Usage

Troubleshooting

Common Issues

“Failed to open Zenoh session”

“No data received”

“High latency”

Debug Logging

Enable Zenoh logging for diagnostics:

RUST_LOG=zenoh=debug cargo run --example zenoh_subscriber --features zenoh

Integration with Other Transports

Bridging to MQTT

Zenoh can bridge to MQTT brokers:

# Run Zenoh router with MQTT bridge
zenohd --rest-http-port 8000 --plugin mqtt

Bridging to DDS

Zenoh has built-in DDS compatibility:

# Run Zenoh router with DDS bridge
zenohd --plugin dds

Security Considerations

TLS Configuration

Zenoh supports TLS for encrypted communications between clients and routers. This section covers complete TLS setup for production deployments.

Certificate Generation

Generate certificates using OpenSSL for testing or production:

#!/bin/bash
# generate_certs.sh - Generate TLS certificates for Zenoh

CERT_DIR="./certs"
mkdir -p "$CERT_DIR"

# 1. Generate CA (Certificate Authority)
openssl genrsa -out "$CERT_DIR/ca.key" 4096
openssl req -new -x509 -days 3650 -key "$CERT_DIR/ca.key" \
    -out "$CERT_DIR/ca.crt" \
    -subj "/C=US/ST=State/L=City/O=Organization/CN=ASTERIX-CA"

# 2. Generate Router Certificate
openssl genrsa -out "$CERT_DIR/router.key" 2048
openssl req -new -key "$CERT_DIR/router.key" \
    -out "$CERT_DIR/router.csr" \
    -subj "/C=US/ST=State/L=City/O=Organization/CN=zenoh-router"

# Sign with CA
openssl x509 -req -days 365 -in "$CERT_DIR/router.csr" \
    -CA "$CERT_DIR/ca.crt" -CAkey "$CERT_DIR/ca.key" \
    -CAcreateserial -out "$CERT_DIR/router.crt"

# 3. Generate Client Certificate (for mTLS)
openssl genrsa -out "$CERT_DIR/client.key" 2048
openssl req -new -key "$CERT_DIR/client.key" \
    -out "$CERT_DIR/client.csr" \
    -subj "/C=US/ST=State/L=City/O=Organization/CN=asterix-client"

openssl x509 -req -days 365 -in "$CERT_DIR/client.csr" \
    -CA "$CERT_DIR/ca.crt" -CAkey "$CERT_DIR/ca.key" \
    -CAcreateserial -out "$CERT_DIR/client.crt"

echo "Certificates generated in $CERT_DIR/"
ls -la "$CERT_DIR/"

Router TLS Configuration

Configure the Zenoh router (zenohd) with TLS:

// zenoh_router_tls.json5
{
  // Listen on TLS endpoint
  listen: {
    endpoints: ["tls/0.0.0.0:7447"]
  },

  // TLS configuration
  transport: {
    link: {
      tls: {
        // Server certificate
        server_certificate: "/path/to/certs/router.crt",
        server_private_key: "/path/to/certs/router.key",

        // CA for client verification (mTLS)
        root_ca_certificate: "/path/to/certs/ca.crt",

        // Require client certificates (mTLS)
        client_auth: true
      }
    }
  }
}

Start router with TLS configuration:

zenohd -c zenoh_router_tls.json5

Client TLS Configuration in Rust

Connect to TLS-enabled router:

use asterix::transport::zenoh::{ZenohPublisher, ZenohConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Simple TLS connection (server certificate verification)
    let config = ZenohConfig::with_router("tls/secure-router.atc.local:7447");
    let publisher = ZenohPublisher::new(config).await?;

    // ... publish data

    publisher.close().await?;
    Ok(())
}

For advanced TLS configuration with client certificates (mTLS), use Zenoh’s native configuration:

use zenoh::Config;
use asterix::transport::zenoh::{ZenohPublisher, ZenohConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create Zenoh config with full TLS settings
    let mut zenoh_config = Config::default();

    // Set TLS endpoint
    zenoh_config.insert_json5(
        "connect/endpoints",
        r#"["tls/secure-router.atc.local:7447"]"#
    )?;

    // Configure client certificate (for mTLS)
    zenoh_config.insert_json5(
        "transport/link/tls/client_certificate",
        r#""/path/to/certs/client.crt""#
    )?;

    zenoh_config.insert_json5(
        "transport/link/tls/client_private_key",
        r#""/path/to/certs/client.key""#
    )?;

    // CA certificate for server verification
    zenoh_config.insert_json5(
        "transport/link/tls/root_ca_certificate",
        r#""/path/to/certs/ca.crt""#
    )?;

    // Create session with TLS config
    let session = zenoh::open(zenoh_config).await?;

    // ... use session for publishing/subscribing

    session.close().await?;
    Ok(())
}

Mutual TLS (mTLS) Architecture

For maximum security in ATM environments, use mutual TLS where both client and server authenticate each other:

┌──────────────────┐                    ┌──────────────────┐
│  ASTERIX Client  │                    │   Zenoh Router   │
│                  │                    │                  │
│  ┌────────────┐  │   TLS Handshake    │  ┌────────────┐  │
│  │client.crt  │──┼───────────────────►│  │server.crt  │  │
│  │client.key  │  │                    │  │server.key  │  │
│  │ca.crt      │◄─┼────────────────────│  │ca.crt      │  │
│  └────────────┘  │   Mutual Auth      │  └────────────┘  │
└──────────────────┘                    └──────────────────┘

1. Client presents client.crt to server
2. Server verifies client.crt against ca.crt
3. Server presents server.crt to client
4. Client verifies server.crt against ca.crt
5. Encrypted channel established

TLS Best Practices for ATM/ATC

Practice Description
Use strong keys RSA 2048+ or ECDSA P-256+
Short certificate lifetimes 90-365 days for operational certs
Certificate revocation Implement CRL or OCSP checking
Separate CA Use dedicated CA for ASTERIX infrastructure
HSM for keys Store private keys in Hardware Security Modules
TLS 1.3 Use TLS 1.3 where possible for improved security
Certificate pinning Consider for high-security deployments

Access Control

For production deployments:

  1. Use dedicated network segments for ASTERIX traffic
  2. Configure router access control to limit who can publish/subscribe
  3. Enable authentication on Zenoh routers
  4. Encrypt data in transit using TLS
  5. Audit logging - Enable connection and message logging

Network Isolation

Consider network segmentation:

┌─────────────────────────────────────────────────────────┐
│                  Operational Network                     │
│  ┌──────────┐    ┌──────────┐    ┌──────────┐          │
│  │ Radar 1  │    │ Radar 2  │    │ Radar 3  │          │
│  │ (CAT048) │    │ (CAT048) │    │ (CAT062) │          │
│  └────┬─────┘    └────┬─────┘    └────┬─────┘          │
│       │               │               │                  │
│       └───────────────┼───────────────┘                  │
│                       │                                   │
│              ┌────────▼────────┐                         │
│              │  Zenoh Router   │                         │
│              │  (DMZ/Bridge)   │                         │
│              └────────┬────────┘                         │
└───────────────────────┼──────────────────────────────────┘
                        │
                        │ (Filtered/Encrypted)
                        ▼
            ┌───────────────────────┐
            │   External Network    │
            │  (Cloud/Analytics)    │
            └───────────────────────┘