From 20444b7de04689ae47dfc807b5e73f097ac0f1f8 Mon Sep 17 00:00:00 2001 From: KF7EEL Date: Fri, 28 May 2021 11:10:24 -0700 Subject: [PATCH] add hotspot proxy from FreeDMR --- full_bridge-SAMPLE.cfg | 16 ++++ hotspot_proxy_v2.py | 206 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 222 insertions(+) create mode 100644 hotspot_proxy_v2.py diff --git a/full_bridge-SAMPLE.cfg b/full_bridge-SAMPLE.cfg index a7b504b..f932e0e 100644 --- a/full_bridge-SAMPLE.cfg +++ b/full_bridge-SAMPLE.cfg @@ -301,6 +301,22 @@ SUB_ACL: DENY:1 TGID_TS1_ACL: PERMIT:ALL TGID_TS2_ACL: PERMIT:ALL + +# Utilizing the hotspot proxy from FreeDMR +##[PROXY_TEMPLATE] +##STATIC_APRS_POSITION_ENABLED: False +##REPEAT: True +##PASSPHRASE: password +##GROUP_HANGTIME: 5 +##USE_ACL: True +##REG_ACL: DENY:1 +##SUB_ACL: DENY:1 +##TGID_TS1_ACL: PERMIT:ALL +##TGID_TS2_ACL: PERMIT:ALL + + + + # PEER INSTANCES - DUPLICATE SECTION FOR MULTIPLE PEERS # There are a LOT of errors in the HB Protocol specifications on this one! # MOST of these items are just strings and will be properly dealt with by the program diff --git a/hotspot_proxy_v2.py b/hotspot_proxy_v2.py new file mode 100644 index 0000000..adf04a0 --- /dev/null +++ b/hotspot_proxy_v2.py @@ -0,0 +1,206 @@ +from twisted.internet.protocol import DatagramProtocol +from twisted.internet import reactor, task +from time import time +from resettabletimer import ResettableTimer +from dmr_utils3.utils import int_id +import random + +# Does anybody read this stuff? There's a PEP somewhere that says I should do this. +__author__ = 'Simon Adlem - G7RZU' +__copyright__ = 'Copyright (c) Simon Adlem, G7RZU 2020,2021' +__credits__ = 'Jon Lee, G4TSN; Norman Williams, M6NBP' +__license__ = 'GNU GPLv3' +__maintainer__ = 'Simon Adlem G7RZU' +__email__ = 'simon@gb7fr.org.uk' + +class Proxy(DatagramProtocol): + + def __init__(self,Master,ListenPort,connTrack,blackList,Timeout,Debug,DestportStart,DestPortEnd): + self.master = Master + self.connTrack = connTrack + self.peerTrack = {} + self.timeout = Timeout + self.debug = Debug + self.blackList = blackList + self.destPortStart = DestportStart + self.destPortEnd = DestPortEnd + self.numPorts = DestPortEnd - DestportStart + + + def reaper(self,_peer_id): + if self.debug: + print("dead",_peer_id) + self.transport.write(b'RPTCL'+_peer_id, ('127.0.0.1',self.peerTrack[_peer_id]['dport'])) + self.connTrack[self.peerTrack[_peer_id]['dport']] = False + del self.peerTrack[_peer_id] + + + def datagramReceived(self, data, addr): + + # HomeBrew Protocol Commands + DMRD = b'DMRD' + DMRA = b'DMRA' + MSTCL = b'MSTCL' + MSTNAK = b'MSTNAK' + MSTPONG = b'MSTPONG' + MSTN = b'MSTN' + MSTP = b'MSTP' + MSTC = b'MSTC' + RPTL = b'RPTL' + RPTPING = b'RPTPING' + RPTCL = b'RPTCL' + RPTL = b'RPTL' + RPTACK = b'RPTACK' + RPTK = b'RPTK' + RPTC = b'RPTC' + RPTP = b'RPTP' + RPTA = b'RPTA' + RPTO = b'RPTO' + + host,port = addr + + nowtime = time() + + Debug = self.debug + print(host) + print(self.master) + + #If the packet comes from the master + if host == self.master: + _command = data[:4] + + if _command == DMRD: + _peer_id = data[11:15] + elif _command == RPTA: + if data[6:10] in self.peerTrack: + _peer_id = data[6:10] + else: + _peer_id = self.connTrack[port] + elif _command == MSTN: + _peer_id = data[6:10] + self.peerTrack[_peer_id]['timer'].cancel() + self.reaper(_peer_id) + return + elif _command == MSTP: + _peer_id = data[7:11] + elif _command == MSTC: + _peer_id = data[5:9] + self.peerTrack[_peer_id]['timer'].cancel() + self.reaper(_peer_id) + return + + # _peer_id = self.connTrack[port] + if self.debug: + print(data) + if _peer_id and _peer_id in self.peerTrack: + self.transport.write(data,(self.peerTrack[_peer_id]['shost'],self.peerTrack[_peer_id]['sport'])) + #self.peerTrack[_peer_id]['timer'].reset() + return + + + + else: + _command = data[:4] + + if _command == DMRD: # DMRData -- encapsulated DMR data frame + _peer_id = data[11:15] + elif _command == DMRA: # DMRAlias -- Talker Alias information + _peer_id = _data[4:8] + elif _command == RPTL: # RPTLogin -- a repeater wants to login + _peer_id = data[4:8] + elif _command == RPTK: # Repeater has answered our login challenge + _peer_id = data[4:8] + elif _command == RPTC: # Repeater is sending it's configuraiton OR disconnecting + if data[:5] == RPTCL: # Disconnect command + _peer_id = data[5:9] + else: + _peer_id = data[4:8] # Configure Command + elif _command == RPTO: # options + _peer_id = data[4:8] + elif _command == RPTP: # RPTPing -- peer is pinging us + _peer_id = data[7:11] + else: + return + + if _peer_id in self.peerTrack: + _dport = self.peerTrack[_peer_id]['dport'] + self.peerTrack[_peer_id]['sport'] = port + self.peerTrack[_peer_id]['shost'] = host + self.transport.write(data, ('127.0.0.1',_dport)) + self.peerTrack[_peer_id]['timer'].reset() + if self.debug: + print(data) + print(_dport) + return + else: + + if int_id(_peer_id) in self.blackList: + return + #for _dport in self.connTrack: + while True: + _dport = random.randint(1,(self.numPorts - 1)) + _dport = _dport + self.destPortStart + if not self.connTrack[_dport]: + break + self.connTrack[_dport] = _peer_id + self.peerTrack[_peer_id] = {} + self.peerTrack[_peer_id]['dport'] = _dport + self.peerTrack[_peer_id]['sport'] = port + self.peerTrack[_peer_id]['shost'] = host + self.peerTrack[_peer_id]['timer'] = ResettableTimer(self.timeout,self.reaper,[_peer_id]) + self.peerTrack[_peer_id]['timer'].start() + self.transport.write(data, (self.master,_dport)) + if self.debug: + print(data) + return + + +if __name__ == '__main__': + +#*** CONFIG HERE *** + + Master = "127.0.0.1" + ListenPort = 62031 + DestportStart = 54000 + DestPortEnd = 54002 + Timeout = 30 + Stats = True + Debug = True + BlackList = [1234567] + +#******************* + + + CONNTRACK = {} + + for port in range(DestportStart,DestPortEnd+1,1): + CONNTRACK[port] = False + + + reactor.listenUDP(ListenPort,Proxy(Master,ListenPort,CONNTRACK,BlackList,Timeout,Debug,DestportStart,DestPortEnd)) + + def loopingErrHandle(failure): + print('(GLOBAL) STOPPING REACTOR TO AVOID MEMORY LEAK: Unhandled error innowtimed loop.\n {}'.format(failure)) + reactor.stop() + + def stats(): + count = 0 + nowtime = time() + for port in CONNTRACK: + if CONNTRACK[port]: + count = count+1 + + totalPorts = DestPortEnd - DestportStart + freePorts = totalPorts - count + + print("{} ports out of {} in use ({} free)".format(count,totalPorts,freePorts)) + + + + if Stats == True: + stats_task = task.LoopingCall(stats) + statsa = stats_task.start(30) + statsa.addErrback(loopingErrHandle) + + reactor.run() +