aboutsummaryrefslogtreecommitdiffstats
path: root/src/protocol/README.md
blob: c6b4512ebc4b4a075cd1339582efde345ca2355e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
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<RwLock<_>>` (or `Weak<RwLock<>>` 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.