Skip to content

Instantly share code, notes, and snippets.

@little-dude
Created January 16, 2024 09:41
Show Gist options
  • Save little-dude/5e15da39be8c71f3a35364028f2e058a to your computer and use it in GitHub Desktop.
Save little-dude/5e15da39be8c71f3a35364028f2e058a to your computer and use it in GitHub Desktop.
why unix sockets
//! 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