This is a walk-through of the protocol implementation. Hope it will be helpful if you want to review/hack on it. ## `anti_replay.rs` Anti replay algorithm from RFC 6479. It is mostly a straightforward translation from the C code there, with only one notable difference: the handling of seq number zero. It is reasonably well tested. ## `cookie.rs` Cookie signing, verification, cookie reply message generation and parsing. It is reasonably well tested. ## `timer.rs` Timer facility. Uses the hashed timing wheel algorithm. It is optimized for WireGuard use cases (frequent operations on a mostly fixed set of timers) with the "activated" atomic boolean flag. It is under tested. ## `ip.rs` Parsing of IP(v6) packets. ## `types.rs` Some common types and constants. ## `handshake.rs` Handshake initiation and response message generation and parsing. It is reasonably well tested. ## `controller.rs` This beast is the most complex... It manages all the states, timers and does the actuall IO. It also has a lot of locks and `Arc`s. `WgState` represents the state of a WireGuard interface. It contains a hash table that maps pubkeys to peers, another hash table that maps session IDs to peers, and routing tables that map allowed IP(v6) addresses to peers. The ID table changes often and needs to be carefully kept in sync with actuall peer states. (Or we will leak memory.) For this we use `IdMapGuard` which when dropped will automatically remove an ID from the map. `PeerState` represents the state of a peer. It is shared across the maps and timers with `Arc>` (or `Weak>` for timers). The actually IO happens in two threads, one for processing UDP datagrams, another for processing packets from the TUN device. (It is possible to use more threads, but there does not seem to be much benefit without also using SO_REUSEPORT or multi queue tun device.) The UDP thread repeatedly `recv_from` the socket, and take action based the type of the message. The TUN thread repeatedly `read` packets from the TUN device, find the corresponding peer by looking up the route tables, encrypt them and send them out if a valid session exists, and/or initiate handshake if necessary. ### Lock Order Because we use a lot of locks, care must be taken to avoid deadlock. We adhere to the following partial order: info > pubkey_map > any peers > id_map > anything else any peers > rt4 > rt6 Locks whose order are not defined should not be held at the same time. ### Timer Management Each peer is associated with the following timers: #### `rekey_no_recv` Initiate handshake because we have send a transport message but haven't received any in 15s (KEEPALIVE_TIMEOUT + REKEY_TIMEOUT). It is activated and set to 15s when we send a (non-keep-alive) transport message, unless it is already activated. It is de-activated when we receive a transport message. #### `keep_alive` Send a keep-alive message because we have received a transport message but haven't sent any in 10s (KEEPALIVE_TIMEOUT). It is activated and set to 10s when we receive a (non-keep-alive) transport message, unless it is already activated. It is de-activated when we send a transport message. #### `persistent_keep_alive` Persistent keep-alive. It is activated and set to the configured interval when a new session is established, or when we send a transport message. #### `clear` Clear handshake and all transport sessions, and de-activate all timers, because no new session has been established in 9min (3 * REJECT_AFTER_TIME). It is activated and set to 9 min when we initiate a handshake, unless it is already activated. It is activated and set to 9 min when a new session is established. #### Not managed by timer `REKEY_AFTER_TIME` is not managed by a timer. Instead, a `created` time-stamp is stored within each transport session, and compared with when sending messages, to decide on whether to rekey. The secret used to calculate cookies, which is supposed to change every two minutes, is also handled this way. ### Testing This module is under tested.