2022-04-01 15:18:18 +00:00
# MIT License
#
# Copyright (c) 2016-2022 Mark Qvist / unsigned.io
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
2021-12-08 19:46:53 +00:00
from . Interface import Interface
2023-05-11 17:54:26 +00:00
from collections import deque
2021-12-08 19:46:53 +00:00
import socketserver
import threading
2023-02-28 14:47:09 +00:00
import re
2021-12-08 19:46:53 +00:00
import socket
import struct
import time
import sys
import RNS
class AutoInterface ( Interface ) :
DEFAULT_DISCOVERY_PORT = 29716
DEFAULT_DATA_PORT = 42671
DEFAULT_GROUP_ID = " reticulum " . encode ( " utf-8 " )
2024-11-20 19:14:02 +00:00
DEFAULT_IFAC_SIZE = 16
2021-12-08 19:46:53 +00:00
SCOPE_LINK = " 2 "
SCOPE_ADMIN = " 4 "
SCOPE_SITE = " 5 "
SCOPE_ORGANISATION = " 8 "
2021-12-09 15:07:36 +00:00
SCOPE_GLOBAL = " e "
2021-12-08 19:46:53 +00:00
2024-03-23 03:54:56 +00:00
MULTICAST_PERMANENT_ADDRESS_TYPE = " 0 "
MULTICAST_TEMPORARY_ADDRESS_TYPE = " 1 "
2022-02-25 19:29:47 +00:00
PEERING_TIMEOUT = 7.5
2021-12-08 19:46:53 +00:00
2023-03-07 15:43:10 +00:00
ALL_IGNORE_IFS = [ " lo0 " ]
2021-12-10 10:10:09 +00:00
DARWIN_IGNORE_IFS = [ " awdl0 " , " llw0 " , " lo0 " , " en5 " ]
2022-01-12 11:12:04 +00:00
ANDROID_IGNORE_IFS = [ " dummy0 " , " lo " , " tun0 " ]
2021-12-10 09:58:28 +00:00
2022-04-17 17:07:32 +00:00
BITRATE_GUESS = 10 * 1000 * 1000
2023-09-14 20:14:31 +00:00
MULTI_IF_DEQUE_LEN = 48
MULTI_IF_DEQUE_TTL = 0.75
2023-05-11 17:54:26 +00:00
2022-11-24 16:19:01 +00:00
def handler_factory ( self , callback ) :
def create_handler ( * args , * * keys ) :
return AutoInterfaceHandler ( callback , * args , * * keys )
return create_handler
2023-02-28 14:47:09 +00:00
def descope_linklocal ( self , link_local_addr ) :
# Drop scope specifier expressd as %ifname (macOS)
link_local_addr = link_local_addr . split ( " % " ) [ 0 ]
# Drop embedded scope specifier (NetBSD, OpenBSD)
link_local_addr = re . sub ( r " fe80:[0-9a-f]*:: " , " fe80:: " , link_local_addr )
return link_local_addr
2023-05-04 15:55:58 +00:00
def list_interfaces ( self ) :
ifs = self . netinfo . interfaces ( )
return ifs
def list_addresses ( self , ifname ) :
ifas = self . netinfo . ifaddresses ( ifname )
return ifas
2024-05-16 16:09:11 +00:00
def interface_name_to_index ( self , ifname ) :
# socket.if_nametoindex doesn't work with uuid interface names on windows, it wants the ethernet_0 style
# we will just get the index from netinfo instead as it seems to work
if RNS . vendor . platformutils . is_windows ( ) :
return self . netinfo . interface_names_to_indexes ( ) [ ifname ]
return socket . if_nametoindex ( ifname )
2024-11-20 19:14:02 +00:00
# def __init__(self, owner, name, group_id=None, discovery_scope=None, discovery_port=None, multicast_address_type=None, data_port=None, allowed_interfaces=None, ignored_interfaces=None, configured_bitrate=None):
def __init__ ( self , owner , configuration ) :
c = configuration
name = c [ " name " ]
group_id = c [ " group_id " ] if " group_id " in c else None
discovery_scope = c [ " discovery_scope " ] if " discovery_scope " in c else None
discovery_port = int ( c [ " discovery_port " ] ) if " discovery_port " in c else None
multicast_address_type = c [ " multicast_address_type " ] if " multicast_address_type " in c else None
data_port = int ( c [ " data_port " ] ) if " data_port " in c else None
allowed_interfaces = c . as_list ( " devices " ) if " devices " in c else None
ignored_interfaces = c . as_list ( " ignored_devices " ) if " ignored_devices " in c else None
configured_bitrate = c [ " configured_bitrate " ]
2023-05-04 15:55:58 +00:00
from RNS . vendor . ifaddr import niwrapper
2023-09-30 17:11:10 +00:00
super ( ) . __init__ ( )
2023-05-04 15:55:58 +00:00
self . netinfo = niwrapper
2021-12-08 19:46:53 +00:00
2022-05-29 13:43:50 +00:00
self . HW_MTU = 1064
2021-12-08 19:46:53 +00:00
self . IN = True
self . OUT = False
self . name = name
self . online = False
self . peers = { }
2021-12-09 15:07:36 +00:00
self . link_local_addresses = [ ]
self . adopted_interfaces = { }
2022-11-24 16:19:01 +00:00
self . interface_servers = { }
2022-02-22 13:43:14 +00:00
self . multicast_echoes = { }
2022-02-22 19:16:02 +00:00
self . timed_out_interfaces = { }
2023-05-11 17:54:26 +00:00
self . mif_deque = deque ( maxlen = AutoInterface . MULTI_IF_DEQUE_LEN )
2023-09-14 20:14:31 +00:00
self . mif_deque_times = deque ( maxlen = AutoInterface . MULTI_IF_DEQUE_LEN )
2022-12-22 17:20:34 +00:00
self . carrier_changed = False
2021-12-08 19:46:53 +00:00
self . outbound_udp_socket = None
2022-10-12 12:58:00 +00:00
self . announce_rate_target = None
2022-04-27 11:19:48 +00:00
self . announce_interval = AutoInterface . PEERING_TIMEOUT / 6.0
2021-12-11 14:41:34 +00:00
self . peer_job_interval = AutoInterface . PEERING_TIMEOUT * 1.1
2021-12-09 15:07:36 +00:00
self . peering_timeout = AutoInterface . PEERING_TIMEOUT
2022-02-25 19:29:47 +00:00
self . multicast_echo_timeout = AutoInterface . PEERING_TIMEOUT / 2
2021-12-09 15:07:36 +00:00
2023-09-19 22:53:51 +00:00
# Increase peering timeout on Android, due to potential
# low-power modes implemented on many chipsets.
if RNS . vendor . platformutils . is_android ( ) :
self . peering_timeout * = 3
2021-12-09 15:07:36 +00:00
if allowed_interfaces == None :
self . allowed_interfaces = [ ]
else :
self . allowed_interfaces = allowed_interfaces
if ignored_interfaces == None :
self . ignored_interfaces = [ ]
else :
self . ignored_interfaces = ignored_interfaces
2021-12-08 19:46:53 +00:00
if group_id == None :
self . group_id = AutoInterface . DEFAULT_GROUP_ID
else :
self . group_id = group_id . encode ( " utf-8 " )
if discovery_port == None :
self . discovery_port = AutoInterface . DEFAULT_DISCOVERY_PORT
else :
self . discovery_port = discovery_port
2024-03-23 03:54:56 +00:00
if multicast_address_type == None :
self . multicast_address_type = AutoInterface . MULTICAST_TEMPORARY_ADDRESS_TYPE
elif str ( multicast_address_type ) . lower ( ) == " temporary " :
self . multicast_address_type = AutoInterface . MULTICAST_TEMPORARY_ADDRESS_TYPE
elif str ( multicast_address_type ) . lower ( ) == " permanent " :
self . multicast_address_type = AutoInterface . MULTICAST_PERMANENT_ADDRESS_TYPE
2024-05-01 13:49:48 +00:00
else :
self . multicast_address_type = AutoInterface . MULTICAST_TEMPORARY_ADDRESS_TYPE
2024-03-23 03:54:56 +00:00
2021-12-08 19:46:53 +00:00
if data_port == None :
self . data_port = AutoInterface . DEFAULT_DATA_PORT
else :
self . data_port = data_port
if discovery_scope == None :
self . discovery_scope = AutoInterface . SCOPE_LINK
elif str ( discovery_scope ) . lower ( ) == " link " :
self . discovery_scope = AutoInterface . SCOPE_LINK
elif str ( discovery_scope ) . lower ( ) == " admin " :
self . discovery_scope = AutoInterface . SCOPE_ADMIN
elif str ( discovery_scope ) . lower ( ) == " site " :
self . discovery_scope = AutoInterface . SCOPE_SITE
elif str ( discovery_scope ) . lower ( ) == " organisation " :
self . discovery_scope = AutoInterface . SCOPE_ORGANISATION
elif str ( discovery_scope ) . lower ( ) == " global " :
self . discovery_scope = AutoInterface . SCOPE_GLOBAL
2021-12-09 15:07:36 +00:00
self . group_hash = RNS . Identity . full_hash ( self . group_id )
g = self . group_hash
#gt = "{:02x}".format(g[1]+(g[0]<<8))
gt = " 0 "
gt + = " : " + " {:02x} " . format ( g [ 3 ] + ( g [ 2 ] << 8 ) )
gt + = " : " + " {:02x} " . format ( g [ 5 ] + ( g [ 4 ] << 8 ) )
gt + = " : " + " {:02x} " . format ( g [ 7 ] + ( g [ 6 ] << 8 ) )
gt + = " : " + " {:02x} " . format ( g [ 9 ] + ( g [ 8 ] << 8 ) )
gt + = " : " + " {:02x} " . format ( g [ 11 ] + ( g [ 10 ] << 8 ) )
gt + = " : " + " {:02x} " . format ( g [ 13 ] + ( g [ 12 ] << 8 ) )
2024-03-23 03:54:56 +00:00
self . mcast_discovery_address = " ff " + self . multicast_address_type + self . discovery_scope + " : " + gt
2021-12-09 15:07:36 +00:00
2021-12-08 19:46:53 +00:00
suitable_interfaces = 0
2023-05-04 15:55:58 +00:00
for ifname in self . list_interfaces ( ) :
2024-05-17 21:54:48 +00:00
try :
if RNS . vendor . platformutils . is_darwin ( ) and ifname in AutoInterface . DARWIN_IGNORE_IFS and not ifname in self . allowed_interfaces :
RNS . log ( str ( self ) + " skipping Darwin AWDL or tethering interface " + str ( ifname ) , RNS . LOG_EXTREME )
elif RNS . vendor . platformutils . is_darwin ( ) and ifname == " lo0 " :
RNS . log ( str ( self ) + " skipping Darwin loopback interface " + str ( ifname ) , RNS . LOG_EXTREME )
elif RNS . vendor . platformutils . is_android ( ) and ifname in AutoInterface . ANDROID_IGNORE_IFS and not ifname in self . allowed_interfaces :
RNS . log ( str ( self ) + " skipping Android system interface " + str ( ifname ) , RNS . LOG_EXTREME )
elif ifname in self . ignored_interfaces :
RNS . log ( str ( self ) + " ignoring disallowed interface " + str ( ifname ) , RNS . LOG_EXTREME )
elif ifname in AutoInterface . ALL_IGNORE_IFS :
RNS . log ( str ( self ) + " skipping interface " + str ( ifname ) , RNS . LOG_EXTREME )
2021-12-08 19:46:53 +00:00
else :
2024-05-17 21:54:48 +00:00
if len ( self . allowed_interfaces ) > 0 and not ifname in self . allowed_interfaces :
RNS . log ( str ( self ) + " ignoring interface " + str ( ifname ) + " since it was not allowed " , RNS . LOG_EXTREME )
else :
addresses = self . list_addresses ( ifname )
if self . netinfo . AF_INET6 in addresses :
link_local_addr = None
for address in addresses [ self . netinfo . AF_INET6 ] :
if " addr " in address :
if address [ " addr " ] . startswith ( " fe80: " ) :
link_local_addr = self . descope_linklocal ( address [ " addr " ] )
self . link_local_addresses . append ( link_local_addr )
self . adopted_interfaces [ ifname ] = link_local_addr
self . multicast_echoes [ ifname ] = time . time ( )
nice_name = self . netinfo . interface_name_to_nice_name ( ifname )
if nice_name != None and nice_name != ifname :
RNS . log ( f " { self } Selecting link-local address { link_local_addr } for interface { nice_name } / { ifname } " , RNS . LOG_EXTREME )
else :
RNS . log ( f " { self } Selecting link-local address { link_local_addr } for interface { ifname } " , RNS . LOG_EXTREME )
if link_local_addr == None :
RNS . log ( str ( self ) + " No link-local IPv6 address configured for " + str ( ifname ) + " , skipping interface " , RNS . LOG_EXTREME )
2023-05-02 15:39:06 +00:00
else :
2024-05-17 21:54:48 +00:00
mcast_addr = self . mcast_discovery_address
RNS . log ( str ( self ) + " Creating multicast discovery listener on " + str ( ifname ) + " with address " + str ( mcast_addr ) , RNS . LOG_EXTREME )
# Struct with interface index
if_struct = struct . pack ( " I " , self . interface_name_to_index ( ifname ) )
# Set up multicast socket
discovery_socket = socket . socket ( socket . AF_INET6 , socket . SOCK_DGRAM )
discovery_socket . setsockopt ( socket . SOL_SOCKET , socket . SO_REUSEADDR , 1 )
if hasattr ( socket , " SO_REUSEPORT " ) :
discovery_socket . setsockopt ( socket . SOL_SOCKET , socket . SO_REUSEPORT , 1 )
discovery_socket . setsockopt ( socket . IPPROTO_IPV6 , socket . IPV6_MULTICAST_IF , if_struct )
# Join multicast group
mcast_group = socket . inet_pton ( socket . AF_INET6 , mcast_addr ) + if_struct
discovery_socket . setsockopt ( socket . IPPROTO_IPV6 , socket . IPV6_JOIN_GROUP , mcast_group )
# Bind socket
if RNS . vendor . platformutils . is_windows ( ) :
# window throws "[WinError 10049] The requested address is not valid in its context"
# when trying to use the multicast address as host, or when providing interface index
# passing an empty host appears to work, but probably not exactly how we want it to...
discovery_socket . bind ( ( ' ' , self . discovery_port ) )
2023-05-02 15:39:06 +00:00
2024-05-16 16:09:11 +00:00
else :
2024-05-17 21:54:48 +00:00
if self . discovery_scope == AutoInterface . SCOPE_LINK :
addr_info = socket . getaddrinfo ( mcast_addr + " % " + ifname , self . discovery_port , socket . AF_INET6 , socket . SOCK_DGRAM )
else :
addr_info = socket . getaddrinfo ( mcast_addr , self . discovery_port , socket . AF_INET6 , socket . SOCK_DGRAM )
discovery_socket . bind ( addr_info [ 0 ] [ 4 ] )
2021-12-09 15:07:36 +00:00
2024-05-17 21:54:48 +00:00
# Set up thread for discovery packets
def discovery_loop ( ) :
self . discovery_handler ( discovery_socket , ifname )
2021-12-09 15:07:36 +00:00
2024-05-17 21:54:48 +00:00
thread = threading . Thread ( target = discovery_loop )
thread . daemon = True
thread . start ( )
2021-12-09 15:07:36 +00:00
2024-05-17 21:54:48 +00:00
suitable_interfaces + = 1
except Exception as e :
nice_name = self . netinfo . interface_name_to_nice_name ( ifname )
if nice_name != None and nice_name != ifname :
RNS . log ( f " Could not configure the system interface { nice_name } / { ifname } for use with { self } , skipping it. The contained exception was: { e } " , RNS . LOG_ERROR )
else :
RNS . log ( f " Could not configure the system interface { ifname } for use with { self } , skipping it. The contained exception was: { e } " , RNS . LOG_ERROR )
2021-12-08 19:46:53 +00:00
if suitable_interfaces == 0 :
2021-12-11 14:41:34 +00:00
RNS . log ( str ( self ) + " could not autoconfigure. This interface currently provides no connectivity. " , RNS . LOG_WARNING )
2021-12-08 19:46:53 +00:00
else :
self . receives = True
2023-03-09 17:32:14 +00:00
if configured_bitrate != None :
self . bitrate = configured_bitrate
else :
self . bitrate = AutoInterface . BITRATE_GUESS
2021-12-11 15:42:15 +00:00
peering_wait = self . announce_interval * 1.2
RNS . log ( str ( self ) + " discovering peers for " + str ( round ( peering_wait , 2 ) ) + " seconds... " , RNS . LOG_VERBOSE )
2021-12-11 14:41:34 +00:00
2021-12-08 19:46:53 +00:00
self . owner = owner
socketserver . UDPServer . address_family = socket . AF_INET6
2021-12-10 09:35:25 +00:00
for ifname in self . adopted_interfaces :
2024-05-16 16:09:11 +00:00
local_addr = self . adopted_interfaces [ ifname ] + " % " + str ( self . interface_name_to_index ( ifname ) )
2021-12-10 09:35:25 +00:00
addr_info = socket . getaddrinfo ( local_addr , self . data_port , socket . AF_INET6 , socket . SOCK_DGRAM )
address = addr_info [ 0 ] [ 4 ]
2022-11-24 16:19:01 +00:00
udp_server = socketserver . UDPServer ( address , self . handler_factory ( self . processIncoming ) )
self . interface_servers [ ifname ] = udp_server
2021-12-10 09:35:25 +00:00
2022-11-24 16:19:01 +00:00
thread = threading . Thread ( target = udp_server . serve_forever )
2022-09-30 17:02:25 +00:00
thread . daemon = True
2021-12-10 09:35:25 +00:00
thread . start ( )
2021-12-08 19:46:53 +00:00
2021-12-09 15:07:36 +00:00
job_thread = threading . Thread ( target = self . peer_jobs )
2022-09-30 17:02:25 +00:00
job_thread . daemon = True
2021-12-09 15:07:36 +00:00
job_thread . start ( )
2021-12-11 14:41:34 +00:00
time . sleep ( peering_wait )
2021-12-08 19:46:53 +00:00
self . online = True
def discovery_handler ( self , socket , ifname ) :
2021-12-09 15:07:36 +00:00
def announce_loop ( ) :
self . announce_handler ( ifname )
thread = threading . Thread ( target = announce_loop )
2022-09-30 17:02:25 +00:00
thread . daemon = True
2021-12-09 15:07:36 +00:00
thread . start ( )
2021-12-08 19:46:53 +00:00
while True :
data , ipv6_src = socket . recvfrom ( 1024 )
2021-12-09 15:07:36 +00:00
expected_hash = RNS . Identity . full_hash ( self . group_id + ipv6_src [ 0 ] . encode ( " utf-8 " ) )
if data == expected_hash :
2021-12-08 19:46:53 +00:00
self . add_peer ( ipv6_src [ 0 ] , ifname )
2021-12-09 15:07:36 +00:00
else :
RNS . log ( str ( self ) + " received peering packet on " + str ( ifname ) + " from " + str ( ipv6_src [ 0 ] ) + " , but authentication hash was incorrect. " , RNS . LOG_DEBUG )
2021-12-08 19:46:53 +00:00
2021-12-09 15:07:36 +00:00
def peer_jobs ( self ) :
while True :
time . sleep ( self . peer_job_interval )
now = time . time ( )
timed_out_peers = [ ]
2022-02-22 13:43:14 +00:00
# Check for timed out peers
2021-12-09 15:07:36 +00:00
for peer_addr in self . peers :
peer = self . peers [ peer_addr ]
last_heard = peer [ 1 ]
if now > last_heard + self . peering_timeout :
timed_out_peers . append ( peer_addr )
2022-02-22 13:43:14 +00:00
# Remove any timed out peers
2021-12-09 15:07:36 +00:00
for peer_addr in timed_out_peers :
2021-12-10 13:48:30 +00:00
removed_peer = self . peers . pop ( peer_addr )
RNS . log ( str ( self ) + " removed peer " + str ( peer_addr ) + " on " + str ( removed_peer [ 0 ] ) , RNS . LOG_DEBUG )
2022-02-22 19:16:02 +00:00
for ifname in self . adopted_interfaces :
2022-10-19 09:57:09 +00:00
# Check that the link-local address has not changed
try :
2023-05-04 15:55:58 +00:00
addresses = self . list_addresses ( ifname )
if self . netinfo . AF_INET6 in addresses :
2022-10-19 09:57:09 +00:00
link_local_addr = None
2023-05-04 15:55:58 +00:00
for address in addresses [ self . netinfo . AF_INET6 ] :
2022-10-19 09:57:09 +00:00
if " addr " in address :
if address [ " addr " ] . startswith ( " fe80: " ) :
2023-02-28 14:47:09 +00:00
link_local_addr = self . descope_linklocal ( address [ " addr " ] )
2022-10-19 09:57:09 +00:00
if link_local_addr != self . adopted_interfaces [ ifname ] :
2022-12-22 16:46:46 +00:00
old_link_local_address = self . adopted_interfaces [ ifname ]
RNS . log ( " Replacing link-local address " + str ( old_link_local_address ) + " for " + str ( ifname ) + " with " + str ( link_local_addr ) , RNS . LOG_DEBUG )
2022-10-19 09:57:09 +00:00
self . adopted_interfaces [ ifname ] = link_local_addr
2022-12-22 16:46:46 +00:00
self . link_local_addresses . append ( link_local_addr )
if old_link_local_address in self . link_local_addresses :
self . link_local_addresses . remove ( old_link_local_address )
2022-10-19 09:57:09 +00:00
2022-11-24 16:19:01 +00:00
local_addr = link_local_addr + " % " + ifname
addr_info = socket . getaddrinfo ( local_addr , self . data_port , socket . AF_INET6 , socket . SOCK_DGRAM )
listen_address = addr_info [ 0 ] [ 4 ]
if ifname in self . interface_servers :
2022-12-22 16:46:46 +00:00
RNS . log ( " Shutting down previous UDP listener for " + str ( self ) + " " + str ( ifname ) , RNS . LOG_DEBUG )
2022-11-24 16:19:01 +00:00
previous_server = self . interface_servers [ ifname ]
def shutdown_server ( ) :
previous_server . shutdown ( )
threading . Thread ( target = shutdown_server , daemon = True ) . start ( )
2022-12-22 16:46:46 +00:00
RNS . log ( " Starting new UDP listener for " + str ( self ) + " " + str ( ifname ) , RNS . LOG_DEBUG )
2022-11-24 16:19:01 +00:00
udp_server = socketserver . UDPServer ( listen_address , self . handler_factory ( self . processIncoming ) )
self . interface_servers [ ifname ] = udp_server
thread = threading . Thread ( target = udp_server . serve_forever )
thread . daemon = True
thread . start ( )
2023-12-05 23:06:45 +00:00
self . carrier_changed = True
2022-10-19 09:57:09 +00:00
except Exception as e :
RNS . log ( " Could not get device information while updating link-local addresses for " + str ( self ) + " . The contained exception was: " + str ( e ) , RNS . LOG_ERROR )
# Check multicast echo timeouts
2022-02-22 19:16:02 +00:00
last_multicast_echo = 0
if ifname in self . multicast_echoes :
last_multicast_echo = self . multicast_echoes [ ifname ]
if now - last_multicast_echo > self . multicast_echo_timeout :
if ifname in self . timed_out_interfaces and self . timed_out_interfaces [ ifname ] == False :
2022-12-22 17:20:34 +00:00
self . carrier_changed = True
2022-02-22 19:16:02 +00:00
RNS . log ( " Multicast echo timeout for " + str ( ifname ) + " . Carrier lost. " , RNS . LOG_WARNING )
self . timed_out_interfaces [ ifname ] = True
else :
if ifname in self . timed_out_interfaces and self . timed_out_interfaces [ ifname ] == True :
2022-12-22 17:20:34 +00:00
self . carrier_changed = True
2022-02-22 19:16:02 +00:00
RNS . log ( str ( self ) + " Carrier recovered on " + str ( ifname ) , RNS . LOG_WARNING )
self . timed_out_interfaces [ ifname ] = False
2021-12-09 15:07:36 +00:00
def announce_handler ( self , ifname ) :
while True :
self . peer_announce ( ifname )
time . sleep ( self . announce_interval )
def peer_announce ( self , ifname ) :
2022-02-22 13:43:14 +00:00
try :
link_local_address = self . adopted_interfaces [ ifname ]
discovery_token = RNS . Identity . full_hash ( self . group_id + link_local_address . encode ( " utf-8 " ) )
announce_socket = socket . socket ( socket . AF_INET6 , socket . SOCK_DGRAM )
addr_info = socket . getaddrinfo ( self . mcast_discovery_address , self . discovery_port , socket . AF_INET6 , socket . SOCK_DGRAM )
2024-05-16 16:09:11 +00:00
ifis = struct . pack ( " I " , self . interface_name_to_index ( ifname ) )
2022-02-22 13:43:14 +00:00
announce_socket . setsockopt ( socket . IPPROTO_IPV6 , socket . IPV6_MULTICAST_IF , ifis )
announce_socket . sendto ( discovery_token , addr_info [ 0 ] [ 4 ] )
2022-06-09 06:48:55 +00:00
announce_socket . close ( )
2022-06-10 15:05:00 +00:00
2022-02-22 13:43:14 +00:00
except Exception as e :
2022-02-22 19:16:02 +00:00
if ( ifname in self . timed_out_interfaces and self . timed_out_interfaces [ ifname ] == False ) or not ifname in self . timed_out_interfaces :
RNS . log ( str ( self ) + " Detected possible carrier loss on " + str ( ifname ) + " : " + str ( e ) , RNS . LOG_WARNING )
else :
pass
2021-12-09 15:07:36 +00:00
def add_peer ( self , addr , ifname ) :
2022-02-22 13:43:14 +00:00
if addr in self . link_local_addresses :
ifname = None
for interface_name in self . adopted_interfaces :
if self . adopted_interfaces [ interface_name ] == addr :
ifname = interface_name
if ifname != None :
self . multicast_echoes [ ifname ] = time . time ( )
else :
2022-02-25 19:29:47 +00:00
RNS . log ( str ( self ) + " received multicast echo on unexpected interface " + str ( ifname ) , RNS . LOG_WARNING )
2022-02-22 13:43:14 +00:00
else :
2021-12-09 15:07:36 +00:00
if not addr in self . peers :
self . peers [ addr ] = [ ifname , time . time ( ) ]
RNS . log ( str ( self ) + " added peer " + str ( addr ) + " on " + str ( ifname ) , RNS . LOG_DEBUG )
else :
self . refresh_peer ( addr )
def refresh_peer ( self , addr ) :
2021-12-08 19:46:53 +00:00
self . peers [ addr ] [ 1 ] = time . time ( )
def processIncoming ( self , data ) :
2023-05-11 17:54:26 +00:00
data_hash = RNS . Identity . full_hash ( data )
2023-09-14 20:14:31 +00:00
deque_hit = False
if data_hash in self . mif_deque :
for te in self . mif_deque_times :
if te [ 0 ] == data_hash and time . time ( ) < te [ 1 ] + AutoInterface . MULTI_IF_DEQUE_TTL :
deque_hit = True
break
if not deque_hit :
2023-05-11 17:54:26 +00:00
self . mif_deque . append ( data_hash )
2023-09-14 20:14:31 +00:00
self . mif_deque_times . append ( [ data_hash , time . time ( ) ] )
2023-05-11 17:54:26 +00:00
self . rxb + = len ( data )
self . owner . inbound ( data , self )
2021-12-08 19:46:53 +00:00
def processOutgoing ( self , data ) :
for peer in self . peers :
try :
if self . outbound_udp_socket == None :
self . outbound_udp_socket = socket . socket ( socket . AF_INET6 , socket . SOCK_DGRAM )
2024-05-16 16:09:11 +00:00
peer_addr = str ( peer ) + " % " + str ( self . interface_name_to_index ( self . peers [ peer ] [ 0 ] ) )
2021-12-10 15:23:35 +00:00
addr_info = socket . getaddrinfo ( peer_addr , self . data_port , socket . AF_INET6 , socket . SOCK_DGRAM )
self . outbound_udp_socket . sendto ( data , addr_info [ 0 ] [ 4 ] )
2022-06-10 15:05:00 +00:00
2021-12-08 19:46:53 +00:00
except Exception as e :
RNS . log ( " Could not transmit on " + str ( self ) + " . The contained exception was: " + str ( e ) , RNS . LOG_ERROR )
self . txb + = len ( data )
2023-10-01 09:39:24 +00:00
# Until per-device sub-interfacing is implemented,
# ingress limiting should be disabled on AutoInterface
def should_ingress_limit ( self ) :
return False
2021-12-08 19:46:53 +00:00
def __str__ ( self ) :
return " AutoInterface[ " + self . name + " ] "
class AutoInterfaceHandler ( socketserver . BaseRequestHandler ) :
def __init__ ( self , callback , * args , * * keys ) :
self . callback = callback
socketserver . BaseRequestHandler . __init__ ( self , * args , * * keys )
def handle ( self ) :
data = self . request [ 0 ]
2021-12-09 15:07:36 +00:00
self . callback ( data )