Skip to content

Commit 57719dd

Browse files
authored
Merge pull request #407 from GUVWAF/traceRoute
Traceroute option
2 parents c5c9723 + d0b8b9f commit 57719dd

File tree

4 files changed

+56
-1
lines changed

4 files changed

+56
-1
lines changed

meshtastic/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,5 +183,6 @@ def _receiveInfoUpdate(iface, asDict):
183183
portnums_pb2.PortNum.ROUTING_APP: KnownProtocol("routing", mesh_pb2.Routing),
184184
portnums_pb2.PortNum.TELEMETRY_APP: KnownProtocol("telemetry", telemetry_pb2.Telemetry),
185185
portnums_pb2.PortNum.REMOTE_HARDWARE_APP: KnownProtocol("remotehw", remote_hardware_pb2.HardwareMessage),
186-
portnums_pb2.PortNum.SIMULATOR_APP: KnownProtocol("simulator", mesh_pb2.Compressed)
186+
portnums_pb2.PortNum.SIMULATOR_APP: KnownProtocol("simulator", mesh_pb2.Compressed),
187+
portnums_pb2.PortNum.TRACEROUTE_APP: KnownProtocol("traceroute", mesh_pb2.RouteDiscovery)
187188
}

meshtastic/__main__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,13 @@ def onConnected(interface):
330330
interface.sendData(payload, args.dest, portNum=portnums_pb2.PortNum.REPLY_APP,
331331
wantAck=True, wantResponse=True)
332332

333+
if args.traceroute:
334+
loraConfig = getattr(interface.localNode.localConfig, 'lora')
335+
hopLimit = getattr(loraConfig, 'hop_limit')
336+
dest = str(args.traceroute)
337+
print(f"Sending traceroute request to {dest} (this could take a while)")
338+
interface.sendTraceRoute(dest, hopLimit)
339+
333340
if args.gpio_wrb or args.gpio_rd or args.gpio_watch:
334341
if args.dest == BROADCAST_ADDR:
335342
meshtastic.util.our_exit("Warning: Must use a destination node ID.")
@@ -935,6 +942,12 @@ def initParser():
935942
parser.add_argument(
936943
"--sendping", help="Send a ping message (which requests a reply)", action="store_true")
937944

945+
parser.add_argument(
946+
"--traceroute", help="Traceroute from connected node to a destination. " \
947+
"You need pass the destination ID as argument, like " \
948+
"this: '--traceroute !ba4bf9d0' " \
949+
"Only nodes that have the encryption key can be traced.")
950+
938951
parser.add_argument(
939952
"--reboot", help="Tell the destination node to reboot", action="store_true")
940953

meshtastic/mesh_interface.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,29 @@ def sendPosition(self, latitude=0.0, longitude=0.0, altitude=0, timeSec=0,
291291
portNum=portnums_pb2.PortNum.POSITION_APP,
292292
wantAck=wantAck,
293293
wantResponse=wantResponse)
294+
295+
def sendTraceRoute(self, dest, hopLimit):
296+
r = mesh_pb2.RouteDiscovery()
297+
self.sendData(r, destinationId=dest, portNum=portnums_pb2.PortNum.TRACEROUTE_APP,
298+
wantResponse=True, onResponse=self.onResponseTraceRoute)
299+
# extend timeout based on number of nodes, limit by configured hopLimit
300+
waitFactor = min(len(self.nodes)-1, hopLimit)
301+
self.waitForTraceRoute(waitFactor)
302+
303+
def onResponseTraceRoute(self, p):
304+
routeDiscovery = mesh_pb2.RouteDiscovery()
305+
routeDiscovery.ParseFromString(p["decoded"]["payload"])
306+
asDict = google.protobuf.json_format.MessageToDict(routeDiscovery)
307+
308+
print("Route traced:")
309+
routeStr = self._nodeNumToId(p["to"])
310+
if "route" in asDict:
311+
for nodeNum in asDict["route"]:
312+
routeStr += " --> " + self._nodeNumToId(nodeNum)
313+
routeStr += " --> " + self._nodeNumToId(p["from"])
314+
print(routeStr)
315+
316+
self._acknowledgment.receivedTraceRoute = True
294317

295318
def _addResponseHandler(self, requestId, callback):
296319
self.responseHandlers[requestId] = ResponseHandler(callback)
@@ -365,6 +388,11 @@ def waitForAckNak(self):
365388
if not success:
366389
raise Exception("Timed out waiting for an acknowledgment")
367390

391+
def waitForTraceRoute(self, waitFactor):
392+
success = self._timeout.waitForTraceRoute(waitFactor, self._acknowledgment)
393+
if not success:
394+
raise Exception("Timed out waiting for traceroute")
395+
368396
def getMyNodeInfo(self):
369397
"""Get info about my node."""
370398
if self.myInfo is None:

meshtastic/util.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,17 +169,30 @@ def waitForAckNak(self, acknowledgment, attrs=('receivedAck', 'receivedNak', 're
169169
time.sleep(self.sleepInterval)
170170
return False
171171

172+
def waitForTraceRoute(self, waitFactor, acknowledgment, attr='receivedTraceRoute'):
173+
"""Block until traceroute response is received. Returns True if traceroute response has been received."""
174+
self.expireTimeout *= waitFactor
175+
self.reset()
176+
while time.time() < self.expireTime:
177+
if getattr(acknowledgment, attr, None):
178+
acknowledgment.reset()
179+
return True
180+
time.sleep(self.sleepInterval)
181+
return False
182+
172183
class Acknowledgment:
173184
"A class that records which type of acknowledgment was just received, if any."
174185
def __init__(self):
175186
self.receivedAck = False
176187
self.receivedNak = False
177188
self.receivedImplAck = False
189+
self.receivedTraceRoute = False
178190

179191
def reset(self):
180192
self.receivedAck = False
181193
self.receivedNak = False
182194
self.receivedImplAck = False
195+
self.receivedTraceRoute = False
183196

184197
class DeferredExecution():
185198
"""A thread that accepts closures to run, and runs them as they are received"""

0 commit comments

Comments
 (0)