markqvist / Reticulum

The cryptography-based networking stack for building unstoppable networks with LoRa, Packet Radio, WiFi and everything in between.
https://reticulum.network
MIT License
2k stars 124 forks source link

Implement AutoInterface support on Windows #506

Closed liamcottle closed 4 months ago

liamcottle commented 4 months ago

This PR adds support for using the AutoInterface on Windows machines.

I have tested the following setups, and they appear to be working!

Setup 1

Setup 2

Setup 3

Note: The Ethernet and WiFi devices in this setup are connected to the same physical Router/Switch.

Notes

I have tested running this commit on the Windows PC as well as the MacBook and the changes don't appear to have broken anything, however I have not added this commit to Sideband or Nomadnet to see if they still behave the same way. I would assume so...

The Windows specific changes to the socket.bind(...) call are put behind an os check, but I did make a few other changes to how the interface names are converted to index numbers.

I found on Windows, it would return a UUID as the interface name, but when this was passed to socket.if_nametoindex(...) it would complain about the interface not being found. I could pass in ethernet_0 and it would find it. I opted to use the netinfo wrapper to get the index instead. Unsure if this is the best for performance, however this PR is intended to provide a working solution that we can improve upon.

When passing the multicast address as the host to discovery_socket.bind, or when passing the interface index as the 4th parameter, Windows throws [WinError 10049] The requested address is not valid in its context.

I was able to get around this by not passing a host at all, and omitting the interface index. This too, is behind an os check to avoid changing the functionality on other platforms that already work as intended.

This is my first time playing around with IPv6/Socket Multicasting, so I have no idea of any unintended consequences of not passing in the multicast address and interface index to the socket binding.

Original Discussion

Here's a link to the Matrix discussion where @faragher confirms this is working on Windows 10 Pro as well.

faragher commented 4 months ago

As noted: Tested on Windows 10 Pro, two NICs but with only one active. Windows used Sideband with this PR's modifications, other Linux systems were stock.

markqvist commented 4 months ago

Great work. Thank you very much for taking the initiative on this, and for providing a thorough description on your well-considered implementation and test setup. It's all looking good. I'll be merging this now, testing it in scenarios that have not been covered yet, and make necessary adjustments, if any.

markqvist commented 4 months ago

It seems that devices are named is similar fashion to {BAC39ED9-E825-4A37-4468-FE9A875D7AD3} on Windows internally, but I wasn't able to find these IDs anywhere in the networking control panels or similar. Maybe I just missed it, and admittedly, I am not well versed in where stuff is stashed away in Windows.

This could be a problem when people want to specify specifically what devices on a system that Reticulum should adopt to an AutoInterface, by using the devices = ABC, XYZ config statement.

Therefore I've added some extra logging on Windows, that displays the "nice name" of networking device, along with its interface ID, which can be used for configuration purposes.

I've also added some additional error handling, since testing on the only Windows machine I have access to right now revealed, that some interfaces (here a virtual WiFi Direct interface) apparently cannot have IPv6 multicast SOCKOPTs set on them, which will cause an exception. This is gracefully handled now, and will simply be reported as an error in the log, instead of tearing down the entire interface, and other adoptable system interfaces will be accepted and used.

faragher commented 4 months ago

As far as I remember, and I didn't keep any notes, Windows produces a UUID, has a strange reference name, and stores them all in an index. There was a lot of dancing to even attach to the interface, which would then throw the "not valid in this context" error.

Python tends to point to things that aren't terribly useful as default, and Liam's solution is where I got to and beyond. I think I actually ended up iterating through the index to find a friendly name or something of the sort.

I'll keep an eye open if I see anything on IPv6 multicast.

markqvist commented 4 months ago

All good to know. It should work out either way now, and just fail gracefully. The printing of nice names is only happening if loglevel is set to 7.