Added ability to include operator LXMF address in interface discovery info

This commit is contained in:
Mark Qvist 2026-08-16 00:29:14 +02:00
parent ebd9b862e9
commit 38a73f9508
No known key found for this signature in database
3 changed files with 42 additions and 7 deletions

View file

@ -27,6 +27,7 @@ SPREADINGFACTOR = 0x0B
CODINGRATE = 0x0C
MODULATION = 0x0D
CHANNEL = 0x0E
OP_ADDR = 0xF0
APP_NAME = "rnstransport"
@ -123,7 +124,7 @@ class InterfaceAnnouncer():
interface.discovery_height = dhgt
except Exception as e:
RNS.log(f"Error while getting reachable_on from executable at {interface.reachable_on}: {e}", RNS.LOG_ERROR)
RNS.log(f"Error while getting discovery location from executable at {interface.reachable_on}: {e}", RNS.LOG_ERROR)
RNS.log(f"Aborting discovery announce", RNS.LOG_ERROR)
return None
@ -136,11 +137,14 @@ class InterfaceAnnouncer():
LONGITUDE: interface.discovery_longitude,
HEIGHT: interface.discovery_height}
if interface.discovery_lxmf_address: info[OP_ADDR] = interface.discovery_lxmf_address
if interface_type == "TCPClientInterface" and not interface.kiss_framing:
RNS.log(f"Invalid interface discovery configuration for {interface}, aborting discovery announce", RNS.LOG_ERROR)
return None
if interface_type in ["BackboneInterface", "TCPServerInterface"]:
if not interface.reachable_on: return None
reachable_on = self.sanitize(interface.reachable_on)
if not RNS.vendor.platformutils.is_windows():
@ -410,6 +414,11 @@ class InterfaceAnnounceHandler:
discovery_hash_material = info["transport_id"]+info["name"]
info["discovery_hash"] = RNS.Identity.full_hash(discovery_hash_material.encode("utf-8"))
if info and OP_ADDR in unpacked:
if type(unpacked[OP_ADDR]) not in [type(None), bytes]: raise ValueError("Invalid data in operator LXMF address field of announce")
if unpacked[OP_ADDR] and len(unpacked[OP_ADDR]) == RNS.Identity.TRUNCATED_HASHLENGTH//8:
info["operator_lxmf_address"] = RNS.hexrep(unpacked[OP_ADDR], delimit=False)
if self.callback and callable(self.callback): self.callback(info)
except Exception as e:
@ -713,14 +722,22 @@ class InterfaceDiscovery():
return
if interface_type == "I2PInterface":
RNS.log(f"Auto-connecting discovered I2P interfaces is not yet implemented, aborting auto-connect", RNS.LOG_WARNING)
RNS.log(f"You can obtain the configuration entry and add this interface manually instead using rnstatus -D", RNS.LOG_WARNING)
RNS.log(f"Auto-connecting discovered I2P interfaces is not yet implemented, aborting auto-connect", RNS.LOG_DEBUG)
RNS.log(f"You can obtain the configuration entry and add this interface manually instead using rnstatus -D", RNS.LOG_DEBUG)
return
if is_ygg_ipv6(info["reachable_on"]):
# TODO: Somehow detect if yggdrasil is enabled on the system
return
if is_onion_address(info["reachable_on"]):
# TODO: Somehow detect if TOR is enabled on the system
return
if is_ip_address(info["reachable_on"]) and is_invalid_ip_address(info["reachable_on"]):
RNS.log(f"Not auto-connecting discovered interface with invalid IP address: {info["reachable_on"]}", RNS.LOG_DEBUG)
return
interface_name = info["name"]
config_entry = info["config_entry"]
interface_config = {}
@ -851,6 +868,15 @@ def is_ygg_ipv6(address_string):
try: return ipaddress.ip_address(address_string) in ipaddress.IPv6Network("200::/7")
except: return False
def is_onion_address(address_string):
try: return address_string.lower().endswith(".onion")
except: return False
INVALID_IP_ADDRESSES = ["127.0.0.1", "0.0.0.0"]
def is_invalid_ip_address(address_string):
if not address_string in INVALID_IP_ADDRESSES: return False
else: return True
def is_hostname(hostname):
if hostname[-1] == ".": hostname = hostname[:-1]
if len(hostname) > 253: return False

View file

@ -860,6 +860,7 @@ class Reticulum:
discovery_announce_interval = None
discovery_stamp_value = None
discovery_name = None
discovery_lxmf_address = None
discovery_encrypt = False
reachable_on = None
publish_ifac = False
@ -891,6 +892,11 @@ class Reticulum:
if "discovery_frequency" in c: discovery_frequency = c.as_int("discovery_frequency")
if "discovery_bandwidth" in c: discovery_bandwidth = c.as_int("discovery_bandwidth")
if "discovery_modulation" in c: discovery_modulation = c.as_int("discovery_modulation")
if "discovery_lxmf_address" in c:
if len(c["discovery_lxmf_address"]) == RNS.Identity.TRUNCATED_HASHLENGTH//8*2:
try: discovery_lxmf_address = bytes.fromhex(c["discovery_lxmf_address"])
except: RNS.log(f"Invalid interface discovery LXMF address: {c['discovery_lxmf_address']}", RNS.LOG_ERROR)
else: RNS.log(f"Invalid length for interface discovery LXMF address: {c['discovery_lxmf_address']}", RNS.LOG_ERROR)
if not interface_mode in [Interface.Interface.MODE_GATEWAY, Interface.Interface.MODE_ACCESS_POINT, Interface.Interface.MODE_INTERNAL]:
if not ignore_config_warnings:
@ -922,6 +928,7 @@ class Reticulum:
interface.discovery_publish_ifac = publish_ifac
interface.reachable_on = reachable_on
interface.discovery_name = discovery_name
interface.discovery_lxmf_address = discovery_lxmf_address
interface.discovery_encrypt = discovery_encrypt
interface.discovery_stamp_value = discovery_stamp_value
interface.discovery_location = discovery_location

View file

@ -225,15 +225,18 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
location = f"{lat}, {lon}{height}"
else: location = "Unknown"
lxmf_addr = None
transport_id = None
network = None
network = None
if "operator_lxmf_address" in i: lxmf_addr = i['operator_lxmf_address']
if "transport_id" in i: transport_id = i["transport_id"]
if "transport_id" in i and "network_id" in i and i["transport_id"] != i["network_id"]:
network = i["network_id"]
if idx > 0: print("\n"+"="*32+"\n")
if idx > 0: print("\n"+"="*47+"\n")
if network: print(f"Network ID : {network}")
if transport_id: print(f"Transport ID : {transport_id}")
if lxmf_addr: print(f"LXMF address : {lxmf_addr}")
print(f"Name : {name}")
print(f"Type : {if_type}")
@ -258,8 +261,7 @@ def program_setup(configdir, dispall=False, verbosity=0, name_filter=None, json=
config_lines = i["config_entry"].split('\n')
for line in config_lines: print(f" {line}")
except Exception as e:
pass
except Exception as e: pass
else:
print(f"{'Name':<25} {'Type':<12} {'Status':<12} {'Last Heard':<12} {'Value':<8} {'Location':<15}")