You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
209 lines
10 KiB
209 lines
10 KiB
# ADN Systems DMR Peer Server
|
|
|
|
## Overview
|
|
|
|
ADN Systems DMR Peer Server is a fork of FreeDMR, implementing a Digital Mobile Radio (DMR) network server. Launched in April 2024 by international amateur radio enthusiasts, it operates on an Open Bridge Protocol (OBP) fostering a decentralized network architecture. The system handles DMR voice and data communication, acting as a conference bridge/reflector that routes traffic between connected systems (repeaters, hotspots, peers) based on configurable bridge rules.
|
|
|
|
## User Preferences
|
|
|
|
Preferred communication style: Simple, everyday language.
|
|
|
|
## System Architecture
|
|
|
|
### Core Protocol Implementation
|
|
|
|
**Problem**: Need to implement HomeBrew Repeater Protocol (HBP) and Open Bridge Protocol for DMR communication
|
|
**Solution**: Python-based protocol handlers using Twisted framework for asynchronous networking
|
|
- `hblink.py` serves as the core protocol engine implementing HBSYSTEM and OPENBRIDGE classes
|
|
- Supports both peer and master/server modes for network topology flexibility
|
|
- Uses DatagramProtocol for UDP-based DMR packet handling
|
|
- Implements custom authentication using SHA256/BLAKE2b hashing with HMAC for security
|
|
|
|
**Rationale**: Twisted provides battle-tested async I/O suitable for handling multiple simultaneous connections with low latency requirements critical for voice communication.
|
|
|
|
### Bridge/Routing Architecture
|
|
|
|
**Problem**: Route voice traffic between multiple DMR systems based on talkgroups and timeslots
|
|
**Solution**: Conference bridge pattern implemented in `bridge_master.py` and `bridge.py`
|
|
- Traffic routing based on rules defined in `rules.py` configuration
|
|
- Systems can be dynamically activated/deactivated on conference bridges
|
|
- Supports timeout-based automatic disconnection
|
|
- Static routing option available for permanent connections
|
|
|
|
**Alternatives Considered**: End-to-end routing was rejected in favor of conference bridge approach for better scalability and simpler management.
|
|
|
|
**Pros**: Flexible routing, easy management, scalable
|
|
**Cons**: Systems must individually join bridges (not transparent end-to-end)
|
|
|
|
### Proxy Architecture
|
|
|
|
**Problem**: Allow multiple hotspots/repeaters to connect through a single public IP address
|
|
**Solution**: UDP proxy implementation in `hotspot_proxy_v2.py` and `hotspot_proxy_self_service.py`
|
|
- Dynamic port allocation for incoming connections
|
|
- Connection tracking with timeout-based cleanup
|
|
- Blacklist support for access control
|
|
- Self-service variant includes database-backed client management
|
|
|
|
**Rationale**: Many amateur radio operators are behind NAT/firewalls; proxy enables connectivity without port forwarding.
|
|
|
|
### Voice Synthesis System
|
|
|
|
**Problem**: Generate voice announcements for system events (linking, unlinking, status)
|
|
**Solution**: AMBE (Advanced Multi-Band Excitation) voice codec integration
|
|
- Pre-recorded indexed voice files in multiple languages (en_GB, es_ES, fr_FR, etc.)
|
|
- `read_ambe.py` handles reading/parsing of AMBE audio files
|
|
- `mk_voice.py` generates HBP-compliant voice packets from AMBE data
|
|
- `playback.py` and `play_ambe.py` handle voice injection into streams
|
|
|
|
**Rationale**: Provides accessible feedback to users without requiring external TTS systems, maintains compatibility with DMR audio codecs.
|
|
|
|
### Individual Password Authentication
|
|
|
|
**Problem**: Need individual password authentication per Radio ID (indicativo) for enhanced security
|
|
**Solution**: JSON-based password storage with automatic reload
|
|
- Password file: `data/user_passwords.json` stores individual passwords by Radio ID
|
|
- Web dashboard: `dashboard.py` provides admin interface for password management
|
|
- Auto-reload: Passwords are reloaded every 10 seconds without server restart
|
|
- Fallback: Global passphrase used if no individual password is configured
|
|
- Mandatory authentication: Individual or global password required (no null passphrase allowed)
|
|
|
|
**Suffix Support for Radio IDs**:
|
|
- Radio IDs of 9 digits (ej: 214501601-214501699) can use the password of their 7-digit base ID (ej: 2145016)
|
|
- Useful for users with multiple hotspots/devices using suffix extensions
|
|
- Priority: Exact ID match first, then base ID match
|
|
|
|
**Authentication Priority**:
|
|
1. Individual password for exact Radio ID (if configured)
|
|
2. Individual password for base ID (7 digits, for 9-digit IDs with suffix)
|
|
3. Global passphrase (if configured)
|
|
4. Reject connection (no valid credentials)
|
|
|
|
**Dashboard Credentials**: Stored in `config/dashboard_credentials.json` file (not uploaded to GitHub)
|
|
- Supports multiple administrators with the following format:
|
|
```json
|
|
{
|
|
"admins": [
|
|
{"user": "admin", "password": "admin123"},
|
|
{"user": "operador1", "password": "clave456"}
|
|
]
|
|
}
|
|
```
|
|
- Also supports legacy single-admin format for backwards compatibility
|
|
|
|
**Dashboard Web con Acceso Dual**:
|
|
- Pagina principal (`/`): Selector entre acceso usuario y administrador
|
|
- Acceso Usuario (`/user/login`): Login con Radio ID y contrasena para cambiar su propia contrasena
|
|
- Acceso Administrador (`/admin/login`): Login para gestionar todas las contrasenas
|
|
- Los usuarios pueden cambiar su contrasena sin necesitar al administrador
|
|
|
|
**Busqueda por Indicativo (Callsign)**:
|
|
- Descarga automatica diaria de la base de datos de RadioID.net (`data/user.csv`)
|
|
- Formato CSV con campos: RADIO_ID, CALLSIGN, FIRST_NAME, LAST_NAME, CITY, STATE, COUNTRY
|
|
- Funcion de busqueda en el panel de administracion para encontrar Radio IDs por indicativo
|
|
- Muestra toda la informacion del registro: nombre, ciudad, estado, pais
|
|
- Soporta busqueda parcial (ej: "EA4" muestra todos los indicativos que empiezan por EA4)
|
|
- Limita resultados a 20 para mejor rendimiento
|
|
- Boton "Usar ID" para autorellenar el formulario de agregar contrasena
|
|
|
|
### Servidor de Seguridad Centralizado (Rama: descentralizada)
|
|
|
|
**Problema**: Gestionar credenciales de forma centralizada desde un servidor remoto
|
|
**Solucion**: Descarga automatica de archivos de seguridad desde servidor central
|
|
|
|
**Configuracion en adn.cfg seccion [GLOBAL]**:
|
|
- `URL_SECURITY`: IP o DNS del servidor central de seguridad
|
|
- `PORT_SECURITY`: Puerto del servidor central
|
|
- `PASS_SECURITY`: Contrasena de autenticacion del administrador
|
|
- `USERS_PASS`: Nombre del archivo de contrasenas (default: user_passwords.json)
|
|
- `HASH_ENCRYPT`: Nombre del archivo de clave de encriptacion (default: encryption_key.secret)
|
|
|
|
**Comportamiento de Descarga**:
|
|
- `encryption_key.secret`: Se descarga SOLO al arrancar el servidor, sobrescribe el existente
|
|
- `user_passwords.json`: Se descarga cada 5 minutos, compara contenido antes de actualizar
|
|
|
|
**Sintaxis de Descarga (curl)**:
|
|
```
|
|
curl -L "http://URL_SECURITY:PORT_SECURITY/descargar?pass=PASS_SECURITY&encryption_key.secret"
|
|
curl -L "http://URL_SECURITY:PORT_SECURITY/descargar?pass=PASS_SECURITY&user_passwords.json"
|
|
```
|
|
|
|
**Seguridad y Tolerancia a Fallos**:
|
|
- Si la descarga falla, se conservan los archivos locales existentes
|
|
- No se permite contrasena nula (ALLOW_NULL_PASSPHRASE eliminado)
|
|
- Autenticacion obligatoria: contrasena individual o global requerida
|
|
|
|
**Archivos Descargados**:
|
|
- `config/encryption_key.secret`: Clave de encriptacion para descifrar contrasenas
|
|
- `data/user_passwords.json`: Archivo JSON con contrasenas encriptadas por Radio ID
|
|
|
|
### Configuration Management
|
|
|
|
**Problem**: Complex multi-system configuration with ACLs, bridges, and network parameters
|
|
**Solution**: INI-based configuration parsed by `config.py`
|
|
- Centralized configuration in `config/adn.cfg`
|
|
- ACL (Access Control List) processing for registration and talkgroup filtering
|
|
- Support for both whitelist and blacklist approaches
|
|
- Language preferences and multi-language support via `languages.py`
|
|
|
|
### Monitoring and Reporting
|
|
|
|
**Problem**: Need visibility into system operation, connections, and traffic
|
|
**Solution**: Network-based reporting protocol
|
|
- `report_receiver.py` and `report_sql.py` consume events from the core server
|
|
- Pickle-based serialization for config/bridge state transmission
|
|
- Opcode-based protocol defined in `reporting_const.py`
|
|
- SQL integration option for persistent event logging
|
|
|
|
**Rationale**: Separates monitoring concerns from core routing logic, enables multiple monitoring tools.
|
|
|
|
### Asterisk Integration
|
|
|
|
**Problem**: Integration with Asterisk PBX for advanced features (app_rpt)
|
|
**Solution**: Asterisk Manager Interface (AMI) client in `AMI.py`
|
|
- Sends RPT (repeater) commands to Asterisk
|
|
- Uses Twisted LineReceiver for line-based protocol handling
|
|
- Supports node-specific command routing
|
|
|
|
### API Layer
|
|
|
|
**Problem**: External systems need programmatic access to server functions
|
|
**Solution**: XML-RPC and SOAP API implementation
|
|
- `API.py` implements Spyne-based SOAP service (FD_API)
|
|
- Peer validation and authentication endpoints
|
|
- `api_client.py` provides example XML-RPC client
|
|
- Key-based authentication for peer systems
|
|
|
|
## External Dependencies
|
|
|
|
### Network Protocols
|
|
- **Twisted** (>= 16.3.0) - Asynchronous networking framework for all protocol handling
|
|
- **dmr_utils3** (>= 0.1.19) - DMR protocol utilities for packet encoding/decoding, BPTC, Golay error correction
|
|
|
|
### Database Systems
|
|
- **MySQL/MariaDB** - Via twisted.enterprise.adbapi and MySQLdb
|
|
- Used by `proxy_db.py` for self-service proxy client management
|
|
- Stores client registrations, authentication, and connection tracking
|
|
- Optional for report_sql.py event logging
|
|
|
|
### Remote Object Communication
|
|
- **Pyro5** - Python Remote Objects for inter-process communication
|
|
- Used by proxy services for distributed architecture
|
|
- Enables separation of proxy components across processes/hosts
|
|
|
|
### Third-Party Services
|
|
- **radioid.net API** - DMR ID database lookups
|
|
- `peer_ids.json`, `subscriber_ids.json`, `talkgroup_ids.json` downloaded from external sources
|
|
- Cached locally with staleness checking via `utils.py::try_download()`
|
|
- Provides callsign/name resolution for DMR IDs
|
|
|
|
### Supporting Libraries
|
|
- **bitarray/bitstring** - Binary data manipulation for DMR packet construction
|
|
- **configparser** - INI file parsing for hblink.cfg
|
|
- **setproctitle** - Process naming for easier system monitoring
|
|
- **resettabletimer** - Timeout management for connection tracking
|
|
- **hashlib/hmac** - Cryptographic functions for authentication
|
|
|
|
### Language/Voice Assets
|
|
- Pre-recorded AMBE voice files in Audio/ directory
|
|
- Multiple language support (en_GB, es_ES, fr_FR, de_DE, dk_DK, it_IT, no_NO, pl_PL, se_SE, pt_PT, cy_GB, el_GR, th_TH, CW)
|
|
- Voice file indexing via i8n_voice_map.py |