Work on Master Support

This commit is contained in:
Cort Buffington 2014-05-14 14:18:33 -05:00
parent 9accbafe68
commit 134a34f22e

View File

@ -547,6 +547,10 @@ def print_master(_network):
class IPSC(DatagramProtocol): class IPSC(DatagramProtocol):
#************************************************
# IPSC INSTANCE INSTANTIATION
#************************************************
# Modify the initializer to set up our environment and build the packets # Modify the initializer to set up our environment and build the packets
# we need to maintain connections # we need to maintain connections
# #
@ -613,22 +617,6 @@ class IPSC(DatagramProtocol):
self.validate_auth = self.unauth_validate_auth self.validate_auth = self.unauth_validate_auth
# This is called by REACTOR when it starts, We use it to set up the timed
# loop for each instance of the IPSC engine
#
def startProtocol(self):
# Timed loops for:
# IPSC connection establishment and maintenance
# Reporting/Housekeeping
#
if not self._local['MASTER_PEER']:
self._maintenance = task.LoopingCall(self.maintenance_loop)
self._maintenance_loop = self._maintenance.start(self._local['ALIVE_TIMER'])
#
self._reporting = task.LoopingCall(self.reporting_loop)
self._reporting_loop = self._reporting.start(10)
#************************************************ #************************************************
# CALLBACK FUNCTIONS FOR USER PACKET TYPES # CALLBACK FUNCTIONS FOR USER PACKET TYPES
#************************************************ #************************************************
@ -664,6 +652,11 @@ class IPSC(DatagramProtocol):
_packettype = h(_packettype) _packettype = h(_packettype)
logger.error('(%s) Unknown message type encountered\n\tPacket Type: %s\n\tFrom: %s\n\tPacket: %s', _network, _packettype, _peerid, h(_data)) logger.error('(%s) Unknown message type encountered\n\tPacket Type: %s\n\tFrom: %s\n\tPacket: %s', _network, _packettype, _peerid, h(_data))
#************************************************
# IPSC SPECIFIC MAINTENANCE FUNCTIONS
#************************************************
# Reset the outstanding keep-alive counter for _peerid... # Reset the outstanding keep-alive counter for _peerid...
# Used when receiving acks OR when we see traffic from a repeater, since they ignore keep-alives when transmitting # Used when receiving acks OR when we see traffic from a repeater, since they ignore keep-alives when transmitting
# #
@ -720,10 +713,27 @@ class IPSC(DatagramProtocol):
return True return True
#************************************************ #************************************************
# TIMED LOOP - MY CONNECTION MAINTENANCE # TIMED LOOP - CONNECTION MAINTENANCE
#************************************************ #************************************************
# Timed loop initialization (called by the twisted reactor)
#
def startProtocol(self):
# Timed loops for:
# IPSC connection establishment and maintenance
# Reporting/Housekeeping
#
if not self._local['MASTER_PEER']:
self._maintenance = task.LoopingCall(self.maintenance_loop)
self._maintenance_loop = self._maintenance.start(self._local['ALIVE_TIMER'])
#
self._reporting = task.LoopingCall(self.reporting_loop)
self._reporting_loop = self._reporting.start(10)
# Timed loop used for reporting IPSC status
#
def reporting_loop(self): def reporting_loop(self):
# Right now, without this, we really don't know anything is happening. # Right now, without this, we really don't know anything is happening.
logger.debug('(%s) Periodic Reporting Loop Started', self._network) logger.debug('(%s) Periodic Reporting Loop Started', self._network)
@ -731,6 +741,8 @@ class IPSC(DatagramProtocol):
print_master(self._network) print_master(self._network)
print_peer_list(self._network) print_peer_list(self._network)
# Timed loop used for IPSC connection Maintenance if we are a PEER
#
def maintenance_loop(self): def maintenance_loop(self):
logger.debug('(%s) Periodic Connection Maintenance Loop Started', self._network) logger.debug('(%s) Periodic Connection Maintenance Loop Started', self._network)
@ -773,9 +785,13 @@ class IPSC(DatagramProtocol):
# #
if (self._master_stat['CONNECTED'] == True) and (self._master_stat['PEER_LIST'] == False): if (self._master_stat['CONNECTED'] == True) and (self._master_stat['PEER_LIST'] == False):
# Ask the master for a peer-list # Ask the master for a peer-list
if self._local['NUM_PEERS']:
peer_list_req_packet = self.hashed_packet(self._local['AUTH_KEY'], self.PEER_LIST_REQ_PKT) peer_list_req_packet = self.hashed_packet(self._local['AUTH_KEY'], self.PEER_LIST_REQ_PKT)
self.transport.write(peer_list_req_packet, self._master_sock) self.transport.write(peer_list_req_packet, self._master_sock)
logger.info('(%s), No Peer List - Requesting One From the Master', self._network) logger.info('(%s), No Peer List - Requesting One From the Master', self._network)
else:
self._master_stat['PEER_LIST'] = True
logger.debug('(%s), Skip asking for a Peer List, we are the only Peer', self._network)
# If we do have a peer-list, we need to register with the peers and send keep-alives... # If we do have a peer-list, we need to register with the peers and send keep-alives...
@ -831,9 +847,10 @@ class IPSC(DatagramProtocol):
pass pass
#************************************************
# RECEIVED DATAGRAM - ACT IMMEDIATELY!!! #************************************************
#************************************************ # MESSAGE RECEIVED - TAKE ACTION
#************************************************
# Actions for received packets by type: For every packet received, there are some things that we need to do: # Actions for received packets by type: For every packet received, there are some things that we need to do:
# Decode some of the info # Decode some of the info
@ -842,25 +859,27 @@ class IPSC(DatagramProtocol):
# #
# Once they're done, we move on to the processing or callbacks for each packet type. # Once they're done, we move on to the processing or callbacks for each packet type.
# #
# Callbacks are iterated in the order of "more likely" to "less likely" to reduce processing time
#
def datagramReceived(self, data, (host, port)): def datagramReceived(self, data, (host, port)):
_packettype = data[0:1] _packettype = data[0:1]
_peerid = data[1:5] _peerid = data[1:5]
# Authenticate the packet # AUTHENTICATE THE PACKET
if not self.validate_auth(self._local['AUTH_KEY'], data): if not self.validate_auth(self._local['AUTH_KEY'], data):
logger.warning('(%s) AuthError: IPSC packet failed authentication. Type %s: Peer ID: %s', self._network, h(_packettype), int_id(_peerid)) logger.warning('(%s) AuthError: IPSC packet failed authentication. Type %s: Peer ID: %s', self._network, h(_packettype), int_id(_peerid))
return return
# Strip the hash, we won't need it anymore # REMOVE SHA-1 AUTHENTICATION HASH: WE NO LONGER NEED IT
data = self.strip_hash(data) data = self.strip_hash(data)
# Packets types that must be originated from a peer (including master peer) # PACKETS THAT WE RECEIVE FROM ANY VALID PEER OR VALID MASTER
if _packettype in ANY_PEER_REQUIRED: if _packettype in ANY_PEER_REQUIRED:
if not(valid_master(self._network, _peerid) == False or valid_peer(self._peers.keys(), _peerid) == False): 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_id(_peerid)) logger.warning('(%s) PeerError: Peer not in peer-list: %s', self._network, int_id(_peerid))
return return
# User, as in "subscriber" generated packets - a.k.a someone transmitted # ORIGINATED BY SUBSCRIBER UNITS - a.k.a someone transmitted
if _packettype in USER_PACKETS: if _packettype in USER_PACKETS:
# Extract commonly used items from the packet header # Extract commonly used items from the packet header
_src_sub = data[6:9] _src_sub = data[6:9]
@ -895,11 +914,12 @@ class IPSC(DatagramProtocol):
return return
return return
# Other peer-required types that we don't do much or anything with yet # MOTOROLA XCMP/XNL CONTROL PROTOCOL: We don't process these (yet)
elif _packettype == XCMP_XNL: elif _packettype == XCMP_XNL:
self.xcmp_xnl(self._network, data) self.xcmp_xnl(self._network, data)
return return
# ORIGINATED BY PEERS, NOT IPSC MAINTENANCE: Call monitoring is all we've found here so far
elif _packettype == CALL_MON_ORIGIN: elif _packettype == CALL_MON_ORIGIN:
self.call_mon_origin(self._network, data) self.call_mon_origin(self._network, data)
return return
@ -912,7 +932,7 @@ class IPSC(DatagramProtocol):
self.call_mon_nack(self._network, data) self.call_mon_nack(self._network, data)
return return
# Connection maintenance packets that fall into this category # IPSC CONNECTION MAINTENANCE MESSAGES
elif _packettype == DE_REG_REQ: elif _packettype == DE_REG_REQ:
de_register_peer(self._network, _peerid) de_register_peer(self._network, _peerid)
logger.warning('(%s) Peer De-Registration Request From: %s', self._network, int_id(_peerid)) logger.warning('(%s) Peer De-Registration Request From: %s', self._network, int_id(_peerid))
@ -928,14 +948,17 @@ class IPSC(DatagramProtocol):
return return
return return
#
# THE FOLLOWING PACKETS ARE RECEIVED ONLY IF WE ARE OPERATING AS A PEER
#
# Packets types that must be originated from a peer # ONLY ACCEPT FROM A PREVIOUSLY VALIDATED PEER
if _packettype in PEER_REQUIRED: if _packettype in PEER_REQUIRED:
if not valid_peer(self._peers.keys(), _peerid): if not valid_peer(self._peers.keys(), _peerid):
logger.warning('(%s) PeerError: Peer %s not in peer-list', self._network, int_id(_peerid)) logger.warning('(%s) PeerError: Peer %s not in peer-list', self._network, int_id(_peerid))
return return
# Packets we send... # REQUESTS FROM PEERS: WE MUST REPLY IMMEDIATELY FOR IPSC MAINTENANCE
if _packettype == PEER_ALIVE_REQ: if _packettype == PEER_ALIVE_REQ:
_hex_mode = (data[5]) _hex_mode = (data[5])
_hex_flags = (data[6:10]) _hex_flags = (data[6:10])
@ -959,7 +982,7 @@ class IPSC(DatagramProtocol):
logger.info('(%s) Peer Registration Request From: %s', self._network, int_id(_peerid)) logger.info('(%s) Peer Registration Request From: %s', self._network, int_id(_peerid))
return return
# Packets we receive... # ANSWERS FROM REQUESTS WE SENT TO PEERS: WE DO NOT REPLY
elif _packettype == PEER_ALIVE_REPLY: elif _packettype == PEER_ALIVE_REPLY:
self.reset_keep_alive(_peerid) self.reset_keep_alive(_peerid)
logger.debug('(%s) Keep-Alive Reply (we sent the request) Received from Peer %s', self._network, int_id(_peerid)) logger.debug('(%s) Keep-Alive Reply (we sent the request) Received from Peer %s', self._network, int_id(_peerid))
@ -973,13 +996,15 @@ class IPSC(DatagramProtocol):
return return
# Packets types that must be originated from a Master # PACKETS ONLY ACCEPTED FROM OUR MASTER
# Packets we receive...
# PACKETS WE ONLY ACCEPT IF WE HAVE FINISHED REGISTERING WITH OUR MASTER
if _packettype in MASTER_REQUIRED: if _packettype in MASTER_REQUIRED:
if not valid_master(self._network, _peerid): if not valid_master(self._network, _peerid):
logger.warning('(%s) MasterError: %s is not the master peer', self._network, int_id(_peerid)) logger.warning('(%s) MasterError: %s is not the master peer', self._network, int_id(_peerid))
return return
# ANSWERS FROM REQUESTS WE SENT TO THE MASTER: WE DO NOT REPLY
if _packettype == MASTER_ALIVE_REPLY: if _packettype == MASTER_ALIVE_REPLY:
self.reset_keep_alive(_peerid) self.reset_keep_alive(_peerid)
logger.debug('(%s) Keep-Alive Reply (we sent the request) Received from the Master %s', self._network, int_id(_peerid)) logger.debug('(%s) Keep-Alive Reply (we sent the request) Received from the Master %s', self._network, int_id(_peerid))
@ -994,7 +1019,7 @@ class IPSC(DatagramProtocol):
return return
# When we hear from the master, record it's ID, flag that we're connected, and reset the dead counter. # THIS MEANS WE HAVE SUCCESSFULLY REGISTERED TO OUR MASTER - RECORD MASTER INFORMATION
elif _packettype == MASTER_REG_REPLY: elif _packettype == MASTER_REG_REPLY:
_hex_mode = data[5] _hex_mode = data[5]
@ -1014,42 +1039,62 @@ class IPSC(DatagramProtocol):
logger.warning('(%s) Registration response (we requested reg) from the Master %s (%s peers)', self._network, int_id(_peerid), self._local['NUM_PEERS']) logger.warning('(%s) Registration response (we requested reg) from the Master %s (%s peers)', self._network, int_id(_peerid), self._local['NUM_PEERS'])
return return
# This is if we operate as a master... work in progress
# THE FOLLOWING PACKETS ARE RECEIVED ONLLY IF WE ARE OPERATING AS A MASTER
# REQUESTS FROM PEERS: WE MUST REPLY IMMEDIATELY FOR IPSC MAINTENANCE
# REQUEST TO REGISTER TO THE IPSC
elif _packettype == MASTER_REG_REQ: elif _packettype == MASTER_REG_REQ:
_ip_address = host
_port = port
_hex_mode = data[5] _hex_mode = data[5]
_hex_flags = data[6:10] _hex_flags = data[6:10]
_decoded_mode = process_mode_byte(_hex_mode) _decoded_mode = process_mode_byte(_hex_mode)
_decoded_flags = process_flags_bytes(_hex_flags) _decoded_flags = process_flags_bytes(_hex_flags)
''' BELOW STOLEN FROM MASTER_REG_REPLY - MUST PARSE TO SEE IF THE PEER EXISTS AND IF NOT, THEN
WE NEED TO ADD IT TO THE PEER LIST WE KEEP IN OUR DATA STRUCTURE... WORK STARTS HERE
self._master['RADIO_ID'] = _peerid
self._master['MODE'] = _hex_mode
self._master['MODE_DECODE'] = _decoded_mode
self._master['FLAGS'] = _hex_flags
self._master['FLAGS_DECODE'] = _decoded_flags
self._master_stat['CONNECTED'] = True
self._master_stat['KEEP_ALIVES_OUTSTANDING'] = 0
'''
master_reg_reply_packet = self.hashed_packet(self._local['AUTH_KEY'], self.MASTER_REG_REPLY_PKT) master_reg_reply_packet = self.hashed_packet(self._local['AUTH_KEY'], self.MASTER_REG_REPLY_PKT)
self.transport.write(master_reg_reply_packet, (host, port)) self.transport.write(master_reg_reply_packet, (host, port))
logger.debug('(%s) Master Registration Packet Received from peer %s', self._network, int_id(_peerid)) logger.debug('(%s) Master Registration Packet Received from peer %s', self._network, int_id(_peerid))
# If this entry was NOT already in our list, add it.
if _peerid not in self._peers.keys():
self._peers[_peerid] = {
'IP': _ip_address,
'PORT': _port,
'MODE': _hex_mode,
'MODE_DECODE': _decoded_mode,
'FLAGS': _hex_flags,
'FLAGS_DECODE': _decoded_flags,
'STATUS': {
'CONNECTED': True,
'KEEP_ALIVES_SENT': 0,
'KEEP_ALIVES_MISSED': 0,
'KEEP_ALIVES_OUTSTANDING': 0
}
}
self._local['NUM_PEERS'] = len(self.peers)
logger.debug('(%s) Peer Added To Peer List: %s (IPSC now has %s Peers)', _network, self._peers[_peerid], self._local['NUM_PEERS'])
return return
# REQUEST FOR A KEEP-ALIVE REPLY (WE KNOW THE PEER IS STILL ALIVE TOO)
elif _packettype == MASTER_ALIVE_REQ: elif _packettype == MASTER_ALIVE_REQ:
self._peers[_peerid]['STATUS']['KEEP_ALIVES_SENT'] += 1
master_alive_reply_packet = self.hashed_packet(self._local['AUTH_KEY'], self.MASTER_ALIVE_REPLY_PKT) master_alive_reply_packet = self.hashed_packet(self._local['AUTH_KEY'], self.MASTER_ALIVE_REPLY_PKT)
self.transport.write(master_alive_reply_packet, (host, port)) self.transport.write(master_alive_reply_packet, (host, port))
logger.debug('(%s) Master Keep-Alive Request Received from peer %s', self._network, int_id(_peerid)) logger.debug('(%s) Master Keep-Alive Request Received from peer %s', self._network, int_id(_peerid))
return return
# REQUEST FOR A PEER LIST
elif _packettype == PEER_LIST_REQ: elif _packettype == PEER_LIST_REQ:
logger.debug('(%s) Peer List Received from peer %s', self._network, int_id(_peerid)) logger.debug('(%s) Peer List Received from peer %s', self._network, int_id(_peerid))
return return
# If there's a packet type we don't know about, it should be logged so we can figure it out and take an appropriate action!
# PACKET IS OF AN UNKNOWN TYPE. LOG IT AND IDENTTIFY IT!
else: else:
self.unknown_message(self._network, _packettype, _peerid, data) self.unknown_message(self._network, _packettype, _peerid, data)
return return