From a99fa58a505bd3018ed00f21d42bcf772f2d8a06 Mon Sep 17 00:00:00 2001 From: Cort Buffington Date: Sat, 20 Jul 2013 08:28:52 -0500 Subject: [PATCH] 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. --- ipsc.py | 237 ++++++++++++++++++++++----------------- my_ipsc_config_SAMPLE.py | 30 +++-- 2 files changed, 158 insertions(+), 109 deletions(-) diff --git a/ipsc.py b/ipsc.py index 5328689..3ba7b37 100644 --- a/ipsc.py +++ b/ipsc.py @@ -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() \ No newline at end of file diff --git a/my_ipsc_config_SAMPLE.py b/my_ipsc_config_SAMPLE.py index 7d351aa..8e35619 100644 --- a/my_ipsc_config_SAMPLE.py +++ b/my_ipsc_config_SAMPLE.py @@ -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 # } # } } -} \ No newline at end of file +}