Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Via LTE no connection (double-NAT issue) #17

Open
do1kbl opened this issue Aug 12, 2023 · 11 comments
Open

Via LTE no connection (double-NAT issue) #17

do1kbl opened this issue Aug 12, 2023 · 11 comments

Comments

@do1kbl
Copy link

do1kbl commented Aug 12, 2023

Hello,
in the Lan it work fine. But if I install it on a Computer make the port forwarding correct the hytera repeater wont connect.
on DMR+ and Brandmeister I can connect. maybe we need dynamic ports?
Logs:
INFO - 2023-08-12 10:48:42,239 - BridgeSettings - Hytera Repeater is expected to connect at 192.168.1.106 and ports [MASTER PORT: 50000] [DMR PORT: 50001] [RDAC PORT: 50002]
ERROR - 2023-08-12 10:48:59,245 - MMDVMProtocol - PARAM: homebrew.repeater_dmr_id CURRENT_VALUE: 0 MESSAGE: Value might have not been configured and was not obtained in
Hytera repeater configuration process (either P2P, RDAC or SNMP)
ERROR - 2023-08-12 10:48:59,245 - MMDVMProtocol - PARAM: homebrew.callsign CURRENT_VALUE: None MESSAGE: Value might have not been configured and was not obtained in Hyt
era repeater configuration process (either P2P, RDAC or SNMP)
INFO - 2023-08-12 10:48:59,245 - MMDVMProtocol - Sending Login Request
DEBUG - 2023-08-12 10:48:59,246 - HyteraP2PProtocol - RDAC Accept for 46.114.174.112.50428
INFO - 2023-08-12 10:48:59,247 - MMDVMProtocol - Not sending packet, waiting for Hytera repeater to connect first
DEBUG - 2023-08-12 10:48:59,248 - HyteraP2PProtocol - DMR Accept for 46.114.174.112.60789
DEBUG - 2023-08-12 10:48:59,251 - HyteraP2PProtocol - RDAC Accept for 46.114.174.112.50428
DEBUG - 2023-08-12 10:48:59,252 - HyteraP2PProtocol - DMR Accept for 46.114.174.112.60789
DEBUG - 2023-08-12 10:49:02,212 - HyteraP2PProtocol - RDAC Accept for 46.114.174.112.50428

You its other port 60789 and 50428.

maybe its possible to solve it

@do1kbl
Copy link
Author

do1kbl commented Aug 16, 2023

I chage on the file hytera_protocols.py from line 148
I read the incomming Port and overwrite the ports from the settings.ini file

from

def datagram_received(self, data: bytes, address: Tuple[str, int]) -> None:
        packet_type = self.command_get_type(data)
        is_command = self.packet_is_command(data)
        if is_command:
            if packet_type not in self.KNOWN_PACKET_TYPES:
                if not self.packet_is_ack(data):
                    self.log_error("Received %s bytes from %s" % (len(data), address))
                    self.log_error(data.hex())
                    self.log_error("Unknown packet of type:%s received" % packet_type)
            if packet_type == self.PACKET_TYPE_REQUEST_REGISTRATION:
                self.handle_registration(data, address)
            elif packet_type == self.PACKET_TYPE_REQUEST_RDAC_STARTUP:
                self.handle_rdac_request(data, address)
            elif packet_type == self.PACKET_TYPE_REQUEST_DMR_STARTUP:
                self.handle_dmr_request(data, address)

into:

def datagram_received(self, data: bytes, address: Tuple[str, int]) -> None:
        packet_type = self.command_get_type(data)
        is_command = self.packet_is_command(data)
        #self.log_debug("P2P port?: %s.%s" % address)
        if is_command:
            if packet_type not in self.KNOWN_PACKET_TYPES:
                if not self.packet_is_ack(data):
                    self.log_error("Received %s bytes from %s" % (len(data), address))
                    self.log_error(data.hex())
                    self.log_error("Unknown packet of type:%s received" % packet_type)
            if packet_type == self.PACKET_TYPE_REQUEST_REGISTRATION:
                self.handle_registration(data, address)
                self.log_debug("P2P port?: %s.%s" % address)
                print("Wechsel P2P Port von %s in %s" % (self.settings.p2p_port, address[1]))
                self.settings.p2p_port = address[1]
            elif packet_type == self.PACKET_TYPE_REQUEST_RDAC_STARTUP:
                self.handle_rdac_request(data, address)
                print("Wechsel RDAC Port von %s in %s" % (self.settings.rdac_port, address[1]))
                self.settings.rdac_port = address[1]
            elif packet_type == self.PACKET_TYPE_REQUEST_DMR_STARTUP:
                self.handle_dmr_request(data, address)
                print("Wechsel DMR Port von %s in %s" % (self.settings.dmr_port, address[1]))
                self.settings.dmr_port = address[1]

after this the repeater will connect the data will read from the repeater (Frequency, call, radio id) but after this I received not Heartbeat (HyteraMmdvmTranslator - HYTER->HHB Received IPSC Heartbeat, not translating)

And no voice data will send to eachother

if someone speak on mmdvm site I see the Data in the consol window but the repeater do nothing.
if someone speal on the hytera repeater I see nothing in the log

the log says only:
DEBUG - 2023-08-17 00:09:48,588 - MMDVMProtocol - HHB->MMDVM Unsupported common_log_format for data TypeRepeaterPing
DEBUG - 2023-08-17 00:09:48,605 - MMDVMProtocol - Master PONG received
DEBUG - 2023-08-17 00:09:53,594 - MMDVMProtocol - Sending PING
DEBUG - 2023-08-17 00:09:53,596 - MMDVMProtocol - HHB->MMDVM Unsupported common_log_format for data TypeRepeaterPing
DEBUG - 2023-08-17 00:09:53,613 - MMDVMProtocol - Master PONG received

@smarek
Copy link
Member

smarek commented Aug 27, 2023

This is LAN with NAT issue

VT0z2y8m483XFR_Yw6H1GzCFKGS7GMWN4USkfqPQi6c8D_ZplLYhhOXpxddlY4L7zQcsQ0Y2WxOQIRlIJBPtScWqGR67E9P98jDKPbaO0Dx0lUQ47UeTqXTKagLCfwKf38yRl89QnbBD5_oWkfP16cVoVCEPD_2PZuawL5L78VehOBRsbHK4vyR16GdPNrpR30y7Qljpma3mk5US-Q46wvvgj5q8Hjxss_y78byO_Pzu0000

We need to separate Incoming Address (ip, port) from Outgoing Address (ip, port)

Incoming transmission will look like the NAT (router) is the Repeater, and HHB will try to respond to NAT IP instead of Repeater IP


plantuml: VT0z2y8m483XFR_Yw6H1GzCFKGS7GMWN4USkfqPQi6c8D_ZplLYhhOXpxddlY4L7zQcsQ0Y2WxOQIRlIJBPtScWqGR67E9P98jDKPbaO0Dx0lUQ47UeTqXTKagLCfwKf38yRl89QnbBD5_oWkfP16cVoVCEPD_2PZuawL5L78VehOBRsbHK4vyR16GdPNrpR30y7Qljpma3mk5US-Q46wvvgj5q8Hjxss_y78byO_Pzu0000

@smarek
Copy link
Member

smarek commented Aug 27, 2023

@do1kbl how is this configured/handled in kurt's gw_hytera_mmdvm ?

@do1kbl
Copy link
Author

do1kbl commented Aug 27, 2023

I fix it quick and dity I will sent u the file later, but maybe you find a better way but its working.

@smarek
Copy link
Member

smarek commented Aug 27, 2023

"the fix" i received, cannot be solved like this

> git diff --no-index hytera_protocols_Original.py hytera_protocols.py
diff --git a/hytera_protocols_Original.py b/hytera_protocols.py
index a063a4e..e4a4c23 100644
--- a/hytera_protocols_Original.py
+++ b/hytera_protocols.py
@@ -92,7 +92,7 @@ class HyteraP2PProtocol(CustomBridgeDatagramProtocol):
         self.log_debug("RDAC Accept for %s.%s" % address)
 
         # redirect repeater to correct RDAC port
-        data = self.get_redirect_packet(data, self.settings.rdac_port)
+        data = self.get_redirect_packet(data, 62007) #self.settings.rdac_port)
         self.transport.sendto(data, response_address)
 
     @staticmethod
@@ -123,8 +123,9 @@ class HyteraP2PProtocol(CustomBridgeDatagramProtocol):
 
         self.transport.sendto(data, response_address)
         self.log_debug("DMR Accept for %s.%s" % address)
+        self.settings.dmr_port = address[1]
 
-        data = self.get_redirect_packet(data, self.settings.dmr_port)
+        data = self.get_redirect_packet(data, 62006) #self.settings.dmr_port)
         self.transport.sendto(data, response_address)
 
     def handle_ping(self, data: bytes, address: tuple) -> None:
@@ -156,10 +157,18 @@ class HyteraP2PProtocol(CustomBridgeDatagramProtocol):
                     self.log_error("Unknown packet of type:%s received" % packet_type)
             if packet_type == self.PACKET_TYPE_REQUEST_REGISTRATION:
                 self.handle_registration(data, address)
+                self.log_debug("P2P port?: %s.%s" % address)
+                print("Wechsel P2P Port von %s in %s" % (self.settings.p2p_port, address[1]))
+                self.settings.p2p_port = address[1]
             elif packet_type == self.PACKET_TYPE_REQUEST_RDAC_STARTUP:
                 self.handle_rdac_request(data, address)
+                print("Wechsel RDAC Port von %s in %s" % (self.settings.rdac_port, address[1]))
+                self.settings.rdac_port = address[1]
             elif packet_type == self.PACKET_TYPE_REQUEST_DMR_STARTUP:
                 self.handle_dmr_request(data, address)
+                print("Wechsel DMR Port von %s in %s:%s" % (self.settings.dmr_port, address[0], address[1]))
+                self.settings.hytera_repeater_ip = address[0]
+                #self.settings.dmr_port = address[1]
         elif self.packet_is_ping(data):
             self.handle_ping(data, address)
         else:
@@ -541,6 +550,9 @@ class HyteraDMRProtocol(CustomBridgeDatagramProtocol):
 
     def datagram_received(self, data: bytes, addr: Tuple[str, int]) -> None:
         try:
+            #print(addr)
+            self.settings.hytera_repeater_ip = addr[0]
+            self.settings.dmr_port = addr[1]
             self.queue_incoming.put_nowait(parse_hytera_data(data))
         except EOFError as e:
             self.log_error(f"Cannot parse IPSC DMR packet {hexlify(data)} from {addr}")

@do1kbl
Copy link
Author

do1kbl commented Aug 27, 2023

yes but this 6200x ports what is fix in the code need to be from the config but not overwritten by the program

@smarek
Copy link
Member

smarek commented Aug 27, 2023

@do1kbl how would you expect 2 or more repeaters over NAT to be routed? We need unique DMR/RDAC incoming port because we cannot distinguish them from incoming data

@smarek
Copy link
Member

smarek commented Aug 27, 2023

@do1kbl are we solving HHB behind NAT or Repeater behind NAT ?

@smarek
Copy link
Member

smarek commented Aug 27, 2023

Ok, to conclude here

@do1kbl has double-NAT setup

[HHB] <-> [NAT A] <-> Internet <-> [NAT B] <-> [Hytera Repeater]

This can be solved by 2 ways

  1. UDP hole punching (requires STUN server in the internet, https://github.com/pradt2/always-online-stun )
  2. Configure both NATs static port-forwarding

[Untested] With static port-forwarding:

  • HHB sends data to [NAT B External IP + Static port]
  • HHB receives data from [NAT A internal IP + dynamic port]
  • Hytera RPT sends data to [NAT A External IP + Static port]
  • Hytera RPT receives data from [NAT B internal IP + dynamic port]

From previous testing Hytera Repeater is sensitive about the IP+Port data come from, otherwise it's just rejected

I will solve the "single-NAT scenarios" (both "HHB in the LAN" and "RPT in the LAN"), nothing more

@smarek
Copy link
Member

smarek commented Aug 27, 2023

Scenarios

  1. Repeater in LAN, HHB in internet/extranet

repeater_behind_nat

  1. Repeater in internet/extranet, HHB in LAN

repeater_in_the_internet

@smarek
Copy link
Member

smarek commented Aug 27, 2023

Testing results:

  • For host behind NAT the packet comes from the External IP (same UDP src-port, but that might be only routeros if the src-port is not already used on NAT mapping) not the NAT Internal IP
  • For host in the internet, packet comes from NAT External IP (), responding on the IP+Port from which the data came

test script for NAT traversal

# Run as "test_nat.py <LISTEN_IP> <LISTEN_PORT OR 0 FOR DYNAMIC> <TARGET_IP> <TARGET_PORT>

import sys
import asyncio

from asyncio import DatagramProtocol
from asyncio import transports
from typing import Any, Union

listen_ip: str = sys.argv[1]
listen_port: int = int(sys.argv[2])
target_ip: str = sys.argv[3]
target_port: int = int(sys.argv[4])

print(f"Listen {listen_ip}:{listen_port} \nTarget {target_ip}:{target_port}")


class TestProtocol(DatagramProtocol):
    def datagram_received(self, data: bytes, addr: tuple[Union[str, Any], int]) -> None:
        print(f"datagram_received from {addr} data {data.hex()}")
        self.transport.sendto(bytes([0x44, 0x55, 0x66]), addr)

    def connection_made(self, transport: transports.DatagramTransport) -> None:
        print(f"connection_made {transport}")
        self.transport: transports.DatagramTransport = transport
        transport.sendto(bytes([0x11, 0x22, 0x33]), (target_ip, target_port))


async def main() -> None:
    proto = TestProtocol()
    _transport, _protocol = await asyncio.get_running_loop().create_datagram_endpoint(
        lambda: proto, local_addr=(listen_ip, listen_port)
    )
    await asyncio.sleep(60)


asyncio.run(main())

@smarek smarek changed the title Via LTE no connection Via LTE no connection (double-NAT issue) Aug 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants