INITIAL BRANCH COMMIT

0) CONFIGURATION FILES WILL NEED UPDATED TO CHANGE NAMING
CONFIGURATIONS!

1) Updated use of socket address instead of discrete IP/port. This will
be needed for socket address based validation and is faster… duh…
should have done this in the beginning.

2) Changed all references to “clients” as HBP systems to “peers”. This
sets the stage for having a peer_id and an originator_id for DMRD
packets.

3) Found and squashed a number of other random bugs, including the
registration ACL not working in confbridge or bridge_all.

4) router is now retired. confbridge is the future.
This commit is contained in:
Cort Buffington 2018-08-07 16:05:27 -06:00
parent d7f7288b81
commit d1af0560c4
8 changed files with 217 additions and 215 deletions

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
############################################################################### ###############################################################################
# Copyright (C) 2016 Cortney T. Buffington, N0MJS <n0mjs@me.com> # Copyright (C) 2016-2018 Cortney T. Buffington, N0MJS <n0mjs@me.com>
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -45,7 +45,7 @@ from twisted.protocols.basic import NetstringReceiver
from twisted.internet import reactor, task from twisted.internet import reactor, task
# Things we import from the main hblink module # Things we import from the main hblink module
from hblink import HBSYSTEM, systems, hblink_handler, reportFactory, REPORT_OPCODES, config_reports from hblink import HBSYSTEM, systems, hblink_handler, reportFactory, REPORT_OPCODES, config_reports, build_reg_acl
from dmr_utils.utils import hex_str_3, int_id, get_alias from dmr_utils.utils import hex_str_3, int_id, get_alias
from dmr_utils import decode, bptc, const from dmr_utils import decode, bptc, const
from acl import acl_check, acl_build from acl import acl_check, acl_build
@ -55,7 +55,7 @@ import hb_const
# Does anybody read this stuff? There's a PEP somewhere that says I should do this. # Does anybody read this stuff? There's a PEP somewhere that says I should do this.
__author__ = 'Cortney T. Buffington, N0MJS' __author__ = 'Cortney T. Buffington, N0MJS'
__copyright__ = 'Copyright (c) 2016 Cortney T. Buffington, N0MJS and the K0USY Group' __copyright__ = 'Copyright (c) 2016-2018 Cortney T. Buffington, N0MJS and the K0USY Group'
__credits__ = 'Colin Durbridge, G4EML, Steve Zingman, N4IRS; Mike Zingman, N4IRR; Jonathan Naylor, G4KLX; Hans Barthen, DL5DI; Torsten Shultze, DG1HT' __credits__ = 'Colin Durbridge, G4EML, Steve Zingman, N4IRS; Mike Zingman, N4IRR; Jonathan Naylor, G4KLX; Hans Barthen, DL5DI; Torsten Shultze, DG1HT'
__license__ = 'GNU GPLv3' __license__ = 'GNU GPLv3'
__maintainer__ = 'Cort Buffington, N0MJS' __maintainer__ = 'Cort Buffington, N0MJS'
@ -133,7 +133,7 @@ class bridgeallSYSTEM(HBSYSTEM):
} }
} }
def dmrd_received(self, _radio_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data): def dmrd_received(self, _peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data):
pkt_time = time() pkt_time = time()
dmrpkt = _data[20:53] dmrpkt = _data[20:53]
_bits = int_id(_data[15]) _bits = int_id(_data[15])
@ -143,40 +143,40 @@ class bridgeallSYSTEM(HBSYSTEM):
# Check for GLOBAL Subscriber ID ACL Match # Check for GLOBAL Subscriber ID ACL Match
if acl_check(_rf_src, ACL['SID']['GLOBAL'][_slot]) == False: if acl_check(_rf_src, ACL['SID']['GLOBAL'][_slot]) == False:
if (_stream_id != self.STATUS[_slot]['RX_STREAM_ID']): if (_stream_id != self.STATUS[_slot]['RX_STREAM_ID']):
self._logger.warning('(%s) Group Voice Call ***REJECTED BY INGRESS GLOBAL ACL*** SID: %s SLOT: %s HBP Peer %s', self._system, int_id(_rf_src), _slot, int_id(_radio_id)) self._logger.warning('(%s) Group Voice Call ***REJECTED BY INGRESS GLOBAL ACL*** SID: %s SLOT: %s HBP Peer %s', self._system, int_id(_rf_src), _slot, int_id(_peer_id))
self.STATUS[_slot]['RX_STREAM_ID'] = _stream_id self.STATUS[_slot]['RX_STREAM_ID'] = _stream_id
return return
# Check for SYSTEM Subscriber ID ACL Match # Check for SYSTEM Subscriber ID ACL Match
if acl_check(_rf_src, ACL['SID'][self._system][_slot]) == False: if acl_check(_rf_src, ACL['SID'][self._system][_slot]) == False:
if (_stream_id != self.STATUS[_slot]['RX_STREAM_ID']): if (_stream_id != self.STATUS[_slot]['RX_STREAM_ID']):
self._logger.warning('(%s) Group Voice Call ***REJECTED BY INGRESS SYSTEM ACL*** SID: %s SLOT: %s HBP Peer %s', self._system, int_id(_rf_src), _slot, int_id(_radio_id)) self._logger.warning('(%s) Group Voice Call ***REJECTED BY INGRESS SYSTEM ACL*** SID: %s SLOT: %s HBP Peer %s', self._system, int_id(_rf_src), _slot, int_id(_peer_id))
self.STATUS[_slot]['RX_STREAM_ID'] = _stream_id self.STATUS[_slot]['RX_STREAM_ID'] = _stream_id
return return
# Check for GLOBAL Talkgroup ID ACL Match # Check for GLOBAL Talkgroup ID ACL Match
if acl_check(_dst_id, ACL['TGID']['GLOBAL'][_slot]) == False: if acl_check(_dst_id, ACL['TGID']['GLOBAL'][_slot]) == False:
if (_stream_id != self.STATUS[_slot]['RX_STREAM_ID']): if (_stream_id != self.STATUS[_slot]['RX_STREAM_ID']):
self._logger.warning('(%s) Group Voice Call ***REJECTED BY INGRESS GLOBAL ACL*** TGID: %s SLOT: %s HBP Peer %s', self._system, int_id(_dst_id), _slot, int_id(_radio_id)) self._logger.warning('(%s) Group Voice Call ***REJECTED BY INGRESS GLOBAL ACL*** TGID: %s SLOT: %s HBP Peer %s', self._system, int_id(_dst_id), _slot, int_id(_peer_id))
self.STATUS[_slot]['RX_STREAM_ID'] = _stream_id self.STATUS[_slot]['RX_STREAM_ID'] = _stream_id
return return
# Check for SYSTEM Talkgroup ID ID ACL Match # Check for SYSTEM Talkgroup ID ID ACL Match
if acl_check(_dst_id, ACL['TGID'][self._system][_slot]) == False: if acl_check(_dst_id, ACL['TGID'][self._system][_slot]) == False:
if (_stream_id != self.STATUS[_slot]['RX_STREAM_ID']): if (_stream_id != self.STATUS[_slot]['RX_STREAM_ID']):
self._logger.warning('(%s) Group Voice Call ***REJECTED BY INGRESS SYSTEM ACL*** TGID: %s SLOT: %s HBP Peer %s', self._system, int_id(_dst_id), _slot, int_id(_radio_id)) self._logger.warning('(%s) Group Voice Call ***REJECTED BY INGRESS SYSTEM ACL*** TGID: %s SLOT: %s HBP Peer %s', self._system, int_id(_dst_id), _slot, int_id(_peer_id))
self.STATUS[_slot]['RX_STREAM_ID'] = _stream_id self.STATUS[_slot]['RX_STREAM_ID'] = _stream_id
return return
# Is this is a new call stream? # Is this is a new call stream?
if (_stream_id != self.STATUS[_slot]['RX_STREAM_ID']): if (_stream_id != self.STATUS[_slot]['RX_STREAM_ID']):
self.STATUS['RX_START'] = pkt_time self.STATUS['RX_START'] = pkt_time
self._logger.info('(%s) *CALL START* STREAM ID: %s SUB: %s (%s) REPEATER: %s (%s) TGID %s (%s), TS %s', \ self._logger.info('(%s) *CALL START* STREAM ID: %s SUB: %s (%s) PEER: %s (%s) TGID %s (%s), TS %s', \
self._system, int_id(_stream_id), get_alias(_rf_src, subscriber_ids), int_id(_rf_src), get_alias(_radio_id, peer_ids), int_id(_radio_id), get_alias(_dst_id, talkgroup_ids), int_id(_dst_id), _slot) self._system, int_id(_stream_id), get_alias(_rf_src, subscriber_ids), int_id(_rf_src), get_alias(_peer_id, peer_ids), int_id(_peer_id), get_alias(_dst_id, talkgroup_ids), int_id(_dst_id), _slot)
# Final actions - Is this a voice terminator? # Final actions - Is this a voice terminator?
if (_frame_type == hb_const.HBPF_DATA_SYNC) and (_dtype_vseq == hb_const.HBPF_SLT_VTERM) and (self.STATUS[_slot]['RX_TYPE'] != hb_const.HBPF_SLT_VTERM): if (_frame_type == hb_const.HBPF_DATA_SYNC) and (_dtype_vseq == hb_const.HBPF_SLT_VTERM) and (self.STATUS[_slot]['RX_TYPE'] != hb_const.HBPF_SLT_VTERM):
call_duration = pkt_time - self.STATUS['RX_START'] call_duration = pkt_time - self.STATUS['RX_START']
self._logger.info('(%s) *CALL END* STREAM ID: %s SUB: %s (%s) REPEATER: %s (%s) TGID %s (%s), TS %s, Duration: %s', \ self._logger.info('(%s) *CALL END* STREAM ID: %s SUB: %s (%s) PEER: %s (%s) TGID %s (%s), TS %s, Duration: %s', \
self._system, int_id(_stream_id), get_alias(_rf_src, subscriber_ids), int_id(_rf_src), get_alias(_radio_id, peer_ids), int_id(_radio_id), get_alias(_dst_id, talkgroup_ids), int_id(_dst_id), _slot, call_duration) self._system, int_id(_stream_id), get_alias(_rf_src, subscriber_ids), int_id(_rf_src), get_alias(_peer_id, peer_ids), int_id(_peer_id), get_alias(_dst_id, talkgroup_ids), int_id(_dst_id), _slot, call_duration)
# Mark status variables for use later # Mark status variables for use later
self.STATUS[_slot]['RX_RFS'] = _rf_src self.STATUS[_slot]['RX_RFS'] = _rf_src
@ -195,26 +195,26 @@ class bridgeallSYSTEM(HBSYSTEM):
# Check for GLOBAL Subscriber ID ACL Match # Check for GLOBAL Subscriber ID ACL Match
if acl_check(_rf_src, ACL['SID']['GLOBAL'][_slot]) == False: if acl_check(_rf_src, ACL['SID']['GLOBAL'][_slot]) == False:
if (_stream_id != _target_status[_slot]['TX_STREAM_ID']): if (_stream_id != _target_status[_slot]['TX_STREAM_ID']):
self._logger.warning('(%s) Group Voice Call ***REJECTED BY EGRESS GLOBAL ACL*** SID: %s SLOT: %s HBP Peer %s', _target, int_id(_rf_src), _slot, int_id(_radio_id)) self._logger.warning('(%s) Group Voice Call ***REJECTED BY EGRESS GLOBAL ACL*** SID: %s SLOT: %s HBP Peer %s', _target, int_id(_rf_src), _slot, int_id(_peer_id))
_target_status[_slot]['TX_STREAM_ID'] = _stream_id _target_status[_slot]['TX_STREAM_ID'] = _stream_id
return return
# Check for SYSTEM Subscriber ID ACL Match # Check for SYSTEM Subscriber ID ACL Match
if acl_check(_rf_src, ACL['SID'][_target][_slot]) == False: if acl_check(_rf_src, ACL['SID'][_target][_slot]) == False:
if (_stream_id != _target_status[_slot]['TX_STREAM_ID']): if (_stream_id != _target_status[_slot]['TX_STREAM_ID']):
self._logger.warning('(%s) Group Voice Call ***REJECTED BY EGRESS SYSTEM ACL*** SID: %s SLOT: %s HBP Peer %s', _target, int_id(_rf_src), _slot, int_id(_radio_id)) self._logger.warning('(%s) Group Voice Call ***REJECTED BY EGRESS SYSTEM ACL*** SID: %s SLOT: %s HBP Peer %s', _target, int_id(_rf_src), _slot, int_id(_peer_id))
_target_status[_slot]['TX_STREAM_ID'] = _stream_id _target_status[_slot]['TX_STREAM_ID'] = _stream_id
return return
# Check for GLOBAL Talkgroup ID ACL Match # Check for GLOBAL Talkgroup ID ACL Match
if acl_check(_dst_id, ACL['TGID']['GLOBAL'][_slot]) == False: if acl_check(_dst_id, ACL['TGID']['GLOBAL'][_slot]) == False:
if (_stream_id != _target_status[_slot]['TX_STREAM_ID']): if (_stream_id != _target_status[_slot]['TX_STREAM_ID']):
self._logger.warning('(%s) Group Voice Call ***REJECTED BY EGRESS GLOBAL ACL*** TGID: %s SLOT: %s HBP Peer %s', _target, int_id(_dst_id), _slot, int_id(_radio_id)) self._logger.warning('(%s) Group Voice Call ***REJECTED BY EGRESS GLOBAL ACL*** TGID: %s SLOT: %s HBP Peer %s', _target, int_id(_dst_id), _slot, int_id(_peer_id))
_target_status[_slot]['TX_STREAM_ID'] = _stream_id _target_status[_slot]['TX_STREAM_ID'] = _stream_id
return return
# Check for SYSTEM Talkgroup ID ID ACL Match # Check for SYSTEM Talkgroup ID ID ACL Match
if acl_check(_dst_id, ACL['TGID'][_target][_slot]) == False: if acl_check(_dst_id, ACL['TGID'][_target][_slot]) == False:
if (_stream_id != _target_status[_slot]['TX_STREAM_ID']): if (_stream_id != _target_status[_slot]['TX_STREAM_ID']):
self._logger.warning('(%s) Group Voice Call ***REJECTED BY EGRESS SYSTEM ACL*** TGID: %s HBP Peer %s', _target, int_id(_dst_id), int_id(_radio_id)) self._logger.warning('(%s) Group Voice Call ***REJECTED BY EGRESS SYSTEM ACL*** TGID: %s HBP Peer %s', _target, int_id(_dst_id), int_id(_peer_id))
_target_status[_slot]['TX_STREAM_ID'] = _stream_id _target_status[_slot]['TX_STREAM_ID'] = _stream_id
return return
@ -318,6 +318,9 @@ if __name__ == '__main__':
logger.warning('No %s ACL for system %s - initializing \'PERMIT:ALL\'', acl_type, system) logger.warning('No %s ACL for system %s - initializing \'PERMIT:ALL\'', acl_type, system)
ACL[acl_type].update({system: {1: acl_build('PERMIT:ALL'), 2: acl_build('PERMIT:ALL')}}) ACL[acl_type].update({system: {1: acl_build('PERMIT:ALL'), 2: acl_build('PERMIT:ALL')}})
# Build the Registration Access Control List
REG_ACL = build_reg_acl('reg_acl', logger)
# INITIALIZE THE REPORTING LOOP # INITIALIZE THE REPORTING LOOP
report_server = config_reports(CONFIG, logger, reportFactory) report_server = config_reports(CONFIG, logger, reportFactory)

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
############################################################################### ###############################################################################
# Copyright (C) 2016 Cortney T. Buffington, N0MJS <n0mjs@me.com> # Copyright (C) 2016-2018 Cortney T. Buffington, N0MJS <n0mjs@me.com>
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -45,7 +45,7 @@ from twisted.protocols.basic import NetstringReceiver
from twisted.internet import reactor, task from twisted.internet import reactor, task
# Things we import from the main hblink module # Things we import from the main hblink module
from hblink import HBSYSTEM, systems, hblink_handler, reportFactory, REPORT_OPCODES, config_reports from hblink import HBSYSTEM, systems, hblink_handler, reportFactory, REPORT_OPCODES, config_reports, build_reg_acl
from dmr_utils.utils import hex_str_3, int_id, get_alias from dmr_utils.utils import hex_str_3, int_id, get_alias
from dmr_utils import decode, bptc, const from dmr_utils import decode, bptc, const
import hb_config import hb_config
@ -57,7 +57,7 @@ import cPickle as pickle
# Does anybody read this stuff? There's a PEP somewhere that says I should do this. # Does anybody read this stuff? There's a PEP somewhere that says I should do this.
__author__ = 'Cortney T. Buffington, N0MJS' __author__ = 'Cortney T. Buffington, N0MJS'
__copyright__ = 'Copyright (c) 2016 Cortney T. Buffington, N0MJS and the K0USY Group' __copyright__ = 'Copyright (c) 2016-2018PEER Cortney T. Buffington, N0MJS and the K0USY Group'
__credits__ = 'Colin Durbridge, G4EML, Steve Zingman, N4IRS; Mike Zingman, N4IRR; Jonathan Naylor, G4KLX; Hans Barthen, DL5DI; Torsten Shultze, DG1HT' __credits__ = 'Colin Durbridge, G4EML, Steve Zingman, N4IRS; Mike Zingman, N4IRR; Jonathan Naylor, G4KLX; Hans Barthen, DL5DI; Torsten Shultze, DG1HT'
__license__ = 'GNU GPLv3' __license__ = 'GNU GPLv3'
__maintainer__ = 'Cort Buffington, N0MJS' __maintainer__ = 'Cort Buffington, N0MJS'
@ -234,7 +234,7 @@ class routerSYSTEM(HBSYSTEM):
} }
} }
def dmrd_received(self, _radio_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data): def dmrd_received(self, _peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data):
pkt_time = time() pkt_time = time()
dmrpkt = _data[20:53] dmrpkt = _data[20:53]
_bits = int_id(_data[15]) _bits = int_id(_data[15])
@ -243,21 +243,21 @@ class routerSYSTEM(HBSYSTEM):
# Check for ACL match, and return if the subscriber is not allowed # Check for ACL match, and return if the subscriber is not allowed
if allow_sub(_rf_src) == False: if allow_sub(_rf_src) == False:
self._logger.warning('(%s) Group Voice Packet ***REJECTED BY ACL*** From: %s, HBP Peer %s, Destination TGID %s', self._system, int_id(_rf_src), int_id(_radio_id), int_id(_dst_id)) self._logger.warning('(%s) Group Voice Packet ***REJECTED BY ACL*** From: %s, HBP Peer %s, Destination TGID %s', self._system, int_id(_rf_src), int_id(_peer_id), int_id(_dst_id))
return return
# Is this a new call stream? # Is this a new call stream?
if (_stream_id != self.STATUS[_slot]['RX_STREAM_ID']): if (_stream_id != self.STATUS[_slot]['RX_STREAM_ID']):
if (self.STATUS[_slot]['RX_TYPE'] != hb_const.HBPF_SLT_VTERM) and (pkt_time < (self.STATUS[_slot]['RX_TIME'] + hb_const.STREAM_TO)) and (_rf_src != self.STATUS[_slot]['RX_RFS']): if (self.STATUS[_slot]['RX_TYPE'] != hb_const.HBPF_SLT_VTERM) and (pkt_time < (self.STATUS[_slot]['RX_TIME'] + hb_const.STREAM_TO)) and (_rf_src != self.STATUS[_slot]['RX_RFS']):
self._logger.warning('(%s) Packet received with STREAM ID: %s <FROM> SUB: %s REPEATER: %s <TO> TGID %s, SLOT %s collided with existing call', self._system, int_id(_stream_id), int_id(_rf_src), int_id(_radio_id), int_id(_dst_id), _slot) self._logger.warning('(%s) Packet received with STREAM ID: %s <FROM> SUB: %s PEER: %s <TO> TGID %s, SLOT %s collided with existing call', self._system, int_id(_stream_id), int_id(_rf_src), int_id(_peer_id), int_id(_dst_id), _slot)
return return
# This is a new call stream # This is a new call stream
self.STATUS['RX_START'] = pkt_time self.STATUS['RX_START'] = pkt_time
self._logger.info('(%s) *CALL START* STREAM ID: %s SUB: %s (%s) REPEATER: %s (%s) TGID %s (%s), TS %s', \ self._logger.info('(%s) *CALL START* STREAM ID: %s SUB: %s (%s) PEER: %s (%s) TGID %s (%s), TS %s', \
self._system, int_id(_stream_id), get_alias(_rf_src, subscriber_ids), int_id(_rf_src), get_alias(_radio_id, peer_ids), int_id(_radio_id), get_alias(_dst_id, talkgroup_ids), int_id(_dst_id), _slot) self._system, int_id(_stream_id), get_alias(_rf_src, subscriber_ids), int_id(_rf_src), get_alias(_peer_id, peer_ids), int_id(_peer_id), get_alias(_dst_id, talkgroup_ids), int_id(_dst_id), _slot)
if CONFIG['REPORTS']['REPORT']: if CONFIG['REPORTS']['REPORT']:
self._report.send_bridgeEvent('GROUP VOICE,START,{},{},{},{},{},{}'.format(self._system, int_id(_stream_id), int_id(_radio_id), int_id(_rf_src), _slot, int_id(_dst_id))) self._report.send_bridgeEvent('GROUP VOICE,START,{},{},{},{},{},{}'.format(self._system, int_id(_stream_id), int_id(_peer_id), int_id(_rf_src), _slot, int_id(_dst_id)))
# If we can, use the LC from the voice header as to keep all options intact # If we can, use the LC from the voice header as to keep all options intact
if _frame_type == hb_const.HBPF_DATA_SYNC and _dtype_vseq == hb_const.HBPF_SLT_VHEAD: if _frame_type == hb_const.HBPF_DATA_SYNC and _dtype_vseq == hb_const.HBPF_SLT_VHEAD:
@ -358,10 +358,10 @@ class routerSYSTEM(HBSYSTEM):
# Final actions - Is this a voice terminator? # Final actions - Is this a voice terminator?
if (_frame_type == hb_const.HBPF_DATA_SYNC) and (_dtype_vseq == hb_const.HBPF_SLT_VTERM) and (self.STATUS[_slot]['RX_TYPE'] != hb_const.HBPF_SLT_VTERM): if (_frame_type == hb_const.HBPF_DATA_SYNC) and (_dtype_vseq == hb_const.HBPF_SLT_VTERM) and (self.STATUS[_slot]['RX_TYPE'] != hb_const.HBPF_SLT_VTERM):
call_duration = pkt_time - self.STATUS['RX_START'] call_duration = pkt_time - self.STATUS['RX_START']
self._logger.info('(%s) *CALL END* STREAM ID: %s SUB: %s (%s) REPEATER: %s (%s) TGID %s (%s), TS %s, Duration: %s', \ self._logger.info('(%s) *CALL END* STREAM ID: %s SUB: %s (%s) PEER: %s (%s) TGID %s (%s), TS %s, Duration: %s', \
self._system, int_id(_stream_id), get_alias(_rf_src, subscriber_ids), int_id(_rf_src), get_alias(_radio_id, peer_ids), int_id(_radio_id), get_alias(_dst_id, talkgroup_ids), int_id(_dst_id), _slot, call_duration) self._system, int_id(_stream_id), get_alias(_rf_src, subscriber_ids), int_id(_rf_src), get_alias(_peer_id, peer_ids), int_id(_peer_id), get_alias(_dst_id, talkgroup_ids), int_id(_dst_id), _slot, call_duration)
if CONFIG['REPORTS']['REPORT']: if CONFIG['REPORTS']['REPORT']:
self._report.send_bridgeEvent('GROUP VOICE,END,{},{},{},{},{},{},{:.2f}'.format(self._system, int_id(_stream_id), int_id(_radio_id), int_id(_rf_src), _slot, int_id(_dst_id), call_duration)) self._report.send_bridgeEvent('GROUP VOICE,END,{},{},{},{},{},{},{:.2f}'.format(self._system, int_id(_stream_id), int_id(_peer_id), int_id(_rf_src), _slot, int_id(_dst_id), call_duration))
# #
# Begin in-band signalling for call end. This has nothign to do with routing traffic directly. # Begin in-band signalling for call end. This has nothign to do with routing traffic directly.
@ -515,6 +515,9 @@ if __name__ == '__main__':
# Build the Access Control List # Build the Access Control List
ACL = build_acl('sub_acl') ACL = build_acl('sub_acl')
# Build the Registration Access Control List
REG_ACL = build_reg_acl('reg_acl', logger)
# INITIALIZE THE REPORTING LOOP # INITIALIZE THE REPORTING LOOP
report_server = config_reports(CONFIG, logger, confbridgeReportFactory) report_server = config_reports(CONFIG, logger, confbridgeReportFactory)

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
############################################################################### ###############################################################################
# Copyright (C) 2016 Cortney T. Buffington, N0MJS <n0mjs@me.com> # Copyright (C) 2016-2018 Cortney T. Buffington, N0MJS <n0mjs@me.com>
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -33,7 +33,7 @@ from socket import gethostbyname
# Does anybody read this stuff? There's a PEP somewhere that says I should do this. # Does anybody read this stuff? There's a PEP somewhere that says I should do this.
__author__ = 'Cortney T. Buffington, N0MJS' __author__ = 'Cortney T. Buffington, N0MJS'
__copyright__ = 'Copyright (c) 2016 Cortney T. Buffington, N0MJS and the K0USY Group' __copyright__ = 'Copyright (c) 2016-2018 Cortney T. Buffington, N0MJS and the K0USY Group'
__credits__ = 'Colin Durbridge, G4EML, Steve Zingman, N4IRS; Mike Zingman, N4IRR; Jonathan Naylor, G4KLX; Hans Barthen, DL5DI; Torsten Shultze, DG1HT' __credits__ = 'Colin Durbridge, G4EML, Steve Zingman, N4IRS; Mike Zingman, N4IRR; Jonathan Naylor, G4KLX; Hans Barthen, DL5DI; Torsten Shultze, DG1HT'
__license__ = 'GNU GPLv3' __license__ = 'GNU GPLv3'
__maintainer__ = 'Cort Buffington, N0MJS' __maintainer__ = 'Cort Buffington, N0MJS'
@ -98,14 +98,16 @@ def build_config(_config_file):
}) })
elif config.getboolean(section, 'ENABLED'): elif config.getboolean(section, 'ENABLED'):
if config.get(section, 'MODE') == 'CLIENT': if config.get(section, 'MODE') == 'PEER':
CONFIG['SYSTEMS'].update({section: { CONFIG['SYSTEMS'].update({section: {
'MODE': config.get(section, 'MODE'), 'MODE': config.get(section, 'MODE'),
'ENABLED': config.getboolean(section, 'ENABLED'), 'ENABLED': config.getboolean(section, 'ENABLED'),
'LOOSE': config.getboolean(section, 'LOOSE'), 'LOOSE': config.getboolean(section, 'LOOSE'),
'EXPORT_AMBE': config.getboolean(section, 'EXPORT_AMBE'), 'EXPORT_AMBE': config.getboolean(section, 'EXPORT_AMBE'),
'SOCK_ADDR': (gethostbyname(config.get(section, 'IP')), config.getint(section, 'PORT')),
'IP': gethostbyname(config.get(section, 'IP')), 'IP': gethostbyname(config.get(section, 'IP')),
'PORT': config.getint(section, 'PORT'), 'PORT': config.getint(section, 'PORT'),
'MASTER_SOCKADDR': (gethostbyname(config.get(section, 'MASTER_IP')), config.getint(section, 'MASTER_PORT')),
'MASTER_IP': gethostbyname(config.get(section, 'MASTER_IP')), 'MASTER_IP': gethostbyname(config.get(section, 'MASTER_IP')),
'MASTER_PORT': config.getint(section, 'MASTER_PORT'), 'MASTER_PORT': config.getint(section, 'MASTER_PORT'),
'PASSPHRASE': config.get(section, 'PASSPHRASE'), 'PASSPHRASE': config.get(section, 'PASSPHRASE'),
@ -148,7 +150,7 @@ def build_config(_config_file):
'PASSPHRASE': config.get(section, 'PASSPHRASE'), 'PASSPHRASE': config.get(section, 'PASSPHRASE'),
'GROUP_HANGTIME': config.getint(section, 'GROUP_HANGTIME') 'GROUP_HANGTIME': config.getint(section, 'GROUP_HANGTIME')
}}) }})
CONFIG['SYSTEMS'][section].update({'CLIENTS': {}}) CONFIG['SYSTEMS'][section].update({'PEERS': {}})
except ConfigParser.Error, err: except ConfigParser.Error, err:
print "Cannot parse configuration file. %s" %err print "Cannot parse configuration file. %s" %err

View File

@ -109,7 +109,7 @@ class parrot(HBSYSTEM):
} }
self.CALL_DATA = [] self.CALL_DATA = []
def dmrd_received(self, _radio_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data): def dmrd_received(self, _peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data):
pkt_time = time() pkt_time = time()
dmrpkt = _data[20:53] dmrpkt = _data[20:53]
_bits = int_id(_data[15]) _bits = int_id(_data[15])
@ -120,14 +120,14 @@ class parrot(HBSYSTEM):
if (_stream_id != self.STATUS[_slot]['RX_STREAM_ID']): if (_stream_id != self.STATUS[_slot]['RX_STREAM_ID']):
self.STATUS['RX_START'] = pkt_time self.STATUS['RX_START'] = pkt_time
self._logger.info('(%s) *CALL START* STREAM ID: %s SUB: %s (%s) REPEATER: %s (%s) TGID %s (%s), TS %s', \ self._logger.info('(%s) *CALL START* STREAM ID: %s SUB: %s (%s) REPEATER: %s (%s) TGID %s (%s), TS %s', \
self._system, int_id(_stream_id), get_alias(_rf_src, subscriber_ids), int_id(_rf_src), get_alias(_radio_id, peer_ids), int_id(_radio_id), get_alias(_dst_id, talkgroup_ids), int_id(_dst_id), _slot) self._system, int_id(_stream_id), get_alias(_rf_src, subscriber_ids), int_id(_rf_src), get_alias(_peer_id, peer_ids), int_id(_peer_id), get_alias(_dst_id, talkgroup_ids), int_id(_dst_id), _slot)
# Final actions - Is this a voice terminator? # Final actions - Is this a voice terminator?
if (_frame_type == hb_const.HBPF_DATA_SYNC) and (_dtype_vseq == hb_const.HBPF_SLT_VTERM) and (self.STATUS[_slot]['RX_TYPE'] != hb_const.HBPF_SLT_VTERM): if (_frame_type == hb_const.HBPF_DATA_SYNC) and (_dtype_vseq == hb_const.HBPF_SLT_VTERM) and (self.STATUS[_slot]['RX_TYPE'] != hb_const.HBPF_SLT_VTERM):
call_duration = pkt_time - self.STATUS['RX_START'] call_duration = pkt_time - self.STATUS['RX_START']
self._logger.info('(%s) *CALL END* STREAM ID: %s SUB: %s (%s) REPEATER: %s (%s) TGID %s (%s), TS %s, Duration: %s', \ self._logger.info('(%s) *CALL END* STREAM ID: %s SUB: %s (%s) REPEATER: %s (%s) TGID %s (%s), TS %s, Duration: %s', \
self._system, int_id(_stream_id), get_alias(_rf_src, subscriber_ids), int_id(_rf_src), get_alias(_radio_id, peer_ids), int_id(_radio_id), get_alias(_dst_id, talkgroup_ids), int_id(_dst_id), _slot, call_duration) self._system, int_id(_stream_id), get_alias(_rf_src, subscriber_ids), int_id(_rf_src), get_alias(_peer_id, peer_ids), int_id(_peer_id), get_alias(_dst_id, talkgroup_ids), int_id(_dst_id), _slot, call_duration)
self.CALL_DATA.append(_data) self.CALL_DATA.append(_data)
sleep(2) sleep(2)
logger.info('(%s) Playing back transmission from subscriber: %s', self._system, int_id(_rf_src)) logger.info('(%s) Playing back transmission from subscriber: %s', self._system, int_id(_rf_src))

338
hblink.py
View File

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
############################################################################### ###############################################################################
# Copyright (C) 2016 Cortney T. Buffington, N0MJS <n0mjs@me.com> # Copyright (C) 2016-2018 Cortney T. Buffington, N0MJS <n0mjs@me.com>
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -20,11 +20,11 @@
''' '''
This program does very little on it's own. It is intended to be used as a module This program does very little on it's own. It is intended to be used as a module
to build applcaitons on top of the HomeBrew Repeater Protocol. By itself, it to build applications on top of the HomeBrew Repeater Protocol. By itself, it
will only act as a client or master for the systems specified in its configuration will only act as a peer or master for the systems specified in its configuration
file (usually hblink.cfg). It is ALWAYS best practice to ensure that this program file (usually hblink.cfg). It is ALWAYS best practice to ensure that this program
works stand-alone before troubleshooting any applicaitons that use it. It has works stand-alone before troubleshooting any applications that use it. It has
sufficient logging to be used standalone as a troubeshooting application. sufficient logging to be used standalone as a troubleshooting application.
''' '''
from __future__ import print_function from __future__ import print_function
@ -55,7 +55,7 @@ from reporting_const import *
# Does anybody read this stuff? There's a PEP somewhere that says I should do this. # Does anybody read this stuff? There's a PEP somewhere that says I should do this.
__author__ = 'Cortney T. Buffington, N0MJS' __author__ = 'Cortney T. Buffington, N0MJS'
__copyright__ = 'Copyright (c) 2016 Cortney T. Buffington, N0MJS and the K0USY Group' __copyright__ = 'Copyright (c) 2016-2018 Cortney T. Buffington, N0MJS and the K0USY Group'
__credits__ = 'Colin Durbridge, G4EML, Steve Zingman, N4IRS; Mike Zingman, N4IRR; Jonathan Naylor, G4KLX; Hans Barthen, DL5DI; Torsten Shultze, DG1HT' __credits__ = 'Colin Durbridge, G4EML, Steve Zingman, N4IRS; Mike Zingman, N4IRR; Jonathan Naylor, G4KLX; Hans Barthen, DL5DI; Torsten Shultze, DG1HT'
__license__ = 'GNU GPLv3' __license__ = 'GNU GPLv3'
__maintainer__ = 'Cort Buffington, N0MJS' __maintainer__ = 'Cort Buffington, N0MJS'
@ -86,7 +86,7 @@ def config_reports(_config, _logger, _factory):
return report_server return report_server
# Shut ourselves down gracefully by disconnecting from the masters and clients. # Shut ourselves down gracefully by disconnecting from the masters and peers.
def hblink_handler(_signal, _frame, _logger): def hblink_handler(_signal, _frame, _logger):
for system in systems: for system in systems:
_logger.info('SHUTDOWN: DE-REGISTER SYSTEM: %s', system) _logger.info('SHUTDOWN: DE-REGISTER SYSTEM: %s', system)
@ -94,16 +94,16 @@ def hblink_handler(_signal, _frame, _logger):
# Import subscriber registration ACL # Import subscriber registration ACL
# ACL may be a single list of subscriber IDs # REG_ACL may be a single list of subscriber IDs
# Global action is to allow or deny them. Multiple lists with different actions and ranges # Global action is to allow or deny them. Multiple lists with different actions and ranges
# are not yet implemented. # are not yet implemented.
def build_acl(_reg_acl): def build_reg_acl(_reg_acl, _logger):
ACL = set() REG_ACL = set()
try: try:
logger.info('Registration ACL file found, importing entries. This will take about 1.5 seconds per 1 million IDs')
acl_file = import_module(_reg_acl) acl_file = import_module(_reg_acl)
sections = acl_file.ACL.split(':') _logger.info('Registration ACL file found, importing entries. This will take about 1.5 seconds per 1 million IDs')
ACL_ACTION = sections[0] sections = acl_file.REG_ACL.split(':')
REG_ACL_ACTION = sections[0]
entries_str = sections[1] entries_str = sections[1]
for entry in entries_str.split(','): for entry in entries_str.split(','):
@ -111,29 +111,29 @@ def build_acl(_reg_acl):
start,end = entry.split('-') start,end = entry.split('-')
start,end = int(start), int(end) start,end = int(start), int(end)
for id in range(start, end+1): for id in range(start, end+1):
ACL.add(hex_str_4(id)) REG_ACL.add(hex_str_4(id))
else: else:
id = int(entry) id = int(entry)
ACL.add(hex_str_4(id)) REG_ACL.add(hex_str_4(id))
logger.info('Registration ACL loaded: action "{}" for {:,} registration IDs'.format(ACL_ACTION, len(ACL))) _logger.info('Registration ACL loaded: action "{}" for {:,} registration IDs'.format( REG_ACL_ACTION, len(REG_ACL)))
except ImportError: except ImportError:
logger.info('Registration ACL file not found or invalid - all IDs are valid') _logger.info('Registration ACL file not found or invalid - all IDs may register with this system')
ACL_ACTION = 'NONE' REG_ACL_ACTION = 'NONE'
# Depending on which type of ACL is used (PERMIT, DENY... or there isn't one) # Depending on which type of REG_ACL is used (PERMIT, DENY... or there isn't one)
# define a differnet function to be used to check the ACL # define a differnet function to be used to check the ACL
global allow_reg global allow_reg
if ACL_ACTION == 'PERMIT': if REG_ACL_ACTION == 'PERMIT':
def allow_reg(_id): def allow_reg(_id):
if _id in ACL: if _id in REG_ACL:
return True return True
else: else:
return False return False
elif ACL_ACTION == 'DENY': elif REG_ACL_ACTION == 'DENY':
def allow_reg(_id): def allow_reg(_id):
if _id not in ACL: if _id not in REG_ACL:
return True return True
else: else:
return False return False
@ -141,7 +141,7 @@ def build_acl(_reg_acl):
def allow_reg(_id): def allow_reg(_id):
return True return True
return ACL return REG_ACL
#************************************************ #************************************************
# AMBE CLASS: Used to parse out AMBE and send to gateway # AMBE CLASS: Used to parse out AMBE and send to gateway
@ -156,7 +156,7 @@ class AMBE:
self._exp_ip = self._CONFIG['AMBE']['EXPORT_IP'] self._exp_ip = self._CONFIG['AMBE']['EXPORT_IP']
self._exp_port = self._CONFIG['AMBE']['EXPORT_PORT'] self._exp_port = self._CONFIG['AMBE']['EXPORT_PORT']
def parseAMBE(self, _client, _data): def parseAMBE(self, _peer, _data):
_seq = int_id(_data[4:5]) _seq = int_id(_data[4:5])
_srcID = int_id(_data[5:8]) _srcID = int_id(_data[5:8])
_dstID = int_id(_data[8:11]) _dstID = int_id(_data[8:11])
@ -168,7 +168,7 @@ class AMBE:
_voiceSeq = (_bits & 0x0f) _voiceSeq = (_bits & 0x0f)
_streamID = int_id(_data[16:20]) _streamID = int_id(_data[16:20])
self._logger.debug('(%s) seq: %d srcID: %d dstID: %d rptID: %d bits: %0X slot:%d callType: %d frameType: %d voiceSeq: %d streamID: %0X', self._logger.debug('(%s) seq: %d srcID: %d dstID: %d rptID: %d bits: %0X slot:%d callType: %d frameType: %d voiceSeq: %d streamID: %0X',
_client, _seq, _srcID, _dstID, _rptID, _bits, _slot, _callType, _frameType, _voiceSeq, _streamID ) _peer, _seq, _srcID, _dstID, _rptID, _bits, _slot, _callType, _frameType, _voiceSeq, _streamID )
#logger.debug('Frame 1:(%s)', self.ByteToHex(_data)) #logger.debug('Frame 1:(%s)', self.ByteToHex(_data))
_dmr_frame = BitArray('0x'+ahex(_data[20:])) _dmr_frame = BitArray('0x'+ahex(_data[20:]))
@ -196,42 +196,42 @@ class HBSYSTEM(DatagramProtocol):
# Define shortcuts and generic function names based on the type of system we are # Define shortcuts and generic function names based on the type of system we are
if self._config['MODE'] == 'MASTER': if self._config['MODE'] == 'MASTER':
self._clients = self._CONFIG['SYSTEMS'][self._system]['CLIENTS'] self._peers = self._CONFIG['SYSTEMS'][self._system]['PEERS']
self.send_system = self.send_clients self.send_system = self.send_peers
self.maintenance_loop = self.master_maintenance_loop self.maintenance_loop = self.master_maintenance_loop
self.datagramReceived = self.master_datagramReceived self.datagramReceived = self.master_datagramReceived
self.dereg = self.master_dereg self.dereg = self.master_dereg
elif self._config['MODE'] == 'CLIENT': elif self._config['MODE'] == 'PEER':
self._stats = self._config['STATS'] self._stats = self._config['STATS']
self.send_system = self.send_master self.send_system = self.send_master
self.maintenance_loop = self.client_maintenance_loop self.maintenance_loop = self.peer_maintenance_loop
self.datagramReceived = self.client_datagramReceived self.datagramReceived = self.peer_datagramReceived
self.dereg = self.client_dereg self.dereg = self.peer_dereg
# Configure for AMBE audio export if enabled # Configure for AMBE audio export if enabled
if self._config['EXPORT_AMBE']: if self._config['EXPORT_AMBE']:
self._ambe = AMBE(_config, _logger) self._ambe = AMBE(_config, _logger)
def startProtocol(self): def startProtocol(self):
# Set up periodic loop for tracking pings from clients. Run every 'PING_TIME' seconds # 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 = task.LoopingCall(self.maintenance_loop)
self._system_maintenance_loop = self._system_maintenance.start(self._CONFIG['GLOBAL']['PING_TIME']) self._system_maintenance_loop = self._system_maintenance.start(self._CONFIG['GLOBAL']['PING_TIME'])
# Aliased in __init__ to maintenance_loop if system is a master # Aliased in __init__ to maintenance_loop if system is a master
def master_maintenance_loop(self): def master_maintenance_loop(self):
self._logger.debug('(%s) Master maintenance loop started', self._system) self._logger.debug('(%s) Master maintenance loop started', self._system)
for client in self._clients: for peer in self._peers:
_this_client = self._clients[client] _this_peer = self._peers[peer]
# Check to see if any of the clients have been quiet (no ping) longer than allowed # Check to see if any of the peers have been quiet (no ping) longer than allowed
if _this_client['LAST_PING']+self._CONFIG['GLOBAL']['PING_TIME']*self._CONFIG['GLOBAL']['MAX_MISSED'] < time(): if _this_peer['LAST_PING']+self._CONFIG['GLOBAL']['PING_TIME']*self._CONFIG['GLOBAL']['MAX_MISSED'] < time():
self._logger.info('(%s) Client %s (%s) has timed out', self._system, _this_client['CALLSIGN'], _this_client['RADIO_ID']) self._logger.info('(%s) Peer %s (%s) has timed out', self._system, _this_peer['CALLSIGN'], _this_peer['RADIO_ID'])
# Remove any timed out clients from the configuration # Remove any timed out peers from the configuration
del self._CONFIG['SYSTEMS'][self._system]['CLIENTS'][client] del self._CONFIG['SYSTEMS'][self._system]['PEERS'][peer]
# Aliased in __init__ to maintenance_loop if system is a client # Aliased in __init__ to maintenance_loop if system is a peer
def client_maintenance_loop(self): def peer_maintenance_loop(self):
self._logger.debug('(%s) Client maintenance loop started', self._system) self._logger.debug('(%s) Peer maintenance loop started', self._system)
if self._stats['PING_OUTSTANDING']: if self._stats['PING_OUTSTANDING']:
self._stats['NUM_OUTSTANDING'] += 1 self._stats['NUM_OUTSTANDING'] += 1
# If we're not connected, zero out the stats and send a login request RPTL # If we're not connected, zero out the stats and send a login request RPTL
@ -250,53 +250,50 @@ class HBSYSTEM(DatagramProtocol):
self._stats['PINGS_SENT'] += 1 self._stats['PINGS_SENT'] += 1
self._stats['PING_OUTSTANDING'] = True self._stats['PING_OUTSTANDING'] = True
def send_clients(self, _packet): def send_peers(self, _packet):
for _client in self._clients: for _peer in self._peers:
self.send_client(_client, _packet) self.send_peer(_peer, _packet)
#self._logger.debug('(%s) Packet sent to client %s', self._system, self._clients[_client]['RADIO_ID']) #self._logger.debug('(%s) Packet sent to peer %s', self._system, self._peers[_peer]['RADIO_ID'])
def send_client(self, _client, _packet): def send_peer(self, _peer, _packet):
_ip = self._clients[_client]['IP']
_port = self._clients[_client]['PORT']
if _packet[:4] == 'DMRD': if _packet[:4] == 'DMRD':
_packet = _packet[:11] + _client + _packet[15:] _packet = _packet[:11] + _peer + _packet[15:]
self.transport.write(_packet, (_ip, _port)) self.transport.write(_packet, self._peers[_peer]['SOCKADDR'])
# KEEP THE FOLLOWING COMMENTED OUT UNLESS YOU'RE DEBUGGING DEEPLY!!!! # KEEP THE FOLLOWING COMMENTED OUT UNLESS YOU'RE DEBUGGING DEEPLY!!!!
#self._logger.debug('(%s) TX Packet to %s on port %s: %s', self._clients[_client]['RADIO_ID'], self._clients[_client]['IP'], self._clients[_client]['PORT'], ahex(_packet)) #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): def send_master(self, _packet):
if _packet[:4] == 'DMRD': if _packet[:4] == 'DMRD':
_packet = _packet[:11] + self._config['RADIO_ID'] + _packet[15:] _packet = _packet[:11] + self._config['RADIO_ID'] + _packet[15:]
self.transport.write(_packet, (self._config['MASTER_IP'], self._config['MASTER_PORT'])) self.transport.write(_packet, self._config['MASTER_SOCKADDR'])
# KEEP THE FOLLOWING COMMENTED OUT UNLESS YOU'RE DEBUGGING DEEPLY!!!! # 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 %s:%s -- %s', self._system, self._config['MASTER_IP'], self._config['MASTER_PORT'], ahex(_packet))
def dmrd_received(self, _radio_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data): def dmrd_received(self, _peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data):
pass pass
def master_dereg(self): def master_dereg(self):
for _client in self._clients: for _peer in self._peers:
self.send_client(_client, 'MSTCL'+_client) self.send_peer(_peer, 'MSTCL'+_peer)
self._logger.info('(%s) De-Registration sent to Client: %s (%s)', self._system, self._clients[_client]['CALLSIGN'], self._clients[_client]['RADIO_ID']) self._logger.info('(%s) De-Registration sent to Peer: %s (%s)', self._system, self._peers[_peer]['CALLSIGN'], self._peers[_peer]['RADIO_ID'])
def client_dereg(self): def peer_dereg(self):
self.send_master('RPTCL'+self._config['RADIO_ID']) self.send_master('RPTCL'+self._config['RADIO_ID'])
self._logger.info('(%s) De-Registeration sent to Master: %s:%s', self._system, self._config['MASTER_IP'], self._config['MASTER_PORT']) 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 # Aliased in __init__ to datagramReceived if system is a master
def master_datagramReceived(self, _data, (_host, _port)): def master_datagramReceived(self, _data, _sockaddr):
# Keep This Line Commented Unless HEAVILY Debugging! # Keep This Line Commented Unless HEAVILY Debugging!
# self._logger.debug('(%s) RX packet from %s:%s -- %s', self._system, _host, _port, ahex(_data)) # 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 # Extract the command, which is various length, all but one 4 significant characters -- RPTCL
_command = _data[:4] _command = _data[:4]
if _command == 'DMRD': # DMRData -- encapsulated DMR data frame if _command == 'DMRD': # DMRData -- encapsulated DMR data frame
_radio_id = _data[11:15] _peer_id = _data[11:15]
if _radio_id in self._clients \ if _peer_id in self._peers \
and self._clients[_radio_id]['CONNECTION'] == 'YES' \ and self._peers[_peer_id]['CONNECTION'] == 'YES' \
and self._clients[_radio_id]['IP'] == _host \ and self._peers[_peer_id]['SOCKADDR'] == _sockaddr:
and self._clients[_radio_id]['PORT'] == _port:
_seq = _data[4] _seq = _data[4]
_rf_src = _data[5:8] _rf_src = _data[5:8]
_dst_id = _data[8:11] _dst_id = _data[8:11]
@ -312,29 +309,30 @@ class HBSYSTEM(DatagramProtocol):
if self._config['EXPORT_AMBE']: if self._config['EXPORT_AMBE']:
self._ambe.parseAMBE(self._system, _data) self._ambe.parseAMBE(self._system, _data)
# The basic purpose of a master is to repeat to the clients # The basic purpose of a master is to repeat to the peers
if self._config['REPEAT'] == True: if self._config['REPEAT'] == True:
for _client in self._clients: for _peer in self._peers:
if _client != _radio_id: if _peer != _peer_id:
#self.send_client(_client, _data) #self.send_peer(_peer, _data)
self.send_client(_client, _data[:11] + _client + _data[15:]) self.send_peer(_peer, _data[:11] + _peer + _data[15:])
#self.send_client(_client, _data[:11] + self._config['RADIO_ID'] + _data[15:]) #self.send_peer(_peer, _data[:11] + self._config['RADIO_ID'] + _data[15:])
#self._logger.debug('(%s) Packet on TS%s from %s (%s) for destination ID %s repeated to client: %s (%s) [Stream ID: %s]', self._system, _slot, self._clients[_radio_id]['CALLSIGN'], int_id(_radio_id), int_id(_dst_id), self._clients[_client]['CALLSIGN'], int_id(_client), int_id(_stream_id)) #self._logger.debug('(%s) Packet on TS%s from %s (%s) for destination ID %s repeated to peer: %s (%s) [Stream ID: %s]', self._system, _slot, self._peers[_peer_id]['CALLSIGN'], int_id(_peer_id), int_id(_dst_id), self._peers[_peer]['CALLSIGN'], int_id(_peer), int_id(_stream_id))
# Userland actions -- typically this is the function you subclass for an application # Userland actions -- typically this is the function you subclass for an application
self.dmrd_received(_radio_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data) self.dmrd_received(_peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data)
elif _command == 'RPTL': # RPTLogin -- a repeater wants to login elif _command == 'RPTL': # RPTLogin -- a repeater wants to login
_radio_id = _data[4:8] _peer_id = _data[4:8]
if allow_reg(_radio_id): # Future check here for valid Radio ID if allow_reg(_peer_id): # Check for valid Radio ID
self._clients.update({_radio_id: { # Build the configuration data strcuture for the client self._peers.update({_peer_id: { # Build the configuration data strcuture for the peer
'CONNECTION': 'RPTL-RECEIVED', 'CONNECTION': 'RPTL-RECEIVED',
'PINGS_RECEIVED': 0, 'PINGS_RECEIVED': 0,
'LAST_PING': time(), 'LAST_PING': time(),
'IP': _host, 'SOCKADDR': _sockaddr,
'PORT': _port, 'IP': _sockaddr[0],
'PORT': _sockaddr[1],
'SALT': randint(0,0xFFFFFFFF), 'SALT': randint(0,0xFFFFFFFF),
'RADIO_ID': str(int(ahex(_radio_id), 16)), 'RADIO_ID': str(int(ahex(_peer_id), 16)),
'CALLSIGN': '', 'CALLSIGN': '',
'RX_FREQ': '', 'RX_FREQ': '',
'TX_FREQ': '', 'TX_FREQ': '',
@ -350,108 +348,104 @@ class HBSYSTEM(DatagramProtocol):
'SOFTWARE_ID': '', 'SOFTWARE_ID': '',
'PACKAGE_ID': '', 'PACKAGE_ID': '',
}}) }})
self._logger.info('(%s) Repeater Logging in with Radio ID: %s, %s:%s', self._system, int_id(_radio_id), _host, _port) self._logger.info('(%s) Repeater Logging in with Radio ID: %s, %s:%s', self._system, int_id(_peer_id), _sockaddr[0], _sockaddr[1])
_salt_str = hex_str_4(self._clients[_radio_id]['SALT']) _salt_str = hex_str_4(self._peers[_peer_id]['SALT'])
self.send_client(_radio_id, 'RPTACK'+_salt_str) self.send_peer(_peer_id, 'RPTACK'+_salt_str)
self._clients[_radio_id]['CONNECTION'] = 'CHALLENGE_SENT' self._peers[_peer_id]['CONNECTION'] = 'CHALLENGE_SENT'
self._logger.info('(%s) Sent Challenge Response to %s for login: %s', self._system, int_id(_radio_id), self._clients[_radio_id]['SALT']) self._logger.info('(%s) Sent Challenge Response to %s for login: %s', self._system, int_id(_peer_id), self._peers[_peer_id]['SALT'])
else: else:
self.transport.write('MSTNAK'+_radio_id, (_host, _port)) self.transport.write('MSTNAK'+_peer_id, _sockaddr)
self._logger.warning('(%s) Invalid Login from Radio ID: %s Denied by Registation ACL', self._system, int_id(_radio_id)) self._logger.warning('(%s) Invalid Login from Radio ID: %s Denied by Registation ACL', self._system, int_id(_peer_id))
elif _command == 'RPTK': # Repeater has answered our login challenge elif _command == 'RPTK': # Repeater has answered our login challenge
_radio_id = _data[4:8] _peer_id = _data[4:8]
if _radio_id in self._clients \ if _peer_id in self._peers \
and self._clients[_radio_id]['CONNECTION'] == 'CHALLENGE_SENT' \ and self._peers[_peer_id]['CONNECTION'] == 'CHALLENGE_SENT' \
and self._clients[_radio_id]['IP'] == _host \ and self._peers[_peer_id]['SOCKADDR'] == _sockaddr:
and self._clients[_radio_id]['PORT'] == _port: _this_peer = self._peers[_peer_id]
_this_client = self._clients[_radio_id] _this_peer['LAST_PING'] = time()
_this_client['LAST_PING'] = time()
_sent_hash = _data[8:] _sent_hash = _data[8:]
_salt_str = hex_str_4(_this_client['SALT']) _salt_str = hex_str_4(_this_peer['SALT'])
_calc_hash = bhex(sha256(_salt_str+self._config['PASSPHRASE']).hexdigest()) _calc_hash = bhex(sha256(_salt_str+self._config['PASSPHRASE']).hexdigest())
if _sent_hash == _calc_hash: if _sent_hash == _calc_hash:
_this_client['CONNECTION'] = 'WAITING_CONFIG' _this_peer['CONNECTION'] = 'WAITING_CONFIG'
self.send_client(_radio_id, 'RPTACK'+_radio_id) self.send_peer(_peer_id, 'RPTACK'+_peer_id)
self._logger.info('(%s) Client %s has completed the login exchange successfully', self._system, _this_client['RADIO_ID']) self._logger.info('(%s) Peer %s has completed the login exchange successfully', self._system, _this_peer['RADIO_ID'])
else: else:
self._logger.info('(%s) Client %s has FAILED the login exchange successfully', self._system, _this_client['RADIO_ID']) self._logger.info('(%s) Peer %s has FAILED the login exchange successfully', self._system, _this_peer['RADIO_ID'])
self.transport.write('MSTNAK'+_radio_id, (_host, _port)) self.transport.write('MSTNAK'+_peer_id, _sockaddr)
del self._clients[_radio_id] del self._peers[_peer_id]
else: else:
self.transport.write('MSTNAK'+_radio_id, (_host, _port)) self.transport.write('MSTNAK'+_peer_id, _sockaddr)
self._logger.warning('(%s) Login challenge from Radio ID that has not logged in: %s', self._system, int_id(_radio_id)) self._logger.warning('(%s) Login challenge from Radio ID that has not logged in: %s', self._system, int_id(_peer_id))
elif _command == 'RPTC': # Repeater is sending it's configuraiton OR disconnecting elif _command == 'RPTC': # Repeater is sending it's configuraiton OR disconnecting
if _data[:5] == 'RPTCL': # Disconnect command if _data[:5] == 'RPTCL': # Disconnect command
_radio_id = _data[5:9] _peer_id = _data[5:9]
if _radio_id in self._clients \ if _peer_id in self._peers \
and self._clients[_radio_id]['CONNECTION'] == 'YES' \ and self._peers[_peer_id]['CONNECTION'] == 'YES' \
and self._clients[_radio_id]['IP'] == _host \ and self._peers[_peer_id]['SOCKADDR'] == _sockaddr:
and self._clients[_radio_id]['PORT'] == _port: self._logger.info('(%s) Peer is closing down: %s (%s)', self._system, self._peers[_peer_id]['CALLSIGN'], int_id(_peer_id))
self._logger.info('(%s) Client is closing down: %s (%s)', self._system, self._clients[_radio_id]['CALLSIGN'], int_id(_radio_id)) self.transport.write('MSTNAK'+_peer_id, _sockaddr)
self.transport.write('MSTNAK'+_radio_id, (_host, _port)) del self._peers[_peer_id]
del self._clients[_radio_id]
else: else:
_radio_id = _data[4:8] # Configure Command _peer_id = _data[4:8] # Configure Command
if _radio_id in self._clients \ if _peer_id in self._peers \
and self._clients[_radio_id]['CONNECTION'] == 'WAITING_CONFIG' \ and self._peers[_peer_id]['CONNECTION'] == 'WAITING_CONFIG' \
and self._clients[_radio_id]['IP'] == _host \ and self._peers[_peer_id]['SOCKADDR'] == _sockaddr:
and self._clients[_radio_id]['PORT'] == _port: _this_peer = self._peers[_peer_id]
_this_client = self._clients[_radio_id] _this_peer['CONNECTION'] = 'YES'
_this_client['CONNECTION'] = 'YES' _this_peer['LAST_PING'] = time()
_this_client['LAST_PING'] = time() _this_peer['CALLSIGN'] = _data[8:16]
_this_client['CALLSIGN'] = _data[8:16] _this_peer['RX_FREQ'] = _data[16:25]
_this_client['RX_FREQ'] = _data[16:25] _this_peer['TX_FREQ'] = _data[25:34]
_this_client['TX_FREQ'] = _data[25:34] _this_peer['TX_POWER'] = _data[34:36]
_this_client['TX_POWER'] = _data[34:36] _this_peer['COLORCODE'] = _data[36:38]
_this_client['COLORCODE'] = _data[36:38] _this_peer['LATITUDE'] = _data[38:46]
_this_client['LATITUDE'] = _data[38:46] _this_peer['LONGITUDE'] = _data[46:55]
_this_client['LONGITUDE'] = _data[46:55] _this_peer['HEIGHT'] = _data[55:58]
_this_client['HEIGHT'] = _data[55:58] _this_peer['LOCATION'] = _data[58:78]
_this_client['LOCATION'] = _data[58:78] _this_peer['DESCRIPTION'] = _data[78:97]
_this_client['DESCRIPTION'] = _data[78:97] _this_peer['SLOTS'] = _data[97:98]
_this_client['SLOTS'] = _data[97:98] _this_peer['URL'] = _data[98:222]
_this_client['URL'] = _data[98:222] _this_peer['SOFTWARE_ID'] = _data[222:262]
_this_client['SOFTWARE_ID'] = _data[222:262] _this_peer['PACKAGE_ID'] = _data[262:302]
_this_client['PACKAGE_ID'] = _data[262:302]
self.send_client(_radio_id, 'RPTACK'+_radio_id) self.send_peer(_peer_id, 'RPTACK'+_peer_id)
self._logger.info('(%s) Client %s (%s) has sent repeater configuration', self._system, _this_client['CALLSIGN'], _this_client['RADIO_ID']) self._logger.info('(%s) Peer %s (%s) has sent repeater configuration', self._system, _this_peer['CALLSIGN'], _this_peer['RADIO_ID'])
else: else:
self.transport.write('MSTNAK'+_radio_id, (_host, _port)) self.transport.write('MSTNAK'+_peer_id, _sockaddr)
self._logger.warning('(%s) Client info from Radio ID that has not logged in: %s', self._system, int_id(_radio_id)) self._logger.warning('(%s) Peer info from Radio ID that has not logged in: %s', self._system, int_id(_peer_id))
elif _command == 'RPTP': # RPTPing -- client is pinging us elif _command == 'RPTP': # RPTPing -- peer is pinging us
_radio_id = _data[7:11] _peer_id = _data[7:11]
if _radio_id in self._clients \ if _peer_id in self._peers \
and self._clients[_radio_id]['CONNECTION'] == "YES" \ and self._peers[_peer_id]['CONNECTION'] == "YES" \
and self._clients[_radio_id]['IP'] == _host \ and self._peers[_peer_id]['SOCKADDR'] == _sockaddr:
and self._clients[_radio_id]['PORT'] == _port: self._peers[_peer_id]['PINGS_RECEIVED'] += 1
self._clients[_radio_id]['PINGS_RECEIVED'] += 1 self._peers[_peer_id]['LAST_PING'] = time()
self._clients[_radio_id]['LAST_PING'] = time() self.send_peer(_peer_id, 'MSTPONG'+_peer_id)
self.send_client(_radio_id, 'MSTPONG'+_radio_id) self._logger.debug('(%s) Received and answered RPTPING from peer %s (%s)', self._system, self._peers[_peer_id]['CALLSIGN'], int_id(_peer_id))
self._logger.debug('(%s) Received and answered RPTPING from client %s (%s)', self._system, self._clients[_radio_id]['CALLSIGN'], int_id(_radio_id))
else: else:
self.transport.write('MSTNAK'+_radio_id, (_host, _port)) self.transport.write('MSTNAK'+_peer_id, _sockaddr)
self._logger.warning('(%s) Client info from Radio ID that has not logged in: %s', self._system, int_id(_radio_id)) self._logger.warning('(%s) Peer info from Radio ID that has not logged in: %s', self._system, int_id(_peer_id))
else: else:
self._logger.error('(%s) Unrecognized command. Raw HBP PDU: %s', self._system, ahex(_data)) self._logger.error('(%s) Unrecognized command. Raw HBP PDU: %s', self._system, ahex(_data))
# Aliased in __init__ to datagramReceived if system is a client # Aliased in __init__ to datagramReceived if system is a peer
def client_datagramReceived(self, _data, (_host, _port)): def peer_datagramReceived(self, _data, _sockaddr):
# Keep This Line Commented Unless HEAVILY Debugging! # Keep This Line Commented Unless HEAVILY Debugging!
# self._logger.debug('(%s) RX packet from %s:%s -- %s', self._system, _host, _port, ahex(_data)) # self._logger.debug('(%s) RX packet from %s -- %s', self._system, _sockaddr, ahex(_data))
# Validate that we receveived this packet from the master - security check! # Validate that we receveived this packet from the master - security check!
if self._config['MASTER_IP'] == _host and self._config['MASTER_PORT'] == _port: if self._config['MASTER_SOCKADDR'] == _sockaddr:
# Extract the command, which is various length, but only 4 significant characters # Extract the command, which is various length, but only 4 significant characters
_command = _data[:4] _command = _data[:4]
if _command == 'DMRD': # DMRData -- encapsulated DMR data frame if _command == 'DMRD': # DMRData -- encapsulated DMR data frame
_radio_id = _data[11:15] _peer_id = _data[11:15]
if self._config['LOOSE'] or _radio_id == self._config['RADIO_ID']: # Validate the Radio_ID unless using loose validation if self._config['LOOSE'] or _peer_id == self._config['RADIO_ID']: # Validate the Radio_ID unless using loose validation
_seq = _data[4:5] _seq = _data[4:5]
_rf_src = _data[5:8] _rf_src = _data[5:8]
_dst_id = _data[8:11] _dst_id = _data[8:11]
@ -468,11 +462,11 @@ class HBSYSTEM(DatagramProtocol):
self._ambe.parseAMBE(self._system, _data) self._ambe.parseAMBE(self._system, _data)
# Userland actions -- typically this is the function you subclass for an application # Userland actions -- typically this is the function you subclass for an application
self.dmrd_received(_radio_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data) self.dmrd_received(_peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data)
elif _command == 'MSTN': # Actually MSTNAK -- a NACK from the master elif _command == 'MSTN': # Actually MSTNAK -- a NACK from the master
_radio_id = _data[6:10] _peer_id = _data[6:10]
if self._config['LOOSE'] or _radio_id == self._config['RADIO_ID']: # Validate the Radio_ID unless using loose validation if self._config['LOOSE'] or _peer_id == self._config['RADIO_ID']: # Validate the Radio_ID unless using loose validation
self._logger.warning('(%s) MSTNAK Received. Resetting connection to the Master.', self._system) self._logger.warning('(%s) MSTNAK Received. Resetting connection to the Master.', self._system)
self._stats['CONNECTION'] = 'NO' # Disconnect ourselves and re-register self._stats['CONNECTION'] = 'NO' # Disconnect ourselves and re-register
@ -487,8 +481,8 @@ class HBSYSTEM(DatagramProtocol):
self._stats['CONNECTION'] = 'AUTHENTICATED' self._stats['CONNECTION'] = 'AUTHENTICATED'
elif self._stats['CONNECTION'] == 'AUTHENTICATED': # If we've sent the login challenge... elif self._stats['CONNECTION'] == 'AUTHENTICATED': # If we've sent the login challenge...
_radio_id = _data[6:10] _peer_id = _data[6:10]
if self._config['LOOSE'] or _radio_id == self._config['RADIO_ID']: # Validate the Radio_ID unless using loose validation if self._config['LOOSE'] or _peer_id == self._config['RADIO_ID']: # Validate the Radio_ID unless using loose validation
self._logger.info('(%s) Repeater Authentication Accepted', self._system) self._logger.info('(%s) Repeater Authentication Accepted', self._system)
_config_packet = self._config['RADIO_ID']+\ _config_packet = self._config['RADIO_ID']+\
self._config['CALLSIGN']+\ self._config['CALLSIGN']+\
@ -514,8 +508,8 @@ class HBSYSTEM(DatagramProtocol):
self._logger.error('(%s) Master ACK Contained wrong ID - Connection Reset', self._system) self._logger.error('(%s) Master ACK Contained wrong ID - Connection Reset', self._system)
elif self._stats['CONNECTION'] == 'CONFIG-SENT': # If we've sent out configuration to the master elif self._stats['CONNECTION'] == 'CONFIG-SENT': # If we've sent out configuration to the master
_radio_id = _data[6:10] _peer_id = _data[6:10]
if self._config['LOOSE'] or _radio_id == self._config['RADIO_ID']: # Validate the Radio_ID unless using loose validation 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) self._logger.info('(%s) Repeater Configuration Accepted', self._system)
if self._config['OPTIONS']: if self._config['OPTIONS']:
self.send_master('RPTO'+self._config['RADIO_ID']+self._config['OPTIONS']) self.send_master('RPTO'+self._config['RADIO_ID']+self._config['OPTIONS'])
@ -529,8 +523,8 @@ class HBSYSTEM(DatagramProtocol):
self._logger.error('(%s) Master ACK Contained wrong ID - Connection Reset', self._system) self._logger.error('(%s) Master ACK Contained wrong ID - Connection Reset', self._system)
elif self._stats['CONNECTION'] == 'OPTIONS-SENT': # If we've sent out options to the master elif self._stats['CONNECTION'] == 'OPTIONS-SENT': # If we've sent out options to the master
_radio_id = _data[6:10] _peer_id = _data[6:10]
if self._config['LOOSE'] or _radio_id == self._config['RADIO_ID']: # Validate the Radio_ID unless using loose validation if self._config['LOOSE'] or _peer_id == self._config['RADIO_ID']: # Validate the Radio_ID unless using loose validation
self._logger.info('(%s) Repeater Options Accepted', self._system) self._logger.info('(%s) Repeater Options Accepted', self._system)
self._stats['CONNECTION'] = 'YES' self._stats['CONNECTION'] = 'YES'
self._logger.info('(%s) Connection to Master Completed with options', self._system) self._logger.info('(%s) Connection to Master Completed with options', self._system)
@ -538,17 +532,17 @@ class HBSYSTEM(DatagramProtocol):
self._stats['CONNECTION'] = 'NO' self._stats['CONNECTION'] = 'NO'
self._logger.error('(%s) Master ACK Contained wrong ID - Connection Reset', self._system) self._logger.error('(%s) Master ACK Contained wrong ID - Connection Reset', self._system)
elif _command == 'MSTP': # Actually MSTPONG -- a reply to RPTPING (send by client) elif _command == 'MSTP': # Actually MSTPONG -- a reply to RPTPING (send by peer)
_radio_id = _data[7:11] _peer_id = _data[7:11]
if self._config['LOOSE'] or _radio_id == self._config['RADIO_ID']: # Validate the Radio_ID unless using loose validation if self._config['LOOSE'] or _peer_id == self._config['RADIO_ID']: # Validate the Radio_ID unless using loose validation
self._stats['PING_OUTSTANDING'] = False self._stats['PING_OUTSTANDING'] = False
self._stats['NUM_OUTSTANDING'] = 0 self._stats['NUM_OUTSTANDING'] = 0
self._stats['PINGS_ACKD'] += 1 self._stats['PINGS_ACKD'] += 1
self._logger.debug('(%s) MSTPONG Received. Pongs Since Connected: %s', self._system, self._stats['PINGS_ACKD']) self._logger.debug('(%s) MSTPONG Received. Pongs Since Connected: %s', self._system, self._stats['PINGS_ACKD'])
elif _command == 'MSTC': # Actually MSTCL -- notify us the master is closing down elif _command == 'MSTC': # Actually MSTCL -- notify us the master is closing down
_radio_id = _data[5:9] _peer_id = _data[5:9]
if self._config['LOOSE'] or _radio_id == self._config['RADIO_ID']: # Validate the Radio_ID unless using loose validation if self._config['LOOSE'] or _peer_id == self._config['RADIO_ID']: # Validate the Radio_ID unless using loose validation
self._stats['CONNECTION'] = 'NO' self._stats['CONNECTION'] = 'NO'
self._logger.info('(%s) MSTCL Recieved', self._system) self._logger.info('(%s) MSTCL Recieved', self._system)
@ -648,8 +642,8 @@ if __name__ == '__main__':
for sig in [signal.SIGTERM, signal.SIGINT]: for sig in [signal.SIGTERM, signal.SIGINT]:
signal.signal(sig, sig_handler) signal.signal(sig, sig_handler)
# Build the Access Control List # Build the Registration Access Control List
ACL = build_acl('reg_acl') REG_ACL = build_reg_acl('reg_acl', logger)
# INITIALIZE THE REPORTING LOOP # INITIALIZE THE REPORTING LOOP
report_server = config_reports(CONFIG, logger, reportFactory) report_server = config_reports(CONFIG, logger, reportFactory)

View File

@ -8,4 +8,4 @@
# Each entry may be a single radio id, or a hypenated range (e.g. 1-2999) # Each entry may be a single radio id, or a hypenated range (e.g. 1-2999)
# Format: # Format:
# ACL = 'action:id|start-end|,id|start-end,....' # ACL = 'action:id|start-end|,id|start-end,....'
ACL = 'DENY:1' REG_ACL = 'DENY:1'

View File

@ -227,7 +227,7 @@ class routerSYSTEM(HBSYSTEM):
} }
} }
def dmrd_received(self, _radio_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data): def dmrd_received(self, _peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data):
pkt_time = time() pkt_time = time()
dmrpkt = _data[20:53] dmrpkt = _data[20:53]
_bits = int_id(_data[15]) _bits = int_id(_data[15])
@ -236,19 +236,19 @@ class routerSYSTEM(HBSYSTEM):
# Check for ACL match, and return if the subscriber is not allowed # Check for ACL match, and return if the subscriber is not allowed
if allow_sub(_rf_src) == False: if allow_sub(_rf_src) == False:
self._logger.warning('(%s) Group Voice Packet ***REJECTED BY ACL*** From: %s, HBP Peer %s, Destination TGID %s', self._system, int_id(_rf_src), int_id(_radio_id), int_id(_dst_id)) self._logger.warning('(%s) Group Voice Packet ***REJECTED BY ACL*** From: %s, HBP Peer %s, Destination TGID %s', self._system, int_id(_rf_src), int_id(_peer_id), int_id(_dst_id))
return return
# Is this a new call stream? # Is this a new call stream?
if (_stream_id != self.STATUS[_slot]['RX_STREAM_ID']): if (_stream_id != self.STATUS[_slot]['RX_STREAM_ID']):
if (self.STATUS[_slot]['RX_TYPE'] != hb_const.HBPF_SLT_VTERM) and (pkt_time < (self.STATUS[_slot]['RX_TIME'] + hb_const.STREAM_TO)) and (_rf_src != self.STATUS[_slot]['RX_RFS']): if (self.STATUS[_slot]['RX_TYPE'] != hb_const.HBPF_SLT_VTERM) and (pkt_time < (self.STATUS[_slot]['RX_TIME'] + hb_const.STREAM_TO)) and (_rf_src != self.STATUS[_slot]['RX_RFS']):
self._logger.warning('(%s) Packet received with STREAM ID: %s <FROM> SUB: %s REPEATER: %s <TO> TGID %s, SLOT %s collided with existing call', self._system, int_id(_stream_id), int_id(_rf_src), int_id(_radio_id), int_id(_dst_id), _slot) self._logger.warning('(%s) Packet received with STREAM ID: %s <FROM> SUB: %s PEER: %s <TO> TGID %s, SLOT %s collided with existing call', self._system, int_id(_stream_id), int_id(_rf_src), int_id(_peer_id), int_id(_dst_id), _slot)
return return
# This is a new call stream # This is a new call stream
self.STATUS['RX_START'] = pkt_time self.STATUS['RX_START'] = pkt_time
self._logger.info('(%s) *CALL START* STREAM ID: %s SUB: %s (%s) REPEATER: %s (%s) TGID %s (%s), TS %s', \ self._logger.info('(%s) *CALL START* STREAM ID: %s SUB: %s (%s) PEER: %s (%s) TGID %s (%s), TS %s', \
self._system, int_id(_stream_id), get_alias(_rf_src, subscriber_ids), int_id(_rf_src), get_alias(_radio_id, peer_ids), int_id(_radio_id), get_alias(_dst_id, talkgroup_ids), int_id(_dst_id), _slot) self._system, int_id(_stream_id), get_alias(_rf_src, subscriber_ids), int_id(_rf_src), get_alias(_peer_id, peer_ids), int_id(_peer_id), get_alias(_dst_id, talkgroup_ids), int_id(_dst_id), _slot)
# If we can, use the LC from the voice header as to keep all options intact # If we can, use the LC from the voice header as to keep all options intact
if _frame_type == hb_const.HBPF_DATA_SYNC and _dtype_vseq == hb_const.HBPF_SLT_VHEAD: if _frame_type == hb_const.HBPF_DATA_SYNC and _dtype_vseq == hb_const.HBPF_SLT_VHEAD:
@ -345,8 +345,8 @@ class routerSYSTEM(HBSYSTEM):
# Final actions - Is this a voice terminator? # Final actions - Is this a voice terminator?
if (_frame_type == hb_const.HBPF_DATA_SYNC) and (_dtype_vseq == hb_const.HBPF_SLT_VTERM) and (self.STATUS[_slot]['RX_TYPE'] != hb_const.HBPF_SLT_VTERM): if (_frame_type == hb_const.HBPF_DATA_SYNC) and (_dtype_vseq == hb_const.HBPF_SLT_VTERM) and (self.STATUS[_slot]['RX_TYPE'] != hb_const.HBPF_SLT_VTERM):
call_duration = pkt_time - self.STATUS['RX_START'] call_duration = pkt_time - self.STATUS['RX_START']
self._logger.info('(%s) *CALL END* STREAM ID: %s SUB: %s (%s) REPEATER: %s (%s) TGID %s (%s), TS %s, Duration: %s', \ self._logger.info('(%s) *CALL END* STREAM ID: %s SUB: %s (%s) PEER: %s (%s) TGID %s (%s), TS %s, Duration: %s', \
self._system, int_id(_stream_id), get_alias(_rf_src, subscriber_ids), int_id(_rf_src), get_alias(_radio_id, peer_ids), int_id(_radio_id), get_alias(_dst_id, talkgroup_ids), int_id(_dst_id), _slot, call_duration) self._system, int_id(_stream_id), get_alias(_rf_src, subscriber_ids), int_id(_rf_src), get_alias(_peer_id, peer_ids), int_id(_peer_id), get_alias(_dst_id, talkgroup_ids), int_id(_dst_id), _slot, call_duration)
# #
# Begin in-band signalling for call end. This has nothign to do with routing traffic directly. # Begin in-band signalling for call end. This has nothign to do with routing traffic directly.