MAJOR - Data Structure Update

Internal data structure change for how peers are stored. Instead of a
list of dicts, it is now a dict of dicts where the dict key IS the
radio ID, and the Radio ID is no longer stored in the "inner" dict.
This does NOT affect bridge.py or log.py, only dmrlink.py
This commit is contained in:
Cort Buffington 2013-11-13 16:19:32 -06:00
parent 3f0e2724db
commit b90ec9b7f4
2 changed files with 69 additions and 64 deletions

View File

@ -74,7 +74,6 @@ class bridgeIPSC(IPSC):
if source['SRC_GROUP'] == _dst_sub:
_tmp_data = _data
_target = source['DST_NET']
_target_sock = NETWORK[_target]['MASTER']['IP'], NETWORK[_target]['MASTER']['PORT']
# 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 destinaion Group ID

View File

@ -10,6 +10,7 @@ from __future__ import print_function
from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor
from twisted.internet import task
from binascii import b2a_hex as h
import ConfigParser
import os
import sys
@ -93,7 +94,7 @@ try:
if section == 'GLOBAL':
pass
else:
NETWORK.update({section: {'LOCAL': {}, 'MASTER': {}, 'PEERS': []}})
NETWORK.update({section: {'LOCAL': {}, 'MASTER': {}, 'PEERS': {}}})
NETWORK[section]['LOCAL'].update({
'MODE': '',
'PEER_OPER': True,
@ -158,7 +159,7 @@ except:
# Convert a hex string to an int (radio ID, etc.)
#
def int_id(_hex_string):
return int(binascii.b2a_hex(_hex_string), 16)
return int(h(_hex_string), 16)
# Re-Write Source Radio-ID (DMR NAT)
#
@ -197,7 +198,7 @@ def send_to_ipsc(_target, _packet):
# Send to the Master
networks[_target].transport.write(_packet, (NETWORK[_target]['MASTER']['IP'], NETWORK[_target]['MASTER']['PORT']))
# Send to each connected Peer
for peer in NETWORK[_target]['PEERS']:
for peer in self._peers.keys():
if peer['STATUS']['CONNECTED'] == True:
networks[_target].transport.write(_packet, (peer['IP'], peer['PORT']))
@ -206,23 +207,21 @@ def send_to_ipsc(_target, _packet):
#
def de_register_peer(_network, _peerid):
# Iterate for the peer in our data
for peer in NETWORK[_network]['PEERS']:
# If we find the peer, remove it (we should find it)
if _peerid == peer['RADIO_ID']:
NETWORK[_network]['PEERS'].remove(peer)
logger.info('(%s) Peer De-Registration Requested for: %s', _network, binascii.b2a_hex(_peerid))
return
else:
logger.warning('(%s) Peer De-Registration Requested for: %s, but we don\'t have a listing for this peer', _network, binascii.b2a_hex(_peerid))
pass
if _peerid in self._peers.keys():
del self._peers[_peerid]
logger.info('(%s) Peer De-Registration Requested for: %s', _network, h(_peerid))
return
else:
logger.warning('(%s) Peer De-Registration Requested for: %s, but we don\'t have a listing for this peer', _network, h(_peerid))
pass
# Take a recieved peer list and the network it belongs to, process and populate the
# data structure in my_ipsc_config with the results, and return a simple list of peers.
#
def process_peer_list(_data, _network, _peer_list):
def process_peer_list(_data, _network):
# Determine the length of the peer list for the parsing iterator
_peer_list_length = int(binascii.b2a_hex(_data[5:7]), 16)
_peer_list_length = int(h(_data[5:7]), 16)
# Record the number of peers in the data structure... we'll use it later (11 bytes per peer entry)
NETWORK[_network]['LOCAL']['NUM_PEERS'] = _peer_list_length/11
logger.info('(%s) Peer List Received from Master: %s peers in this IPSC', _network, _peer_list_length/11)
@ -234,9 +233,9 @@ def process_peer_list(_data, _network, _peer_list):
_hex_address = (_data[i+4:i+8])
_ip_address = socket.inet_ntoa(_hex_address)
_hex_port = (_data[i+8:i+10])
_port = int(binascii.b2a_hex(_hex_port), 16)
_port = int(h(_hex_port), 16)
_hex_mode = (_data[i+10:i+11])
_mode = int(binascii.b2a_hex(_hex_mode), 16)
_mode = int(h(_hex_mode), 16)
# mask individual Mode parameters
_link_op = _mode & PEER_OP_MSK
_link_mode = _mode & PEER_MODE_MSK
@ -275,10 +274,8 @@ def process_peer_list(_data, _network, _peer_list):
# Note: We keep a "simple" peer list in addition to the large data
# structure because soemtimes, we just need to identify a
# peer quickly.
if _hex_radio_id not in _peer_list:
_peer_list.append(_hex_radio_id)
NETWORK[_network]['PEERS'].append({
'RADIO_ID': _hex_radio_id,
if _hex_radio_id not in NETWORK[_network]['PEERS'].keys():
NETWORK[_network]['PEERS'][_hex_radio_id] = {
'IP': _ip_address,
'PORT': _port,
'MODE': _hex_mode,
@ -286,15 +283,20 @@ def process_peer_list(_data, _network, _peer_list):
'PEER_MODE': _peer_mode,
'TS1_LINK': _ts1,
'TS2_LINK': _ts2,
'STATUS': {'CONNECTED': False, 'KEEP_ALIVES_SENT': 0, 'KEEP_ALIVES_MISSED': 0, 'KEEP_ALIVES_OUTSTANDING': 0}
})
return _peer_list
'STATUS': {
'CONNECTED': False,
'KEEP_ALIVES_SENT': 0,
'KEEP_ALIVES_MISSED': 0,
'KEEP_ALIVES_OUTSTANDING': 0
}
}
# Gratuituous print-out of the peer list.. Pretty much debug stuff.
#
def print_peer_list(_network):
# _log = logger.info
_peers = NETWORK[_network]['PEERS']
_status = NETWORK[_network]['MASTER']['STATUS']['PEER_LIST']
#print('Peer List Status for {}: {}' .format(_network, _status))
@ -302,26 +304,30 @@ def print_peer_list(_network):
print('We are the only peer for: %s' % _network)
print('')
return
print('Peer List for: %s' % _network)
for dictionary in NETWORK[_network]['PEERS']:
if dictionary['RADIO_ID'] == NETWORK[_network]['LOCAL']['RADIO_ID']:
for peer in _peers.keys():
_this_peer = _peers[peer]
_this_peer_stat = _this_peer['STATUS']
if peer == NETWORK[_network]['LOCAL']['RADIO_ID']:
me = '(self)'
else:
me = ''
print('\tRADIO ID: {} {}' .format(int(binascii.b2a_hex(dictionary['RADIO_ID']), 16), me))
print('\t\tIP Address: {}:{}' .format(dictionary['IP'], dictionary['PORT']))
print('\t\tOperational: {}, Mode: {}, TS1 Link: {}, TS2 Link: {}' .format(dictionary['PEER_OPER'], dictionary['PEER_MODE'], dictionary['TS1_LINK'], dictionary['TS2_LINK']))
print('\t\tStatus: {}, KeepAlives Sent: {}, KeepAlives Outstanding: {}, KeepAlives Missed: {}' .format(dictionary['STATUS']['CONNECTED'], dictionary['STATUS']['KEEP_ALIVES_SENT'], dictionary['STATUS']['KEEP_ALIVES_OUTSTANDING'], dictionary['STATUS']['KEEP_ALIVES_MISSED']))
print('\tRADIO ID: {} {}' .format(int(h(peer), 16), me))
print('\t\tIP Address: {}:{}' .format(_this_peer['IP'], _this_peer['PORT']))
print('\t\tOperational: {}, Mode: {}, TS1 Link: {}, TS2 Link: {}' .format(_this_peer['PEER_OPER'], _this_peer['PEER_MODE'], _this_peer['TS1_LINK'], _this_peer['TS2_LINK']))
print('\t\tStatus: {}, KeepAlives Sent: {}, KeepAlives Outstanding: {}, KeepAlives Missed: {}' .format(_this_peer_stat['CONNECTED'], _this_peer_stat['KEEP_ALIVES_SENT'], _this_peer_stat['KEEP_ALIVES_OUTSTANDING'], _this_peer_stat['KEEP_ALIVES_MISSED']))
print('')
# Gratuituous print-out of Master info.. Pretty much debug stuff.
#
def print_master(_network):
# _log = logger.info
_master = NETWORK[_network]['MASTER']
print('Master for %s' % _network)
print('\tRADIO ID: {}' .format(int(binascii.b2a_hex(_master['RADIO_ID']), 16)))
print('\tRADIO ID: {}' .format(int(h(_master['RADIO_ID']), 16)))
print('\t\tIP Address: {}:{}' .format(_master['IP'], _master['PORT']))
print('\t\tOperational: {}, Mode: {}, TS1 Link: {}, TS2 Link: {}' .format(_master['PEER_OPER'], _master['PEER_MODE'], _master['TS1_LINK'], _master['TS2_LINK']))
print('\t\tStatus: {}, KeepAlives Sent: {}, KeepAlives Outstanding: {}, KeepAlives Missed: {}' .format(_master['STATUS']['CONNECTED'], _master['STATUS']['KEEP_ALIVES_SENT'], _master['STATUS']['KEEP_ALIVES_OUTSTANDING'], _master['STATUS']['KEEP_ALIVES_MISSED']))
@ -367,7 +373,6 @@ class IPSC(DatagramProtocol):
# Spendy than iterating a list of dictionaries... Maybe I'll find a better way in the future. Also
# We have to know when we have a new peer list, so a variable to indicate we do (or don't)
#
self._peer_list = []
args = ()
@ -446,10 +451,10 @@ class IPSC(DatagramProtocol):
def unknown_message(self, _network, _packettype, _peerid, _data):
_time = time.strftime('%m/%d/%y %H:%M:%S')
_packettype = binascii.b2a_hex(_packettype)
_packettype = h(_packettype)
_peerid = get_info(int_id(_peerid), peer_ids)
print('{} ({}) Unknown message type encountered\n\tPacket Type: {}\n\tFrom: {}' .format(_time, _network, _packettype, _peerid))
print('\t', binascii.b2a_hex(_data))
print('\t', h(_data))
# Take a packet to be SENT, calcualte auth hash and return the whole thing
@ -534,31 +539,34 @@ class IPSC(DatagramProtocol):
#
if (self._master_stat['PEER_LIST'] == True):
# Iterate the list of peers... so we do this for each one.
for peer in (self._peers):
for peer_id in self._peers.keys():
peer = self._peers[peer_id]
# We will show up in the peer list, but shouldn't try to talk to ourselves.
if (peer['RADIO_ID'] == self._local_id):
if peer_id == self._local_id:
continue
# If we haven't registered to a peer, send a registration
if peer['STATUS']['CONNECTED'] == False:
peer_reg_packet = self.hashed_packet(self._local['AUTH_KEY'], self.PEER_REG_REQ_PKT)
self.transport.write(peer_reg_packet, (peer['IP'], peer['PORT']))
logger.info('(%s) Registering with Peer %s', self._network, int_id(peer['RADIO_ID']))
logger.info('(%s) Registering with Peer %s', self._network, int_id(peer_id))
# If we have registered with the peer, then send a keep-alive
elif peer['STATUS']['CONNECTED'] == True:
peer_alive_req_packet = self.hashed_packet(self._local['AUTH_KEY'], self.PEER_ALIVE_REQ_PKT)
self.transport.write(peer_alive_req_packet, (peer['IP'], peer['PORT']))
# If we have a keep-alive outstanding by the time we send another, mark it missed.
if peer['STATUS']['KEEP_ALIVES_OUTSTANDING'] > 0:
peer['STATUS']['KEEP_ALIVES_MISSED'] += 1
logger.info('(%s) Peer Keep-Alive Missed for %s', self._network, int_id(peer['RADIO_ID']))
logger.info('(%s) Peer Keep-Alive Missed for %s', self._network, int_id(peer_id))
# If we have missed too many keep-alives, de-register the peer and start over.
if peer['STATUS']['KEEP_ALIVES_OUTSTANDING'] >= self._local['MAX_MISSED']:
peer['STATUS']['CONNECTED'] = False
self._peer_list.remove(peer['RADIO_ID']) # Remove the peer from the simple list FIRST
self._peers.remove(peer) # Becuase once it's out of the dictionary, you can't use it for anything else.
logger.warning('(%s) Maximum Peer Keep-Alives Missed -- De-registering the Peer: %s', self._network, int_id(peer['RADIO_ID']))
del peer # Becuase once it's out of the dictionary, you can't use it for anything else.
logger.warning('(%s) Maximum Peer Keep-Alives Missed -- De-registering the Peer: %s', self._network, int_id(peer_id))
# Update our stats before moving on...
peer['STATUS']['KEEP_ALIVES_SENT'] += 1
@ -596,7 +604,7 @@ class IPSC(DatagramProtocol):
# Authenticate the packet
if self.validate_auth(self._local['AUTH_KEY'], data) == False:
logger.warning('(%s) AuthError: IPSC packet failed authentication. Type %s: Peer ID: %s', self._network, binascii.b2a_hex(_packettype), int(binascii.b2a_hex(_peerid), 16))
logger.warning('(%s) AuthError: IPSC packet failed authentication. Type %s: Peer ID: %s', self._network, h(_packettype), int(h(_peerid), 16))
return
# Strip the hash, we won't need it anymore
@ -604,8 +612,8 @@ class IPSC(DatagramProtocol):
# Packets types that must be originated from a peer (including master peer)
if (_packettype in ANY_PEER_REQUIRED):
if not(valid_master(self._network, _peerid) == False or valid_peer(self._peer_list, _peerid) == False):
logger.warning('(%s) PeerError: Peer not in peer-list: %s', self._network, int(binascii.b2a_hex(_peerid), 16))
if not(valid_master(self._network, _peerid) == False or valid_peer(self._peers.keys(), _peerid) == False):
logger.warning('(%s) PeerError: Peer not in peer-list: %s', self._network, int(h(_peerid), 16))
return
# User, as in "subscriber" generated packets - a.k.a someone trasmitted
@ -619,22 +627,22 @@ class IPSC(DatagramProtocol):
# User Voice and Data Call Types:
if (_packettype == GROUP_VOICE):
self._notify_event(self._network, 'group_voice', {'peer_id': int(binascii.b2a_hex(_peerid), 16)})
self._notify_event(self._network, 'group_voice', {'peer_id': int(h(_peerid), 16)})
self.group_voice(self._network, _src_sub, _dst_sub, _ts, _end, _peerid, data)
return
elif (_packettype == PVT_VOICE):
self._notify_event(self._network, 'private_voice', {'peer_id': int(binascii.b2a_hex(_peerid), 16)})
self._notify_event(self._network, 'private_voice', {'peer_id': int(h(_peerid), 16)})
self.private_voice(self._network, _src_sub, _dst_sub, _ts, _end, _peerid, data)
return
elif (_packettype == GROUP_DATA):
self._notify_event(self._network, 'group_data', {'peer_id': int(binascii.b2a_hex(_peerid), 16)})
self._notify_event(self._network, 'group_data', {'peer_id': int(h(_peerid), 16)})
self.group_data(self._network, _src_sub, _dst_sub, _ts, _end, _peerid, data)
return
elif (_packettype == PVT_DATA):
self._notify_event(self._network, 'private_voice', {'peer_id': int(binascii.b2a_hex(_peerid), 16)})
self._notify_event(self._network, 'private_voice', {'peer_id': int(h(_peerid), 16)})
self.private_data(self._network, _src_sub, _dst_sub, _ts, _end, _peerid, data)
return
return
@ -674,8 +682,8 @@ class IPSC(DatagramProtocol):
# Packets types that must be originated from a peer
if (_packettype in PEER_REQUIRED):
if valid_peer(self._peer_list, _peerid) == False:
logger.warning('(%s) PeerError: Peer %s not in peer-list: %s', self._network, int(binascii.b2a_hex(_peerid), 16), self._peer_list)
if valid_peer(self._peers.keys(), _peerid) == False:
logger.warning('(%s) PeerError: Peer %s not in peer-list: %s', self._network, int(h(_peerid), 16), self._peers.keys())
return
# Packets we send...
@ -692,15 +700,13 @@ class IPSC(DatagramProtocol):
# Packets we receive...
elif (_packettype == PEER_ALIVE_REPLY):
for peer in self._config['PEERS']:
if peer['RADIO_ID'] == _peerid:
peer['STATUS']['KEEP_ALIVES_OUTSTANDING'] = 0
if _peerid in self._peers.keys():
self._peers[_peerid]['STATUS']['KEEP_ALIVES_OUTSTANDING'] = 0
return
elif (_packettype == PEER_REG_REPLY):
for peer in self._config['PEERS']:
if peer['RADIO_ID'] == _peerid:
peer['STATUS']['CONNECTED'] = True
if _peerid in self._peers.keys():
self._peers[_peerid]['STATUS']['CONNECTED'] = True
return
return
@ -709,7 +715,7 @@ class IPSC(DatagramProtocol):
# Packets we receive...
if (_packettype in MASTER_REQUIRED):
if valid_master(self._network, _peerid) == False:
logger.warning('(%s) MasterError: %s is not the master peer', self._network, int(binascii.b2a_hex(_peerid), 16))
logger.warning('(%s) MasterError: %s is not the master peer', self._network, int(h(_peerid), 16))
return
if (_packettype == MASTER_ALIVE_REPLY):
@ -720,7 +726,7 @@ class IPSC(DatagramProtocol):
elif (_packettype == PEER_LIST_REPLY):
NETWORK[self._network]['MASTER']['STATUS']['PEER_LIST'] = True
if len(data) > 18:
self._peer_list = process_peer_list(data, self._network, self._peer_list)
process_peer_list(data, self._network)
return
return