mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 18:18:55 +00:00
This required a mid-sized refactor of the relay's eventloop. The idea is that we can use [`mio`](https://docs.rs/mio/latest/mio/) to do the actual IO handling instead of `tokio`. `tokio` depends on `mio` internally but doesn't expose its primitives. Most importantly, we don't get access to the API where we can dynamically register file descriptors to watch for readiness. In order to avoid allocations on the relaying hotpath, we need to listen on a dynamic number of sockets: 1. Our client-facing socket on port 3478 2. All sockets allocated by clients `mio` is the building block of the async tokio runtime, hence it does not provide an async primitives. Instead, it blocks the current thread that it is running on and feeds you events that you need to deal with. We still need our `tokio` runtime to register timers and for communication with the portal. To integrate the two, we spawn a dedicated thread for `mio::Poll` and communicate with it via channels within the `Sockets` abstraction. Thus, the `Eventloop` itself has no idea that `mio` is used for all the network communication. Whenever `mio` sends us an event that a socket is ready, we try to read from that specific socket. We must read from this socket until it returns `WouldBlock` at which point we move on to the next event. We only register for read-readiness. If a socket is not ready for writing, we just drop the packet. With this design in place, we can now have a single buffer that we read incoming packets into and dispatch it to `Server`, depending on which port is what received on. A future refactoring could maybe even unify these functions and let the `Server` deal with the ports internally. Resolves: #4366.