From 82977e9ef2670cb53a4d6440601c22c6ce1d44c8 Mon Sep 17 00:00:00 2001 From: GUVWAF Date: Mon, 5 Dec 2022 20:02:53 +0100 Subject: [PATCH 1/4] Fix --ch-set help message --- meshtastic/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meshtastic/__main__.py b/meshtastic/__main__.py index e31aa53fe..a5fd3a88d 100644 --- a/meshtastic/__main__.py +++ b/meshtastic/__main__.py @@ -889,7 +889,7 @@ def initParser(): "--ch-set", help=("Set a channel parameter. To see channel settings available:'--ch-set all all --ch-index 0'. " "Can set the 'psk' using this command. To disable encryption on primary channel:'--ch-set psk none --ch-index 0'. " "To set encryption with a new random key on second channel:'--ch-set psk random --ch-index 1'. " - "To set encryption back to the default:'--ch-set default --ch-index 0'. To set encryption with your " + "To set encryption back to the default:'--ch-set psk default --ch-index 0'. To set encryption with your " "own key: '--ch-set psk 0x1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b --ch-index 0'."), nargs=2, action='append') From 2c76c0cafae34c8649d00636d044f42d26fa8ab2 Mon Sep 17 00:00:00 2001 From: GUVWAF Date: Mon, 5 Dec 2022 20:04:38 +0100 Subject: [PATCH 2/4] Remove setting hopLimit to default Instead set is to config of device --- meshtastic/mesh_interface.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/meshtastic/mesh_interface.py b/meshtastic/mesh_interface.py index daf886265..f2949d6e9 100644 --- a/meshtastic/mesh_interface.py +++ b/meshtastic/mesh_interface.py @@ -53,7 +53,6 @@ def __init__(self, debugOut=None, noProto=False): self.currentPacketId = random.randint(0, 0xffffffff) self.nodesByNum = None self.configId = None - self.defaultHopLimit = 3 self.gotResponse = False # used in gpio read self.mask = None # used in gpio read and gpio watch @@ -176,7 +175,6 @@ def sendText(self, text: AnyStr, destinationId=BROADCAST_ADDR, wantAck=False, wantResponse=False, - hopLimit=None, onResponse=None, channelIndex=0): """Send a utf8 string to some other node, if the node has a display it @@ -198,21 +196,17 @@ def sendText(self, text: AnyStr, Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks. """ - if hopLimit is None: - hopLimit = self.defaultHopLimit return self.sendData(text.encode("utf-8"), destinationId, portNum=portnums_pb2.PortNum.TEXT_MESSAGE_APP, wantAck=wantAck, wantResponse=wantResponse, - hopLimit=hopLimit, onResponse=onResponse, channelIndex=channelIndex) def sendData(self, data, destinationId=BROADCAST_ADDR, portNum=portnums_pb2.PortNum.PRIVATE_APP, wantAck=False, wantResponse=False, - hopLimit=None, onResponse=None, channelIndex=0): """Send a data packet to some other node @@ -237,8 +231,6 @@ def sendData(self, data, destinationId=BROADCAST_ADDR, Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks. """ - if hopLimit is None: - hopLimit = self.defaultHopLimit if getattr(data, "SerializeToString", None): logging.debug(f"Serializing protobuf as data: {stripnl(data)}") @@ -261,8 +253,7 @@ def sendData(self, data, destinationId=BROADCAST_ADDR, if onResponse is not None: self._addResponseHandler(meshPacket.id, onResponse) - p = self._sendPacket(meshPacket, destinationId, - wantAck=wantAck, hopLimit=hopLimit) + p = self._sendPacket(meshPacket, destinationId, wantAck=wantAck) return p def sendPosition(self, latitude=0.0, longitude=0.0, altitude=0, timeSec=0, @@ -306,15 +297,13 @@ def _addResponseHandler(self, requestId, callback): def _sendPacket(self, meshPacket, destinationId=BROADCAST_ADDR, - wantAck=False, hopLimit=None): + wantAck=False): """Send a MeshPacket to the specified node (or if unspecified, broadcast). You probably don't want this - use sendData instead. Returns the sent packet. The id field will be populated in this packet and can be used to track future message acks/naks. """ - if hopLimit is None: - hopLimit = self.defaultHopLimit # We allow users to talk to the local node before we've completed the full connection flow... if(self.myInfo is not None and destinationId != self.myInfo.my_node_num): @@ -348,6 +337,8 @@ def _sendPacket(self, meshPacket, meshPacket.to = nodeNum meshPacket.want_ack = wantAck + loraConfig = getattr(self.localNode.localConfig, 'lora') + hopLimit = getattr(loraConfig, 'hop_limit') meshPacket.hop_limit = hopLimit # if the user hasn't set an ID for this packet (likely and recommended), From 7d3a9178ea24094978481bc2493bea514c87f22d Mon Sep 17 00:00:00 2001 From: GUVWAF Date: Mon, 5 Dec 2022 20:36:34 +0100 Subject: [PATCH 3/4] Add traceroute option --- meshtastic/__init__.py | 3 ++- meshtastic/__main__.py | 13 +++++++++++++ meshtastic/mesh_interface.py | 26 ++++++++++++++++++++++++++ meshtastic/util.py | 13 +++++++++++++ 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/meshtastic/__init__.py b/meshtastic/__init__.py index e4a172c8e..cb2ffa3b2 100644 --- a/meshtastic/__init__.py +++ b/meshtastic/__init__.py @@ -183,5 +183,6 @@ def _receiveInfoUpdate(iface, asDict): portnums_pb2.PortNum.ROUTING_APP: KnownProtocol("routing", mesh_pb2.Routing), portnums_pb2.PortNum.TELEMETRY_APP: KnownProtocol("telemetry", telemetry_pb2.Telemetry), portnums_pb2.PortNum.REMOTE_HARDWARE_APP: KnownProtocol("remotehw", remote_hardware_pb2.HardwareMessage), - portnums_pb2.PortNum.SIMULATOR_APP: KnownProtocol("simulator", mesh_pb2.Compressed) + portnums_pb2.PortNum.SIMULATOR_APP: KnownProtocol("simulator", mesh_pb2.Compressed), + portnums_pb2.PortNum.TRACEROUTE_APP: KnownProtocol("traceroute", mesh_pb2.RouteDiscovery) } diff --git a/meshtastic/__main__.py b/meshtastic/__main__.py index a5fd3a88d..3b2cf93a8 100644 --- a/meshtastic/__main__.py +++ b/meshtastic/__main__.py @@ -330,6 +330,13 @@ def onConnected(interface): interface.sendData(payload, args.dest, portNum=portnums_pb2.PortNum.REPLY_APP, wantAck=True, wantResponse=True) + if args.traceroute: + loraConfig = getattr(interface.localNode.localConfig, 'lora') + hopLimit = getattr(loraConfig, 'hop_limit') + dest = str(args.traceroute) + print(f"Sending traceroute request to {dest} (this could take a while)") + interface.sendTraceRoute(dest, hopLimit) + if args.gpio_wrb or args.gpio_rd or args.gpio_watch: if args.dest == BROADCAST_ADDR: meshtastic.util.our_exit("Warning: Must use a destination node ID.") @@ -935,6 +942,12 @@ def initParser(): parser.add_argument( "--sendping", help="Send a ping message (which requests a reply)", action="store_true") + parser.add_argument( + "--traceroute", help="Traceroute from connected node to a destination. " \ + "You need pass the destination ID as argument, like " \ + "this: '--traceroute !ba4bf9d0' " \ + "Only nodes that have the encryption key can be traced.") + parser.add_argument( "--reboot", help="Tell the destination node to reboot", action="store_true") diff --git a/meshtastic/mesh_interface.py b/meshtastic/mesh_interface.py index f2949d6e9..cef68a5bd 100644 --- a/meshtastic/mesh_interface.py +++ b/meshtastic/mesh_interface.py @@ -291,6 +291,27 @@ def sendPosition(self, latitude=0.0, longitude=0.0, altitude=0, timeSec=0, portNum=portnums_pb2.PortNum.POSITION_APP, wantAck=wantAck, wantResponse=wantResponse) + + def sendTraceRoute(self, dest, hopLimit): + r = mesh_pb2.RouteDiscovery() + self.sendData(r, destinationId=dest, portNum=70, wantResponse=True, onResponse=self.onResponseTraceRoute) + waitFactor = min(len(self.nodes)-1, hopLimit) # extend timeout based on number of nodes, limit by configured hopLimit + self.waitForTraceRoute(waitFactor) + + def onResponseTraceRoute(self, p): + routeDiscovery = mesh_pb2.RouteDiscovery() + routeDiscovery.ParseFromString(p["decoded"]["payload"]) + asDict = google.protobuf.json_format.MessageToDict(routeDiscovery) + + print("Route traced:") + routeStr = self._nodeNumToId(p["to"]) + if "route" in asDict: + for nodeNum in asDict["route"]: + routeStr += " --> " + self._nodeNumToId(nodeNum) + routeStr += " --> " + self._nodeNumToId(p["from"]) + print(routeStr) + + self._acknowledgment.receivedTraceRoute = True def _addResponseHandler(self, requestId, callback): self.responseHandlers[requestId] = ResponseHandler(callback) @@ -365,6 +386,11 @@ def waitForAckNak(self): if not success: raise Exception("Timed out waiting for an acknowledgment") + def waitForTraceRoute(self, waitFactor): + success = self._timeout.waitForTraceRoute(waitFactor, self._acknowledgment) + if not success: + raise Exception("Timed out waiting for traceroute") + def getMyNodeInfo(self): """Get info about my node.""" if self.myInfo is None: diff --git a/meshtastic/util.py b/meshtastic/util.py index f83a4f2c8..dcd7c9a4d 100644 --- a/meshtastic/util.py +++ b/meshtastic/util.py @@ -169,17 +169,30 @@ def waitForAckNak(self, acknowledgment, attrs=('receivedAck', 'receivedNak', 're time.sleep(self.sleepInterval) return False + def waitForTraceRoute(self, waitFactor, acknowledgment, attr='receivedTraceRoute'): + """Block until traceroute response is received. Returns True if traceroute response has been received.""" + self.expireTimeout *= waitFactor + self.reset() + while time.time() < self.expireTime: + if getattr(acknowledgment, attr, None): + acknowledgment.reset() + return True + time.sleep(self.sleepInterval) + return False + class Acknowledgment: "A class that records which type of acknowledgment was just received, if any." def __init__(self): self.receivedAck = False self.receivedNak = False self.receivedImplAck = False + self.receivedTraceRoute = False def reset(self): self.receivedAck = False self.receivedNak = False self.receivedImplAck = False + self.receivedTraceRoute = False class DeferredExecution(): """A thread that accepts closures to run, and runs them as they are received""" From d0b8b9ff1b237b8771da2a11573c220e2a83a4e2 Mon Sep 17 00:00:00 2001 From: GUVWAF Date: Tue, 6 Dec 2022 14:18:30 +0100 Subject: [PATCH 4/4] Forgot to convert into TRACEROUTE_APP --- meshtastic/mesh_interface.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/meshtastic/mesh_interface.py b/meshtastic/mesh_interface.py index cef68a5bd..c9ee60214 100644 --- a/meshtastic/mesh_interface.py +++ b/meshtastic/mesh_interface.py @@ -294,8 +294,10 @@ def sendPosition(self, latitude=0.0, longitude=0.0, altitude=0, timeSec=0, def sendTraceRoute(self, dest, hopLimit): r = mesh_pb2.RouteDiscovery() - self.sendData(r, destinationId=dest, portNum=70, wantResponse=True, onResponse=self.onResponseTraceRoute) - waitFactor = min(len(self.nodes)-1, hopLimit) # extend timeout based on number of nodes, limit by configured hopLimit + self.sendData(r, destinationId=dest, portNum=portnums_pb2.PortNum.TRACEROUTE_APP, + wantResponse=True, onResponse=self.onResponseTraceRoute) + # extend timeout based on number of nodes, limit by configured hopLimit + waitFactor = min(len(self.nodes)-1, hopLimit) self.waitForTraceRoute(waitFactor) def onResponseTraceRoute(self, p):