Lots 'O Comments, work on open issues

Work done on issues of timed actions for us to initiate relationships
with IPSC peers.

Tons of comments added so it likely actually makes sense to a reader
now.
This commit is contained in:
Cort Buffington 2013-07-20 08:28:52 -05:00
parent 30afe9041c
commit a99fa58a50
2 changed files with 158 additions and 109 deletions

237
ipsc.py
View File

@ -17,24 +17,28 @@ import socket
#************************************************
# Import system logger configuration
#
try:
from ipsc_logger import logger
except ImportError:
sys.exit('System logger configuraiton not found or invalid')
# Import configuration and informational data structures
#
try:
from my_ipsc_config import NETWORK
except ImportError:
sys.exit('Configuration file not found, or not valid formatting')
# Import IPSC message types and version information
#
try:
from ipsc_message_types import *
except ImportError:
sys.exit('IPSC message types file not found or invalid')
# Import IPSC flag mask values
#
try:
from ipsc_mask import *
except ImportError:
@ -46,13 +50,22 @@ except ImportError:
# GLOBALLY SCOPED FUNCTIONS
#************************************************
# Take a packet to be SENT, calcualte auth hash and return the whole thing
#
def hashed_packet(_key, _data):
hash = binascii.unhexlify((hmac.new(_key,_data,hashlib.sha1)).hexdigest()[:20])
return (_data + hash)
# Take a RECEIVED packet, calculate the auth hash and verify authenticity
#
def validate_auth(_key, _data):
return
# Decide the Mode bit flags and print them - later, use this for more
# than just informational purposes, for now, it's FYI/Debug info.
#
def print_mode_decode(_mode):
_mode = int(binascii.b2a_hex(_mode), 16)
link_op = _mode & PEER_OP_MSK
@ -81,64 +94,42 @@ def print_mode_decode(_mode):
if ts2 == 0b00000010:
logger.info('\t\tIPSC Enabled on TS2')
def mode_decode(_mode):
_mode = int(binascii.b2a_hex(_mode), 16)
link_op = _mode & PEER_OP_MSK
link_mode = _mode & PEER_MODE_MSK
ts1 = _mode & IPSC_TS1_MSK
ts2 = _mode & IPSC_TS2_MSK
if link_op == 0b01000000:
logger.info('\t\tPeer Operational')
elif link_op == 0b00000000:
logger.info('\t\tPeer Not Operational')
else:
logger.warning('\t\tPeer Mode Invalid')
if link_mode == 0b00000000:
logger.info('\t\tNo RF Interface')
elif link_mode == 0b00010000:
logger.info('\t\tRadio in Analog Mode')
elif link_mode == 0b00100000:
logger.info('\t\tRadio in Digital Mode')
else:
logger.warning('\t\tRadio Mode Invalid')
if ts1 == 0b00001000:
logger.info('\t\tIPSC Enabled on TS1')
if ts2 == 0b00000010:
logger.info('\t\tIPSC Enabled on TS2')
def print_peer_list(_ipsc_network):
logger.info('\t%s', _ipsc_network['LOCAL']['DESCRIPTION'])
for dictionary in _ipsc_network['PEERS']:
address = dictionary['IP']
port = dictionary['PORT']
radio_id = int(binascii.b2a_hex(dictionary['RADIO_ID']), 16)
mode = dictionary['RAW_MODE']
int_connected = dictionary['STATUS']['CONNECTED']
int_missed = dictionary['STATUS']['KEEP_ALIVES_MISSED']
logger.info('\tIP Address: %s:%s', address, port)
logger.info('\tRADIO ID: %s ', radio_id)
# Gratuituous print-out of the peer list.. Pretty much debug stuff.
#
def print_peer_list(_network_name):
logger.info('\t%s', _network_name)
for dictionary in NETWORK[_network_name]['PEERS']:
logger.info('\tIP Address: %s:%s', dictionary['IP'], dictionary['PORT'])
logger.info('\tRADIO ID: %s ', int(binascii.b2a_hex(dictionary['RADIO_ID']), 16))
logger.info("\tIPSC Mode:")
print_mode_decode(mode)
logger.info('\tConnection Status: %s', int_connected)
logger.info('\tKeepAlives Missed: %s', int_missed)
print_mode_decode(dictionary['RAW_MODE'])
logger.info('\tConnection Status: %s', dictionary['STATUS']['CONNECTED'])
logger.info('\tKeepAlives Missed: %s', dictionary['STATUS']['KEEP_ALIVES_MISSED'])
logger.info("")
#************************************************
# IPSC Network Engine
#******** ***********
#******** IPSC Network "Engine" ***********
#******** ***********
#************************************************
#************************************************
# INITIAL SETUP of IPSC INSTANCE
#************************************************
class IPSC(DatagramProtocol):
# Modify the initializer to set up our environment and build the packets
# we need to maitain connections
#
def __init__(self, *args, **kwargs):
if len(args) == 1:
self._config = args[0]
self._network_name = args[0]
self._config = NETWORK[self._network_name]
args = ()
self.TS_FLAGS = (self._config['LOCAL']['MODE'] + self._config['LOCAL']['FLAGS'])
self.MASTER_REG_REQ_PKT = (MASTER_REG_REQ + self._config['LOCAL']['RADIO_ID'] + self.TS_FLAGS + IPSC_VER)
@ -147,58 +138,37 @@ class IPSC(DatagramProtocol):
self.PEER_REG_REQ_PKT = (PEER_REG_REQ + self._config['LOCAL']['RADIO_ID'] + IPSC_VER)
self.PEER_REG_REPLY_PKT = (PEER_REG_REPLY + self._config['LOCAL']['RADIO_ID'] + IPSC_VER)
self.PEER_ALIVE_REQ_PKT = (PEER_ALIVE_REQ + self._config['LOCAL']['RADIO_ID'] + self.TS_FLAGS)
self.PEER_ALIVE_REPLY_PKT = (PEER_ALIVE_REPLY + self._config['LOCAL']['RADIO_ID'] + self.TS_FLAGS)
self.PEER_ALIVE_REPLY_PKT = (PEER_ALIVE_REPLY + self._config['LOCAL']['RADIO_ID'] + self.TS_FLAGS)
self._peer_list_new = False
else:
logger.error("Unexpected arguments found.")
def timed_loop(self):
_master_connected = self._config['MASTER']['STATUS']['CONNECTED']
_master_alives_missed = self._config['MASTER']['STATUS']['KEEP_ALIVES_MISSED']
if (_master_connected == 0):
reg_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.MASTER_REG_REQ_PKT)
self.transport.write(reg_packet, (self._config['MASTER']['IP'], self._config['MASTER']['PORT']))
logger.info("->> Master Registration Request To:%s:%s From:%s", self._config['MASTER']['IP'], self._config['MASTER']['PORT'], binascii.b2a_hex(self._config['LOCAL']['RADIO_ID']))
elif (_master_connected in (1,2)):
if (_master_connected == 1):
peer_list_req_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.PEER_LIST_REQ_PKT)
self.transport.write(peer_list_req_packet, (self._config['MASTER']['IP'], self._config['MASTER']['PORT']))
logger.info("->> List Reqested from Master:%s:%s", self._config['MASTER']['IP'], self._config['MASTER']['PORT'])
master_alive_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.MASTER_ALIVE_PKT)
self.transport.write(master_alive_packet, (self._config['MASTER']['IP'], self._config['MASTER']['PORT']))
logger.info("->> Master Keep-alive Sent To:%s:%s", self._config['MASTER']['IP'], self._config['MASTER']['PORT'])
else:
logger.error("->> Master in UNKOWN STATE:%s:%s", self._config['MASTER']['IP'], self._config['MASTER']['PORT'])
for peer in (self._config['PEERS']):
if (peer['RADIO_ID'] == binascii.b2a_hex(self._config['LOCAL']['RADIO_ID'])):
continue
if peer['STATUS']['CONNECTED'] == 0:
peer_reg_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.PEER_REG_REQ_PKT)
self.transport.write(peer_reg_packet, (peer['IP'], peer['PORT']))
logger.info('->> Peer Registration Request To:%s:%s From:%s', peer['IP'], peer['PORT'], binascii.b2a_hex(self._config['LOCAL']['RADIO_ID']))
elif peer['STATUS']['CONNECTED'] == 1:
peer_alive_req_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.PEER_ALIVE_REQ_PKT)
self.transport.write(peer_reg_packet, (peer['IP'], peer['PORT']))
logger.info('->> Peer Keep-Alive Request To:%s:%s From:%s', peer['IP'], peer['PORT'], binascii.b2a_hex(self._config['LOCAL']['RADIO_ID']))
# 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):
#logger.debug("*** config: %s", self._config)
#logger.info("")
self._call = task.LoopingCall(self.timed_loop)
self._loop = self._call.start(self._config['LOCAL']['ALIVE_TIMER'])
#************************************************
# FUNCTIONS FOR IPSC Network Engine
#************************************************
# Process a received peer list:
# Flag we have a list
# Flag the list is new (needed elsewhere)
# Populate the peer information from the list
#
def peer_list_received(self, _data, (_host, _port)):
logger.info("<<- The Peer List has been Received from Master:%s:%s Setting Condition 2", _host, _port)
self._config['MASTER']['STATUS']['PEER-LIST'] = True
self._peer_list_new = True
logger.info("<<- The Peer List has been Received from Master:%s:%s ", _host, _port)
_num_peers = int(str(int(binascii.b2a_hex(_data[5:7]), 16))[1:])
self._config['LOCAL']['NUM_PEERS'] = _num_peers
self._config['MASTER']['STATUS']['CONNECTED'] = 2
logger.info(' There are %s peers in this IPSC Network', _num_peers)
del self._config['PEERS'][:]
for i in range(7, (_num_peers*11)+7, 11):
@ -208,12 +178,78 @@ class IPSC(DatagramProtocol):
'IP': socket.inet_ntoa(hex_address),
'PORT': int(binascii.b2a_hex(_data[i+8:i+10]), 16),
'RAW_MODE': _data[i+10:i+11],
'MODE': mode_decode(_data[i+10:i+11]),
'STATUS': {'CONNECTED': 0, 'KEEP_ALIVES_MISSED': 0}
})
print_peer_list(self._config)
print_peer_list(self._network_name)
#************************************************
# TIMED LOOP - MY CONNECTION MAINTENANCE
#************************************************
def timed_loop(self):
print('timed loop started') # temporary debugging to make sure this part runs
_master_connected = self._config['MASTER']['STATUS']['CONNECTED']
_master_alives_missed = self._config['MASTER']['STATUS']['KEEP_ALIVES_MISSED']
_peer_list_rx = self._config['MASTER']['STATUS']['PEER-LIST']
if (_master_connected == False):
reg_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.MASTER_REG_REQ_PKT)
self.transport.write(reg_packet, (self._config['MASTER']['IP'], self._config['MASTER']['PORT']))
logger.info("->> Master Registration Request To:%s:%s From:%s", self._config['MASTER']['IP'], self._config['MASTER']['PORT'], binascii.b2a_hex(self._config['LOCAL']['RADIO_ID']))
elif (_master_connected == True):
master_alive_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.MASTER_ALIVE_PKT)
self.transport.write(master_alive_packet, (self._config['MASTER']['IP'], self._config['MASTER']['PORT']))
logger.info("->> Master Keep-alive Sent To:%s:%s", self._config['MASTER']['IP'], self._config['MASTER']['PORT'])
self._config['MASTER']['STATUS']['KEEP_ALIVES_SENT'] += 1
if (self._config['MASTER']['STATUS']['KEEP_ALIVES_OUTSTANDING']) > 0:
self._config['MASTER']['STATUS']['KEEP_ALIVES_MISSED'] += 1
if self._config['MASTER']['STATUS']['KEEP_ALIVES_OUTSTANDING'] >= self._config['LOCAL']['MAX_MISSED']:
pass
self._config['MASTER']['STATUS']['CONNECTED'] = False
logger.error('Maximum Master Keep-Alives Missed -- De-registering the Master')
else:
logger.error("->> Master in UNKOWN STATE:%s:%s", self._config['MASTER']['IP'], self._config['MASTER']['PORT'])
if ((_master_connected == True) and (_peer_list_rx == False)):
peer_list_req_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.PEER_LIST_REQ_PKT)
self.transport.write(peer_list_req_packet, (self._config['MASTER']['IP'], self._config['MASTER']['PORT']))
logger.info("->> List Reqested from Master:%s:%s", self._config['MASTER']['IP'], self._config['MASTER']['PORT'])
# Logic problems in the next if.... bad ones. Fix them.
if (self._peer_list_new == True):
self._peer_list_new = False
logger.info('*** NEW PEER LIST RECEIEVED - PROCESSING!')
for peer in (self._config['PEERS']):
if (peer['RADIO_ID'] == binascii.b2a_hex(self._config['LOCAL']['RADIO_ID'])):
continue
if peer['STATUS']['CONNECTED'] == 0:
peer_reg_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.PEER_REG_REQ_PKT)
self.transport.write(peer_reg_packet, (peer['IP'], peer['PORT']))
logger.info('->> Peer Registration Request To:%s:%s From:%s', peer['IP'], peer['PORT'], binascii.b2a_hex(self._config['LOCAL']['RADIO_ID']))
elif peer['STATUS']['CONNECTED'] == 1:
peer_alive_req_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.PEER_ALIVE_REQ_PKT)
self.transport.write(peer_reg_packet, (peer['IP'], peer['PORT']))
logger.info('->> Peer Keep-Alive Request To:%s:%s From:%s', peer['IP'], peer['PORT'], binascii.b2a_hex(self._config['LOCAL']['RADIO_ID']))
print('timed loop finished') # temporary debugging to make sure this part runs
#************************************************
# RECEIVED DATAGRAM - ACT IMMEDIATELY!!!
#************************************************
# Work in progress -- at the very least, notify we have the packet. Ultimately
# call a function or process immediately if only a few actions
#
def datagramReceived(self, data, (host, port)):
print('datagram received') # temporary debugging to make sure this part runs
dest_ip = self._config['MASTER']['IP']
dest_port = self._config['MASTER']['PORT']
#logger.info("received %r from %s:%d", binascii.b2a_hex(data), host, port)
@ -223,38 +259,37 @@ class IPSC(DatagramProtocol):
if (_packettype == PEER_ALIVE_REQ):
logger.info("<<- Peer Keep-alive Request From Peer ID %s at:%s:%s", int(binascii.b2a_hex(_peerid), 16), host, port)
peer_alive_req_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.PEER_ALIVE_REQ_PKT)
peer_alive_reply_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.PEER_ALIVE_REPLY_PKT)
self.transport.write(peer_alive_reply_packet, (host, port))
logger.info("->> Peer Keep-alive Reply sent To:%s:%s", host, port)
self.transport.write(peer_alive_req_packet, (host, port))
logger.info("->> Peer Keep-alive Request sent To:%s:%s", host, port)
elif (_packettype == MASTER_ALIVE_REPLY):
logger.info("<<- Master Keep-alive Reply From:%s:%s", host, port)
elif (_packettype == PEER_ALIVE_REPLY):
self.config['MASTER']['STATUS']['KEEP_ALIVES_OUTSTANDING'] = 0
logger.info("<<- Peer Keep-alive Reply From:%s:%s", host, port)
elif (_packettype == MASTER_REG_REQ):
logger.info("<<- Registration Packet Recieved")
elif (_packettype == MASTER_REG_REPLY):
self._config['MASTER']['STATUS']['CONNECTED'] = 1
logger.info("<<- Master Registration Reply From:%s:%s Setting Condition %s", host, port,self._config['MASTER']['STATUS']['CONNECTED'])
self._config['MASTER']['STATUS']['CONNECTED'] = True
self._config['MASTER']['STATUS']['KEEP_ALIVES_OUTSTANDING'] = 0
logger.info("<<- Master Registration Reply From:%s:%s ", host, port)
elif (_packettype == PEER_REG_REQ):
logger.info("<<- Peer Registration Request From Peer ID %s at:%s:%s", int(binascii.b2a_hex(_peerid), 16), host, port)
peer_reg_reply_packet = hashed_packet(self._config['LOCAL']['AUTH_KEY'], self.PEER_REG_REPLY_PKT)
self.transport.write(peer_reg_reply_packet, (host, port))
logger.info("->> Peer Registration Reply Sent To:%s:%s", host, port)
logger.warning("->> Peer Registration Reply Sent To:%s:%s", host, port)
elif (_packettype == PEER_REG_REPLY):
logger.info('<<- Peer Registration Reply From: %s', int(binascii.b2a_hex(_peerid), 16))
logger.warning('<<- Peer Registration Reply From: %s', int(binascii.b2a_hex(_peerid), 16))
elif (_packettype == XCMP_XNL):
logger.warning("<<- XCMP_XNL From:%s:%s, but we did not indicate XCMP capable!", host, port)
logger.info("<<- XCMP_XNL From:%s:%s, but we did not indicate XCMP capable!", host, port)
elif (_packettype == PEER_LIST_REPLY):
self.peer_list_received(data, (host, port))
@ -272,10 +307,10 @@ class IPSC(DatagramProtocol):
logger.info("<<- Private Data Packet From From:%s:%s", host, port)
elif (_packettype == RPT_WAKE_UP):
logger.info("<<- Repeater Wake-Up Packet From:%s:%s", host, port)
logger.warning("<<- Repeater Wake-Up Packet From:%s:%s", host, port)
elif (_packettype == DE_REG_REQ):
logger.info("<<- Peer De-Registration Request From:%s:%s", host, port)
logger.warning("<<- Peer De-Registration Request From:%s:%s", host, port)
elif (_packettype == DE_REG_REPLY):
logger.info("<<- Peer De-Registration Reply From:%s:%s", host, port)
@ -287,6 +322,8 @@ class IPSC(DatagramProtocol):
packet_type = binascii.b2a_hex(_packettype)
logger.error("<<- Received Unprocessed Type %s From:%s:%s", packet_type, host, port)
#************************************************
# MAIN PROGRAM LOOP STARTS HERE
#************************************************
@ -294,5 +331,5 @@ class IPSC(DatagramProtocol):
if __name__ == '__main__':
logger.info('SYSTEM STARTING UP')
for ipsc_network in NETWORK:
reactor.listenUDP(NETWORK[ipsc_network]['LOCAL']['PORT'], IPSC(NETWORK[ipsc_network]))
reactor.listenUDP(NETWORK[ipsc_network]['LOCAL']['PORT'], IPSC(ipsc_network))
reactor.run()

View File

@ -2,34 +2,46 @@
NETWORK = {
'IPSC1': {
'LOCAL': {
'DESCRIPTION': 'IPSC Network name',
'MODE': b'\x6A',
'FLAGS': b'\x00\x00\x00\x14',
'PORT': 50001,
'NUM_PEERS': 0
'ALIVE_TIMER': 5, # Seconds between keep-alives and registration attempts
'MAX_MISSED': 5, # Maximum number of keep-alives missed before de-registration
'RADIO_ID': b'\x00\x00\x00\x0A',
'AUTH_KEY': b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01'
'ENABLED': True,
'STATUS': {
'ACTIVE': False
}
},
'MASTER': {
'IP': '1.2.3.4',
'PORT': 50000,
'RADIO_ID': b'\x00\x00\x00\x00',
'MODE': b'\x00',
'FLAGS': b'\x00\x00\x00\x00',
'STATUS': {
'RADIO_ID': b'\x00\x00\x00\x00',
'CONNECTED': 0,
'CONNECTED': False,
'PEER-LIST': False,
'KEEP_ALIVES_SENT': 0,
'KEEP_ALIVES_MISSED': 0,
'MODE': b'\x00',
'FLAGS': b'\x00\x00\x00\x00',
'KEEP_ALIVES_OUTSTANDING': 0
}
},
'PEERS': []
# each list item contains {
# 'IP': '100.200.1.1',
# 'PORT': 50000,
# 'RADIO_ID': b'\x00\x00\x00\xFF',
# 'RADIO_ID': b'\x00\x00\x00\x00',
# 'MODE': b'\x00,
# 'FLAGS': b'\x00\x00\x00\x00',
# 'STATUS': {
# 'CONNECTED': 0,
# 'KEEP_ALIVES_MISSED': 0
# 'CONNECTED': False,
# 'KEEP_ALIVES_SENT': 0,
# 'KEEP_ALIVES_MISSED': 0,
# 'KEEP_ALIVES_OUTSTANDING': 0
# }
# }
}
}
}