From 5511a3b25aa51d944c4b0a34d530877ea4e629f7 Mon Sep 17 00:00:00 2001 From: Cort Buffington Date: Tue, 25 Sep 2018 20:17:55 -0500 Subject: [PATCH] OpenBridge initial commit The beginning of OpenBridge support. It does not yet do anything, so downloading this is worthless for all but selected alpha testers. --- hb_config.py | 13 +++++++++ hb_const.py | 2 +- hblink-SAMPLE.cfg | 22 +++++++++++++++ hblink.py | 72 +++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 105 insertions(+), 4 deletions(-) diff --git a/hb_config.py b/hb_config.py index 628f6ac..b892205 100755 --- a/hb_config.py +++ b/hb_config.py @@ -151,6 +151,19 @@ def build_config(_config_file): 'GROUP_HANGTIME': config.getint(section, 'GROUP_HANGTIME') }}) CONFIG['SYSTEMS'][section].update({'PEERS': {}}) + + elif config.get(section, 'MODE') == 'OPENBRIDGE': + CONFIG['SYSTEMS'].update({section: { + 'MODE': config.get(section, 'MODE'), + 'ENABLED': config.getboolean(section, 'ENABLED'), + 'NETWORK_ID': config.getint(section, 'NETWORK_ID'), + 'IP': gethostbyname(config.get(section, 'IP')), + 'PORT': config.getint(section, 'PORT'), + 'PASSPHRASE': config.get(section, 'PASSPHRASE').ljust(20,'\x00')[:20], + 'TARGET_IP': gethostbyname(config.get(section, 'TARGET_IP')), + 'TARGET_PORT': config.getint(section, 'TARGET_PORT'), + }}) + except ConfigParser.Error, err: print "Cannot parse configuration file. %s" %err diff --git a/hb_const.py b/hb_const.py index 815f5e5..7732b73 100755 --- a/hb_const.py +++ b/hb_const.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # ############################################################################### -# Copyright (C) 2016 Cortney T. Buffington, N0MJS +# Copyright (C) 2016-2018 Cortney T. Buffington, N0MJS # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/hblink-SAMPLE.cfg b/hblink-SAMPLE.cfg index 8b69fc9..e9644c8 100644 --- a/hblink-SAMPLE.cfg +++ b/hblink-SAMPLE.cfg @@ -75,6 +75,28 @@ STALE_DAYS: 7 EXPORT_IP: 127.0.0.1 EXPORT_PORT: 1234 +# OPENBRIDGE INSTANCES - DUPLICATE SECTION FOR MULTIPLE CONNECTIONS +# OpenBridge is a protocol originall created by DMR+ for connection between an +# IPSC2 server and Brandmeister. It has been implemented here at the suggestion +# of the Brandmeister team as a way to legitimately connect HBlink to the +# Brandemiester network. +# It is recommended to name the system the ID of the Brandmeister server that +# it connects to, but is not necessary. TARGET_IP and TARGET_PORT are of the +# Brandmeister or IPSC2 server you are connecting to. PASSPHRASE is the password +# that must be agreed upon between you and the operator of the server you are +# connecting to. NETWORK_ID is a number in the format of a DMR Radio ID that +# will be sent to the other server to identify this connection. +# other parameters follow the other system types. +[3102] +MODE: OPENBRIDGE +ENABLED: True +IP: +PORT: 62035 +NETWORK_ID: 3120101 +PASSPHRASE: c0edbabe +TARGET_IP: 74.91.114.19 +TARGET_PORT: 62035 + # MASTER INSTANCES - DUPLICATE SECTION FOR MULTIPLE MASTERS # HomeBrew Protocol Master instances go here. # IP may be left blank if there's one interface on your system. diff --git a/hblink.py b/hblink.py index c003c3f..e022af8 100755 --- a/hblink.py +++ b/hblink.py @@ -33,7 +33,8 @@ from __future__ import print_function from binascii import b2a_hex as ahex from binascii import a2b_hex as bhex from random import randint -from hashlib import sha256 +from hashlib import sha256, sha1 +from hmac import new as hmac_new, compare_digest from time import time from bitstring import BitArray from importlib import import_module @@ -181,6 +182,68 @@ class AMBE: self._sock.sendto(ambeBytes[18:27], (self._exp_ip, self._exp_port)) +#************************************************ +# OPENBRIDGE CLASS +#************************************************ + +class OPENBRIDGE(DatagramProtocol): + def __init__(self, _name, _config, _logger, _report): + # Define a few shortcuts to make the rest of the class more readable + self._CONFIG = _config + self._system = _name + self._logger = _logger + self._report = _report + self._config = self._CONFIG['SYSTEMS'][self._system] + self._localsock = (self._config['IP'], self._config['PORT']) + self._targetsock = (self._config['TARGET_IP'], self._config['TARGET_PORT']) + print(self._config['NETWORK_ID']) + + def dereg(self): + self._logger.info('(%s) is mode OPENBRIDGE. No De-Registration required, continuing shutdown', self._system) + + def send_system(self, _packet): + if _packet[:4] == 'DMRD': + self.transport.write(_packet, (self._config['TARGET_IP'], self._config['TARGET_PORT'])) + + def dmrd_received(self, _peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data): + pass + #print(int_id(_peer_id), int_id(_rf_src), int_id(_dst_id), int_id(_seq), _slot, _call_type, _frame_type, repr(_dtype_vseq), int_id(_stream_id)) + + _dmrd = _data[:53] + _hash = _data[53:] + + _ckhs = hmac_new(self._config['PASSPHRASE'],_dmrd,sha1).digest() + if compare_digest(_hash, _ckhs): + print('PEER:', int_id(_peer_id), 'RF SOURCE:', int_id(_rf_src), 'DESTINATION:', int_id(_dst_id), 'SLOT', _slot, 'SEQ:', int_id(_seq), 'STREAM:', int_id(_stream_id)) + else: + self._logger.info('(%s) OpenBridge HMAC failed, packet discarded', self._system) + + # Aliased in __init__ to datagramReceived if system is a master + def datagramReceived(self, _data, _sockaddr): + # Keep This Line Commented Unless HEAVILY Debugging! + # self._logger.debug('(%s) RX packet from %s -- %s', self._system, _sockaddr, ahex(_data)) + + # Extract the command, which is various length, all but one 4 significant characters -- RPTCL + _command = _data[:4] + + if _command == 'DMRD': # DMRData -- encapsulated DMR data frame + _peer_id = _data[11:15] + if _sockaddr == self._targetsock: + _seq = _data[4] + _rf_src = _data[5:8] + _dst_id = _data[8:11] + _bits = int_id(_data[15]) + _slot = 2 if (_bits & 0x80) else 1 + _call_type = 'unit' if (_bits & 0x40) else 'group' + _frame_type = (_bits & 0x30) >> 4 + _dtype_vseq = (_bits & 0xF) # data, 1=voice header, 2=voice terminator; voice, 0=burst A ... 5=burst F + _stream_id = _data[16:20] + #self._logger.debug('(%s) DMRD - Seqence: %s, RF Source: %s, Destination ID: %s', self._system, int_id(_seq), int_id(_rf_src), int_id(_dst_id)) + + # Userland actions -- typically this is the function you subclass for an application + self.dmrd_received(_peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data) + + #************************************************ # HB MASTER CLASS #************************************************ @@ -649,10 +712,13 @@ if __name__ == '__main__': report_server = config_reports(CONFIG, logger, reportFactory) # HBlink instance creation - logger.info('HBlink \'HBlink.py\' (c) 2016 N0MJS & the K0USY Group - SYSTEM STARTING...') + logger.info('HBlink \'HBlink.py\' (c) 2016-2018 N0MJS & the K0USY Group - SYSTEM STARTING...') for system in CONFIG['SYSTEMS']: if CONFIG['SYSTEMS'][system]['ENABLED']: - systems[system] = HBSYSTEM(system, CONFIG, logger, report_server) + if CONFIG['SYSTEMS'][system]['MODE'] == 'OPENBRIDGE': + systems[system] = OPENBRIDGE(system, CONFIG, logger, report_server) + else: + systems[system] = HBSYSTEM(system, CONFIG, logger, report_server) reactor.listenUDP(CONFIG['SYSTEMS'][system]['PORT'], systems[system], interface=CONFIG['SYSTEMS'][system]['IP']) logger.debug('%s instance created: %s, %s', CONFIG['SYSTEMS'][system]['MODE'], system, systems[system])