Added IPv6 support to BackboneInterface

This commit is contained in:
Mark Qvist 2025-04-09 14:23:39 +02:00
parent b58cb3c0ed
commit 628c4984a3
3 changed files with 49 additions and 18 deletions

View file

@ -64,9 +64,9 @@ class BackboneInterface(Interface):
bind_ip = ifaddr[netinfo.AF_INET6][0]["addr"] bind_ip = ifaddr[netinfo.AF_INET6][0]["addr"]
if bind_ip.lower().startswith("fe80::"): if bind_ip.lower().startswith("fe80::"):
# We'll need to add the interface as scope for link-local addresses # We'll need to add the interface as scope for link-local addresses
return self.get_address_for_host(f"{bind_ip}%{name}", bind_port) return BackboneInterface.get_address_for_host(f"{bind_ip}%{name}", bind_port, prefer_ipv6)
else: else:
return self.get_address_for_host(bind_ip, bind_port) return BackboneInterface.get_address_for_host(bind_ip, bind_port, prefer_ipv6)
elif netinfo.AF_INET in ifaddr: elif netinfo.AF_INET in ifaddr:
bind_ip = ifaddr[netinfo.AF_INET][0]["addr"] bind_ip = ifaddr[netinfo.AF_INET][0]["addr"]
return (bind_ip, bind_port) return (bind_ip, bind_port)
@ -74,8 +74,15 @@ class BackboneInterface(Interface):
raise SystemError(f"No addresses available on specified kernel interface \"{name}\" for BackboneInterface to bind to") raise SystemError(f"No addresses available on specified kernel interface \"{name}\" for BackboneInterface to bind to")
@staticmethod @staticmethod
def get_address_for_host(name, bind_port): def get_address_for_host(name, bind_port, prefer_ipv6=False):
address_info = socket.getaddrinfo(name, bind_port, proto=socket.IPPROTO_TCP)[0] address_infos = socket.getaddrinfo(name, bind_port, proto=socket.IPPROTO_TCP)
address_info = address_infos[0]
for entry in address_infos:
if prefer_ipv6 and entry[0] == socket.AF_INET6:
address_info = entry; break
elif not prefer_ipv6 and entry[0] == socket.AF_INET:
address_info = entry; break
if address_info[0] == socket.AF_INET6: if address_info[0] == socket.AF_INET6:
return (name, bind_port, address_info[4][2], address_info[4][3]) return (name, bind_port, address_info[4][2], address_info[4][3])
elif address_info[0] == socket.AF_INET: elif address_info[0] == socket.AF_INET:
@ -124,14 +131,16 @@ class BackboneInterface(Interface):
else: else:
if bindip == None: if bindip == None:
raise SystemError(f"No TCP bind IP configured for interface \"{name}\"") raise SystemError(f"No TCP bind IP configured for interface \"{name}\"")
bind_address = self.get_address_for_host(bindip, self.bind_port) bind_address = self.get_address_for_host(bindip, self.bind_port, prefer_ipv6)
if bind_address != None: if bind_address != None:
self.receives = True self.receives = True
self.bind_ip = bind_address[0] self.bind_ip = bind_address[0]
self.owner = owner self.owner = owner
BackboneInterface.add_listener(self, bind_address) if len(bind_address) == 2 : BackboneInterface.add_listener(self, bind_address, socket_type=socket.AF_INET)
elif len(bind_address) == 4: BackboneInterface.add_listener(self, bind_address, socket_type=socket.AF_INET6)
self.bitrate = self.BITRATE_GUESS self.bitrate = self.BITRATE_GUESS
self.online = True self.online = True
@ -153,6 +162,10 @@ class BackboneInterface(Interface):
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(bind_address) server_socket.bind(bind_address)
elif socket_type == socket.AF_INET6:
server_socket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(bind_address)
elif socket_type == socket.AF_UNIX: elif socket_type == socket.AF_UNIX:
server_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) server_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
server_socket.bind(bind_address) server_socket.bind(bind_address)
@ -216,7 +229,6 @@ class BackboneInterface(Interface):
with BackboneInterface._job_lock: with BackboneInterface._job_lock:
if BackboneInterface._job_active: return if BackboneInterface._job_active: return
else: else:
RNS.log(f"Starting BackboneInterface I/O handler", RNS.LOG_DEBUG) # TODO: Remove debug
BackboneInterface._job_active = True BackboneInterface._job_active = True
BackboneInterface.ensure_epoll() BackboneInterface.ensure_epoll()
try: try:
@ -419,6 +431,7 @@ class BackboneClientInterface(Interface):
i2p_tunneled = c.as_bool("i2p_tunneled") if "i2p_tunneled" in c else False i2p_tunneled = c.as_bool("i2p_tunneled") if "i2p_tunneled" in c else False
connect_timeout = c.as_int("connect_timeout") if "connect_timeout" in c else None connect_timeout = c.as_int("connect_timeout") if "connect_timeout" in c else None
max_reconnect_tries = c.as_int("max_reconnect_tries") if "max_reconnect_tries" in c else None max_reconnect_tries = c.as_int("max_reconnect_tries") if "max_reconnect_tries" in c else None
prefer_ipv6 = c.as_bool("prefer_ipv6") if "prefer_ipv6" in c else False
self.HW_MTU = BackboneInterface.HW_MTU self.HW_MTU = BackboneInterface.HW_MTU
self.IN = True self.IN = True
@ -432,6 +445,7 @@ class BackboneClientInterface(Interface):
self.owner = owner self.owner = owner
self.online = False self.online = False
self.detached = False self.detached = False
self.prefer_ipv6 = prefer_ipv6
self.i2p_tunneled = i2p_tunneled self.i2p_tunneled = i2p_tunneled
self.mode = RNS.Interfaces.Interface.Interface.MODE_FULL self.mode = RNS.Interfaces.Interface.Interface.MODE_FULL
self.bitrate = BackboneClientInterface.BITRATE_GUESS self.bitrate = BackboneClientInterface.BITRATE_GUESS
@ -507,7 +521,14 @@ class BackboneClientInterface(Interface):
if initial: if initial:
RNS.log("Establishing TCP connection for "+str(self)+"...", RNS.LOG_DEBUG) RNS.log("Establishing TCP connection for "+str(self)+"...", RNS.LOG_DEBUG)
address_info = socket.getaddrinfo(self.target_ip, self.target_port, proto=socket.IPPROTO_TCP)[0] address_infos = socket.getaddrinfo(self.target_ip, self.target_port, proto=socket.IPPROTO_TCP)
address_info = address_infos[0]
for entry in address_infos:
if self.prefer_ipv6 and entry[0] == socket.AF_INET6:
address_info = entry; break
elif not self.prefer_ipv6 and entry[0] == socket.AF_INET:
address_info = entry; break
address_family = address_info[0] address_family = address_info[0]
target_address = address_info[4] target_address = address_info[4]

View file

@ -454,9 +454,9 @@ class TCPServerInterface(Interface):
bind_ip = ifaddr[netinfo.AF_INET6][0]["addr"] bind_ip = ifaddr[netinfo.AF_INET6][0]["addr"]
if bind_ip.lower().startswith("fe80::"): if bind_ip.lower().startswith("fe80::"):
# We'll need to add the interface as scope for link-local addresses # We'll need to add the interface as scope for link-local addresses
return TCPServerInterface.get_address_for_host(f"{bind_ip}%{name}", bind_port) return TCPServerInterface.get_address_for_host(f"{bind_ip}%{name}", bind_port, prefer_ipv6)
else: else:
return TCPServerInterface.get_address_for_host(bind_ip, bind_port) return TCPServerInterface.get_address_for_host(bind_ip, bind_port, prefer_ipv6)
elif netinfo.AF_INET in ifaddr: elif netinfo.AF_INET in ifaddr:
bind_ip = ifaddr[netinfo.AF_INET][0]["addr"] bind_ip = ifaddr[netinfo.AF_INET][0]["addr"]
return (bind_ip, bind_port) return (bind_ip, bind_port)
@ -464,8 +464,15 @@ class TCPServerInterface(Interface):
raise SystemError(f"No addresses available on specified kernel interface \"{name}\" for TCPServerInterface to bind to") raise SystemError(f"No addresses available on specified kernel interface \"{name}\" for TCPServerInterface to bind to")
@staticmethod @staticmethod
def get_address_for_host(name, bind_port): def get_address_for_host(name, bind_port, prefer_ipv6=False):
address_info = socket.getaddrinfo(name, bind_port, proto=socket.IPPROTO_TCP)[0] address_infos = socket.getaddrinfo(name, bind_port, proto=socket.IPPROTO_TCP)
address_info = address_infos[0]
for entry in address_infos:
if prefer_ipv6 and entry[0] == socket.AF_INET6:
address_info = entry; break
elif not prefer_ipv6 and entry[0] == socket.AF_INET:
address_info = entry; break
if address_info[0] == socket.AF_INET6: if address_info[0] == socket.AF_INET6:
return (name, bind_port, address_info[4][2], address_info[4][3]) return (name, bind_port, address_info[4][2], address_info[4][3])
elif address_info[0] == socket.AF_INET: elif address_info[0] == socket.AF_INET:
@ -517,7 +524,7 @@ class TCPServerInterface(Interface):
else: else:
if bindip == None: if bindip == None:
raise SystemError(f"No TCP bind IP configured for interface \"{name}\"") raise SystemError(f"No TCP bind IP configured for interface \"{name}\"")
bind_address = TCPServerInterface.get_address_for_host(bindip, self.bind_port) bind_address = TCPServerInterface.get_address_for_host(bindip, self.bind_port, prefer_ipv6)
if bind_address != None: if bind_address != None:
self.receives = True self.receives = True

View file

@ -672,8 +672,15 @@ class Reticulum:
interface = AutoInterface.AutoInterface(RNS.Transport, interface_config) interface = AutoInterface.AutoInterface(RNS.Transport, interface_config)
interface_post_init(interface) interface_post_init(interface)
if c["type"] == "BackboneInterface" or c["type"] == "BackboneClientInterface":
if "port" in c: c["listen_port"] = c["port"]
if "port" in c: c["target_port"] = c["port"]
if "remote" in c: c["target_host"] = c["remote"]
if "listen_on" in c: c["listen_ip"] = c["listen_on"]
if c["type"] == "BackboneInterface": if c["type"] == "BackboneInterface":
interface = BackboneInterface.BackboneInterface(RNS.Transport, interface_config) if "target_host" in c: interface = BackboneInterface.BackboneClientInterface(RNS.Transport, interface_config)
else: interface = BackboneInterface.BackboneInterface(RNS.Transport, interface_config)
interface_post_init(interface) interface_post_init(interface)
if c["type"] == "BackboneClientInterface": if c["type"] == "BackboneClientInterface":
@ -1195,10 +1202,6 @@ class Reticulum:
rpc_connection.send({"get": "next_hop", "destination_hash": destination}) rpc_connection.send({"get": "next_hop", "destination_hash": destination})
response = rpc_connection.recv() response = rpc_connection.recv()
# TODO: Remove this debugging function
# if not response:
# response = RNS.Transport.next_hop(destination)
return response return response
else: else: