mirror of
				https://github.com/markqvist/Reticulum.git
				synced 2025-10-30 22:19:00 -04:00 
			
		
		
		
	Path MTU discovery for links
This commit is contained in:
		
							parent
							
								
									503f475ca5
								
							
						
					
					
						commit
						bf6e73e163
					
				
					 5 changed files with 90 additions and 22 deletions
				
			
		|  | @ -65,11 +65,12 @@ class Interface: | |||
|     IC_HELD_RELEASE_INTERVAL = 30 | ||||
| 
 | ||||
|     def __init__(self): | ||||
|         self.rxb = 0 | ||||
|         self.txb = 0 | ||||
|         self.rxb     = 0 | ||||
|         self.txb     = 0 | ||||
|         self.created = time.time() | ||||
|         self.online = False | ||||
|         self.online  = False | ||||
|         self.bitrate = 1e6 | ||||
|         self.HW_MTU  = None | ||||
| 
 | ||||
|         self.ingress_control = True | ||||
|         self.ic_max_held_announces = Interface.MAX_HELD_ANNOUNCES | ||||
|  |  | |||
|  | @ -56,9 +56,6 @@ class LocalClientInterface(Interface): | |||
|     def __init__(self, owner, name, target_port = None, connected_socket=None): | ||||
|         super().__init__() | ||||
| 
 | ||||
|         # TODO: Remove at some point | ||||
|         # self.rxptime = 0 | ||||
|          | ||||
|         self.HW_MTU = 32768 | ||||
| 
 | ||||
|         self.online  = False | ||||
|  |  | |||
|  | @ -96,8 +96,7 @@ class TCPClientInterface(Interface): | |||
|         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 | ||||
|          | ||||
|         self.HW_MTU = 32768 | ||||
|          | ||||
|         self.HW_MTU           = 32768 | ||||
|         self.IN               = True | ||||
|         self.OUT              = False | ||||
|         self.socket           = None | ||||
|  |  | |||
							
								
								
									
										65
									
								
								RNS/Link.py
									
										
									
									
									
								
							
							
						
						
									
										65
									
								
								RNS/Link.py
									
										
									
									
									
								
							|  | @ -28,6 +28,7 @@ from time import sleep | |||
| from .vendor import umsgpack as umsgpack | ||||
| import threading | ||||
| import inspect | ||||
| import struct | ||||
| import math | ||||
| import time | ||||
| import RNS | ||||
|  | @ -107,6 +108,29 @@ class Link: | |||
|     ACCEPT_ALL  = 0x02 | ||||
|     resource_strategies = [ACCEPT_NONE, ACCEPT_APP, ACCEPT_ALL] | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def mtu_bytes(mtu): | ||||
|         return struct.pack(">I", mtu & 0xFFFFFF)[1:] | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def mtu_from_lr_packet(packet): | ||||
|         if len(packet.data) == Link.ECPUBSIZE+Link.LINK_MTU_SIZE: | ||||
|             return (packet.data[Link.ECPUBSIZE] << 16) + (packet.data[Link.ECPUBSIZE+1] << 8) + (packet.data[Link.ECPUBSIZE+2]) | ||||
|         else: | ||||
|             return None | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def mtu_from_lp_packet(packet): | ||||
|         if len(packet.data) == RNS.Identity.SIGLENGTH//8+Link.ECPUBSIZE//2+Link.LINK_MTU_SIZE: | ||||
|             mtu_bytes = packet.data[RNS.Identity.SIGLENGTH//8+Link.ECPUBSIZE//2:RNS.Identity.SIGLENGTH//8+Link.ECPUBSIZE//2+Link.LINK_MTU_SIZE] | ||||
|             return (mtu_bytes[0] << 16) + (mtu_bytes[1] << 8) + (mtu_bytes[2]) | ||||
|         else: | ||||
|             return None | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def link_id_from_lr_packet(packet): | ||||
|         return RNS.Identity.truncated_hash(packet.get_hashable_part()[:Link.ECPUBSIZE]) | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def validate_request(owner, data, packet): | ||||
|         if len(data) == Link.ECPUBSIZE or len(data) == Link.ECPUBSIZE+Link.LINK_MTU_SIZE: | ||||
|  | @ -115,15 +139,17 @@ class Link: | |||
|                 link.set_link_id(packet) | ||||
| 
 | ||||
|                 if len(data) == Link.ECPUBSIZE+Link.LINK_MTU_SIZE: | ||||
|                     RNS.log("Link request includes MTU signalling") # TODO: Remove debug | ||||
|                     try: | ||||
|                         link.mtu = (ord(a[Link.ECPUBSIZE]) << 16) + (ord(a[Link.ECPUBSIZE+1]) << 8) + (ord(a[Link.ECPUBSIZE+2])) | ||||
|                         link.mtu = Link.mtu_from_lr_packet(packet) or Reticulum.MTU | ||||
|                     except Exception as e: | ||||
|                         link.mtu = Reticulum.MTU | ||||
|                         RNS.trace_exception(e) | ||||
|                         link.mtu = RNS.Reticulum.MTU | ||||
| 
 | ||||
|                 link.destination = packet.destination | ||||
|                 link.establishment_timeout = Link.ESTABLISHMENT_TIMEOUT_PER_HOP * max(1, packet.hops) + Link.KEEPALIVE | ||||
|                 link.establishment_cost += len(packet.raw) | ||||
|                 RNS.log(f"Validating link request {RNS.prettyhexrep(link.link_id), RNS.LOG_VERBOSE}") | ||||
|                 RNS.log(f"Validating link request {RNS.prettyhexrep(link.link_id)}", RNS.LOG_VERBOSE) | ||||
|                 RNS.log(f"Link MTU configured to {RNS.prettysize(link.mtu)}", RNS.LOG_EXTREME) | ||||
|                 RNS.log(f"Establishment timeout is {RNS.prettytime(link.establishment_timeout)} for incoming link request "+RNS.prettyhexrep(link.link_id), RNS.LOG_EXTREME) | ||||
|                 link.handshake() | ||||
|  | @ -143,7 +169,7 @@ class Link: | |||
|                 return None | ||||
| 
 | ||||
|         else: | ||||
|             RNS.log("Invalid link request payload size, dropping request", RNS.LOG_DEBUG) | ||||
|             RNS.log(f"Invalid link request payload size of {len(data)} bytes, dropping request", RNS.LOG_DEBUG) | ||||
|             return None | ||||
| 
 | ||||
| 
 | ||||
|  | @ -151,7 +177,7 @@ class Link: | |||
|         if destination != None and destination.type != RNS.Destination.SINGLE: | ||||
|             raise TypeError("Links can only be established to the \"single\" destination type") | ||||
|         self.rtt = None | ||||
|         self.mtu = Reticulum.MTU | ||||
|         self.mtu = RNS.Reticulum.MTU | ||||
|         self.establishment_cost = 0 | ||||
|         self.establishment_rate = None | ||||
|         self.callbacks = LinkCallbacks() | ||||
|  | @ -218,8 +244,13 @@ class Link: | |||
|         if closed_callback != None: | ||||
|             self.set_link_closed_callback(closed_callback) | ||||
| 
 | ||||
|         if (self.initiator): | ||||
|             self.request_data = self.pub_bytes+self.sig_pub_bytes | ||||
|         if self.initiator: | ||||
|             link_mtu = b"" | ||||
|             nh_hw_mtu = RNS.Transport.next_hop_interface_hw_mtu(destination.hash) | ||||
|             if nh_hw_mtu: | ||||
|                 link_mtu = Link.mtu_bytes(nh_hw_mtu) | ||||
|                 RNS.log(f"Signalling link MTU of {RNS.prettysize(nh_hw_mtu)} for link") # TODO: Remove debug | ||||
|             self.request_data = self.pub_bytes+self.sig_pub_bytes+link_mtu | ||||
|             self.packet = RNS.Packet(destination, self.request_data, packet_type=RNS.Packet.LINKREQUEST) | ||||
|             self.packet.pack() | ||||
|             self.establishment_cost += len(self.packet.raw) | ||||
|  | @ -244,7 +275,7 @@ class Link: | |||
|             self.peer_pub.curve = Link.CURVE | ||||
| 
 | ||||
|     def set_link_id(self, packet): | ||||
|         self.link_id = packet.getTruncatedHash() | ||||
|         self.link_id = Link.link_id_from_lr_packet(packet) | ||||
|         self.hash = self.link_id | ||||
| 
 | ||||
|     def handshake(self): | ||||
|  | @ -263,10 +294,14 @@ class Link: | |||
| 
 | ||||
| 
 | ||||
|     def prove(self): | ||||
|         signed_data = self.link_id+self.pub_bytes+self.sig_pub_bytes | ||||
|         mtu_bytes = b"" | ||||
|         if self.mtu != RNS.Reticulum.MTU: | ||||
|             mtu_bytes = Link.mtu_bytes(self.mtu) | ||||
| 
 | ||||
|         signed_data = self.link_id+self.pub_bytes+self.sig_pub_bytes+mtu_bytes | ||||
|         signature = self.owner.identity.sign(signed_data) | ||||
| 
 | ||||
|         proof_data = signature+self.pub_bytes | ||||
|         proof_data = signature+self.pub_bytes+mtu_bytes | ||||
|         proof = RNS.Packet(self, proof_data, packet_type=RNS.Packet.PROOF, context=RNS.Packet.LRPROOF) | ||||
|         proof.send() | ||||
|         self.establishment_cost += len(proof.raw) | ||||
|  | @ -289,6 +324,14 @@ class Link: | |||
|     def validate_proof(self, packet): | ||||
|         try: | ||||
|             if self.status == Link.PENDING: | ||||
|                 mtu_bytes = b"" | ||||
|                 confirmed_mtu = None | ||||
|                 if len(packet.data) == RNS.Identity.SIGLENGTH//8+Link.ECPUBSIZE//2+Link.LINK_MTU_SIZE: | ||||
|                     confirmed_mtu = Link.mtu_from_lp_packet(packet) | ||||
|                     mtu_bytes = Link.mtu_bytes(confirmed_mtu) | ||||
|                     packet.data = packet.data[:RNS.Identity.SIGLENGTH//8+Link.ECPUBSIZE//2] | ||||
|                     RNS.log(f"Destination confirmed link MTU of {RNS.prettysize(confirmed_mtu)}") # TODO: Remove debug | ||||
| 
 | ||||
|                 if self.initiator and len(packet.data) == RNS.Identity.SIGLENGTH//8+Link.ECPUBSIZE//2: | ||||
|                     peer_pub_bytes = packet.data[RNS.Identity.SIGLENGTH//8:RNS.Identity.SIGLENGTH//8+Link.ECPUBSIZE//2] | ||||
|                     peer_sig_pub_bytes = self.destination.identity.get_public_key()[Link.ECPUBSIZE//2:Link.ECPUBSIZE] | ||||
|  | @ -296,7 +339,7 @@ class Link: | |||
|                     self.handshake() | ||||
| 
 | ||||
|                     self.establishment_cost += len(packet.raw) | ||||
|                     signed_data = self.link_id+self.peer_pub_bytes+self.peer_sig_pub_bytes | ||||
|                     signed_data = self.link_id+self.peer_pub_bytes+self.peer_sig_pub_bytes+mtu_bytes | ||||
|                     signature = packet.data[:RNS.Identity.SIGLENGTH//8] | ||||
|                      | ||||
|                     if self.destination.identity.validate(signature, signed_data): | ||||
|  |  | |||
|  | @ -1282,6 +1282,22 @@ class Transport: | |||
|                                 now = time.time() | ||||
|                                 proof_timeout  = Transport.extra_link_proof_timeout(packet.receiving_interface) | ||||
|                                 proof_timeout += now + RNS.Link.ESTABLISHMENT_TIMEOUT_PER_HOP * max(1, remaining_hops) | ||||
|                                  | ||||
|                                 path_mtu       = RNS.Link.mtu_from_lr_packet(packet) | ||||
|                                 nh_mtu         = outbound_interface.HW_MTU | ||||
|                                 if path_mtu: | ||||
|                                     RNS.log(f"Seeing transported LR path MTU of {RNS.prettysize(path_mtu)}") # TODO: Remove debug | ||||
|                                     if outbound_interface.HW_MTU == None: | ||||
|                                         RNS.log(f"No next-hop HW MTU, disabling link MTU upgrade") # TODO: Remove debug | ||||
|                                         path_mtu = None | ||||
|                                         new_raw  = new_raw[:RNS.Link.ECPUBSIZE] | ||||
|                                     else: | ||||
|                                         if nh_mtu < path_mtu: | ||||
|                                             path_mtu = nh_mtu | ||||
|                                             clamped_mtu = RNS.Link.mtu_bytes(path_mtu) | ||||
|                                             RNS.log(f"Clamping link MTU to {RNS.prettysize(nh_mtu)}: {RNS.hexrep(clamped_mtu)}") # TODO: Remove debug | ||||
|                                             RNS.log(f"New raw: {RNS.hexrep(new_raw)}") | ||||
|                                             new_raw  = new_raw[:-RNS.Link.LINK_MTU_SIZE]+clamped_mtu | ||||
| 
 | ||||
|                                 # Entry format is | ||||
|                                 link_entry = [  now,                            # 0: Timestamp, | ||||
|  | @ -1294,7 +1310,7 @@ class Transport: | |||
|                                                 False,                          # 7: Validated | ||||
|                                                 proof_timeout]                  # 8: Proof timeout timestamp | ||||
| 
 | ||||
|                                 Transport.link_table[packet.getTruncatedHash()] = link_entry | ||||
|                                 Transport.link_table[RNS.Link.link_id_from_lr_packet(packet)] = link_entry | ||||
| 
 | ||||
|                             else: | ||||
|                                 # Entry format is | ||||
|  | @ -1790,12 +1806,16 @@ class Transport: | |||
|                         if packet.hops == link_entry[3]: | ||||
|                             if packet.receiving_interface == link_entry[2]: | ||||
|                                 try: | ||||
|                                     if len(packet.data) == RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2: | ||||
|                                     if len(packet.data) == RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2 or len(packet.data) == RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2+RNS.Link.LINK_MTU_SIZE: | ||||
|                                         mtu_bytes = b"" | ||||
|                                         if len(packet.data) == RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2+RNS.Link.LINK_MTU_SIZE: | ||||
|                                             mtu_bytes = RNS.Link.mtu_bytes(RNS.Link.mtu_from_lp_packet(packet)) | ||||
| 
 | ||||
|                                         peer_pub_bytes = packet.data[RNS.Identity.SIGLENGTH//8:RNS.Identity.SIGLENGTH//8+RNS.Link.ECPUBSIZE//2] | ||||
|                                         peer_identity = RNS.Identity.recall(link_entry[6]) | ||||
|                                         peer_sig_pub_bytes = peer_identity.get_public_key()[RNS.Link.ECPUBSIZE//2:RNS.Link.ECPUBSIZE] | ||||
| 
 | ||||
|                                         signed_data = packet.destination_hash+peer_pub_bytes+peer_sig_pub_bytes | ||||
|                                         signed_data = packet.destination_hash+peer_pub_bytes+peer_sig_pub_bytes+mtu_bytes | ||||
|                                         signature = packet.data[:RNS.Identity.SIGLENGTH//8] | ||||
| 
 | ||||
|                                         if peer_identity.validate(signature, signed_data): | ||||
|  | @ -2189,6 +2209,14 @@ class Transport: | |||
|         else: | ||||
|             return None | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def next_hop_interface_hw_mtu(destination_hash): | ||||
|         next_hop_interface = Transport.next_hop_interface(destination_hash) | ||||
|         if next_hop_interface != None: | ||||
|             return next_hop_interface.HW_MTU | ||||
|         else: | ||||
|             return None | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def next_hop_per_bit_latency(destination_hash): | ||||
|         next_hop_interface_bitrate = Transport.next_hop_interface_bitrate(destination_hash) | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Mark Qvist
						Mark Qvist