diff --git a/bridge.py b/bridge.py index 472cdee..1939711 100755 --- a/bridge.py +++ b/bridge.py @@ -47,6 +47,13 @@ __email__ = 'n0mjs@me.com' __status__ = 'beta' +BURST_DATA_TYPE = { + 'VOICE_HEAD': '\x01', + 'VOICE_TERM': '\x02', + 'SLOT1_VOICE': '\x0A', + 'SLOT2_VOICE': '\x8A' +} + # Notes and pieces of next steps... # RPT_WAKE_UP = b'\x85' + NETWORK[_network]['LOCAL']['RADIO_ID] + b'\x00\x00\x00\x01' + b'\x01' + b'\x01' # TS1 = 0, TS2 = 1 @@ -70,7 +77,9 @@ for _ipsc in RULES: for _rule in RULES[_ipsc]['GROUP_VOICE']: _rule['SRC_GROUP'] = hex_str_3(_rule['SRC_GROUP']) _rule['DST_GROUP'] = hex_str_3(_rule['DST_GROUP']) -print() + _rule['SRC_TS'] = _rule['SRC_TS'] - 1 + _rule['DST_TS'] = _rule['DST_TS'] - 1 + # Import List of Bridges # This is how we identify known bridges. If one of these is present @@ -154,6 +163,30 @@ if BRIDGES: _tmp_data = _tmp_data.replace(_peerid, NETWORK[_target]['LOCAL']['RADIO_ID']) # Re-Write the destination Group ID _tmp_data = _tmp_data.replace(_dst_group, rule['DST_GROUP']) + + # Re-Write IPSC timeslot value + _call_info = int_id(_data[17:18]) + if rule['DST_TS'] == 0: + _call_info &= ~(1 << 5) + elif rule['DST_TS'] == 1: + _call_info |= 1 << 5 + _call_info = chr(_call_info) + _tmp_data = _tmp_data[:17] + _call_info + _tmp_data[18:] + + # Re-Write DMR timeslot value + # Determine if the slot is present, so we can translate if need be + _burst_data_type = _data[30] + if _burst_data_type == BURST_DATA_TYPE['SLOT1_VOICE'] or _burst_data_type == BURST_DATA_TYPE['SLOT2_VOICE']: + _slot_valid = True + else: + _slot_valid = False + # Re-Write timeslot if necessary... + if _slot_valid: + if rule['DST_TS'] == 0: + _burst_data_type = BURST_DATA_TYPE['SLOT1_VOICE'] + elif rule['DST_TS'] == 1: + _burst_data_type = BURST_DATA_TYPE['SLOT2_VOICE'] + _tmp_data = _tmp_data[:30] + _burst_data_type + _tmp_data[31:] # Calculate and append the authentication hash for the target network... if necessary if NETWORK[_target]['LOCAL']['AUTH_ENABLED']: @@ -184,15 +217,39 @@ else: # timer = time() for rule in RULES[_network]['GROUP_VOICE']: + _target = rule['DST_NET'] # Matching for rules is against the Destination Group in the SOURCE packet (SRC_GROUP) if rule['SRC_GROUP'] == _dst_group and rule['SRC_TS'] == _ts: _tmp_data = _data - _target = rule['DST_NET'] # Re-Write the IPSC SRC to match the target network's ID _tmp_data = _tmp_data.replace(_peerid, NETWORK[_target]['LOCAL']['RADIO_ID']) # Re-Write the destination Group ID _tmp_data = _tmp_data.replace(_dst_group, rule['DST_GROUP']) + # Re-Write IPSC timeslot value + _call_info = int_id(_data[17:18]) + if rule['DST_TS'] == 0: + _call_info &= ~(1 << 5) + elif rule['DST_TS'] == 1: + _call_info |= 1 << 5 + _call_info = chr(_call_info) + _tmp_data = _tmp_data[:17] + _call_info + _tmp_data[18:] + + # Re-Write DMR timeslot value + # Determine if the slot is present, so we can translate if need be + _burst_data_type = _data[30] + if _burst_data_type == BURST_DATA_TYPE['SLOT1_VOICE'] or _burst_data_type == BURST_DATA_TYPE['SLOT2_VOICE']: + _slot_valid = True + else: + _slot_valid = False + # Re-Write timeslot if necessary... + if _slot_valid: + if rule['DST_TS'] == 0: + _burst_data_type = BURST_DATA_TYPE['SLOT1_VOICE'] + elif rule['DST_TS'] == 1: + _burst_data_type = BURST_DATA_TYPE['SLOT2_VOICE'] + _tmp_data = _tmp_data[:30] + _burst_data_type + _tmp_data[31:] + # Calculate and append the authentication hash for the target network... if necessary if NETWORK[_target]['LOCAL']['AUTH_ENABLED']: _tmp_data = self.hashed_packet(NETWORK[_target]['LOCAL']['AUTH_KEY'], _tmp_data) diff --git a/bridge_rules_SAMPLE.py b/bridge_rules_SAMPLE.py index 61a65b9..6ba9bf6 100644 --- a/bridge_rules_SAMPLE.py +++ b/bridge_rules_SAMPLE.py @@ -10,14 +10,14 @@ The IPSC name must match an IPSC name from dmrlink.cfg, and any IPSC network def as "active" in the dmrlink.cfg *MUST* have an entry here. It may be an empty entry, but there must be one so that the data structure can be parsed. -The example below cross-patches TGID 1 on an IPSC network named "IPSC_FOO" with TGID 2 -on an IPSC network named "IPSC_BAR". Note, one entry must be made on EACH IPSC network -(IPSC_FOO and IPSC_BAR in this example) for bridging to occur in both directions. +The example below cross-patches TS 1/TGID 1 on an IPSC network named "IPSC_FOO" with +TS 2/TGID 2 on an IPSC network named "IPSC_BAR". Note, one entry must be made on EACH +IPSC network (IPSC_FOO and IPSC_BAR in this example) for bridging to occur in both +directions. THIS EXAMPLE WILL NOT WORK AS IT IS - YOU MUST SPECIFY NAMES AND GROUP IDS!!! NOTES: - * Timeslot transcoding does not yet work (SRC_TS) and (DST_TS) are ignored. * Only GROUP_VOICE is currently used by the bridge.py appication, the other types are placeholders for when it does more. ''' @@ -25,7 +25,7 @@ NOTES: RULES = { 'IPSC_FOO': { 'GROUP_VOICE': [ - {'SRC_GROUP': 1, 'SRC_TS': 1, 'DST_NET': 'IPSC_BAR', 'DST_GROUP': 2, 'DST_TS': 1}, + {'SRC_GROUP': 1, 'SRC_TS': 1, 'DST_NET': 'IPSC_BAR', 'DST_GROUP': 2, 'DST_TS': 2}, # Repeat the above line for as many rules for this IPSC network as you want. ], 'PRIVATE_VOICE': [ @@ -37,7 +37,7 @@ RULES = { }, 'IPSC_BAR': { 'GROUP_VOICE': [ - {'SRC_GROUP': 2, 'SRC_TS': 1, 'DST_NET': 'IPSC_FOO', 'DST_GROUP': 1, 'DST_TS': 1}, + {'SRC_GROUP': 2, 'SRC_TS': 2, 'DST_NET': 'IPSC_FOO', 'DST_GROUP': 1, 'DST_TS': 1}, # Repeat the above line for as many rules for this IPSC network as you want. ], 'PRIVATE_VOICE': [ diff --git a/ipsc/ipsc_message_types.py b/ipsc/ipsc_message_types.py index 6668523..086a20a 100644 --- a/ipsc/ipsc_message_types.py +++ b/ipsc/ipsc_message_types.py @@ -111,41 +111,11 @@ REPEAT = { } -# Conditions for accepting certain types of messages... the cornerstone of a secure IPSC system :) -''' -REQ_VALID_PEER = [ - PEER_REG_REQ, - PEER_REG_REPLY -] +# DMR IPSC Contants (in the RTP Payload) -REQ_VALID_MASTER = [ - MASTER_REG_REQ, - MASTER_REG_REPLY -] - -REQ_MASTER_CONNECTED = [ - CALL_MON_STATUS, - CALL_MON_RPT, - CALL_MON_NACK, - XCMP_XNL, - GROUP_VOICE, - PVT_VOICE, - GROUP_DATA, - GROUP_VOICE, - PVT_DATA, - RPT_WAKE_UP, - MASTER_ALIVE_REQ, - MASTER_ALIVE_REPLY, - DE_REG_REQ, - DE_REG_REPLY -] - -REQ_PEER_CONNECTED = [ - PEER_ALIVE_REQ, - PEER_ALIVE_REPLY -] - -REQ_VALID_MASTER_OR_PEER = [ - REQ_VALID_PEER, REQ_VALID_MASTER -] -''' \ No newline at end of file +BURST_DATA_TYPE = { + 'VOICE_HEAD': '\x01', + 'VOICE_TERM': '\x02', + 'SLOT1_VOICE': '\x0A', + 'SLOT2_VOICE': '\x8A' +}