Reorganization 1 class per system type

Separating HBSYSTEM into HBMASTER and HBPEER to reduce memory footprint
and allow easier updating. Also cleaning up and normalizing the
OPENBRIDGE class to match the standard HB classes better.
This commit is contained in:
Cort Buffington 2018-09-26 09:02:10 -05:00
parent 41fd8e6964
commit b20309c776
2 changed files with 91 additions and 83 deletions

View File

@ -160,6 +160,7 @@ def build_config(_config_file):
'IP': gethostbyname(config.get(section, 'IP')),
'PORT': config.getint(section, 'PORT'),
'PASSPHRASE': config.get(section, 'PASSPHRASE').ljust(20,'\x00')[:20],
'TARGET_SOCK': (gethostbyname(config.get(section, 'TARGET_IP')), config.getint(section, 'TARGET_PORT')),
'TARGET_IP': gethostbyname(config.get(section, 'TARGET_IP')),
'TARGET_PORT': config.getint(section, 'TARGET_PORT'),
}})

173
hblink.py
View File

@ -194,41 +194,33 @@ class OPENBRIDGE(DatagramProtocol):
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':
_packet = _packet[:11] + self._config['NETWORK_ID'] + _packet[15:]
self.transport.write(_packet, (self._config['TARGET_IP'], self._config['TARGET_PORT']))
# KEEP THE FOLLOWING COMMENTED OUT UNLESS YOU'RE DEBUGGING DEEPLY!!!!
# self._logger.debug('(%s) TX Packet to OpenBridge %s:%s -- %s', self._system, self._config['TARGET_IP'], self._config['TARGET_PORT'], ahex(_packet))
else:
self._logger.error('(%s) OpenBridge system was asked to send non DMRD packet')
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):
def datagramReceived(self, _packet, _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:
if _packet[:4] == 'DMRD': # DMRData -- encapsulated DMR data frame
_data = _data[:53]
_ckhs = hmac_new(self._config['PASSPHRASE'],_data[53:],sha1).digest()
if compare_digest(_hash, _ckhs) and _sockaddr == self._config['TARGET_SOCK']:
_peer_id = _data[11:15]
_seq = _data[4]
_rf_src = _data[5:8]
_dst_id = _data[8:11]
@ -242,13 +234,15 @@ class OPENBRIDGE(DatagramProtocol):
# 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)
else:
self._logger.info('(%s) OpenBridge HMAC failed, packet discarded', self._system)
#************************************************
# HB MASTER CLASS
#************************************************
class HBSYSTEM(DatagramProtocol):
class HBMASTER(DatagramProtocol):
def __init__(self, _name, _config, _logger, _report):
# Define a few shortcuts to make the rest of the class more readable
self._CONFIG = _config
@ -256,21 +250,7 @@ class HBSYSTEM(DatagramProtocol):
self._logger = _logger
self._report = _report
self._config = self._CONFIG['SYSTEMS'][self._system]
# Define shortcuts and generic function names based on the type of system we are
if self._config['MODE'] == 'MASTER':
self._peers = self._CONFIG['SYSTEMS'][self._system]['PEERS']
self.send_system = self.send_peers
self.maintenance_loop = self.master_maintenance_loop
self.datagramReceived = self.master_datagramReceived
self.dereg = self.master_dereg
elif self._config['MODE'] == 'PEER':
self._stats = self._config['STATS']
self.send_system = self.send_master
self.maintenance_loop = self.peer_maintenance_loop
self.datagramReceived = self.peer_datagramReceived
self.dereg = self.peer_dereg
self._peers = self._CONFIG['SYSTEMS'][self._system]['PEERS']
# Configure for AMBE audio export if enabled
if self._config['EXPORT_AMBE']:
@ -281,8 +261,7 @@ class HBSYSTEM(DatagramProtocol):
self._system_maintenance = task.LoopingCall(self.maintenance_loop)
self._system_maintenance_loop = self._system_maintenance.start(self._CONFIG['GLOBAL']['PING_TIME'])
# Aliased in __init__ to maintenance_loop if system is a master
def master_maintenance_loop(self):
def maintenance_loop(self):
self._logger.debug('(%s) Master maintenance loop started', self._system)
for peer in self._peers:
_this_peer = self._peers[peer]
@ -291,29 +270,8 @@ class HBSYSTEM(DatagramProtocol):
self._logger.info('(%s) Peer %s (%s) has timed out', self._system, _this_peer['CALLSIGN'], _this_peer['RADIO_ID'])
# Remove any timed out peers from the configuration
del self._CONFIG['SYSTEMS'][self._system]['PEERS'][peer]
# Aliased in __init__ to maintenance_loop if system is a peer
def peer_maintenance_loop(self):
self._logger.debug('(%s) Peer maintenance loop started', self._system)
if self._stats['PING_OUTSTANDING']:
self._stats['NUM_OUTSTANDING'] += 1
# If we're not connected, zero out the stats and send a login request RPTL
if self._stats['CONNECTION'] != 'YES' or self._stats['NUM_OUTSTANDING'] >= self._CONFIG['GLOBAL']['MAX_MISSED']:
self._stats['PINGS_SENT'] = 0
self._stats['PINGS_ACKD'] = 0
self._stats['NUM_OUTSTANDING'] = 0
self._stats['PING_OUTSTANDING'] = False
self._stats['CONNECTION'] = 'RPTL_SENT'
self.send_master('RPTL'+self._config['RADIO_ID'])
self._logger.info('(%s) Sending login request to master %s:%s', self._system, self._config['MASTER_IP'], self._config['MASTER_PORT'])
# If we are connected, sent a ping to the master and increment the counter
if self._stats['CONNECTION'] == 'YES':
self.send_master('RPTPING'+self._config['RADIO_ID'])
self._logger.debug('(%s) RPTPING Sent to Master. Total Sent: %s, Total Missed: %s, Currently Outstanding: %s', self._system, self._stats['PINGS_SENT'], self._stats['PINGS_SENT'] - self._stats['PINGS_ACKD'], self._stats['NUM_OUTSTANDING'])
self._stats['PINGS_SENT'] += 1
self._stats['PING_OUTSTANDING'] = True
def send_peers(self, _packet):
def send_system(self, _packet):
for _peer in self._peers:
self.send_peer(_peer, _packet)
#self._logger.debug('(%s) Packet sent to peer %s', self._system, self._peers[_peer]['RADIO_ID'])
@ -323,29 +281,17 @@ class HBSYSTEM(DatagramProtocol):
_packet = _packet[:11] + _peer + _packet[15:]
self.transport.write(_packet, self._peers[_peer]['SOCKADDR'])
# KEEP THE FOLLOWING COMMENTED OUT UNLESS YOU'RE DEBUGGING DEEPLY!!!!
#self._logger.debug('(%s) TX Packet to %s on port %s: %s', self._peers[_peer]['RADIO_ID'], self._peers[_peer]['IP'], self._peers[_peer]['PORT'], ahex(_packet))
def send_master(self, _packet):
if _packet[:4] == 'DMRD':
_packet = _packet[:11] + self._config['RADIO_ID'] + _packet[15:]
self.transport.write(_packet, self._config['MASTER_SOCKADDR'])
# KEEP THE FOLLOWING COMMENTED OUT UNLESS YOU'RE DEBUGGING DEEPLY!!!!
# self._logger.debug('(%s) TX Packet to %s:%s -- %s', self._system, self._config['MASTER_IP'], self._config['MASTER_PORT'], ahex(_packet))
#self._logger.debug('(%s) TX Packet to Peer %s on port %s: %s', self._peers[_peer]['RADIO_ID'], self._peers[_peer]['IP'], self._peers[_peer]['PORT'], ahex(_packet))
def dmrd_received(self, _peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data):
pass
def master_dereg(self):
def dereg(self):
for _peer in self._peers:
self.send_peer(_peer, 'MSTCL'+_peer)
self._logger.info('(%s) De-Registration sent to Peer: %s (%s)', self._system, self._peers[_peer]['CALLSIGN'], self._peers[_peer]['RADIO_ID'])
def peer_dereg(self):
self.send_master('RPTCL'+self._config['RADIO_ID'])
self._logger.info('(%s) De-Registration sent to Master: %s:%s', self._system, self._config['MASTER_SOCKADDR'][0], self._config['MASTER_SOCKADDR'][1])
# Aliased in __init__ to datagramReceived if system is a master
def master_datagramReceived(self, _data, _sockaddr):
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))
@ -497,8 +443,65 @@ class HBSYSTEM(DatagramProtocol):
else:
self._logger.error('(%s) Unrecognized command. Raw HBP PDU: %s', self._system, ahex(_data))
# Aliased in __init__ to datagramReceived if system is a peer
def peer_datagramReceived(self, _data, _sockaddr):
#************************************************
# HB PEER CLASS
#************************************************
class HBPEER(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._stats = self._config['STATS']
# Configure for AMBE audio export if enabled
if self._config['EXPORT_AMBE']:
self._ambe = AMBE(_config, _logger)
def startProtocol(self):
# Set up periodic loop for tracking pings from peers. Run every 'PING_TIME' seconds
self._system_maintenance = task.LoopingCall(self.maintenance_loop)
self._system_maintenance_loop = self._system_maintenance.start(self._CONFIG['GLOBAL']['PING_TIME'])
def maintenance_loop(self):
self._logger.debug('(%s) Peer maintenance loop started', self._system)
if self._stats['PING_OUTSTANDING']:
self._stats['NUM_OUTSTANDING'] += 1
# If we're not connected, zero out the stats and send a login request RPTL
if self._stats['CONNECTION'] != 'YES' or self._stats['NUM_OUTSTANDING'] >= self._CONFIG['GLOBAL']['MAX_MISSED']:
self._stats['PINGS_SENT'] = 0
self._stats['PINGS_ACKD'] = 0
self._stats['NUM_OUTSTANDING'] = 0
self._stats['PING_OUTSTANDING'] = False
self._stats['CONNECTION'] = 'RPTL_SENT'
self.send_system('RPTL'+self._config['RADIO_ID'])
self._logger.info('(%s) Sending login request to master %s:%s', self._system, self._config['MASTER_IP'], self._config['MASTER_PORT'])
# If we are connected, sent a ping to the master and increment the counter
if self._stats['CONNECTION'] == 'YES':
self.send_system('RPTPING'+self._config['RADIO_ID'])
self._logger.debug('(%s) RPTPING Sent to Master. Total Sent: %s, Total Missed: %s, Currently Outstanding: %s', self._system, self._stats['PINGS_SENT'], self._stats['PINGS_SENT'] - self._stats['PINGS_ACKD'], self._stats['NUM_OUTSTANDING'])
self._stats['PINGS_SENT'] += 1
self._stats['PING_OUTSTANDING'] = True
def send_system(self, _packet):
if _packet[:4] == 'DMRD':
_packet = _packet[:11] + self._config['RADIO_ID'] + _packet[15:]
self.transport.write(_packet, self._config['MASTER_SOCKADDR'])
# KEEP THE FOLLOWING COMMENTED OUT UNLESS YOU'RE DEBUGGING DEEPLY!!!!
# self._logger.debug('(%s) TX Packet to Master %s:%s -- %s', self._system, self._config['MASTER_IP'], self._config['MASTER_PORT'], ahex(_packet))
def dmrd_received(self, _peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data):
pass
def dereg(self):
self.send_system('RPTCL'+self._config['RADIO_ID'])
self._logger.info('(%s) De-Registration sent to Master: %s:%s', self._system, self._config['MASTER_SOCKADDR'][0], self._config['MASTER_SOCKADDR'][1])
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))
@ -540,7 +543,7 @@ class HBSYSTEM(DatagramProtocol):
self._logger.info('(%s) Repeater Login ACK Received with 32bit ID: %s', self._system, int_id(_login_int32))
_pass_hash = sha256(_login_int32+self._config['PASSPHRASE']).hexdigest()
_pass_hash = bhex(_pass_hash)
self.send_master('RPTK'+self._config['RADIO_ID']+_pass_hash)
self.send_system('RPTK'+self._config['RADIO_ID']+_pass_hash)
self._stats['CONNECTION'] = 'AUTHENTICATED'
elif self._stats['CONNECTION'] == 'AUTHENTICATED': # If we've sent the login challenge...
@ -563,7 +566,7 @@ class HBSYSTEM(DatagramProtocol):
self._config['SOFTWARE_ID']+\
self._config['PACKAGE_ID']
self.send_master('RPTC'+_config_packet)
self.send_system('RPTC'+_config_packet)
self._stats['CONNECTION'] = 'CONFIG-SENT'
self._logger.info('(%s) Repeater Configuration Sent', self._system)
else:
@ -575,7 +578,7 @@ class HBSYSTEM(DatagramProtocol):
if self._config['LOOSE'] or _peer_id == self._config['RADIO_ID']: # Validate the Radio_ID unless using loose validation
self._logger.info('(%s) Repeater Configuration Accepted', self._system)
if self._config['OPTIONS']:
self.send_master('RPTO'+self._config['RADIO_ID']+self._config['OPTIONS'])
self.send_system('RPTO'+self._config['RADIO_ID']+self._config['OPTIONS'])
self._stats['CONNECTION'] = 'OPTIONS-SENT'
self._logger.info('(%s) Sent options: (%s)', self._system, self._config['OPTIONS'])
else:
@ -717,8 +720,12 @@ if __name__ == '__main__':
if CONFIG['SYSTEMS'][system]['ENABLED']:
if CONFIG['SYSTEMS'][system]['MODE'] == 'OPENBRIDGE':
systems[system] = OPENBRIDGE(system, CONFIG, logger, report_server)
elif CONFIG['SYSTEMS'][system]['MODE'] == 'MASTER':
systems[system] = HBMASTER(system, CONFIG, logger, report_server)
elif CONFIG['SYSTEMS'][system]['MODE'] == 'PEER':
systems[system] = HBPEER(system, CONFIG, logger, report_server)
else:
systems[system] = HBSYSTEM(system, CONFIG, logger, report_server)
logger.error('%s instance error: %s, %s. No such MODE: %s', CONFIG['SYSTEMS'][system]['MODE'], system, systems[system], CONFIG['SYSTEMS'][system]['MODE'])
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])