Exemple #1
0
 def group_voice(self, _network, _src_sub, _dst_group, _ts, _end, _peerid, _data):
     logger.debug('(%s) Group Voice Packet Received From: %s, IPSC Peer %s, Destination %s', _network, int_id(_src_sub), int_id(_peerid), int_id(_dst_group))
     if _ts not in self.ACTIVE_CALLS:
         self.ACTIVE_CALLS.append(_ts)
         # send repeater wake up, but send them when a repeater is likely not TXing check time since end (see below)
     if _end:
         self.ACTIVE_CALLS.remove(_ts)
         # flag the time here so we can test to see if the last call ended long enough ago to send a wake-up
         # 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 and (self.BRIDGE == True or networks[_target].BRIDGE == True):
             _tmp_data = _data
             # 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)
             # Send the packet to all peers in the target IPSC
             send_to_ipsc(_target, _tmp_data)
Exemple #2
0
    def private_data(self, _network, _src_sub, _dst_sub, _ts, _end, _peerid, _data):    
        logger.debug('(%s) Private Data Packet Received From: %s, IPSC Peer %s, Destination %s', _network, int_id(_src_sub), int_id(_peerid), int_id(_dst_sub))
        
        for target in RULES[_network]['PRIVATE_DATA']:
                   
            if self.BRIDGE == True or networks[target].BRIDGE == True:
                _tmp_data = _data
                # Re-Write the IPSC SRC to match the target network's ID
                _tmp_data = _tmp_data.replace(_peerid, NETWORK[target]['LOCAL']['RADIO_ID'])

                # 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)
                # Send the packet to all peers in the target IPSC
                networks[target].send_to_ipsc(_tmp_data)
Exemple #3
0
    def private_data(self, _network, _src_sub, _dst_sub, _ts, _end, _peerid, _data):    
        logger.debug('(%s) Private Data Packet Received From: %s, IPSC Peer %s, Destination %s', _network, int_id(_src_sub), int_id(_peerid), int_id(_dst_sub))
        
        for target in RULES[_network]['PRIVATE_DATA']:
                   
            if self.BRIDGE == True or networks[target].BRIDGE == True:
                _tmp_data = _data
                # Re-Write the IPSC SRC to match the target network's ID
                _tmp_data = _tmp_data.replace(_peerid, NETWORK[target]['LOCAL']['RADIO_ID'])

                # Calculate and append the authentication hash for the target network... if necessary
                if NETWORK[target]['LOCAL']['AUTH_ENABLED']:
                    _tmp_data = self.auth_hashed_packet(NETWORK[target]['LOCAL']['AUTH_KEY'], _tmp_data)
                # Send the packet to all peers in the target IPSC
                networks[target].send_to_ipsc(_tmp_data)
Exemple #4
0
    def group_data(self, _network, _src_sub, _dst_sub, _ts, _end, _peerid, _data):    
        logger.debug('(%s) Group Data Packet Received From: %s, IPSC Peer %s, Destination %s', _network, int_id(_src_sub), int_id(_peerid), int_id(_dst_sub))
        
        for rule in RULES[_network]['GROUP_DATA']:
            _target = rule                          # Shorthand to reduce length and make it easier to read
            
            if self.BRIDGE == True or networks[_target].BRIDGE == True:
                _tmp_data = _data
                # Re-Write the IPSC SRC to match the target network's ID
                _tmp_data = _tmp_data.replace(_peerid, NETWORK[_target]['LOCAL']['RADIO_ID'])

                # 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)
                # Send the packet to all peers in the target IPSC
                networks[_target].send_to_ipsc(_tmp_data)
Exemple #5
0
 def bridge_presence_loop(self):
     _temp_bridge = True
     for peer in BRIDGES:
         _peer = hex_str_4(peer)
     
         if _peer in self._peers.keys() and (self._peers[_peer]['MODE_DECODE']['TS_1'] or self._peers[_peer]['MODE_DECODE']['TS_2']):
             _temp_bridge = False
             logger.debug('(%s) Peer %s is an active bridge', self._network, int_id(_peer))
     
         if _peer == self._master['RADIO_ID'] \
             and self._master['STATUS']['CONNECTED'] \
             and (self._master['MODE_DECODE']['TS_1'] or self._master['MODE_DECODE']['TS_2']):
             _temp_bridge = False
             logger.debug('(%s) Master %s is an active bridge',self._network, int_id(_peer))
     
     if self.BRIDGE != _temp_bridge:
         logger.info('(%s) Changing bridge status to: %s', self._network, _temp_bridge )
     self.BRIDGE = _temp_bridge
Exemple #6
0
 def bridge_presence_loop(self):
     _temp_bridge = True
     for peer in BRIDGES:
         _peer = hex_str_4(peer)
     
         if _peer in self._peers.keys() and (self._peers[_peer]['MODE_DECODE']['TS_1'] or self._peers[_peer]['MODE_DECODE']['TS_2']):
             _temp_bridge = False
             logger.debug('(%s) Peer %s is an active bridge', self._network, int_id(_peer))
     
         if _peer == self._master['RADIO_ID'] \
             and self._master['STATUS']['CONNECTED'] \
             and (self._master['MODE_DECODE']['TS_1'] or self._master['MODE_DECODE']['TS_2']):
             _temp_bridge = False
             logger.debug('(%s) Master %s is an active bridge',self._network, int_id(_peer))
     
     if self.BRIDGE != _temp_bridge:
         logger.info('(%s) Changing bridge status to: %s', self._network, _temp_bridge )
     self.BRIDGE = _temp_bridge
Exemple #7
0
    def group_voice(self, _network, _src_sub, _dst_group, _ts, _end, _peerid, _data):
        logger.debug('(%s) Group Voice Packet Received From: %s, IPSC Peer %s, Destination %s', _network, int_id(_src_sub), int_id(_peerid), int_id(_dst_group))
        _burst_data_type = _data[30] # Determine the type of voice packet this is (see top of file for possible types)
        if _ts == 0:
            _TS = 'TS1'
        elif _ts == 1:
            _TS = 'TS2'
        
        now = time()                                # Mark packet arrival time -- we'll need this for call contention handling 
        
        for rule in RULES[_network]['GROUP_VOICE']:
            _target = rule['DST_NET']               # Shorthand to reduce length and make it easier to read
            _status = networks[_target].IPSC_STATUS # Shorthand to reduce length and make it easier to read

            # 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:
            #if BRIDGES:
            if (rule['SRC_GROUP'] == _dst_group and rule['SRC_TS'] == _ts) and (self.BRIDGE == True or networks[_target].BRIDGE == True):
                if RULES[_network]['TRUNK'] == False:
                    if ((rule['DST_GROUP'] != _status[_TS]['RX_GROUP']) and ((now - _status[_TS]['RX_TIME']) < RULES[_network]['GROUP_HANGTIME'])):
                        if _burst_data_type == BURST_DATA_TYPE['VOICE_HEAD']:
                            logger.info('(%s) Call not bridged, target active or in group hangtime: IPSC %s, %s, TGID%s', _network, _target, _TS, int_id(rule['DST_GROUP']))
                        continue
                    
                    if ((rule['DST_GROUP'] != _status[_TS]['TX_GROUP']) and ((now - _status[_TS]['TX_TIME']) < RULES[_network]['GROUP_HANGTIME'])):
                        if _burst_data_type == BURST_DATA_TYPE['VOICE_HEAD']:
                            logger.info('(%s) Call not bridged to destination on TGID %s, target in group hangtime: IPSC %s, %s, TGID%s', _network, int_id(_status[_TS]['TX_GROUP']), _target, _TS, int_id(rule['DST_GROUP']))
                        continue

                    if (rule['DST_GROUP'] == _status[_TS]['TX_GROUP']) and (_src_sub != _status[_TS]['TX_SRC_SUB']) and ((now - _status[_TS]['TX_TIME']) < TS_CLEAR_TIME):
                        if _burst_data_type == BURST_DATA_TYPE['VOICE_HEAD']:
                            logger.info('(%s) Call not bridged, call bridge in progress from %s, target: IPSC %s, %s, TGID%s', _network, int_id(_src_sub), _target, _TS, int_id(rule['DST_GROUP']))
                        continue

                    if (rule['DST_GROUP'] == _status[_TS]['RX_GROUP']) and ((now - _status[_TS]['RX_TIME']) < TS_CLEAR_TIME):
                        if _burst_data_type == BURST_DATA_TYPE['VOICE_HEAD']:
                            logger.info('(%s) Call not bridged, matching call already active on target: IPSC %s, %s, TGID%s', _network, _target, _TS, int_id(rule['DST_GROUP']))
                        continue
                       
                _tmp_data = _data
                # 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
                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)
                # Send the packet to all peers in the target IPSC
                networks[_target].send_to_ipsc(_tmp_data)

                _status[_TS]['TX_GROUP'] = rule['DST_GROUP']
                _status[_TS]['TX_TIME'] = now
                _status[_TS]['TX_SRC_SUB'] = _src_sub

        # Mark the group and time that a packet was recieved
        self.IPSC_STATUS[_TS]['RX_GROUP'] = _dst_group
        self.IPSC_STATUS[_TS]['RX_TIME'] = now 
Exemple #8
0
    def group_voice(self, _network, _src_sub, _dst_group, _ts, _end, _peerid, _data):
        logger.debug('(%s) Group Voice Packet Received From: %s, IPSC Peer %s, Destination %s', _network, int_id(_src_sub), int_id(_peerid), int_id(_dst_group))
        _burst_data_type = _data[30] # Determine the type of voice packet this is (see top of file for possible types)
        if _ts == 0:
            _TS = 'TS1'
        elif _ts == 1:
            _TS = 'TS2'
        
        now = time()                                # Mark packet arrival time -- we'll need this for call contention handling 
        
        for rule in RULES[_network]['GROUP_VOICE']:
            _target = rule['DST_NET']               # Shorthand to reduce length and make it easier to read
            _status = networks[_target].IPSC_STATUS # Shorthand to reduce length and make it easier to read

            # 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:
            #if BRIDGES:
            if (rule['SRC_GROUP'] == _dst_group and rule['SRC_TS'] == _ts) and (self.BRIDGE == True or networks[_target].BRIDGE == True):
                
                if ((rule['DST_GROUP'] != _status[_TS]['RX_GROUP']) and ((now - _status[_TS]['RX_TIME']) < RULES[_network]['GROUP_HANGTIME'])):
                    if _burst_data_type == BURST_DATA_TYPE['VOICE_HEAD']:
                        logger.info('(%s) Call not bridged, target active or in group hangtime: IPSC %s, %s, TGID%s', _network, _target, _TS, int_id(rule['DST_GROUP']))
                    return
                    
                if ((rule['DST_GROUP'] != _status[_TS]['TX_GROUP']) and ((now - _status[_TS]['TX_TIME']) < RULES[_network]['GROUP_HANGTIME'])):
                    if _burst_data_type == BURST_DATA_TYPE['VOICE_HEAD']:
                        logger.info('(%s) Call not bridged, target in group hangtime: IPSC %s, %s, TGID%s', _network, _target, _TS, int_id(rule['DST_GROUP']))
                    return

                if (rule['DST_GROUP'] == _status[_TS]['TX_GROUP']) and (_src_sub != _status[_TS]['TX_SRC_SUB']) and ((now - _status[_TS]['TX_TIME']) < TS_CLEAR_TIME):
                    if _burst_data_type == BURST_DATA_TYPE['VOICE_HEAD']:
                        logger.info('(%s) Call not bridged, call bridge in progress from %s, target: IPSC %s, %s, TGID%s', _network, int_id(_src_sub), _target, _TS, int_id(rule['DST_GROUP']))
                    return

                if (rule['DST_GROUP'] == _status[_TS]['RX_GROUP']) and ((now - _status[_TS]['RX_TIME']) < TS_CLEAR_TIME):
                    if _burst_data_type == BURST_DATA_TYPE['VOICE_HEAD']:
                        logger.info('(%s) Call not bridged, matching call already active on target: IPSC %s, %s, TGID%s', _network, _target, _TS, int_id(rule['DST_GROUP']))
                    return
                       
                _tmp_data = _data
                # 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
                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)
                # Send the packet to all peers in the target IPSC
                networks[_target].send_to_ipsc(_tmp_data)

                _status[_TS]['TX_GROUP'] = _dst_group
                _status[_TS]['TX_TIME'] = now
                _status[_TS]['TX_SRC_SUB'] = _src_sub

        # Mark the group and time that a packet was recieved
        self.IPSC_STATUS[_TS]['RX_GROUP'] = _dst_group
        self.IPSC_STATUS[_TS]['RX_TIME'] = now
Exemple #9
0
    def group_voice(self, _network, _src_sub, _dst_group, _ts, _end, _peerid, _data):
        
        # Check for ACL match, and return if the subscriber is not allowed
        if allow_sub(_src_sub) == False:
            logger.warning('(%s) Group Voice Packet ***REJECTED BY ACL*** From: %s, IPSC Peer %s, Destination %s', _network, int_id(_src_sub), int_id(_peerid), int_id(_dst_group))
            return
        
        # Process the packet
        logger.debug('(%s) Group Voice Packet Received From: %s, IPSC Peer %s, Destination %s', _network, int_id(_src_sub), int_id(_peerid), int_id(_dst_group))
        _burst_data_type = _data[30] # Determine the type of voice packet this is (see top of file for possible types)
        if _ts == 0:
            _TS = 'TS1'
        elif _ts == 1:
            _TS = 'TS2'               
        
        now = time() # Mark packet arrival time -- we'll need this for call contention handling 
        
        for rule in RULES[_network]['GROUP_VOICE']:
            _target = rule['DST_NET']               # Shorthand to reduce length and make it easier to read
            _status = networks[_target].IPSC_STATUS # Shorthand to reduce length and make it easier to read

            # 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:
            # if BRIDGES:
            if (rule['SRC_GROUP'] == _dst_group and rule['SRC_TS'] == _ts and rule['ACTIVE'] == True) and (self.BRIDGE == True or networks[_target].BRIDGE == True):
                
                #
                # BEGIN CONTENTION HANDLING
                # 
                # If this is an inter-DMRlink trunk, this isn't necessary
                if RULES[_network]['TRUNK'] == False: 
                    
                    # The rules for each of the 4 "ifs" below are listed here for readability. The Frame To Send is:
                    #   From a different group than last RX from this IPSC, but it has been less than Group Hangtime
                    #   From a different group than last TX to this IPSC, but it has been less than Group Hangtime
                    #   From the same group as the last RX from this IPSC, but from a different subscriber, and it has been less than TS Clear Time
                    #   From the same group as the last TX to this IPSC, but from a different subscriber, and it has been less than TS Clear Time
                    # The "continue" at the end of each means the next iteration of the for loop that tests for matching rules
                    #
                    if ((rule['DST_GROUP'] != _status[_TS]['RX_GROUP']) and ((now - _status[_TS]['RX_TIME']) < RULES[_network]['GROUP_HANGTIME'])):
                        if _burst_data_type == BURST_DATA_TYPE['VOICE_HEAD']:
                            logger.info('(%s) Call not bridged, target active or in group hangtime: IPSC %s, %s, TGID%s', _network, _target, _TS, int_id(rule['DST_GROUP']))
                        continue    
                    if ((rule['DST_GROUP'] != _status[_TS]['TX_GROUP']) and ((now - _status[_TS]['TX_TIME']) < RULES[_network]['GROUP_HANGTIME'])):
                        if _burst_data_type == BURST_DATA_TYPE['VOICE_HEAD']:
                            logger.info('(%s) Call not bridged to destination on TGID %s, target in group hangtime: IPSC %s, %s, TGID%s', _network, int_id(_status[_TS]['TX_GROUP']), _target, _TS, int_id(rule['DST_GROUP']))
                        continue
                    if (rule['DST_GROUP'] == _status[_TS]['RX_GROUP']) and ((now - _status[_TS]['RX_TIME']) < TS_CLEAR_TIME):
                        if _burst_data_type == BURST_DATA_TYPE['VOICE_HEAD']:
                            logger.info('(%s) Call not bridged, matching call already active on target: IPSC %s, %s, TGID%s', _network, _target, _TS, int_id(rule['DST_GROUP']))
                        continue
                    if (rule['DST_GROUP'] == _status[_TS]['TX_GROUP']) and (_src_sub != _status[_TS]['TX_SRC_SUB']) and ((now - _status[_TS]['TX_TIME']) < TS_CLEAR_TIME):
                        if _burst_data_type == BURST_DATA_TYPE['VOICE_HEAD']:
                            logger.info('(%s) Call not bridged, call bridge in progress from %s, target: IPSC %s, %s, TGID%s', _network, int_id(_src_sub), _target, _TS, int_id(rule['DST_GROUP']))
                        continue
                #
                # END CONTENTION HANDLING
                #
                
                
                #
                # BEGIN FRAME FORWARDING
                #     
                # Make a copy of the payload       
                _tmp_data = _data
                
                # 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
                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.auth_hashed_packet(NETWORK[_target]['LOCAL']['AUTH_KEY'], _tmp_data)
                # Send the packet to all peers in the target IPSC
                networks[_target].send_to_ipsc(_tmp_data)
                #
                # END FRAME FORWARDING
                #
                
                
                # Set values for the contention handler to test next time there is a frame to forward
                _status[_TS]['TX_GROUP'] = rule['DST_GROUP']
                _status[_TS]['TX_TIME'] = now
                _status[_TS]['TX_SRC_SUB'] = _src_sub
                

        # Mark the group and time that a packet was recieved for the contention handler to use later
        self.IPSC_STATUS[_TS]['RX_GROUP'] = _dst_group
        self.IPSC_STATUS[_TS]['RX_TIME']  = now
        
        
        #
        # BEGIN IN-BAND SIGNALING BASED ON TGID & VOICE TERMINATOR FRAME
        #
        # Activate/Deactivate rules based on group voice activity -- PTT or UA for you c-Bridge dorks.
        # This will ONLY work for symmetrical rules!!!
        
        # Action happens on un-key
        if _burst_data_type == BURST_DATA_TYPE['VOICE_TERM']:
            
            # Iterate the rules dictionary
            for rule in RULES[_network]['GROUP_VOICE']:
                
                # TGID matches an ACTIVATION trigger
                if _dst_group in rule['ON']:
                    # Set the matching rule as ACTIVE
                    rule['ACTIVE'] = True
                    logger.info('(%s) Primary Bridge Rule \"%s\" changed to state: %s', _network, rule['NAME'], rule['ACTIVE'])
                    
                    # Set reciprocal rules for other IPSCs as ACTIVE
                    _target = rule['DST_NET']
                    for target_rule in RULES[_target]['GROUP_VOICE']:
                        if target_rule['NAME'] == rule['NAME']:
                            target_rule['ACTIVE'] = True
                            logger.info('(%s) Reciprocal Bridge Rule \"%s\" in IPSC \"%s\" changed to state: %s', _network, target_rule['NAME'], _target, rule['ACTIVE'])
                            
                # TGID matches an DE-ACTIVATION trigger
                if _dst_group in rule['OFF']:
                    # Set the matching rule as ACTIVE
                    rule['ACTIVE'] = False
                    logger.info('(%s) Bridge Rule \"%s\" changed to state: %s', _network, rule['NAME'], rule['ACTIVE'])
                    
                    # Set reciprocal rules for other IPSCs as ACTIVE
                    _target = rule['DST_NET']
                    for target_rule in RULES[_target]['GROUP_VOICE']:
                        if target_rule['NAME'] == rule['NAME']:
                            target_rule['ACTIVE'] = False
                            logger.info('(%s) Reciprocal Bridge Rule \"%s\" in IPSC \"%s\" changed to state: %s', _network, target_rule['NAME'], _target, rule['ACTIVE'])