From 97bb174bf61dbde22c24a4640ca35940533aa05d Mon Sep 17 00:00:00 2001 From: KF7EEL Date: Sat, 4 Sep 2021 19:57:15 -0700 Subject: [PATCH] Encrypted OpenBridge, initial commit, add SVRD hooks --- bridge.py | 4 +- config.py | 6 +- const.py | 4 ++ hblink-SAMPLE.cfg | 5 +- hblink.py | 141 +++++++++++++++++++++++++++----------------- obp_generate_key.py | 10 ++++ requirements.txt | 1 + web/app.py | 57 ++++++++++++++---- 8 files changed, 160 insertions(+), 68 deletions(-) create mode 100644 obp_generate_key.py diff --git a/bridge.py b/bridge.py index 150bbbb..8d061c6 100755 --- a/bridge.py +++ b/bridge.py @@ -187,7 +187,7 @@ def download_config(L_CONFIG_FILE, cli_file): for i in iterate_config: ## corrected_config['SYSTEMS'][i]['GROUP_HANGTIME'] = int(iterate_config[i]['GROUP_HANGTIME']) ## corrected_config['SYSTEMS'][i] = {} - print(iterate_config[i]) +## print(iterate_config[i]) if iterate_config[i]['MODE'] == 'MASTER' or iterate_config[i]['MODE'] == 'PROXY' or iterate_config[i]['MODE'] == 'OPENBRIDGE': corrected_config['SYSTEMS'][i]['TG1_ACL'] = config.acl_build(iterate_config[i]['TG1_ACL'], 4294967295) corrected_config['SYSTEMS'][i]['TG2_ACL'] = config.acl_build(iterate_config[i]['TG2_ACL'], 4294967295) @@ -198,6 +198,8 @@ def download_config(L_CONFIG_FILE, cli_file): corrected_config['SYSTEMS'][i]['PASSPHRASE'] = (iterate_config[i]['PASSPHRASE'] + b'\x00' * 30)[:20] #bytes(re.sub('', "b'|'", str(iterate_config[i]['PASSPHRASE'])).ljust(20, '\x00')[:20], 'utf-8') #bytes(iterate_config[i]['PASSPHRASE'].ljust(20,'\x00')[:20], 'utf-8') corrected_config['SYSTEMS'][i]['BOTH_SLOTS'] = iterate_config[i]['BOTH_SLOTS'] corrected_config['SYSTEMS'][i]['TARGET_SOCK'] = (gethostbyname(iterate_config[i]['TARGET_IP']), iterate_config[i]['TARGET_PORT']) + corrected_config['SYSTEMS'][i]['ENCRYPTION_KEY'] = bytes(iterate_config[i]['ENCRYPTION_KEY'], 'utf-8') + corrected_config['SYSTEMS'][i]['USE_ENCRYPTION'] = iterate_config[i]['USE_ENCRYPTION'] if iterate_config[i]['MODE'] == 'PEER' or iterate_config[i]['MODE'] == 'XLXPEER': diff --git a/config.py b/config.py index 9181e82..7c2e43b 100755 --- a/config.py +++ b/config.py @@ -300,7 +300,9 @@ def build_config(_config_file): 'USE_ACL': config.getboolean(section, 'USE_ACL'), 'SUB_ACL': config.get(section, 'SUB_ACL'), 'TG1_ACL': config.get(section, 'TGID_ACL'), - 'TG2_ACL': 'PERMIT:ALL' + 'TG2_ACL': 'PERMIT:ALL', + 'USE_ENCRYPTION': config.getboolean(section, 'USE_ENCRYPTION'), + 'ENCRYPTION_KEY': bytes(config.get(section, 'ENCRYPTION_KEY'), 'utf-8'), }}) elif config.get(section, 'MODE') == 'PROXY': CONFIG['SYSTEMS'].update({section: { @@ -318,7 +320,7 @@ def build_config(_config_file): 'REG_ACL': config.get(section, 'REG_ACL'), 'SUB_ACL': config.get(section, 'SUB_ACL'), 'TG1_ACL': config.get(section, 'TG1_ACL'), - 'TG2_ACL': config.get(section, 'TG2_ACL') + 'TG2_ACL': config.get(section, 'TG2_ACL'), }}) CONFIG['SYSTEMS'][section].update({'PEERS': {}}) diff --git a/const.py b/const.py index f873a94..5e84ce1 100755 --- a/const.py +++ b/const.py @@ -69,6 +69,10 @@ RPTP = b'RPTP' RPTA = b'RPTA' RPTO = b'RPTO' +# Sever Data and Encrypted OBP +SVRD = b'SVRD' +EOBP = b'EOBP' + # Higheset peer ID permitted by HBP PEER_MAX = 4294967295 diff --git a/hblink-SAMPLE.cfg b/hblink-SAMPLE.cfg index a0160cd..cb2a6e4 100755 --- a/hblink-SAMPLE.cfg +++ b/hblink-SAMPLE.cfg @@ -108,7 +108,7 @@ STALE_DAYS: 7 # USER MANAGER # This is where to configure the details for use with a user managment script [USER_MANAGER] -THIS_SERVER_NAME: My MMDVM Server +THIS_SERVER_NAME: MMDVM_Server REMOTE_CONFIG_ENABLED: True # URL of the user managment server URL: http://localhost:8080/svr @@ -160,6 +160,9 @@ BOTH_SLOTS: True USE_ACL: True SUB_ACL: DENY:1 TGID_ACL: PERMIT:ALL +# Experimental encryption +ENCRYPTION_KEY: +USE_ENCRYPTION: False # MASTER INSTANCES - DUPLICATE SECTION FOR MULTIPLE MASTERS # HomeBrew Protocol Master instances go here. diff --git a/hblink.py b/hblink.py index d212de6..6f161d6 100755 --- a/hblink.py +++ b/hblink.py @@ -63,6 +63,9 @@ import base64 import libscrc import re +# Encryption library +from cryptography.fernet import Fernet + # Does anybody read this stuff? There's a PEP somewhere that says I should do this. __author__ = 'Cortney T. Buffington, N0MJS' @@ -75,6 +78,19 @@ __email__ = 'n0mjs@me.com' # Global variables used whether we are a module or __main__ systems = {} +# Functions that provide a basic symetrical encryption using Fernet +def encrypt_packet(key, message): + f = Fernet(key) + token = f.encrypt(message) + + return token + +def decrypt_packet(key, message): + f = Fernet(key) + token = f.decrypt(message, ttl=1) + + return token + # Timed loop used for reporting HBP status def config_reports(_config, _factory): def reporting_loop(_logger, _server): @@ -148,9 +164,18 @@ class OPENBRIDGE(DatagramProtocol): _packet = b''.join([_packet[:11], self._config['NETWORK_ID'], _packet[15:]]) #_packet += hmac_new(self._config['PASSPHRASE'],_packet,sha1).digest() _packet = b''.join([_packet, (hmac_new(self._config['PASSPHRASE'],_packet,sha1).digest())]) + if self._config['USE_ENCRYPTION'] == True: + _enc_pkt = encrypt_packet(self._config['ENCRYPTION_KEY'], _packet) + _packet = b'EOBP' + _enc_pkt self.transport.write(_packet, (self._config['TARGET_IP'], self._config['TARGET_PORT'])) # KEEP THE FOLLOWING COMMENTED OUT UNLESS YOU'RE DEBUGGING DEEPLY!!!! # logger.debug('(%s) TX Packet to OpenBridge %s:%s -- %s', self._system, self._config['TARGET_IP'], self._config['TARGET_PORT'], ahex(_packet)) + # Special Server Data packet, encrypted using frenet, send + elif _packet[:4] == SVRD: + _enc_pkt = encrypt_packet(self._config['ENCRYPTION_KEY'], _packet) + _packet = b'SVRD' + _enc_pkt + self.transport.write(_packet, (self._config['TARGET_IP'], self._config['TARGET_PORT'])) + print('Server data') else: logger.error('(%s) OpenBridge system was asked to send non DMRD packet: %s', self._system, _packet) @@ -160,67 +185,75 @@ class OPENBRIDGE(DatagramProtocol): def datagramReceived(self, _packet, _sockaddr): # Keep This Line Commented Unless HEAVILY Debugging! - #logger.debug('(%s) RX packet from %s -- %s', self._system, _sockaddr, ahex(_packet)) +## logger.debug('(%s) RX packet from %s -- %s', self._system, _sockaddr, ahex(_packet)) + if _packet[:4] == DMRD or _packet[:4] == EOBP: + if _packet[:4] == EOBP: + _d_pkt = decrypt_packet(self._config['ENCRYPTION_KEY'], _packet[4:]) + _packet = _d_pkt - if _packet[:4] == DMRD: # DMRData -- encapsulated DMR data frame - _data = _packet[:53] - _hash = _packet[53:] - _ckhs = hmac_new(self._config['PASSPHRASE'],_data,sha1).digest() + # DMRData -- encapsulated DMR data frame + if _packet[:4] == DMRD: + _data = _packet[:53] + _hash = _packet[53:] + _ckhs = hmac_new(self._config['PASSPHRASE'],_data,sha1).digest() - if compare_digest(_hash, _ckhs) and _sockaddr == self._config['TARGET_SOCK']: - _peer_id = _data[11:15] - _seq = _data[4] - _rf_src = _data[5:8] - _dst_id = _data[8:11] - _bits = _data[15] - _slot = 2 if (_bits & 0x80) else 1 - #_call_type = 'unit' if (_bits & 0x40) else 'group' - if _bits & 0x40: - _call_type = 'unit' - elif (_bits & 0x23) == 0x23: - _call_type = 'vcsbk' - else: - _call_type = 'group' - _frame_type = (_bits & 0x30) >> 4 - _dtype_vseq = (_bits & 0xF) # data, 1=voice header, 2=voice terminator; voice, 0=burst A ... 5=burst F - _stream_id = _data[16:20] - #logger.debug('(%s) DMRD - Seqence: %s, RF Source: %s, Destination ID: %s', self._system, int_id(_seq), int_id(_rf_src), int_id(_dst_id)) + if compare_digest(_hash, _ckhs) and _sockaddr == self._config['TARGET_SOCK']: + _peer_id = _data[11:15] + _seq = _data[4] + _rf_src = _data[5:8] + _dst_id = _data[8:11] + _bits = _data[15] + _slot = 2 if (_bits & 0x80) else 1 + #_call_type = 'unit' if (_bits & 0x40) else 'group' + if _bits & 0x40: + _call_type = 'unit' + elif (_bits & 0x23) == 0x23: + _call_type = 'vcsbk' + else: + _call_type = 'group' + _frame_type = (_bits & 0x30) >> 4 + _dtype_vseq = (_bits & 0xF) # data, 1=voice header, 2=voice terminator; voice, 0=burst A ... 5=burst F + _stream_id = _data[16:20] + #logger.debug('(%s) DMRD - Seqence: %s, RF Source: %s, Destination ID: %s', self._system, int_id(_seq), int_id(_rf_src), int_id(_dst_id)) - # Sanity check for OpenBridge -- all calls must be on Slot 1 for Brandmeister or DMR+. Other HBlinks can process timeslot on OPB if the flag is set - if _slot != 1 and not self._config['BOTH_SLOTS'] and not _call_type == 'unit': - logger.error('(%s) OpenBridge packet discarded because it was not received on slot 1. SID: %s, TGID %s', self._system, int_id(_rf_src), int_id(_dst_id)) - return - - # ACL Processing - if self._CONFIG['GLOBAL']['USE_ACL']: - if not acl_check(_rf_src, self._CONFIG['GLOBAL']['SUB_ACL']): - if _stream_id not in self._laststrid: - logger.info('(%s) CALL DROPPED WITH STREAM ID %s FROM SUBSCRIBER %s BY GLOBAL ACL', self._system, int_id(_stream_id), int_id(_rf_src)) - self._laststrid.append(_stream_id) - return - if _slot == 1 and not acl_check(_dst_id, self._CONFIG['GLOBAL']['TG1_ACL']): - if _stream_id not in self._laststrid: - logger.info('(%s) CALL DROPPED WITH STREAM ID %s ON TGID %s BY GLOBAL TS1 ACL', self._system, int_id(_stream_id), int_id(_dst_id)) - self._laststrid.append(_stream_id) - return - if self._config['USE_ACL']: - if not acl_check(_rf_src, self._config['SUB_ACL']): - if _stream_id not in self._laststrid: - logger.info('(%s) CALL DROPPED WITH STREAM ID %s FROM SUBSCRIBER %s BY SYSTEM ACL', self._system, int_id(_stream_id), int_id(_rf_src)) - self._laststrid.append(_stream_id) - return - if not acl_check(_dst_id, self._config['TG1_ACL']): - if _stream_id not in self._laststrid: - logger.info('(%s) CALL DROPPED WITH STREAM ID %s ON TGID %s BY SYSTEM ACL', self._system, int_id(_stream_id), int_id(_dst_id)) - self._laststrid.append(_stream_id) + # Sanity check for OpenBridge -- all calls must be on Slot 1 for Brandmeister or DMR+. Other HBlinks can process timeslot on OPB if the flag is set + if _slot != 1 and not self._config['BOTH_SLOTS'] and not _call_type == 'unit': + logger.error('(%s) OpenBridge packet discarded because it was not received on slot 1. SID: %s, TGID %s', self._system, int_id(_rf_src), int_id(_dst_id)) return - # Userland actions -- typically this is the function you subclass for an application - self.dmrd_received(_peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data) + # ACL Processing + if self._CONFIG['GLOBAL']['USE_ACL']: + if not acl_check(_rf_src, self._CONFIG['GLOBAL']['SUB_ACL']): + if _stream_id not in self._laststrid: + logger.info('(%s) CALL DROPPED WITH STREAM ID %s FROM SUBSCRIBER %s BY GLOBAL ACL', self._system, int_id(_stream_id), int_id(_rf_src)) + self._laststrid.append(_stream_id) + return + if _slot == 1 and not acl_check(_dst_id, self._CONFIG['GLOBAL']['TG1_ACL']): + if _stream_id not in self._laststrid: + logger.info('(%s) CALL DROPPED WITH STREAM ID %s ON TGID %s BY GLOBAL TS1 ACL', self._system, int_id(_stream_id), int_id(_dst_id)) + self._laststrid.append(_stream_id) + return + if self._config['USE_ACL']: + if not acl_check(_rf_src, self._config['SUB_ACL']): + if _stream_id not in self._laststrid: + logger.info('(%s) CALL DROPPED WITH STREAM ID %s FROM SUBSCRIBER %s BY SYSTEM ACL', self._system, int_id(_stream_id), int_id(_rf_src)) + self._laststrid.append(_stream_id) + return + if not acl_check(_dst_id, self._config['TG1_ACL']): + if _stream_id not in self._laststrid: + logger.info('(%s) CALL DROPPED WITH STREAM ID %s ON TGID %s BY SYSTEM ACL', self._system, int_id(_stream_id), int_id(_dst_id)) + self._laststrid.append(_stream_id) + return + + # Userland actions -- typically this is the function you subclass for an application + self.dmrd_received(_peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data) else: logger.info('(%s) OpenBridge HMAC failed, packet discarded - OPCODE: %s DATA: %s HMAC LENGTH: %s HMAC: %s', self._system, _packet[:4], repr(_packet[:53]), len(_packet[53:]), repr(_packet[53:])) - - + # Server Data packet, decrypt and process it. + elif _packet[:4] == SVRD: + _d_pkt = decrypt_packet(self._config['ENCRYPTION_KEY'], _packet[4:]) + print('svr pakcet') + print(_d_pkt) #************************************************ # HB MASTER CLASS #************************************************ diff --git a/obp_generate_key.py b/obp_generate_key.py new file mode 100644 index 0000000..202318e --- /dev/null +++ b/obp_generate_key.py @@ -0,0 +1,10 @@ +# Script to generate a key for Encrypted OpenBridge + +from cryptography.fernet import Fernet +import re + +def gen_key(): + key = Fernet.generate_key() + return key + +print('Key: ' + str(gen_key())[2:-1]) diff --git a/requirements.txt b/requirements.txt index 6ff04d3..79fc498 100755 --- a/requirements.txt +++ b/requirements.txt @@ -9,4 +9,5 @@ maidenhead requests libscrc resettabletimer +cryptography diff --git a/web/app.py b/web/app.py index 03f83f6..f1b0d9e 100644 --- a/web/app.py +++ b/web/app.py @@ -368,6 +368,8 @@ def create_app(): server = db.Column(db.String(100), nullable=False, server_default='') notes = db.Column(db.String(500), nullable=False, server_default='') other_options = db.Column(db.String(1000), nullable=False, server_default='') + encryption_key = db.Column(db.String(200), nullable=False, server_default='') + obp_encryption = db.Column(db.Boolean(), nullable=False, server_default='0') class BridgeRules(db.Model): __tablename__ = 'bridge_rules' @@ -2793,7 +2795,9 @@ TG #: ''' + str(tg_d.tg) + ''' 'USE_ACL': obp.use_acl, 'SUB_ACL': obp.sub_acl, 'TG1_ACL': obp.tg_acl, - 'TG2_ACL': 'PERMIT:ALL' + 'TG2_ACL': 'PERMIT:ALL', + 'USE_ENCRYPTION': obp.obp_encryption, + 'ENCRYPTION_KEY': obp.encryption_key }}) for pr in p: master_config_list.update({pr.name: { @@ -2943,7 +2947,7 @@ TG #: ''' + str(tg_d.tg) + ''' db.session.delete(m) db.session.commit() - def edit_master(_mode, _name, _server, _static_positions, _repeat, _active, _max_peers, _ip, _port, _enable_um, _passphrase, _group_hang_time, _use_acl, _reg_acl, _sub_acl, _tg1_acl, _tg2_acl, _enable_unit, _notes, _external_proxy, _int_start_port, _int_stop_port, _network_id, _target_ip, _target_port, _both_slots, _public, _other_options): + def edit_master(_mode, _name, _server, _static_positions, _repeat, _active, _max_peers, _ip, _port, _enable_um, _passphrase, _group_hang_time, _use_acl, _reg_acl, _sub_acl, _tg1_acl, _tg2_acl, _enable_unit, _notes, _external_proxy, _int_start_port, _int_stop_port, _network_id, _target_ip, _target_port, _both_slots, _public, _other_options, _encryption_key, _obp_encryption): ## print(_mode) #### print(_server) ## print(_name) @@ -2990,6 +2994,8 @@ TG #: ''' + str(tg_d.tg) + ''' o.enable_unit = _enable_unit o.notes = _notes o.other_options = _other_options + o.encryption_key = _encryption_key + o.obp_encryption = _obp_encryption db.session.commit() if _mode == 'PROXY': ## print(_int_start_port) @@ -3039,7 +3045,7 @@ TG #: ''' + str(tg_d.tg) + ''' ## ) ## db.session.add(add_master) - def add_master(_mode, _name, _server, _static_positions, _repeat, _active, _max_peers, _ip, _port, _enable_um, _passphrase, _group_hang_time, _use_acl, _reg_acl, _sub_acl, _tg1_acl, _tg2_acl, _enable_unit, _notes, _external_proxy, _int_start_port, _int_stop_port, _network_id, _target_ip, _target_port, _both_slots, _public, _other_options): + def add_master(_mode, _name, _server, _static_positions, _repeat, _active, _max_peers, _ip, _port, _enable_um, _passphrase, _group_hang_time, _use_acl, _reg_acl, _sub_acl, _tg1_acl, _tg2_acl, _enable_unit, _notes, _external_proxy, _int_start_port, _int_stop_port, _network_id, _target_ip, _target_port, _both_slots, _public, _other_options, _encryption_key, _obp_encryption): # print(_mode) if _mode == 'MASTER': add_master = MasterList( @@ -3111,7 +3117,9 @@ TG #: ''' + str(tg_d.tg) + ''' enable_unit = _enable_unit, server = _server, notes = _notes, - other_options = _other_options + other_options = _other_options, + encryption_key = _encryption_key, + obp_encryption = _obp_encryption ) db.session.add(add_OBP) db.session.commit() @@ -4365,13 +4373,13 @@ TG #: ''' + str(tg_d.tg) + '''

Redirecting in 3 seconds.

''' else: - add_master('PROXY', request.form.get('name_text'), request.form.get('server'), aprs_pos, repeat, active, 0, request.form.get('ip'), request.form.get('external_port'), enable_um, request.form.get('passphrase'), request.form.get('group_hangtime'), use_acl, request.form.get('reg_acl'), request.form.get('sub_acl'), request.form.get('ts1_acl'), request.form.get('ts2_acl'), enable_unit, request.form.get('notes'), external_proxy, request.form.get('int_port_start'), request.form.get('int_port_stop'), '', '', '', '', public, request.form.get('other_options')) + add_master('PROXY', request.form.get('name_text'), request.form.get('server'), aprs_pos, repeat, active, 0, request.form.get('ip'), request.form.get('external_port'), enable_um, request.form.get('passphrase'), request.form.get('group_hangtime'), use_acl, request.form.get('reg_acl'), request.form.get('sub_acl'), request.form.get('ts1_acl'), request.form.get('ts2_acl'), enable_unit, request.form.get('notes'), external_proxy, request.form.get('int_port_start'), request.form.get('int_port_stop'), '', '', '', '', public, request.form.get('other_options'), '', '') content = '''

PROXY saved.

Redirecting in 3 seconds.

''' elif request.args.get('proxy_save') == 'edit': ## print(request.args.get('name')) - edit_master('PROXY', request.args.get('name'), request.args.get('server'), aprs_pos, repeat, active, 0, request.form.get('ip'), request.form.get('external_port'), enable_um, request.form.get('passphrase'), request.form.get('group_hangtime'), use_acl, request.form.get('reg_acl'), request.form.get('sub_acl'), request.form.get('ts1_acl'), request.form.get('ts2_acl'), enable_unit, request.form.get('notes'), external_proxy, request.form.get('int_port_start'), request.form.get('int_port_stop'), '', '', '', '', public, request.form.get('other_options')) + edit_master('PROXY', request.args.get('name'), request.args.get('server'), aprs_pos, repeat, active, 0, request.form.get('ip'), request.form.get('external_port'), enable_um, request.form.get('passphrase'), request.form.get('group_hangtime'), use_acl, request.form.get('reg_acl'), request.form.get('sub_acl'), request.form.get('ts1_acl'), request.form.get('ts2_acl'), enable_unit, request.form.get('notes'), external_proxy, request.form.get('int_port_start'), request.form.get('int_port_stop'), '', '', '', '', public, request.form.get('other_options'), '', '') content = '''

PROXY changed.

Redirecting in 3 seconds.

''' @@ -4386,6 +4394,9 @@ TG #: ''' + str(tg_d.tg) + ''' use_acl = False enable_unit = False both_slots = True + obp_encryption = False + if request.form.get('obp_encryption') == 'True': + obp_encryption = True if request.form.get('enabled') == 'True': enabled = True if request.form.get('use_acl') == 'True': @@ -4400,12 +4411,12 @@ TG #: ''' + str(tg_d.tg) + '''

Redirecting in 3 seconds.

''' else: - add_master('OBP', request.form.get('name_text'), request.form.get('server'), '', '', enabled, request.form.get('max_peers'), request.form.get('ip'), request.form.get('port'), '', request.form.get('passphrase'), request.form.get('group_hangtime'), use_acl, request.form.get('reg_acl'), request.form.get('sub_acl'), request.form.get('tg_acl'), '', enable_unit, request.form.get('notes'), '', '', '', request.form.get('network_id'), request.form.get('target_ip'), request.form.get('target_port'), both_slots, '', request.form.get('other_options')) + add_master('OBP', request.form.get('name_text'), request.form.get('server'), '', '', enabled, request.form.get('max_peers'), request.form.get('ip'), request.form.get('port'), '', request.form.get('passphrase'), request.form.get('group_hangtime'), use_acl, request.form.get('reg_acl'), request.form.get('sub_acl'), request.form.get('tg_acl'), '', enable_unit, request.form.get('notes'), '', '', '', request.form.get('network_id'), request.form.get('target_ip'), request.form.get('target_port'), both_slots, '', request.form.get('other_options'), request.form.get('encryption_key'), obp_encryption) content = '''

OpenBridge connection saved.

Redirecting in 3 seconds.

''' elif request.args.get('OBP_save') == 'edit': - edit_master('OBP', request.args.get('name'), request.args.get('server'), '', '', enabled, request.form.get('max_peers'), request.form.get('ip'), request.form.get('port'), '', request.form.get('passphrase'), request.form.get('group_hangtime'), use_acl, request.form.get('reg_acl'), request.form.get('sub_acl'), request.form.get('tg_acl'), '', enable_unit, request.form.get('notes'), '', '', '', request.form.get('network_id'), request.form.get('target_ip'), request.form.get('target_port'), both_slots, '', request.form.get('other_options')) + edit_master('OBP', request.args.get('name'), request.args.get('server'), '', '', enabled, request.form.get('max_peers'), request.form.get('ip'), request.form.get('port'), '', request.form.get('passphrase'), request.form.get('group_hangtime'), use_acl, request.form.get('reg_acl'), request.form.get('sub_acl'), request.form.get('tg_acl'), '', enable_unit, request.form.get('notes'), '', '', '', request.form.get('network_id'), request.form.get('target_ip'), request.form.get('target_port'), both_slots, '', request.form.get('other_options'), request.form.get('encryption_key'), obp_encryption) content = '''

OpenBridge connection changed.

Redirecting in 3 seconds.

''' @@ -4443,12 +4454,12 @@ TG #: ''' + str(tg_d.tg) + '''

Redirecting in 3 seconds.

''' else: - add_master('MASTER', request.form.get('name_text'), request.form.get('server'), aprs_pos, repeat, active, request.form.get('max_peers'), request.form.get('ip'), request.form.get('port'), enable_um, request.form.get('passphrase'), request.form.get('group_hangtime'), use_acl, request.form.get('reg_acl'), request.form.get('sub_acl'), request.form.get('ts1_acl'), request.form.get('ts2_acl'), enable_unit, request.form.get('notes'), '', '', '', '', '', '', '', public, request.form.get('other_options')) + add_master('MASTER', request.form.get('name_text'), request.form.get('server'), aprs_pos, repeat, active, request.form.get('max_peers'), request.form.get('ip'), request.form.get('port'), enable_um, request.form.get('passphrase'), request.form.get('group_hangtime'), use_acl, request.form.get('reg_acl'), request.form.get('sub_acl'), request.form.get('ts1_acl'), request.form.get('ts2_acl'), enable_unit, request.form.get('notes'), '', '', '', '', '', '', '', public, request.form.get('other_options'), '', '') content = '''

MASTER saved.

Redirecting in 3 seconds.

''' elif request.args.get('master_save') == 'edit': - edit_master('MASTER', request.args.get('name'), request.args.get('server'), aprs_pos, repeat, active, request.form.get('max_peers'), request.form.get('ip'), request.form.get('port'), enable_um, request.form.get('passphrase'), request.form.get('group_hangtime'), use_acl, request.form.get('reg_acl'), request.form.get('sub_acl'), request.form.get('ts1_acl'), request.form.get('ts2_acl'), enable_unit, request.form.get('notes'), '', '', '', '', '', '', '', public, request.form.get('other_options')) + edit_master('MASTER', request.args.get('name'), request.args.get('server'), aprs_pos, repeat, active, request.form.get('max_peers'), request.form.get('ip'), request.form.get('port'), enable_um, request.form.get('passphrase'), request.form.get('group_hangtime'), use_acl, request.form.get('reg_acl'), request.form.get('sub_acl'), request.form.get('ts1_acl'), request.form.get('ts2_acl'), enable_unit, request.form.get('notes'), '', '', '', '', '', '', '', public, request.form.get('other_options'), '', '') content = '''

MASTER changed.

Redirecting in 3 seconds.

''' @@ -4542,6 +4553,19 @@ TG #: ''' + str(tg_d.tg) + ''' + + Use Encryption: + + + Encryption_key: + +  Misc Options:   @@ -5038,6 +5062,19 @@ TG #: ''' + str(tg_d.tg) + ''' + + Use Encryption: + + + Encryption_key: + +  Misc Options: