diff --git a/FreeDMR-SAMPLE.cfg b/FreeDMR-SAMPLE.cfg new file mode 100755 index 0000000..7b793ad --- /dev/null +++ b/FreeDMR-SAMPLE.cfg @@ -0,0 +1,256 @@ +# PROGRAM-WIDE PARAMETERS GO HERE +# PATH - working path for files, leave it alone unless you NEED to change it +# PING_TIME - the interval that peers will ping the master, and re-try registraion +# - how often the Master maintenance loop runs +# MAX_MISSED - how many pings are missed before we give up and re-register +# - number of times the master maintenance loop runs before de-registering a peer +# +# ACLs: +# +# Access Control Lists are a very powerful tool for administering your system. +# But they consume packet processing time. Disable them if you are not using them. +# But be aware that, as of now, the configuration stanzas still need the ACL +# sections configured even if you're not using them. +# +# REGISTRATION ACLS ARE ALWAYS USED, ONLY SUBSCRIBER AND TGID MAY BE DISABLED!!! +# +# The 'action' May be PERMIT|DENY +# Each entry may be a single radio id, or a hypenated range (e.g. 1-2999) +# Format: +# ACL = 'action:id|start-end|,id|start-end,....' +# --for example-- +# SUB_ACL: DENY:1,1000-2000,4500-60000,17 +# +# ACL Types: +# REG_ACL: peer radio IDs for registration (only used on HBP master systems) +# SUB_ACL: subscriber IDs for end-users +# TGID_TS1_ACL: destination talkgroup IDs on Timeslot 1 +# TGID_TS2_ACL: destination talkgroup IDs on Timeslot 2 +# +# ACLs may be repeated for individual systems if needed for granularity +# Global ACLs will be processed BEFORE the system level ACLs +# Packets will be matched against all ACLs, GLOBAL first. If a packet 'passes' +# All elements, processing continues. Packets are discarded at the first +# negative match, or 'reject' from an ACL element. +# +# If you do not wish to use ACLs, set them to 'PERMIT:ALL' +# TGID_TS1_ACL in the global stanza is used for OPENBRIDGE systems, since all +# traffic is passed as TS 1 between OpenBridges +[GLOBAL] +PATH: ./ +PING_TIME: 10 +MAX_MISSED: 3 +USE_ACL: True +REG_ACL: PERMIT:ALL +SUB_ACL: DENY:1 +TGID_TS1_ACL: PERMIT:ALL +TGID_TS2_ACL: PERMIT:ALL + + +# NOT YET WORKING: NETWORK REPORTING CONFIGURATION +# Enabling "REPORT" will configure a socket-based reporting +# system that will send the configuration and other items +# to a another process (local or remote) that may process +# the information for some useful purpose, like a web dashboard. +# +# REPORT - True to enable, False to disable +# REPORT_INTERVAL - Seconds between reports +# REPORT_PORT - TCP port to listen on if "REPORT_NETWORKS" = NETWORK +# REPORT_CLIENTS - comma separated list of IPs you will allow clients +# to connect on. Entering a * will allow all. +# +# ****FOR NOW MUST BE TRUE - USE THE LOOPBACK IF YOU DON'T USE THIS!!!**** +[REPORTS] +REPORT: True +REPORT_INTERVAL: 60 +REPORT_PORT: 4321 +REPORT_CLIENTS: 127.0.0.1 + + +# SYSTEM LOGGER CONFIGURAITON +# This allows the logger to be configured without chaning the individual +# python logger stuff. LOG_FILE should be a complete path/filename for *your* +# system -- use /dev/null for non-file handlers. +# LOG_HANDLERS may be any of the following, please, no spaces in the +# list if you use several: +# null +# console +# console-timed +# file +# file-timed +# syslog +# LOG_LEVEL may be any of the standard syslog logging levels, though +# as of now, DEBUG, INFO, WARNING and CRITICAL are the only ones +# used. +# +[LOGGER] +LOG_FILE: /tmp/hblink.log +LOG_HANDLERS: console-timed +LOG_LEVEL: DEBUG +LOG_NAME: HBlink + +# DOWNLOAD AND IMPORT SUBSCRIBER, PEER and TGID ALIASES +# Ok, not the TGID, there's no master list I know of to download +# This is intended as a facility for other applcations built on top of +# HBlink to use, and will NOT be used in HBlink directly. +# STALE_DAYS is the number of days since the last download before we +# download again. Don't be an ass and change this to less than a few days. +[ALIASES] +TRY_DOWNLOAD: True +PATH: ./ +PEER_FILE: peer_ids.json +SUBSCRIBER_FILE: subscriber_ids.json +TGID_FILE: talkgroup_ids.json +PEER_URL: https://www.radioid.net/static/rptrs.json +SUBSCRIBER_URL: https://www.radioid.net/static/users.json +STALE_DAYS: 7 + +#Read further repeater configs from MySQL +[MYSQL] +USE_MYSQL: False +USER: hblink +PASS: mypassword +DB: hblink +SERVER: 127.0.0.1 +PORT: 3306 + +# OPENBRIDGE INSTANCES - DUPLICATE SECTION FOR MULTIPLE CONNECTIONS +# OpenBridge is a protocol originall created by DMR+ for connection between an +# IPSC2 server and Brandmeister. It has been implemented here at the suggestion +# of the Brandmeister team as a way to legitimately connect HBlink to the +# Brandemiester network. +# It is recommended to name the system the ID of the Brandmeister server that +# it connects to, but is not necessary. TARGET_IP and TARGET_PORT are of the +# Brandmeister or IPSC2 server you are connecting to. PASSPHRASE is the password +# that must be agreed upon between you and the operator of the server you are +# connecting to. NETWORK_ID is a number in the format of a DMR Radio ID that +# will be sent to the other server to identify this connection. +# other parameters follow the other system types. +# +# ACLs: +# OpenBridge does not 'register', so registration ACL is meaningless. +# OpenBridge passes all traffic on TS1, so there is only 1 TGID ACL. +# Otherwise ACLs work as described in the global stanza +[OBP-TEST] +MODE: OPENBRIDGE +ENABLED: False +IP: +PORT: 62044 +NETWORK_ID: 1 +PASSPHRASE: mypass +TARGET_IP: +TARGET_PORT: 62044 +USE_ACL: True +SUB_ACL: DENY:1 +TGID_ACL: PERMIT:ALL + +# MASTER INSTANCES - DUPLICATE SECTION FOR MULTIPLE MASTERS +# HomeBrew Protocol Master instances go here. +# IP may be left blank if there's one interface on your system. +# Port should be the port you want this master to listen on. It must be unique +# and unused by anything else. +# Repeat - if True, the master repeats traffic to peers, False, it does nothing. +# +# MAX_PEERS -- maximun number of peers that may be connect to this master +# at any given time. This is very handy if you're allowing hotspots to +# connect, or using a limited computer like a Raspberry Pi. +# +# ACLs: +# See comments in the GLOBAL stanza +[G9XYZ] +MODE: MASTER +ENABLED: False +REPEAT: True +MAX_PEERS: 1 +EXPORT_AMBE: False +IP: +PORT: 54001 +PASSPHRASE: passw0rd +GROUP_HANGTIME: 5 +USE_ACL: True +REG_ACL: DENY:1 +SUB_ACL: DENY:1 +TGID_TS1_ACL: PERMIT:ALL +TGID_TS2_ACL: PERMIT:ALL +DEFAULT_UA_TIMER: 10 +SINGLE_MODE: True +VOICE_IDENT: True +TS1_STATIC: +TS2_STATIC: +DEFAULT_REFLECTOR: 0 + +# 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 +# The TX & RX Frequencies are 9-digit numbers, and are the frequency in Hz. +# Latitude is an 8-digit unsigned floating point number. +# Longitude is a 9-digit signed floating point number. +# Height is in meters +# Setting Loose to True relaxes the validation on packets received from the master. +# This will allow HBlink to connect to a non-compliant system such as XLXD, DMR+ etc. +# +# ACLs: +# See comments in the GLOBAL stanza +[REPEATER-1] +MODE: PEER +ENABLED: False +LOOSE: False +EXPORT_AMBE: False +IP: +PORT: 54001 +MASTER_IP: 172.16.1.1 +MASTER_PORT: 54000 +PASSPHRASE: homebrew +CALLSIGN: W1ABC +RADIO_ID: 312000 +RX_FREQ: 449000000 +TX_FREQ: 444000000 +TX_POWER: 25 +COLORCODE: 1 +SLOTS: 1 +LATITUDE: 38.0000 +LONGITUDE: -095.0000 +HEIGHT: 75 +LOCATION: Anywhere, USA +DESCRIPTION: This is a cool repeater +URL: www.w1abc.org +SOFTWARE_ID: 20170620 +PACKAGE_ID: MMDVM_HBlink +GROUP_HANGTIME: 5 +OPTIONS: +USE_ACL: True +SUB_ACL: DENY:1 +TGID_TS1_ACL: PERMIT:ALL +TGID_TS2_ACL: PERMIT:ALL + +[XLX-1] +MODE: XLXPEER +ENABLED: False +LOOSE: True +EXPORT_AMBE: False +IP: +PORT: 54002 +MASTER_IP: 172.16.1.1 +MASTER_PORT: 62030 +PASSPHRASE: passw0rd +CALLSIGN: W1ABC +RADIO_ID: 312000 +RX_FREQ: 449000000 +TX_FREQ: 444000000 +TX_POWER: 25 +COLORCODE: 1 +SLOTS: 1 +LATITUDE: 38.0000 +LONGITUDE: -095.0000 +HEIGHT: 75 +LOCATION: Anywhere, USA +DESCRIPTION: This is a cool repeater +URL: www.w1abc.org +SOFTWARE_ID: 20170620 +PACKAGE_ID: MMDVM_HBlink +GROUP_HANGTIME: 5 +XLXMODULE: 4004 +USE_ACL: True +SUB_ACL: DENY:1 +TGID_TS1_ACL: PERMIT:ALL +TGID_TS2_ACL: PERMIT:ALL diff --git a/HBlink.png b/HBlink.png deleted file mode 100644 index 29fa556..0000000 Binary files a/HBlink.png and /dev/null differ diff --git a/HBlink.svg b/HBlink.svg deleted file mode 100644 index f552227..0000000 --- a/HBlink.svg +++ /dev/null @@ -1,245 +0,0 @@ - - - - - - - - - - - - - - - - Produced by OmniGraffle 7.12.1 - 2018-03-14 13:37:44 +0000 - - - image/svg+xml - - - - - - - Canvas 2 - - Layer 1 - - - path6007 - - - - - - - - - - - - - - - - - - - - - path6381 - - - - - - - - - - - - diff --git a/hblink.py b/hblink.py index e70bc40..eeb5e47 100755 --- a/hblink.py +++ b/hblink.py @@ -551,7 +551,7 @@ class HBSYSTEM(DatagramProtocol): logger.warning('(%s) Ping from Radio ID that is not logged in: %s', self._system, int_id(_peer_id)) else: - logger.error('(%s) Unrecognized command. Raw HBP PDU: %s', self._system, ahex(_data)) + logger.error('(%s) Unrecognized command. Raw HBP PDU: %s', self._system, _data) # Aliased in __init__ to datagramReceived if system is a peer def peer_datagramReceived(self, _data, _sockaddr): diff --git a/hotspot_proxy.py b/hotspot_proxy.py deleted file mode 100644 index 8ce7200..0000000 --- a/hotspot_proxy.py +++ /dev/null @@ -1,106 +0,0 @@ -from twisted.internet.protocol import DatagramProtocol -from twisted.internet import reactor, task -from time import time - - -class Proxy(DatagramProtocol): - - def __init__(self,ListenPort,connTrack,Timeout,Debug): - self.connTrack = connTrack - self.sourceTrack = {} - self.timeout = Timeout - self.debug = Debug - - def datagramReceived(self, data, addr): - host,port = addr - - nowtime = time() - - Debug = self.debug - - #If the packet comes from the master - if host == '127.0.0.1' and port in self.connTrack: - if int(self.connTrack[port]['time'])+self.timeout >nowtime: - self.transport.write(data,(self.connTrack[port]['host'],self.connTrack[port]['sport'])) - if Debug: - print("return path match") - print(data) - elif self.sourceTrack[str(self.connTrack[port]['host'])+":"+str(self.connTrack[port]['sport']) in self.sourceTrack: - del self.sourceTrack[str(self.connTrack[port]['host'])+":"+str(self.connTrack[port]['sport'])] - return - - #If we have a sourcetrack for this connect and thenowtimeout has not expired, forward to tracked port - if host+":"+str(port) in self.sourceTrack and (int(self.sourceTrack[host+":"+str(port)]['time'])+self.timeout >nowtime): - self.transport.write(data, ('127.0.0.1',self.sourceTrack[host+":"+str(port)]['dport'])) - self.connTrack[self.sourceTrack[host+":"+str(port)]['dport']]['time'] =nowtime - self.sourceTrack[host+":"+str(port)]['time'] =nowtime - if Debug: - print("Tracked inbound match") - print(data) - return - elif host+":"+str(port) in self.sourceTrack: - del self.sourceTrack[host+":"+str(port)] - - #Find free port to map for new connection - for dport in self.connTrack: - if (self.connTrack[dport]['time'] == False or (int(self.connTrack[dport]['time'])+self.timeout < nowtime)): - self.connTrack[dport]['sport'] = port - self.connTrack[dport]['host'] = host - self.connTrack[dport]['time'] =nowtime - self.sourceTrack[host+":"+str(port)] = {} - self.sourceTrack[host+":"+str(port)]['dport'] = dport - self.sourceTrack[host+":"+str(port)]['time'] = nowtime - self.transport.write(data, ('127.0.0.1',dport)) - if Debug: - print("New connection") - print(data) - return - - - -if __name__ == '__main__': - -#*** CONFIG HERE *** - - ListenPort = 62031 - DestportStart = 54001 - DestPortEnd = 54002 - Timeout = 35 - Stats = True - Debug = True - -#******************* - - - CONNTRACK = {} - - for port in range(DestportStart,DestPortEnd,1): - CONNTRACK[port] = {'host': False,'time': False,'sport':False, 'nacktime': False} - - reactor.listenUDP(ListenPort,Proxy(ListenPort,CONNTRACK,Timeout,Debug)) - - 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 int(CONNTRACK[port]['time'])+Timeout > nowtime: - 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() - diff --git a/rules_SAMPLE.py b/rules_SAMPLE.py index a3c3701..6270d72 100755 --- a/rules_SAMPLE.py +++ b/rules_SAMPLE.py @@ -1,6 +1,9 @@ ''' THIS EXAMPLE WILL NOT WORK AS IT IS - YOU MUST SPECIFY YOUR OWN VALUES!!! +In FreeDMR, the rules file should be *empty* unless you have static routing required. Please see the +documentation for more details. + This file is organized around the "Conference Bridges" that you wish to use. If you're a c-Bridge person, think of these as "bridge groups". You might also liken them to a "reflector". If a particular system is "ACTIVE" on a particular conference bridge, any traffid from that system will be sent @@ -33,18 +36,7 @@ configuration file. ''' BRIDGES = { - 'WORLDWIDE': [ - {'SYSTEM': 'MASTER-1', 'TS': 1, 'TGID': 1, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'ON', 'ON': [2,], 'OFF': [9,10], 'RESET': []}, - {'SYSTEM': 'CLIENT-1', 'TS': 1, 'TGID': 3100, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'ON', 'ON': [2,], 'OFF': [9,10], 'RESET': []}, - ], - 'ENGLISH': [ - {'SYSTEM': 'MASTER-1', 'TS': 1, 'TGID': 13, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'NONE', 'ON': [3,], 'OFF': [8,10], 'RESET': []}, - {'SYSTEM': 'CLIENT-2', 'TS': 1, 'TGID': 13, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'NONE', 'ON': [3,], 'OFF': [8,10], 'RESET': []}, - ], - 'STATEWIDE': [ - {'SYSTEM': 'MASTER-1', 'TS': 2, 'TGID': 3129, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'NONE', 'ON': [4,], 'OFF': [7,10], 'RESET': []}, - {'SYSTEM': 'CLIENT-2', 'TS': 2, 'TGID': 3129, 'ACTIVE': True, 'TIMEOUT': 2, 'TO_TYPE': 'NONE', 'ON': [4,], 'OFF': [7,10], 'RESET': []}, - ] + } if __name__ == '__main__':