Created
January 16, 2024 09:41
-
-
Save little-dude/5e15da39be8c71f3a35364028f2e058a to your computer and use it in GitHub Desktop.
why unix sockets
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//! This module provides implementations of `ActiveConnector` and `PassiveConnector` that are backed | |
//! by UNIX socket instead of the TCP sockets that are normally used. | |
//! | |
//! Using UNIX sockets instead of TCP ones serves multiple purpose: | |
//! | |
//! - creating and connecting to UNIX socket doesn't require specific permissions so using them in | |
//! tests mean that non-root users can run the tests. With TCP, listening on some ports may | |
//! require root privileges. | |
//! - We can easily create namespaces for the sockets used in each test by simply using the | |
//! filesystem hierarchy which means we avoid the classic "address already in use" pitfall. For | |
//! instance, we cannot have two tests that create a TCP listener on 0.0.0.0:0:179. | |
//! | |
//! Swapping TCP sockets with UNIX sockets required a few tricks the | |
//! testers might need to understand to debug their test. | |
//! | |
//! # Address mapping | |
//! | |
//! The first trick is address mapping. UNIX sockets use paths are an address, while TCP uses an | |
//! (ip, port) tuple. In order to hide the UNIX socket addresses from flock and from the tests, we | |
//! map from one addressing scheme to another using the following rules: | |
//! | |
//! - for a given test, the UNIX sockets are stored under | |
//! `<UDS_SOCKET_PATH>/<process ID>/<test ID>/<VRF_kernel_intf_name>` where: | |
//! - `<UDS_SOCKET_PATH>` is a constant | |
//! - `<process ID` is the current process ID | |
//! - `<test ID>` is an ID generated by each test and stored as a thread-local storage variable | |
//! - '<VRF_kernel_intf_name>' is the kernel's VRF interface name for non-default VRF, for default | |
//! VRF there is no intf name appended to the path | |
//! - a TCP address w.x.y.z:123 in vrf blue is mapped to | |
//! `<UDS_SOCKET_PATH>/<process ID>/<test ID>/blue_kernel_intf_name/z.x.y.z:123` | |
//! and vice-versa | |
//! | |
//! # Unspecified addresses | |
//! | |
//! ## Unspecified IP | |
//! | |
//! `0.0.0.0:179` is a valid TCP address to listen on, and it results in the listener listening for | |
//! connections on all the local addresses on port 179 in default vrf. We cannot really do this with | |
//! UNIX sockets, so such an address is simply mapped to | |
//! `<UDS_SOCKET_PATH>/<process ID>/<test ID>/0.0.0.0:179`, | |
//! and when connectors must connect to `0.0.0.0:179` directly. | |
//! | |
//! ## Unspecified port | |
//! | |
//! Sometimes, the port is left out. Typically, when initiating a connection, we usually let the OS | |
//! pick a port random number for the socket. We sort of emulated a very limited version of this in | |
//! this module. In each `<UDS_SOCKET_PATH>/<process ID>/<test ID>/VRF_kernel_intf_name` directory a | |
//! file called `last-generated-port-number` keeps track of the last port number that was generated. | |
//! Each a new socket is created and the port number is unspecified, the number in that file is | |
//! incremented and used. This is broken two majors ways: | |
//! | |
//! - the filesystem is notoriously racy, multiple threads could try to read/write this file | |
//! - there no guarantee the port number that is picked is not actually already used | |
//! | |
//! However, this seems to be good enough for our tests, so far ¯\_(ツ)_/¯ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment