| @@ -1,1043 +1,1043 @@ | | | @@ -1,1043 +1,1043 @@ |
1 | /* $NetBSD: if_wg.c,v 1.47 2020/08/31 20:31:03 riastradh Exp $ */ | | 1 | /* $NetBSD: if_wg.c,v 1.48 2020/08/31 20:31:43 riastradh Exp $ */ |
2 | | | 2 | |
3 | /* | | 3 | /* |
4 | * Copyright (C) Ryota Ozaki <ozaki.ryota@gmail.com> | | 4 | * Copyright (C) Ryota Ozaki <ozaki.ryota@gmail.com> |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * Redistribution and use in source and binary forms, with or without | | 7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions | | 8 | * modification, are permitted provided that the following conditions |
9 | * are met: | | 9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright | | 10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. | | 11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright | | 12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the | | 13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. | | 14 | * documentation and/or other materials provided with the distribution. |
15 | * 3. Neither the name of the project nor the names of its contributors | | 15 | * 3. Neither the name of the project nor the names of its contributors |
16 | * may be used to endorse or promote products derived from this software | | 16 | * may be used to endorse or promote products derived from this software |
17 | * without specific prior written permission. | | 17 | * without specific prior written permission. |
18 | * | | 18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND | | 19 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND |
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE | | 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE |
23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | | 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | | 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
29 | * SUCH DAMAGE. | | 29 | * SUCH DAMAGE. |
30 | */ | | 30 | */ |
31 | | | 31 | |
32 | /* | | 32 | /* |
33 | * This network interface aims to implement the WireGuard protocol. | | 33 | * This network interface aims to implement the WireGuard protocol. |
34 | * The implementation is based on the paper of WireGuard as of | | 34 | * The implementation is based on the paper of WireGuard as of |
35 | * 2018-06-30 [1]. The paper is referred in the source code with label | | 35 | * 2018-06-30 [1]. The paper is referred in the source code with label |
36 | * [W]. Also the specification of the Noise protocol framework as of | | 36 | * [W]. Also the specification of the Noise protocol framework as of |
37 | * 2018-07-11 [2] is referred with label [N]. | | 37 | * 2018-07-11 [2] is referred with label [N]. |
38 | * | | 38 | * |
39 | * [1] https://www.wireguard.com/papers/wireguard.pdf | | 39 | * [1] https://www.wireguard.com/papers/wireguard.pdf |
40 | * [2] http://noiseprotocol.org/noise.pdf | | 40 | * [2] http://noiseprotocol.org/noise.pdf |
41 | */ | | 41 | */ |
42 | | | 42 | |
43 | #include <sys/cdefs.h> | | 43 | #include <sys/cdefs.h> |
44 | __KERNEL_RCSID(0, "$NetBSD: if_wg.c,v 1.47 2020/08/31 20:31:03 riastradh Exp $"); | | 44 | __KERNEL_RCSID(0, "$NetBSD: if_wg.c,v 1.48 2020/08/31 20:31:43 riastradh Exp $"); |
45 | | | 45 | |
46 | #ifdef _KERNEL_OPT | | 46 | #ifdef _KERNEL_OPT |
47 | #include "opt_inet.h" | | 47 | #include "opt_inet.h" |
48 | #endif | | 48 | #endif |
49 | | | 49 | |
50 | #include <sys/param.h> | | 50 | #include <sys/param.h> |
51 | #include <sys/types.h> | | 51 | #include <sys/types.h> |
52 | | | 52 | |
53 | #include <sys/atomic.h> | | 53 | #include <sys/atomic.h> |
54 | #include <sys/callout.h> | | 54 | #include <sys/callout.h> |
55 | #include <sys/cprng.h> | | 55 | #include <sys/cprng.h> |
56 | #include <sys/cpu.h> | | 56 | #include <sys/cpu.h> |
57 | #include <sys/device.h> | | 57 | #include <sys/device.h> |
58 | #include <sys/domain.h> | | 58 | #include <sys/domain.h> |
59 | #include <sys/errno.h> | | 59 | #include <sys/errno.h> |
60 | #include <sys/intr.h> | | 60 | #include <sys/intr.h> |
61 | #include <sys/ioctl.h> | | 61 | #include <sys/ioctl.h> |
62 | #include <sys/kernel.h> | | 62 | #include <sys/kernel.h> |
63 | #include <sys/kmem.h> | | 63 | #include <sys/kmem.h> |
64 | #include <sys/kthread.h> | | 64 | #include <sys/kthread.h> |
65 | #include <sys/mbuf.h> | | 65 | #include <sys/mbuf.h> |
66 | #include <sys/module.h> | | 66 | #include <sys/module.h> |
67 | #include <sys/mutex.h> | | 67 | #include <sys/mutex.h> |
68 | #include <sys/pcq.h> | | 68 | #include <sys/pcq.h> |
69 | #include <sys/percpu.h> | | 69 | #include <sys/percpu.h> |
70 | #include <sys/pserialize.h> | | 70 | #include <sys/pserialize.h> |
71 | #include <sys/psref.h> | | 71 | #include <sys/psref.h> |
72 | #include <sys/queue.h> | | 72 | #include <sys/queue.h> |
73 | #include <sys/rwlock.h> | | 73 | #include <sys/rwlock.h> |
74 | #include <sys/socket.h> | | 74 | #include <sys/socket.h> |
75 | #include <sys/socketvar.h> | | 75 | #include <sys/socketvar.h> |
76 | #include <sys/sockio.h> | | 76 | #include <sys/sockio.h> |
77 | #include <sys/sysctl.h> | | 77 | #include <sys/sysctl.h> |
78 | #include <sys/syslog.h> | | 78 | #include <sys/syslog.h> |
79 | #include <sys/systm.h> | | 79 | #include <sys/systm.h> |
80 | #include <sys/thmap.h> | | 80 | #include <sys/thmap.h> |
81 | #include <sys/time.h> | | 81 | #include <sys/time.h> |
82 | #include <sys/timespec.h> | | 82 | #include <sys/timespec.h> |
83 | | | 83 | |
84 | #include <net/bpf.h> | | 84 | #include <net/bpf.h> |
85 | #include <net/if.h> | | 85 | #include <net/if.h> |
86 | #include <net/if_types.h> | | 86 | #include <net/if_types.h> |
87 | #include <net/if_wg.h> | | 87 | #include <net/if_wg.h> |
88 | #include <net/route.h> | | 88 | #include <net/route.h> |
89 | | | 89 | |
90 | #include <netinet/in.h> | | 90 | #include <netinet/in.h> |
91 | #include <netinet/in_pcb.h> | | 91 | #include <netinet/in_pcb.h> |
92 | #include <netinet/in_var.h> | | 92 | #include <netinet/in_var.h> |
93 | #include <netinet/ip.h> | | 93 | #include <netinet/ip.h> |
94 | #include <netinet/ip_var.h> | | 94 | #include <netinet/ip_var.h> |
95 | #include <netinet/udp.h> | | 95 | #include <netinet/udp.h> |
96 | #include <netinet/udp_var.h> | | 96 | #include <netinet/udp_var.h> |
97 | | | 97 | |
98 | #ifdef INET6 | | 98 | #ifdef INET6 |
99 | #include <netinet/ip6.h> | | 99 | #include <netinet/ip6.h> |
100 | #include <netinet6/in6_pcb.h> | | 100 | #include <netinet6/in6_pcb.h> |
101 | #include <netinet6/in6_var.h> | | 101 | #include <netinet6/in6_var.h> |
102 | #include <netinet6/ip6_var.h> | | 102 | #include <netinet6/ip6_var.h> |
103 | #include <netinet6/udp6_var.h> | | 103 | #include <netinet6/udp6_var.h> |
104 | #endif /* INET6 */ | | 104 | #endif /* INET6 */ |
105 | | | 105 | |
106 | #include <prop/proplib.h> | | 106 | #include <prop/proplib.h> |
107 | | | 107 | |
108 | #include <crypto/blake2/blake2s.h> | | 108 | #include <crypto/blake2/blake2s.h> |
109 | #include <crypto/sodium/crypto_aead_chacha20poly1305.h> | | 109 | #include <crypto/sodium/crypto_aead_chacha20poly1305.h> |
110 | #include <crypto/sodium/crypto_aead_xchacha20poly1305.h> | | 110 | #include <crypto/sodium/crypto_aead_xchacha20poly1305.h> |
111 | #include <crypto/sodium/crypto_scalarmult.h> | | 111 | #include <crypto/sodium/crypto_scalarmult.h> |
112 | | | 112 | |
113 | #include "ioconf.h" | | 113 | #include "ioconf.h" |
114 | | | 114 | |
115 | #ifdef WG_RUMPKERNEL | | 115 | #ifdef WG_RUMPKERNEL |
116 | #include "wg_user.h" | | 116 | #include "wg_user.h" |
117 | #endif | | 117 | #endif |
118 | | | 118 | |
119 | /* | | 119 | /* |
120 | * Data structures | | 120 | * Data structures |
121 | * - struct wg_softc is an instance of wg interfaces | | 121 | * - struct wg_softc is an instance of wg interfaces |
122 | * - It has a list of peers (struct wg_peer) | | 122 | * - It has a list of peers (struct wg_peer) |
123 | * - It has a kthread that sends/receives handshake messages and | | 123 | * - It has a kthread that sends/receives handshake messages and |
124 | * runs event handlers | | 124 | * runs event handlers |
125 | * - It has its own two routing tables: one is for IPv4 and the other IPv6 | | 125 | * - It has its own two routing tables: one is for IPv4 and the other IPv6 |
126 | * - struct wg_peer is a representative of a peer | | 126 | * - struct wg_peer is a representative of a peer |
127 | * - It has a softint that is used to send packets over an wg interface | | 127 | * - It has a softint that is used to send packets over an wg interface |
128 | * to a peer | | 128 | * to a peer |
129 | * - It has a pair of session instances (struct wg_session) | | 129 | * - It has a pair of session instances (struct wg_session) |
130 | * - It has a pair of endpoint instances (struct wg_sockaddr) | | 130 | * - It has a pair of endpoint instances (struct wg_sockaddr) |
131 | * - Normally one endpoint is used and the second one is used only on | | 131 | * - Normally one endpoint is used and the second one is used only on |
132 | * a peer migration (a change of peer's IP address) | | 132 | * a peer migration (a change of peer's IP address) |
133 | * - It has a list of IP addresses and sub networks called allowedips | | 133 | * - It has a list of IP addresses and sub networks called allowedips |
134 | * (struct wg_allowedip) | | 134 | * (struct wg_allowedip) |
135 | * - A packets sent over a session is allowed if its destination matches | | 135 | * - A packets sent over a session is allowed if its destination matches |
136 | * any IP addresses or sub networks of the list | | 136 | * any IP addresses or sub networks of the list |
137 | * - struct wg_session represents a session of a secure tunnel with a peer | | 137 | * - struct wg_session represents a session of a secure tunnel with a peer |
138 | * - Two instances of sessions belong to a peer; a stable session and a | | 138 | * - Two instances of sessions belong to a peer; a stable session and a |
139 | * unstable session | | 139 | * unstable session |
140 | * - A handshake process of a session always starts with a unstable instace | | 140 | * - A handshake process of a session always starts with a unstable instace |
141 | * - Once a session is established, its instance becomes stable and the | | 141 | * - Once a session is established, its instance becomes stable and the |
142 | * other becomes unstable instead | | 142 | * other becomes unstable instead |
143 | * - Data messages are always sent via a stable session | | 143 | * - Data messages are always sent via a stable session |
144 | * | | 144 | * |
145 | * Locking notes: | | 145 | * Locking notes: |
146 | * - wg interfaces (struct wg_softc, wg) is listed in wg_softcs.list and | | 146 | * - wg interfaces (struct wg_softc, wg) is listed in wg_softcs.list and |
147 | * protected by wg_softcs.lock | | 147 | * protected by wg_softcs.lock |
148 | * - Each wg has a mutex(9) and a rwlock(9) | | 148 | * - Each wg has a mutex(9) and a rwlock(9) |
149 | * - The mutex (wg_lock) protects its peer list (wg_peers) | | 149 | * - The mutex (wg_lock) protects its peer list (wg_peers) |
150 | * - A peer on the list is also protected by pserialize(9) or psref(9) | | 150 | * - A peer on the list is also protected by pserialize(9) or psref(9) |
151 | * - The rwlock (wg_rwlock) protects the routing tables (wg_rtable_ipv[46]) | | 151 | * - The rwlock (wg_rwlock) protects the routing tables (wg_rtable_ipv[46]) |
152 | * - Each peer (struct wg_peer, wgp) has a mutex | | 152 | * - Each peer (struct wg_peer, wgp) has a mutex |
153 | * - The mutex (wgp_lock) protects wgp_session_unstable and wgp_state | | 153 | * - The mutex (wgp_lock) protects wgp_session_unstable and wgp_state |
154 | * - Each session (struct wg_session, wgs) has a mutex | | 154 | * - Each session (struct wg_session, wgs) has a mutex |
155 | * - The mutex (wgs_lock) protects its state (wgs_state) and its handshake | | 155 | * - The mutex (wgs_lock) protects its state (wgs_state) and its handshake |
156 | * states | | 156 | * states |
157 | * - wgs_state of a unstable session can be changed while it never be | | 157 | * - wgs_state of a unstable session can be changed while it never be |
158 | * changed on a stable session, so once get a session instace via | | 158 | * changed on a stable session, so once get a session instace via |
159 | * wgp_session_stable we can safely access wgs_state without | | 159 | * wgp_session_stable we can safely access wgs_state without |
160 | * holding wgs_lock | | 160 | * holding wgs_lock |
161 | * - A session is protected by pserialize or psref like wgp | | 161 | * - A session is protected by pserialize or psref like wgp |
162 | * - On a session swap, we must wait for all readers to release a | | 162 | * - On a session swap, we must wait for all readers to release a |
163 | * reference to a stable session before changing wgs_state and | | 163 | * reference to a stable session before changing wgs_state and |
164 | * session states | | 164 | * session states |
165 | * | | 165 | * |
166 | * Lock order: wg_lock -> wgp_lock -> wgs_lock | | 166 | * Lock order: wg_lock -> wgp_lock -> wgs_lock |
167 | */ | | 167 | */ |
168 | | | 168 | |
169 | | | 169 | |
170 | #define WGLOG(level, fmt, args...) \ | | 170 | #define WGLOG(level, fmt, args...) \ |
171 | log(level, "%s: " fmt, __func__, ##args) | | 171 | log(level, "%s: " fmt, __func__, ##args) |
172 | | | 172 | |
173 | /* Debug options */ | | 173 | /* Debug options */ |
174 | #ifdef WG_DEBUG | | 174 | #ifdef WG_DEBUG |
175 | /* Output debug logs */ | | 175 | /* Output debug logs */ |
176 | #ifndef WG_DEBUG_LOG | | 176 | #ifndef WG_DEBUG_LOG |
177 | #define WG_DEBUG_LOG | | 177 | #define WG_DEBUG_LOG |
178 | #endif | | 178 | #endif |
179 | /* Output trace logs */ | | 179 | /* Output trace logs */ |
180 | #ifndef WG_DEBUG_TRACE | | 180 | #ifndef WG_DEBUG_TRACE |
181 | #define WG_DEBUG_TRACE | | 181 | #define WG_DEBUG_TRACE |
182 | #endif | | 182 | #endif |
183 | /* Output hash values, etc. */ | | 183 | /* Output hash values, etc. */ |
184 | #ifndef WG_DEBUG_DUMP | | 184 | #ifndef WG_DEBUG_DUMP |
185 | #define WG_DEBUG_DUMP | | 185 | #define WG_DEBUG_DUMP |
186 | #endif | | 186 | #endif |
187 | /* Make some internal parameters configurable for testing and debugging */ | | 187 | /* Make some internal parameters configurable for testing and debugging */ |
188 | #ifndef WG_DEBUG_PARAMS | | 188 | #ifndef WG_DEBUG_PARAMS |
189 | #define WG_DEBUG_PARAMS | | 189 | #define WG_DEBUG_PARAMS |
190 | #endif | | 190 | #endif |
191 | #endif | | 191 | #endif |
192 | | | 192 | |
193 | #ifdef WG_DEBUG_TRACE | | 193 | #ifdef WG_DEBUG_TRACE |
194 | #define WG_TRACE(msg) \ | | 194 | #define WG_TRACE(msg) \ |
195 | log(LOG_DEBUG, "%s:%d: %s\n", __func__, __LINE__, (msg)) | | 195 | log(LOG_DEBUG, "%s:%d: %s\n", __func__, __LINE__, (msg)) |
196 | #else | | 196 | #else |
197 | #define WG_TRACE(msg) __nothing | | 197 | #define WG_TRACE(msg) __nothing |
198 | #endif | | 198 | #endif |
199 | | | 199 | |
200 | #ifdef WG_DEBUG_LOG | | 200 | #ifdef WG_DEBUG_LOG |
201 | #define WG_DLOG(fmt, args...) log(LOG_DEBUG, "%s: " fmt, __func__, ##args) | | 201 | #define WG_DLOG(fmt, args...) log(LOG_DEBUG, "%s: " fmt, __func__, ##args) |
202 | #else | | 202 | #else |
203 | #define WG_DLOG(fmt, args...) __nothing | | 203 | #define WG_DLOG(fmt, args...) __nothing |
204 | #endif | | 204 | #endif |
205 | | | 205 | |
206 | #define WG_LOG_RATECHECK(wgprc, level, fmt, args...) do { \ | | 206 | #define WG_LOG_RATECHECK(wgprc, level, fmt, args...) do { \ |
207 | if (ppsratecheck(&(wgprc)->wgprc_lasttime, \ | | 207 | if (ppsratecheck(&(wgprc)->wgprc_lasttime, \ |
208 | &(wgprc)->wgprc_curpps, 1)) { \ | | 208 | &(wgprc)->wgprc_curpps, 1)) { \ |
209 | log(level, fmt, ##args); \ | | 209 | log(level, fmt, ##args); \ |
210 | } \ | | 210 | } \ |
211 | } while (0) | | 211 | } while (0) |
212 | | | 212 | |
213 | #ifdef WG_DEBUG_PARAMS | | 213 | #ifdef WG_DEBUG_PARAMS |
214 | static bool wg_force_underload = false; | | 214 | static bool wg_force_underload = false; |
215 | #endif | | 215 | #endif |
216 | | | 216 | |
217 | #ifdef WG_DEBUG_DUMP | | 217 | #ifdef WG_DEBUG_DUMP |
218 | | | 218 | |
219 | #ifdef WG_RUMPKERNEL | | 219 | #ifdef WG_RUMPKERNEL |
220 | static void | | 220 | static void |
221 | wg_dump_buf(const char *func, const char *buf, const size_t size) | | 221 | wg_dump_buf(const char *func, const char *buf, const size_t size) |
222 | { | | 222 | { |
223 | | | 223 | |
224 | log(LOG_DEBUG, "%s: ", func); | | 224 | log(LOG_DEBUG, "%s: ", func); |
225 | for (int i = 0; i < size; i++) | | 225 | for (int i = 0; i < size; i++) |
226 | log(LOG_DEBUG, "%02x ", (int)(0xff & buf[i])); | | 226 | log(LOG_DEBUG, "%02x ", (int)(0xff & buf[i])); |
227 | log(LOG_DEBUG, "\n"); | | 227 | log(LOG_DEBUG, "\n"); |
228 | } | | 228 | } |
229 | #endif | | 229 | #endif |
230 | | | 230 | |
231 | static void | | 231 | static void |
232 | wg_dump_hash(const uint8_t *func, const uint8_t *name, const uint8_t *hash, | | 232 | wg_dump_hash(const uint8_t *func, const uint8_t *name, const uint8_t *hash, |
233 | const size_t size) | | 233 | const size_t size) |
234 | { | | 234 | { |
235 | | | 235 | |
236 | log(LOG_DEBUG, "%s: %s: ", func, name); | | 236 | log(LOG_DEBUG, "%s: %s: ", func, name); |
237 | for (int i = 0; i < size; i++) | | 237 | for (int i = 0; i < size; i++) |
238 | log(LOG_DEBUG, "%02x ", (int)(0xff & hash[i])); | | 238 | log(LOG_DEBUG, "%02x ", (int)(0xff & hash[i])); |
239 | log(LOG_DEBUG, "\n"); | | 239 | log(LOG_DEBUG, "\n"); |
240 | } | | 240 | } |
241 | | | 241 | |
242 | #define WG_DUMP_HASH(name, hash) \ | | 242 | #define WG_DUMP_HASH(name, hash) \ |
243 | wg_dump_hash(__func__, name, hash, WG_HASH_LEN) | | 243 | wg_dump_hash(__func__, name, hash, WG_HASH_LEN) |
244 | #define WG_DUMP_HASH48(name, hash) \ | | 244 | #define WG_DUMP_HASH48(name, hash) \ |
245 | wg_dump_hash(__func__, name, hash, 48) | | 245 | wg_dump_hash(__func__, name, hash, 48) |
246 | #define WG_DUMP_BUF(buf, size) \ | | 246 | #define WG_DUMP_BUF(buf, size) \ |
247 | wg_dump_buf(__func__, buf, size) | | 247 | wg_dump_buf(__func__, buf, size) |
248 | #else | | 248 | #else |
249 | #define WG_DUMP_HASH(name, hash) __nothing | | 249 | #define WG_DUMP_HASH(name, hash) __nothing |
250 | #define WG_DUMP_HASH48(name, hash) __nothing | | 250 | #define WG_DUMP_HASH48(name, hash) __nothing |
251 | #define WG_DUMP_BUF(buf, size) __nothing | | 251 | #define WG_DUMP_BUF(buf, size) __nothing |
252 | #endif /* WG_DEBUG_DUMP */ | | 252 | #endif /* WG_DEBUG_DUMP */ |
253 | | | 253 | |
254 | #define WG_MTU 1420 | | 254 | #define WG_MTU 1420 |
255 | #define WG_ALLOWEDIPS 16 | | 255 | #define WG_ALLOWEDIPS 16 |
256 | | | 256 | |
257 | #define CURVE25519_KEY_LEN 32 | | 257 | #define CURVE25519_KEY_LEN 32 |
258 | #define TAI64N_LEN sizeof(uint32_t) * 3 | | 258 | #define TAI64N_LEN sizeof(uint32_t) * 3 |
259 | #define POLY1305_AUTHTAG_LEN 16 | | 259 | #define POLY1305_AUTHTAG_LEN 16 |
260 | #define HMAC_BLOCK_LEN 64 | | 260 | #define HMAC_BLOCK_LEN 64 |
261 | | | 261 | |
262 | /* [N] 4.1: "DHLEN must be 32 or greater." WireGuard chooses 32. */ | | 262 | /* [N] 4.1: "DHLEN must be 32 or greater." WireGuard chooses 32. */ |
263 | /* [N] 4.3: Hash functions */ | | 263 | /* [N] 4.3: Hash functions */ |
264 | #define NOISE_DHLEN 32 | | 264 | #define NOISE_DHLEN 32 |
265 | /* [N] 4.3: "Must be 32 or 64." WireGuard chooses 32. */ | | 265 | /* [N] 4.3: "Must be 32 or 64." WireGuard chooses 32. */ |
266 | #define NOISE_HASHLEN 32 | | 266 | #define NOISE_HASHLEN 32 |
267 | #define NOISE_BLOCKLEN 64 | | 267 | #define NOISE_BLOCKLEN 64 |
268 | #define NOISE_HKDF_OUTPUT_LEN NOISE_HASHLEN | | 268 | #define NOISE_HKDF_OUTPUT_LEN NOISE_HASHLEN |
269 | /* [N] 5.1: "k" */ | | 269 | /* [N] 5.1: "k" */ |
270 | #define NOISE_CIPHER_KEY_LEN 32 | | 270 | #define NOISE_CIPHER_KEY_LEN 32 |
271 | /* | | 271 | /* |
272 | * [N] 9.2: "psk" | | 272 | * [N] 9.2: "psk" |
273 | * "... psk is a 32-byte secret value provided by the application." | | 273 | * "... psk is a 32-byte secret value provided by the application." |
274 | */ | | 274 | */ |
275 | #define NOISE_PRESHARED_KEY_LEN 32 | | 275 | #define NOISE_PRESHARED_KEY_LEN 32 |
276 | | | 276 | |
277 | #define WG_STATIC_KEY_LEN CURVE25519_KEY_LEN | | 277 | #define WG_STATIC_KEY_LEN CURVE25519_KEY_LEN |
278 | #define WG_TIMESTAMP_LEN TAI64N_LEN | | 278 | #define WG_TIMESTAMP_LEN TAI64N_LEN |
279 | | | 279 | |
280 | #define WG_PRESHARED_KEY_LEN NOISE_PRESHARED_KEY_LEN | | 280 | #define WG_PRESHARED_KEY_LEN NOISE_PRESHARED_KEY_LEN |
281 | | | 281 | |
282 | #define WG_COOKIE_LEN 16 | | 282 | #define WG_COOKIE_LEN 16 |
283 | #define WG_MAC_LEN 16 | | 283 | #define WG_MAC_LEN 16 |
284 | #define WG_RANDVAL_LEN 24 | | 284 | #define WG_RANDVAL_LEN 24 |
285 | | | 285 | |
286 | #define WG_EPHEMERAL_KEY_LEN CURVE25519_KEY_LEN | | 286 | #define WG_EPHEMERAL_KEY_LEN CURVE25519_KEY_LEN |
287 | /* [N] 5.2: "ck: A chaining key of HASHLEN bytes" */ | | 287 | /* [N] 5.2: "ck: A chaining key of HASHLEN bytes" */ |
288 | #define WG_CHAINING_KEY_LEN NOISE_HASHLEN | | 288 | #define WG_CHAINING_KEY_LEN NOISE_HASHLEN |
289 | /* [N] 5.2: "h: A hash output of HASHLEN bytes" */ | | 289 | /* [N] 5.2: "h: A hash output of HASHLEN bytes" */ |
290 | #define WG_HASH_LEN NOISE_HASHLEN | | 290 | #define WG_HASH_LEN NOISE_HASHLEN |
291 | #define WG_CIPHER_KEY_LEN NOISE_CIPHER_KEY_LEN | | 291 | #define WG_CIPHER_KEY_LEN NOISE_CIPHER_KEY_LEN |
292 | #define WG_DH_OUTPUT_LEN NOISE_DHLEN | | 292 | #define WG_DH_OUTPUT_LEN NOISE_DHLEN |
293 | #define WG_KDF_OUTPUT_LEN NOISE_HKDF_OUTPUT_LEN | | 293 | #define WG_KDF_OUTPUT_LEN NOISE_HKDF_OUTPUT_LEN |
294 | #define WG_AUTHTAG_LEN POLY1305_AUTHTAG_LEN | | 294 | #define WG_AUTHTAG_LEN POLY1305_AUTHTAG_LEN |
295 | #define WG_DATA_KEY_LEN 32 | | 295 | #define WG_DATA_KEY_LEN 32 |
296 | #define WG_SALT_LEN 24 | | 296 | #define WG_SALT_LEN 24 |
297 | | | 297 | |
298 | /* | | 298 | /* |
299 | * The protocol messages | | 299 | * The protocol messages |
300 | */ | | 300 | */ |
301 | struct wg_msg { | | 301 | struct wg_msg { |
302 | uint32_t wgm_type; | | 302 | uint32_t wgm_type; |
303 | } __packed; | | 303 | } __packed; |
304 | | | 304 | |
305 | /* [W] 5.4.2 First Message: Initiator to Responder */ | | 305 | /* [W] 5.4.2 First Message: Initiator to Responder */ |
306 | struct wg_msg_init { | | 306 | struct wg_msg_init { |
307 | uint32_t wgmi_type; | | 307 | uint32_t wgmi_type; |
308 | uint32_t wgmi_sender; | | 308 | uint32_t wgmi_sender; |
309 | uint8_t wgmi_ephemeral[WG_EPHEMERAL_KEY_LEN]; | | 309 | uint8_t wgmi_ephemeral[WG_EPHEMERAL_KEY_LEN]; |
310 | uint8_t wgmi_static[WG_STATIC_KEY_LEN + WG_AUTHTAG_LEN]; | | 310 | uint8_t wgmi_static[WG_STATIC_KEY_LEN + WG_AUTHTAG_LEN]; |
311 | uint8_t wgmi_timestamp[WG_TIMESTAMP_LEN + WG_AUTHTAG_LEN]; | | 311 | uint8_t wgmi_timestamp[WG_TIMESTAMP_LEN + WG_AUTHTAG_LEN]; |
312 | uint8_t wgmi_mac1[WG_MAC_LEN]; | | 312 | uint8_t wgmi_mac1[WG_MAC_LEN]; |
313 | uint8_t wgmi_mac2[WG_MAC_LEN]; | | 313 | uint8_t wgmi_mac2[WG_MAC_LEN]; |
314 | } __packed; | | 314 | } __packed; |
315 | | | 315 | |
316 | /* [W] 5.4.3 Second Message: Responder to Initiator */ | | 316 | /* [W] 5.4.3 Second Message: Responder to Initiator */ |
317 | struct wg_msg_resp { | | 317 | struct wg_msg_resp { |
318 | uint32_t wgmr_type; | | 318 | uint32_t wgmr_type; |
319 | uint32_t wgmr_sender; | | 319 | uint32_t wgmr_sender; |
320 | uint32_t wgmr_receiver; | | 320 | uint32_t wgmr_receiver; |
321 | uint8_t wgmr_ephemeral[WG_EPHEMERAL_KEY_LEN]; | | 321 | uint8_t wgmr_ephemeral[WG_EPHEMERAL_KEY_LEN]; |
322 | uint8_t wgmr_empty[0 + WG_AUTHTAG_LEN]; | | 322 | uint8_t wgmr_empty[0 + WG_AUTHTAG_LEN]; |
323 | uint8_t wgmr_mac1[WG_MAC_LEN]; | | 323 | uint8_t wgmr_mac1[WG_MAC_LEN]; |
324 | uint8_t wgmr_mac2[WG_MAC_LEN]; | | 324 | uint8_t wgmr_mac2[WG_MAC_LEN]; |
325 | } __packed; | | 325 | } __packed; |
326 | | | 326 | |
327 | /* [W] 5.4.6 Subsequent Messages: Transport Data Messages */ | | 327 | /* [W] 5.4.6 Subsequent Messages: Transport Data Messages */ |
328 | struct wg_msg_data { | | 328 | struct wg_msg_data { |
329 | uint32_t wgmd_type; | | 329 | uint32_t wgmd_type; |
330 | uint32_t wgmd_receiver; | | 330 | uint32_t wgmd_receiver; |
331 | uint64_t wgmd_counter; | | 331 | uint64_t wgmd_counter; |
332 | uint32_t wgmd_packet[0]; | | 332 | uint32_t wgmd_packet[0]; |
333 | } __packed; | | 333 | } __packed; |
334 | | | 334 | |
335 | /* [W] 5.4.7 Under Load: Cookie Reply Message */ | | 335 | /* [W] 5.4.7 Under Load: Cookie Reply Message */ |
336 | struct wg_msg_cookie { | | 336 | struct wg_msg_cookie { |
337 | uint32_t wgmc_type; | | 337 | uint32_t wgmc_type; |
338 | uint32_t wgmc_receiver; | | 338 | uint32_t wgmc_receiver; |
339 | uint8_t wgmc_salt[WG_SALT_LEN]; | | 339 | uint8_t wgmc_salt[WG_SALT_LEN]; |
340 | uint8_t wgmc_cookie[WG_COOKIE_LEN + WG_AUTHTAG_LEN]; | | 340 | uint8_t wgmc_cookie[WG_COOKIE_LEN + WG_AUTHTAG_LEN]; |
341 | } __packed; | | 341 | } __packed; |
342 | | | 342 | |
343 | #define WG_MSG_TYPE_INIT 1 | | 343 | #define WG_MSG_TYPE_INIT 1 |
344 | #define WG_MSG_TYPE_RESP 2 | | 344 | #define WG_MSG_TYPE_RESP 2 |
345 | #define WG_MSG_TYPE_COOKIE 3 | | 345 | #define WG_MSG_TYPE_COOKIE 3 |
346 | #define WG_MSG_TYPE_DATA 4 | | 346 | #define WG_MSG_TYPE_DATA 4 |
347 | #define WG_MSG_TYPE_MAX WG_MSG_TYPE_DATA | | 347 | #define WG_MSG_TYPE_MAX WG_MSG_TYPE_DATA |
348 | | | 348 | |
349 | /* Sliding windows */ | | 349 | /* Sliding windows */ |
350 | | | 350 | |
351 | #define SLIWIN_BITS 2048u | | 351 | #define SLIWIN_BITS 2048u |
352 | #define SLIWIN_TYPE uint32_t | | 352 | #define SLIWIN_TYPE uint32_t |
353 | #define SLIWIN_BPW NBBY*sizeof(SLIWIN_TYPE) | | 353 | #define SLIWIN_BPW NBBY*sizeof(SLIWIN_TYPE) |
354 | #define SLIWIN_WORDS howmany(SLIWIN_BITS, SLIWIN_BPW) | | 354 | #define SLIWIN_WORDS howmany(SLIWIN_BITS, SLIWIN_BPW) |
355 | #define SLIWIN_NPKT (SLIWIN_BITS - NBBY*sizeof(SLIWIN_TYPE)) | | 355 | #define SLIWIN_NPKT (SLIWIN_BITS - NBBY*sizeof(SLIWIN_TYPE)) |
356 | | | 356 | |
357 | struct sliwin { | | 357 | struct sliwin { |
358 | SLIWIN_TYPE B[SLIWIN_WORDS]; | | 358 | SLIWIN_TYPE B[SLIWIN_WORDS]; |
359 | uint64_t T; | | 359 | uint64_t T; |
360 | }; | | 360 | }; |
361 | | | 361 | |
362 | static void | | 362 | static void |
363 | sliwin_reset(struct sliwin *W) | | 363 | sliwin_reset(struct sliwin *W) |
364 | { | | 364 | { |
365 | | | 365 | |
366 | memset(W, 0, sizeof(*W)); | | 366 | memset(W, 0, sizeof(*W)); |
367 | } | | 367 | } |
368 | | | 368 | |
369 | static int | | 369 | static int |
370 | sliwin_check_fast(const volatile struct sliwin *W, uint64_t S) | | 370 | sliwin_check_fast(const volatile struct sliwin *W, uint64_t S) |
371 | { | | 371 | { |
372 | | | 372 | |
373 | /* | | 373 | /* |
374 | * If it's more than one window older than the highest sequence | | 374 | * If it's more than one window older than the highest sequence |
375 | * number we've seen, reject. | | 375 | * number we've seen, reject. |
376 | */ | | 376 | */ |
377 | #ifdef __HAVE_ATOMIC64_LOADSTORE | | 377 | #ifdef __HAVE_ATOMIC64_LOADSTORE |
378 | if (S + SLIWIN_NPKT < atomic_load_relaxed(&W->T)) | | 378 | if (S + SLIWIN_NPKT < atomic_load_relaxed(&W->T)) |
379 | return EAUTH; | | 379 | return EAUTH; |
380 | #endif | | 380 | #endif |
381 | | | 381 | |
382 | /* | | 382 | /* |
383 | * Otherwise, we need to take the lock to decide, so don't | | 383 | * Otherwise, we need to take the lock to decide, so don't |
384 | * reject just yet. Caller must serialize a call to | | 384 | * reject just yet. Caller must serialize a call to |
385 | * sliwin_update in this case. | | 385 | * sliwin_update in this case. |
386 | */ | | 386 | */ |
387 | return 0; | | 387 | return 0; |
388 | } | | 388 | } |
389 | | | 389 | |
390 | static int | | 390 | static int |
391 | sliwin_update(struct sliwin *W, uint64_t S) | | 391 | sliwin_update(struct sliwin *W, uint64_t S) |
392 | { | | 392 | { |
393 | unsigned word, bit; | | 393 | unsigned word, bit; |
394 | | | 394 | |
395 | /* | | 395 | /* |
396 | * If it's more than one window older than the highest sequence | | 396 | * If it's more than one window older than the highest sequence |
397 | * number we've seen, reject. | | 397 | * number we've seen, reject. |
398 | */ | | 398 | */ |
399 | if (S + SLIWIN_NPKT < W->T) | | 399 | if (S + SLIWIN_NPKT < W->T) |
400 | return EAUTH; | | 400 | return EAUTH; |
401 | | | 401 | |
402 | /* | | 402 | /* |
403 | * If it's higher than the highest sequence number we've seen, | | 403 | * If it's higher than the highest sequence number we've seen, |
404 | * advance the window. | | 404 | * advance the window. |
405 | */ | | 405 | */ |
406 | if (S > W->T) { | | 406 | if (S > W->T) { |
407 | uint64_t i = W->T / SLIWIN_BPW; | | 407 | uint64_t i = W->T / SLIWIN_BPW; |
408 | uint64_t j = S / SLIWIN_BPW; | | 408 | uint64_t j = S / SLIWIN_BPW; |
409 | unsigned k; | | 409 | unsigned k; |
410 | | | 410 | |
411 | for (k = 0; k < MIN(j - i, SLIWIN_WORDS); k++) | | 411 | for (k = 0; k < MIN(j - i, SLIWIN_WORDS); k++) |
412 | W->B[(i + k + 1) % SLIWIN_WORDS] = 0; | | 412 | W->B[(i + k + 1) % SLIWIN_WORDS] = 0; |
413 | #ifdef __HAVE_ATOMIC64_LOADSTORE | | 413 | #ifdef __HAVE_ATOMIC64_LOADSTORE |
414 | atomic_store_relaxed(&W->T, S); | | 414 | atomic_store_relaxed(&W->T, S); |
415 | #else | | 415 | #else |
416 | W->T = S; | | 416 | W->T = S; |
417 | #endif | | 417 | #endif |
418 | } | | 418 | } |
419 | | | 419 | |
420 | /* Test and set the bit -- if already set, reject. */ | | 420 | /* Test and set the bit -- if already set, reject. */ |
421 | word = (S / SLIWIN_BPW) % SLIWIN_WORDS; | | 421 | word = (S / SLIWIN_BPW) % SLIWIN_WORDS; |
422 | bit = S % SLIWIN_BPW; | | 422 | bit = S % SLIWIN_BPW; |
423 | if (W->B[word] & (1UL << bit)) | | 423 | if (W->B[word] & (1UL << bit)) |
424 | return EAUTH; | | 424 | return EAUTH; |
425 | W->B[word] |= 1UL << bit; | | 425 | W->B[word] |= 1UL << bit; |
426 | | | 426 | |
427 | /* Accept! */ | | 427 | /* Accept! */ |
428 | return 0; | | 428 | return 0; |
429 | } | | 429 | } |
430 | | | 430 | |
431 | struct wg_worker { | | 431 | struct wg_worker { |
432 | kmutex_t wgw_lock; | | 432 | kmutex_t wgw_lock; |
433 | kcondvar_t wgw_cv; | | 433 | kcondvar_t wgw_cv; |
434 | bool wgw_todie; | | 434 | bool wgw_todie; |
435 | struct socket *wgw_so4; | | 435 | struct socket *wgw_so4; |
436 | struct socket *wgw_so6; | | 436 | struct socket *wgw_so6; |
437 | int wgw_wakeup_reasons; | | 437 | int wgw_wakeup_reasons; |
438 | #define WG_WAKEUP_REASON_RECEIVE_PACKETS_IPV4 __BIT(0) | | 438 | #define WG_WAKEUP_REASON_RECEIVE_PACKETS_IPV4 __BIT(0) |
439 | #define WG_WAKEUP_REASON_RECEIVE_PACKETS_IPV6 __BIT(1) | | 439 | #define WG_WAKEUP_REASON_RECEIVE_PACKETS_IPV6 __BIT(1) |
440 | #define WG_WAKEUP_REASON_PEER __BIT(2) | | 440 | #define WG_WAKEUP_REASON_PEER __BIT(2) |
441 | }; | | 441 | }; |
442 | | | 442 | |
443 | struct wg_session { | | 443 | struct wg_session { |
444 | struct wg_peer *wgs_peer; | | 444 | struct wg_peer *wgs_peer; |
445 | struct psref_target | | 445 | struct psref_target |
446 | wgs_psref; | | 446 | wgs_psref; |
447 | kmutex_t *wgs_lock; | | 447 | kmutex_t *wgs_lock; |
448 | | | 448 | |
449 | int wgs_state; | | 449 | int wgs_state; |
450 | #define WGS_STATE_UNKNOWN 0 | | 450 | #define WGS_STATE_UNKNOWN 0 |
451 | #define WGS_STATE_INIT_ACTIVE 1 | | 451 | #define WGS_STATE_INIT_ACTIVE 1 |
452 | #define WGS_STATE_INIT_PASSIVE 2 | | 452 | #define WGS_STATE_INIT_PASSIVE 2 |
453 | #define WGS_STATE_ESTABLISHED 3 | | 453 | #define WGS_STATE_ESTABLISHED 3 |
454 | #define WGS_STATE_DESTROYING 4 | | 454 | #define WGS_STATE_DESTROYING 4 |
455 | | | 455 | |
456 | time_t wgs_time_established; | | 456 | time_t wgs_time_established; |
457 | time_t wgs_time_last_data_sent; | | 457 | time_t wgs_time_last_data_sent; |
458 | bool wgs_is_initiator; | | 458 | bool wgs_is_initiator; |
459 | | | 459 | |
460 | uint32_t wgs_sender_index; | | 460 | uint32_t wgs_sender_index; |
461 | uint32_t wgs_receiver_index; | | 461 | uint32_t wgs_receiver_index; |
462 | #ifdef __HAVE_ATOMIC64_LOADSTORE | | 462 | #ifdef __HAVE_ATOMIC64_LOADSTORE |
463 | volatile uint64_t | | 463 | volatile uint64_t |
464 | wgs_send_counter; | | 464 | wgs_send_counter; |
465 | #else | | 465 | #else |
466 | kmutex_t wgs_send_counter_lock; | | 466 | kmutex_t wgs_send_counter_lock; |
467 | uint64_t wgs_send_counter; | | 467 | uint64_t wgs_send_counter; |
468 | #endif | | 468 | #endif |
469 | | | 469 | |
470 | struct { | | 470 | struct { |
471 | kmutex_t lock; | | 471 | kmutex_t lock; |
472 | struct sliwin window; | | 472 | struct sliwin window; |
473 | } *wgs_recvwin; | | 473 | } *wgs_recvwin; |
474 | | | 474 | |
475 | uint8_t wgs_handshake_hash[WG_HASH_LEN]; | | 475 | uint8_t wgs_handshake_hash[WG_HASH_LEN]; |
476 | uint8_t wgs_chaining_key[WG_CHAINING_KEY_LEN]; | | 476 | uint8_t wgs_chaining_key[WG_CHAINING_KEY_LEN]; |
477 | uint8_t wgs_ephemeral_key_pub[WG_EPHEMERAL_KEY_LEN]; | | 477 | uint8_t wgs_ephemeral_key_pub[WG_EPHEMERAL_KEY_LEN]; |
478 | uint8_t wgs_ephemeral_key_priv[WG_EPHEMERAL_KEY_LEN]; | | 478 | uint8_t wgs_ephemeral_key_priv[WG_EPHEMERAL_KEY_LEN]; |
479 | uint8_t wgs_ephemeral_key_peer[WG_EPHEMERAL_KEY_LEN]; | | 479 | uint8_t wgs_ephemeral_key_peer[WG_EPHEMERAL_KEY_LEN]; |
480 | uint8_t wgs_tkey_send[WG_DATA_KEY_LEN]; | | 480 | uint8_t wgs_tkey_send[WG_DATA_KEY_LEN]; |
481 | uint8_t wgs_tkey_recv[WG_DATA_KEY_LEN]; | | 481 | uint8_t wgs_tkey_recv[WG_DATA_KEY_LEN]; |
482 | }; | | 482 | }; |
483 | | | 483 | |
484 | struct wg_sockaddr { | | 484 | struct wg_sockaddr { |
485 | union { | | 485 | union { |
486 | struct sockaddr_storage _ss; | | 486 | struct sockaddr_storage _ss; |
487 | struct sockaddr _sa; | | 487 | struct sockaddr _sa; |
488 | struct sockaddr_in _sin; | | 488 | struct sockaddr_in _sin; |
489 | struct sockaddr_in6 _sin6; | | 489 | struct sockaddr_in6 _sin6; |
490 | }; | | 490 | }; |
491 | struct psref_target wgsa_psref; | | 491 | struct psref_target wgsa_psref; |
492 | }; | | 492 | }; |
493 | | | 493 | |
494 | #define wgsatoss(wgsa) (&(wgsa)->_ss) | | 494 | #define wgsatoss(wgsa) (&(wgsa)->_ss) |
495 | #define wgsatosa(wgsa) (&(wgsa)->_sa) | | 495 | #define wgsatosa(wgsa) (&(wgsa)->_sa) |
496 | #define wgsatosin(wgsa) (&(wgsa)->_sin) | | 496 | #define wgsatosin(wgsa) (&(wgsa)->_sin) |
497 | #define wgsatosin6(wgsa) (&(wgsa)->_sin6) | | 497 | #define wgsatosin6(wgsa) (&(wgsa)->_sin6) |
498 | | | 498 | |
499 | #define wgsa_family(wgsa) (wgsatosa(wgsa)->sa_family) | | 499 | #define wgsa_family(wgsa) (wgsatosa(wgsa)->sa_family) |
500 | | | 500 | |
501 | struct wg_peer; | | 501 | struct wg_peer; |
502 | struct wg_allowedip { | | 502 | struct wg_allowedip { |
503 | struct radix_node wga_nodes[2]; | | 503 | struct radix_node wga_nodes[2]; |
504 | struct wg_sockaddr _wga_sa_addr; | | 504 | struct wg_sockaddr _wga_sa_addr; |
505 | struct wg_sockaddr _wga_sa_mask; | | 505 | struct wg_sockaddr _wga_sa_mask; |
506 | #define wga_sa_addr _wga_sa_addr._sa | | 506 | #define wga_sa_addr _wga_sa_addr._sa |
507 | #define wga_sa_mask _wga_sa_mask._sa | | 507 | #define wga_sa_mask _wga_sa_mask._sa |
508 | | | 508 | |
509 | int wga_family; | | 509 | int wga_family; |
510 | uint8_t wga_cidr; | | 510 | uint8_t wga_cidr; |
511 | union { | | 511 | union { |
512 | struct in_addr _ip4; | | 512 | struct in_addr _ip4; |
513 | struct in6_addr _ip6; | | 513 | struct in6_addr _ip6; |
514 | } wga_addr; | | 514 | } wga_addr; |
515 | #define wga_addr4 wga_addr._ip4 | | 515 | #define wga_addr4 wga_addr._ip4 |
516 | #define wga_addr6 wga_addr._ip6 | | 516 | #define wga_addr6 wga_addr._ip6 |
517 | | | 517 | |
518 | struct wg_peer *wga_peer; | | 518 | struct wg_peer *wga_peer; |
519 | }; | | 519 | }; |
520 | | | 520 | |
521 | typedef uint8_t wg_timestamp_t[WG_TIMESTAMP_LEN]; | | 521 | typedef uint8_t wg_timestamp_t[WG_TIMESTAMP_LEN]; |
522 | | | 522 | |
523 | struct wg_ppsratecheck { | | 523 | struct wg_ppsratecheck { |
524 | struct timeval wgprc_lasttime; | | 524 | struct timeval wgprc_lasttime; |
525 | int wgprc_curpps; | | 525 | int wgprc_curpps; |
526 | }; | | 526 | }; |
527 | | | 527 | |
528 | struct wg_softc; | | 528 | struct wg_softc; |
529 | struct wg_peer { | | 529 | struct wg_peer { |
530 | struct wg_softc *wgp_sc; | | 530 | struct wg_softc *wgp_sc; |
531 | char wgp_name[WG_PEER_NAME_MAXLEN + 1]; | | 531 | char wgp_name[WG_PEER_NAME_MAXLEN + 1]; |
532 | struct pslist_entry wgp_peerlist_entry; | | 532 | struct pslist_entry wgp_peerlist_entry; |
533 | pserialize_t wgp_psz; | | 533 | pserialize_t wgp_psz; |
534 | struct psref_target wgp_psref; | | 534 | struct psref_target wgp_psref; |
535 | kmutex_t *wgp_lock; | | 535 | kmutex_t *wgp_lock; |
536 | | | 536 | |
537 | uint8_t wgp_pubkey[WG_STATIC_KEY_LEN]; | | 537 | uint8_t wgp_pubkey[WG_STATIC_KEY_LEN]; |
538 | struct wg_sockaddr *wgp_endpoint; | | 538 | struct wg_sockaddr *wgp_endpoint; |
539 | struct wg_sockaddr *wgp_endpoint0; | | 539 | struct wg_sockaddr *wgp_endpoint0; |
540 | bool wgp_endpoint_changing; | | 540 | bool wgp_endpoint_changing; |
541 | bool wgp_endpoint_available; | | 541 | bool wgp_endpoint_available; |
542 | | | 542 | |
543 | /* The preshared key (optional) */ | | 543 | /* The preshared key (optional) */ |
544 | uint8_t wgp_psk[WG_PRESHARED_KEY_LEN]; | | 544 | uint8_t wgp_psk[WG_PRESHARED_KEY_LEN]; |
545 | | | 545 | |
546 | int wgp_state; | | 546 | int wgp_state; |
547 | #define WGP_STATE_INIT 0 | | 547 | #define WGP_STATE_INIT 0 |
548 | #define WGP_STATE_ESTABLISHED 1 | | 548 | #define WGP_STATE_ESTABLISHED 1 |
549 | #define WGP_STATE_GIVEUP 2 | | 549 | #define WGP_STATE_GIVEUP 2 |
550 | #define WGP_STATE_DESTROYING 3 | | 550 | #define WGP_STATE_DESTROYING 3 |
551 | | | 551 | |
552 | void *wgp_si; | | 552 | void *wgp_si; |
553 | pcq_t *wgp_q; | | 553 | pcq_t *wgp_q; |
554 | | | 554 | |
555 | struct wg_session *wgp_session_stable; | | 555 | struct wg_session *wgp_session_stable; |
556 | struct wg_session *wgp_session_unstable; | | 556 | struct wg_session *wgp_session_unstable; |
557 | | | 557 | |
558 | /* timestamp in big-endian */ | | 558 | /* timestamp in big-endian */ |
559 | wg_timestamp_t wgp_timestamp_latest_init; | | 559 | wg_timestamp_t wgp_timestamp_latest_init; |
560 | | | 560 | |
561 | struct timespec wgp_last_handshake_time; | | 561 | struct timespec wgp_last_handshake_time; |
562 | | | 562 | |
563 | callout_t wgp_rekey_timer; | | 563 | callout_t wgp_rekey_timer; |
564 | callout_t wgp_handshake_timeout_timer; | | 564 | callout_t wgp_handshake_timeout_timer; |
565 | callout_t wgp_session_dtor_timer; | | 565 | callout_t wgp_session_dtor_timer; |
566 | | | 566 | |
567 | time_t wgp_handshake_start_time; | | 567 | time_t wgp_handshake_start_time; |
568 | | | 568 | |
569 | int wgp_n_allowedips; | | 569 | int wgp_n_allowedips; |
570 | struct wg_allowedip wgp_allowedips[WG_ALLOWEDIPS]; | | 570 | struct wg_allowedip wgp_allowedips[WG_ALLOWEDIPS]; |
571 | | | 571 | |
572 | time_t wgp_latest_cookie_time; | | 572 | time_t wgp_latest_cookie_time; |
573 | uint8_t wgp_latest_cookie[WG_COOKIE_LEN]; | | 573 | uint8_t wgp_latest_cookie[WG_COOKIE_LEN]; |
574 | uint8_t wgp_last_sent_mac1[WG_MAC_LEN]; | | 574 | uint8_t wgp_last_sent_mac1[WG_MAC_LEN]; |
575 | bool wgp_last_sent_mac1_valid; | | 575 | bool wgp_last_sent_mac1_valid; |
576 | uint8_t wgp_last_sent_cookie[WG_COOKIE_LEN]; | | 576 | uint8_t wgp_last_sent_cookie[WG_COOKIE_LEN]; |
577 | bool wgp_last_sent_cookie_valid; | | 577 | bool wgp_last_sent_cookie_valid; |
578 | | | 578 | |
579 | time_t wgp_last_msg_received_time[WG_MSG_TYPE_MAX]; | | 579 | time_t wgp_last_msg_received_time[WG_MSG_TYPE_MAX]; |
580 | | | 580 | |
581 | time_t wgp_last_genrandval_time; | | 581 | time_t wgp_last_genrandval_time; |
582 | uint32_t wgp_randval; | | 582 | uint32_t wgp_randval; |
583 | | | 583 | |
584 | struct wg_ppsratecheck wgp_ppsratecheck; | | 584 | struct wg_ppsratecheck wgp_ppsratecheck; |
585 | | | 585 | |
586 | volatile unsigned int wgp_tasks; | | 586 | volatile unsigned int wgp_tasks; |
587 | #define WGP_TASK_SEND_INIT_MESSAGE __BIT(0) | | 587 | #define WGP_TASK_SEND_INIT_MESSAGE __BIT(0) |
588 | #define WGP_TASK_ENDPOINT_CHANGED __BIT(1) | | 588 | #define WGP_TASK_ENDPOINT_CHANGED __BIT(1) |
589 | #define WGP_TASK_SEND_KEEPALIVE_MESSAGE __BIT(2) | | 589 | #define WGP_TASK_SEND_KEEPALIVE_MESSAGE __BIT(2) |
590 | #define WGP_TASK_DESTROY_PREV_SESSION __BIT(3) | | 590 | #define WGP_TASK_DESTROY_PREV_SESSION __BIT(3) |
591 | }; | | 591 | }; |
592 | | | 592 | |
593 | struct wg_ops; | | 593 | struct wg_ops; |
594 | | | 594 | |
595 | struct wg_softc { | | 595 | struct wg_softc { |
596 | struct ifnet wg_if; | | 596 | struct ifnet wg_if; |
597 | LIST_ENTRY(wg_softc) wg_list; | | 597 | LIST_ENTRY(wg_softc) wg_list; |
598 | kmutex_t *wg_lock; | | 598 | kmutex_t *wg_lock; |
599 | krwlock_t *wg_rwlock; | | 599 | krwlock_t *wg_rwlock; |
600 | | | 600 | |
601 | uint8_t wg_privkey[WG_STATIC_KEY_LEN]; | | 601 | uint8_t wg_privkey[WG_STATIC_KEY_LEN]; |
602 | uint8_t wg_pubkey[WG_STATIC_KEY_LEN]; | | 602 | uint8_t wg_pubkey[WG_STATIC_KEY_LEN]; |
603 | | | 603 | |
604 | int wg_npeers; | | 604 | int wg_npeers; |
605 | struct pslist_head wg_peers; | | 605 | struct pslist_head wg_peers; |
606 | struct thmap *wg_peers_bypubkey; | | 606 | struct thmap *wg_peers_bypubkey; |
607 | struct thmap *wg_peers_byname; | | 607 | struct thmap *wg_peers_byname; |
608 | struct thmap *wg_sessions_byindex; | | 608 | struct thmap *wg_sessions_byindex; |
609 | uint16_t wg_listen_port; | | 609 | uint16_t wg_listen_port; |
610 | | | 610 | |
611 | struct wg_worker *wg_worker; | | 611 | struct wg_worker *wg_worker; |
612 | lwp_t *wg_worker_lwp; | | 612 | lwp_t *wg_worker_lwp; |
613 | | | 613 | |
614 | struct radix_node_head *wg_rtable_ipv4; | | 614 | struct radix_node_head *wg_rtable_ipv4; |
615 | struct radix_node_head *wg_rtable_ipv6; | | 615 | struct radix_node_head *wg_rtable_ipv6; |
616 | | | 616 | |
617 | struct wg_ppsratecheck wg_ppsratecheck; | | 617 | struct wg_ppsratecheck wg_ppsratecheck; |
618 | | | 618 | |
619 | struct wg_ops *wg_ops; | | 619 | struct wg_ops *wg_ops; |
620 | | | 620 | |
621 | #ifdef WG_RUMPKERNEL | | 621 | #ifdef WG_RUMPKERNEL |
622 | struct wg_user *wg_user; | | 622 | struct wg_user *wg_user; |
623 | #endif | | 623 | #endif |
624 | }; | | 624 | }; |
625 | | | 625 | |
626 | /* [W] 6.1 Preliminaries */ | | 626 | /* [W] 6.1 Preliminaries */ |
627 | #define WG_REKEY_AFTER_MESSAGES (1ULL << 60) | | 627 | #define WG_REKEY_AFTER_MESSAGES (1ULL << 60) |
628 | #define WG_REJECT_AFTER_MESSAGES (UINT64_MAX - (1 << 13)) | | 628 | #define WG_REJECT_AFTER_MESSAGES (UINT64_MAX - (1 << 13)) |
629 | #define WG_REKEY_AFTER_TIME 120 | | 629 | #define WG_REKEY_AFTER_TIME 120 |
630 | #define WG_REJECT_AFTER_TIME 180 | | 630 | #define WG_REJECT_AFTER_TIME 180 |
631 | #define WG_REKEY_ATTEMPT_TIME 90 | | 631 | #define WG_REKEY_ATTEMPT_TIME 90 |
632 | #define WG_REKEY_TIMEOUT 5 | | 632 | #define WG_REKEY_TIMEOUT 5 |
633 | #define WG_KEEPALIVE_TIMEOUT 10 | | 633 | #define WG_KEEPALIVE_TIMEOUT 10 |
634 | | | 634 | |
635 | #define WG_COOKIE_TIME 120 | | 635 | #define WG_COOKIE_TIME 120 |
636 | #define WG_RANDVAL_TIME (2 * 60) | | 636 | #define WG_RANDVAL_TIME (2 * 60) |
637 | | | 637 | |
638 | static uint64_t wg_rekey_after_messages = WG_REKEY_AFTER_MESSAGES; | | 638 | static uint64_t wg_rekey_after_messages = WG_REKEY_AFTER_MESSAGES; |
639 | static uint64_t wg_reject_after_messages = WG_REJECT_AFTER_MESSAGES; | | 639 | static uint64_t wg_reject_after_messages = WG_REJECT_AFTER_MESSAGES; |
640 | static unsigned wg_rekey_after_time = WG_REKEY_AFTER_TIME; | | 640 | static unsigned wg_rekey_after_time = WG_REKEY_AFTER_TIME; |
641 | static unsigned wg_reject_after_time = WG_REJECT_AFTER_TIME; | | 641 | static unsigned wg_reject_after_time = WG_REJECT_AFTER_TIME; |
642 | static unsigned wg_rekey_attempt_time = WG_REKEY_ATTEMPT_TIME; | | 642 | static unsigned wg_rekey_attempt_time = WG_REKEY_ATTEMPT_TIME; |
643 | static unsigned wg_rekey_timeout = WG_REKEY_TIMEOUT; | | 643 | static unsigned wg_rekey_timeout = WG_REKEY_TIMEOUT; |
644 | static unsigned wg_keepalive_timeout = WG_KEEPALIVE_TIMEOUT; | | 644 | static unsigned wg_keepalive_timeout = WG_KEEPALIVE_TIMEOUT; |
645 | | | 645 | |
646 | static struct mbuf * | | 646 | static struct mbuf * |
647 | wg_get_mbuf(size_t, size_t); | | 647 | wg_get_mbuf(size_t, size_t); |
648 | | | 648 | |
649 | static void wg_wakeup_worker(struct wg_worker *, int); | | 649 | static void wg_wakeup_worker(struct wg_worker *, int); |
650 | | | 650 | |
651 | static int wg_send_data_msg(struct wg_peer *, struct wg_session *, | | 651 | static int wg_send_data_msg(struct wg_peer *, struct wg_session *, |
652 | struct mbuf *); | | 652 | struct mbuf *); |
653 | static int wg_send_cookie_msg(struct wg_softc *, struct wg_peer *, | | 653 | static int wg_send_cookie_msg(struct wg_softc *, struct wg_peer *, |
654 | const uint32_t, const uint8_t [], const struct sockaddr *); | | 654 | const uint32_t, const uint8_t [], const struct sockaddr *); |
655 | static int wg_send_handshake_msg_resp(struct wg_softc *, | | 655 | static int wg_send_handshake_msg_resp(struct wg_softc *, |
656 | struct wg_peer *, const struct wg_msg_init *); | | 656 | struct wg_peer *, const struct wg_msg_init *); |
657 | static void wg_send_keepalive_msg(struct wg_peer *, struct wg_session *); | | 657 | static void wg_send_keepalive_msg(struct wg_peer *, struct wg_session *); |
658 | | | 658 | |
659 | static struct wg_peer * | | 659 | static struct wg_peer * |
660 | wg_pick_peer_by_sa(struct wg_softc *, const struct sockaddr *, | | 660 | wg_pick_peer_by_sa(struct wg_softc *, const struct sockaddr *, |
661 | struct psref *); | | 661 | struct psref *); |
662 | static struct wg_peer * | | 662 | static struct wg_peer * |
663 | wg_lookup_peer_by_pubkey(struct wg_softc *, | | 663 | wg_lookup_peer_by_pubkey(struct wg_softc *, |
664 | const uint8_t [], struct psref *); | | 664 | const uint8_t [], struct psref *); |
665 | | | 665 | |
666 | static struct wg_session * | | 666 | static struct wg_session * |
667 | wg_lookup_session_by_index(struct wg_softc *, | | 667 | wg_lookup_session_by_index(struct wg_softc *, |
668 | const uint32_t, struct psref *); | | 668 | const uint32_t, struct psref *); |
669 | | | 669 | |
670 | static void wg_update_endpoint_if_necessary(struct wg_peer *, | | 670 | static void wg_update_endpoint_if_necessary(struct wg_peer *, |
671 | const struct sockaddr *); | | 671 | const struct sockaddr *); |
672 | | | 672 | |
673 | static void wg_schedule_rekey_timer(struct wg_peer *); | | 673 | static void wg_schedule_rekey_timer(struct wg_peer *); |
674 | static void wg_schedule_session_dtor_timer(struct wg_peer *); | | 674 | static void wg_schedule_session_dtor_timer(struct wg_peer *); |
675 | | | 675 | |
676 | static bool wg_is_underload(struct wg_softc *, struct wg_peer *, int); | | 676 | static bool wg_is_underload(struct wg_softc *, struct wg_peer *, int); |
677 | static void wg_calculate_keys(struct wg_session *, const bool); | | 677 | static void wg_calculate_keys(struct wg_session *, const bool); |
678 | | | 678 | |
679 | static void wg_clear_states(struct wg_session *); | | 679 | static void wg_clear_states(struct wg_session *); |
680 | | | 680 | |
681 | static void wg_get_peer(struct wg_peer *, struct psref *); | | 681 | static void wg_get_peer(struct wg_peer *, struct psref *); |
682 | static void wg_put_peer(struct wg_peer *, struct psref *); | | 682 | static void wg_put_peer(struct wg_peer *, struct psref *); |
683 | | | 683 | |
684 | static int wg_send_so(struct wg_peer *, struct mbuf *); | | 684 | static int wg_send_so(struct wg_peer *, struct mbuf *); |
685 | static int wg_send_udp(struct wg_peer *, struct mbuf *); | | 685 | static int wg_send_udp(struct wg_peer *, struct mbuf *); |
686 | static int wg_output(struct ifnet *, struct mbuf *, | | 686 | static int wg_output(struct ifnet *, struct mbuf *, |
687 | const struct sockaddr *, const struct rtentry *); | | 687 | const struct sockaddr *, const struct rtentry *); |
688 | static void wg_input(struct ifnet *, struct mbuf *, const int); | | 688 | static void wg_input(struct ifnet *, struct mbuf *, const int); |
689 | static int wg_ioctl(struct ifnet *, u_long, void *); | | 689 | static int wg_ioctl(struct ifnet *, u_long, void *); |
690 | static int wg_bind_port(struct wg_softc *, const uint16_t); | | 690 | static int wg_bind_port(struct wg_softc *, const uint16_t); |
691 | static int wg_init(struct ifnet *); | | 691 | static int wg_init(struct ifnet *); |
692 | static void wg_stop(struct ifnet *, int); | | 692 | static void wg_stop(struct ifnet *, int); |
693 | | | 693 | |
694 | static int wg_clone_create(struct if_clone *, int); | | 694 | static int wg_clone_create(struct if_clone *, int); |
695 | static int wg_clone_destroy(struct ifnet *); | | 695 | static int wg_clone_destroy(struct ifnet *); |
696 | | | 696 | |
697 | struct wg_ops { | | 697 | struct wg_ops { |
698 | int (*send_hs_msg)(struct wg_peer *, struct mbuf *); | | 698 | int (*send_hs_msg)(struct wg_peer *, struct mbuf *); |
699 | int (*send_data_msg)(struct wg_peer *, struct mbuf *); | | 699 | int (*send_data_msg)(struct wg_peer *, struct mbuf *); |
700 | void (*input)(struct ifnet *, struct mbuf *, const int); | | 700 | void (*input)(struct ifnet *, struct mbuf *, const int); |
701 | int (*bind_port)(struct wg_softc *, const uint16_t); | | 701 | int (*bind_port)(struct wg_softc *, const uint16_t); |
702 | }; | | 702 | }; |
703 | | | 703 | |
704 | struct wg_ops wg_ops_rumpkernel = { | | 704 | struct wg_ops wg_ops_rumpkernel = { |
705 | .send_hs_msg = wg_send_so, | | 705 | .send_hs_msg = wg_send_so, |
706 | .send_data_msg = wg_send_udp, | | 706 | .send_data_msg = wg_send_udp, |
707 | .input = wg_input, | | 707 | .input = wg_input, |
708 | .bind_port = wg_bind_port, | | 708 | .bind_port = wg_bind_port, |
709 | }; | | 709 | }; |
710 | | | 710 | |
711 | #ifdef WG_RUMPKERNEL | | 711 | #ifdef WG_RUMPKERNEL |
712 | static bool wg_user_mode(struct wg_softc *); | | 712 | static bool wg_user_mode(struct wg_softc *); |
713 | static int wg_ioctl_linkstr(struct wg_softc *, struct ifdrv *); | | 713 | static int wg_ioctl_linkstr(struct wg_softc *, struct ifdrv *); |
714 | | | 714 | |
715 | static int wg_send_user(struct wg_peer *, struct mbuf *); | | 715 | static int wg_send_user(struct wg_peer *, struct mbuf *); |
716 | static void wg_input_user(struct ifnet *, struct mbuf *, const int); | | 716 | static void wg_input_user(struct ifnet *, struct mbuf *, const int); |
717 | static int wg_bind_port_user(struct wg_softc *, const uint16_t); | | 717 | static int wg_bind_port_user(struct wg_softc *, const uint16_t); |
718 | | | 718 | |
719 | struct wg_ops wg_ops_rumpuser = { | | 719 | struct wg_ops wg_ops_rumpuser = { |
720 | .send_hs_msg = wg_send_user, | | 720 | .send_hs_msg = wg_send_user, |
721 | .send_data_msg = wg_send_user, | | 721 | .send_data_msg = wg_send_user, |
722 | .input = wg_input_user, | | 722 | .input = wg_input_user, |
723 | .bind_port = wg_bind_port_user, | | 723 | .bind_port = wg_bind_port_user, |
724 | }; | | 724 | }; |
725 | #endif | | 725 | #endif |
726 | | | 726 | |
727 | #define WG_PEER_READER_FOREACH(wgp, wg) \ | | 727 | #define WG_PEER_READER_FOREACH(wgp, wg) \ |
728 | PSLIST_READER_FOREACH((wgp), &(wg)->wg_peers, struct wg_peer, \ | | 728 | PSLIST_READER_FOREACH((wgp), &(wg)->wg_peers, struct wg_peer, \ |
729 | wgp_peerlist_entry) | | 729 | wgp_peerlist_entry) |
730 | #define WG_PEER_WRITER_FOREACH(wgp, wg) \ | | 730 | #define WG_PEER_WRITER_FOREACH(wgp, wg) \ |
731 | PSLIST_WRITER_FOREACH((wgp), &(wg)->wg_peers, struct wg_peer, \ | | 731 | PSLIST_WRITER_FOREACH((wgp), &(wg)->wg_peers, struct wg_peer, \ |
732 | wgp_peerlist_entry) | | 732 | wgp_peerlist_entry) |
733 | #define WG_PEER_WRITER_INSERT_HEAD(wgp, wg) \ | | 733 | #define WG_PEER_WRITER_INSERT_HEAD(wgp, wg) \ |
734 | PSLIST_WRITER_INSERT_HEAD(&(wg)->wg_peers, (wgp), wgp_peerlist_entry) | | 734 | PSLIST_WRITER_INSERT_HEAD(&(wg)->wg_peers, (wgp), wgp_peerlist_entry) |
735 | #define WG_PEER_WRITER_REMOVE(wgp) \ | | 735 | #define WG_PEER_WRITER_REMOVE(wgp) \ |
736 | PSLIST_WRITER_REMOVE((wgp), wgp_peerlist_entry) | | 736 | PSLIST_WRITER_REMOVE((wgp), wgp_peerlist_entry) |
737 | | | 737 | |
738 | struct wg_route { | | 738 | struct wg_route { |
739 | struct radix_node wgr_nodes[2]; | | 739 | struct radix_node wgr_nodes[2]; |
740 | struct wg_peer *wgr_peer; | | 740 | struct wg_peer *wgr_peer; |
741 | }; | | 741 | }; |
742 | | | 742 | |
743 | static struct radix_node_head * | | 743 | static struct radix_node_head * |
744 | wg_rnh(struct wg_softc *wg, const int family) | | 744 | wg_rnh(struct wg_softc *wg, const int family) |
745 | { | | 745 | { |
746 | | | 746 | |
747 | switch (family) { | | 747 | switch (family) { |
748 | case AF_INET: | | 748 | case AF_INET: |
749 | return wg->wg_rtable_ipv4; | | 749 | return wg->wg_rtable_ipv4; |
750 | #ifdef INET6 | | 750 | #ifdef INET6 |
751 | case AF_INET6: | | 751 | case AF_INET6: |
752 | return wg->wg_rtable_ipv6; | | 752 | return wg->wg_rtable_ipv6; |
753 | #endif | | 753 | #endif |
754 | default: | | 754 | default: |
755 | return NULL; | | 755 | return NULL; |
756 | } | | 756 | } |
757 | } | | 757 | } |
758 | | | 758 | |
759 | | | 759 | |
760 | /* | | 760 | /* |
761 | * Global variables | | 761 | * Global variables |
762 | */ | | 762 | */ |
763 | LIST_HEAD(wg_sclist, wg_softc); | | 763 | LIST_HEAD(wg_sclist, wg_softc); |
764 | static struct { | | 764 | static struct { |
765 | struct wg_sclist list; | | 765 | struct wg_sclist list; |
766 | kmutex_t lock; | | 766 | kmutex_t lock; |
767 | } wg_softcs __cacheline_aligned; | | 767 | } wg_softcs __cacheline_aligned; |
768 | | | 768 | |
769 | struct psref_class *wg_psref_class __read_mostly; | | 769 | struct psref_class *wg_psref_class __read_mostly; |
770 | | | 770 | |
771 | static struct if_clone wg_cloner = | | 771 | static struct if_clone wg_cloner = |
772 | IF_CLONE_INITIALIZER("wg", wg_clone_create, wg_clone_destroy); | | 772 | IF_CLONE_INITIALIZER("wg", wg_clone_create, wg_clone_destroy); |
773 | | | 773 | |
774 | | | 774 | |
775 | void wgattach(int); | | 775 | void wgattach(int); |
776 | /* ARGSUSED */ | | 776 | /* ARGSUSED */ |
777 | void | | 777 | void |
778 | wgattach(int count) | | 778 | wgattach(int count) |
779 | { | | 779 | { |
780 | /* | | 780 | /* |
781 | * Nothing to do here, initialization is handled by the | | 781 | * Nothing to do here, initialization is handled by the |
782 | * module initialization code in wginit() below). | | 782 | * module initialization code in wginit() below). |
783 | */ | | 783 | */ |
784 | } | | 784 | } |
785 | | | 785 | |
786 | static void | | 786 | static void |
787 | wginit(void) | | 787 | wginit(void) |
788 | { | | 788 | { |
789 | | | 789 | |
790 | wg_psref_class = psref_class_create("wg", IPL_SOFTNET); | | 790 | wg_psref_class = psref_class_create("wg", IPL_SOFTNET); |
791 | | | 791 | |
792 | mutex_init(&wg_softcs.lock, MUTEX_DEFAULT, IPL_NONE); | | 792 | mutex_init(&wg_softcs.lock, MUTEX_DEFAULT, IPL_NONE); |
793 | LIST_INIT(&wg_softcs.list); | | 793 | LIST_INIT(&wg_softcs.list); |
794 | if_clone_attach(&wg_cloner); | | 794 | if_clone_attach(&wg_cloner); |
795 | } | | 795 | } |
796 | | | 796 | |
797 | static int | | 797 | static int |
798 | wgdetach(void) | | 798 | wgdetach(void) |
799 | { | | 799 | { |
800 | int error = 0; | | 800 | int error = 0; |
801 | | | 801 | |
802 | mutex_enter(&wg_softcs.lock); | | 802 | mutex_enter(&wg_softcs.lock); |
803 | if (!LIST_EMPTY(&wg_softcs.list)) { | | 803 | if (!LIST_EMPTY(&wg_softcs.list)) { |
804 | mutex_exit(&wg_softcs.lock); | | 804 | mutex_exit(&wg_softcs.lock); |
805 | error = EBUSY; | | 805 | error = EBUSY; |
806 | } | | 806 | } |
807 | | | 807 | |
808 | if (error == 0) { | | 808 | if (error == 0) { |
809 | psref_class_destroy(wg_psref_class); | | 809 | psref_class_destroy(wg_psref_class); |
810 | | | 810 | |
811 | if_clone_detach(&wg_cloner); | | 811 | if_clone_detach(&wg_cloner); |
812 | } | | 812 | } |
813 | | | 813 | |
814 | return error; | | 814 | return error; |
815 | } | | 815 | } |
816 | | | 816 | |
817 | static void | | 817 | static void |
818 | wg_init_key_and_hash(uint8_t ckey[WG_CHAINING_KEY_LEN], | | 818 | wg_init_key_and_hash(uint8_t ckey[WG_CHAINING_KEY_LEN], |
819 | uint8_t hash[WG_HASH_LEN]) | | 819 | uint8_t hash[WG_HASH_LEN]) |
820 | { | | 820 | { |
821 | /* [W] 5.4: CONSTRUCTION */ | | 821 | /* [W] 5.4: CONSTRUCTION */ |
822 | const char *signature = "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"; | | 822 | const char *signature = "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"; |
823 | /* [W] 5.4: IDENTIFIER */ | | 823 | /* [W] 5.4: IDENTIFIER */ |
824 | const char *id = "WireGuard v1 zx2c4 Jason@zx2c4.com"; | | 824 | const char *id = "WireGuard v1 zx2c4 Jason@zx2c4.com"; |
825 | struct blake2s state; | | 825 | struct blake2s state; |
826 | | | 826 | |
827 | blake2s(ckey, WG_CHAINING_KEY_LEN, NULL, 0, | | 827 | blake2s(ckey, WG_CHAINING_KEY_LEN, NULL, 0, |
828 | signature, strlen(signature)); | | 828 | signature, strlen(signature)); |
829 | | | 829 | |
830 | CTASSERT(WG_HASH_LEN == WG_CHAINING_KEY_LEN); | | 830 | CTASSERT(WG_HASH_LEN == WG_CHAINING_KEY_LEN); |
831 | memcpy(hash, ckey, WG_CHAINING_KEY_LEN); | | 831 | memcpy(hash, ckey, WG_CHAINING_KEY_LEN); |
832 | | | 832 | |
833 | blake2s_init(&state, WG_HASH_LEN, NULL, 0); | | 833 | blake2s_init(&state, WG_HASH_LEN, NULL, 0); |
834 | blake2s_update(&state, ckey, WG_CHAINING_KEY_LEN); | | 834 | blake2s_update(&state, ckey, WG_CHAINING_KEY_LEN); |
835 | blake2s_update(&state, id, strlen(id)); | | 835 | blake2s_update(&state, id, strlen(id)); |
836 | blake2s_final(&state, hash); | | 836 | blake2s_final(&state, hash); |
837 | | | 837 | |
838 | WG_DUMP_HASH("ckey", ckey); | | 838 | WG_DUMP_HASH("ckey", ckey); |
839 | WG_DUMP_HASH("hash", hash); | | 839 | WG_DUMP_HASH("hash", hash); |
840 | } | | 840 | } |
841 | | | 841 | |
842 | static void | | 842 | static void |
843 | wg_algo_hash(uint8_t hash[WG_HASH_LEN], const uint8_t input[], | | 843 | wg_algo_hash(uint8_t hash[WG_HASH_LEN], const uint8_t input[], |
844 | const size_t inputsize) | | 844 | const size_t inputsize) |
845 | { | | 845 | { |
846 | struct blake2s state; | | 846 | struct blake2s state; |
847 | | | 847 | |
848 | blake2s_init(&state, WG_HASH_LEN, NULL, 0); | | 848 | blake2s_init(&state, WG_HASH_LEN, NULL, 0); |
849 | blake2s_update(&state, hash, WG_HASH_LEN); | | 849 | blake2s_update(&state, hash, WG_HASH_LEN); |
850 | blake2s_update(&state, input, inputsize); | | 850 | blake2s_update(&state, input, inputsize); |
851 | blake2s_final(&state, hash); | | 851 | blake2s_final(&state, hash); |
852 | } | | 852 | } |
853 | | | 853 | |
854 | static void | | 854 | static void |
855 | wg_algo_mac(uint8_t out[], const size_t outsize, | | 855 | wg_algo_mac(uint8_t out[], const size_t outsize, |
856 | const uint8_t key[], const size_t keylen, | | 856 | const uint8_t key[], const size_t keylen, |
857 | const uint8_t input1[], const size_t input1len, | | 857 | const uint8_t input1[], const size_t input1len, |
858 | const uint8_t input2[], const size_t input2len) | | 858 | const uint8_t input2[], const size_t input2len) |
859 | { | | 859 | { |
860 | struct blake2s state; | | 860 | struct blake2s state; |
861 | | | 861 | |
862 | blake2s_init(&state, outsize, key, keylen); | | 862 | blake2s_init(&state, outsize, key, keylen); |
863 | | | 863 | |
864 | blake2s_update(&state, input1, input1len); | | 864 | blake2s_update(&state, input1, input1len); |
865 | if (input2 != NULL) | | 865 | if (input2 != NULL) |
866 | blake2s_update(&state, input2, input2len); | | 866 | blake2s_update(&state, input2, input2len); |
867 | blake2s_final(&state, out); | | 867 | blake2s_final(&state, out); |
868 | } | | 868 | } |
869 | | | 869 | |
870 | static void | | 870 | static void |
871 | wg_algo_mac_mac1(uint8_t out[], const size_t outsize, | | 871 | wg_algo_mac_mac1(uint8_t out[], const size_t outsize, |
872 | const uint8_t input1[], const size_t input1len, | | 872 | const uint8_t input1[], const size_t input1len, |
873 | const uint8_t input2[], const size_t input2len) | | 873 | const uint8_t input2[], const size_t input2len) |
874 | { | | 874 | { |
875 | struct blake2s state; | | 875 | struct blake2s state; |
876 | /* [W] 5.4: LABEL-MAC1 */ | | 876 | /* [W] 5.4: LABEL-MAC1 */ |
877 | const char *label = "mac1----"; | | 877 | const char *label = "mac1----"; |
878 | uint8_t key[WG_HASH_LEN]; | | 878 | uint8_t key[WG_HASH_LEN]; |
879 | | | 879 | |
880 | blake2s_init(&state, sizeof(key), NULL, 0); | | 880 | blake2s_init(&state, sizeof(key), NULL, 0); |
881 | blake2s_update(&state, label, strlen(label)); | | 881 | blake2s_update(&state, label, strlen(label)); |
882 | blake2s_update(&state, input1, input1len); | | 882 | blake2s_update(&state, input1, input1len); |
883 | blake2s_final(&state, key); | | 883 | blake2s_final(&state, key); |
884 | | | 884 | |
885 | blake2s_init(&state, outsize, key, sizeof(key)); | | 885 | blake2s_init(&state, outsize, key, sizeof(key)); |
886 | if (input2 != NULL) | | 886 | if (input2 != NULL) |
887 | blake2s_update(&state, input2, input2len); | | 887 | blake2s_update(&state, input2, input2len); |
888 | blake2s_final(&state, out); | | 888 | blake2s_final(&state, out); |
889 | } | | 889 | } |
890 | | | 890 | |
891 | static void | | 891 | static void |
892 | wg_algo_mac_cookie(uint8_t out[], const size_t outsize, | | 892 | wg_algo_mac_cookie(uint8_t out[], const size_t outsize, |
893 | const uint8_t input1[], const size_t input1len) | | 893 | const uint8_t input1[], const size_t input1len) |
894 | { | | 894 | { |
895 | struct blake2s state; | | 895 | struct blake2s state; |
896 | /* [W] 5.4: LABEL-COOKIE */ | | 896 | /* [W] 5.4: LABEL-COOKIE */ |
897 | const char *label = "cookie--"; | | 897 | const char *label = "cookie--"; |
898 | | | 898 | |
899 | blake2s_init(&state, outsize, NULL, 0); | | 899 | blake2s_init(&state, outsize, NULL, 0); |
900 | blake2s_update(&state, label, strlen(label)); | | 900 | blake2s_update(&state, label, strlen(label)); |
901 | blake2s_update(&state, input1, input1len); | | 901 | blake2s_update(&state, input1, input1len); |
902 | blake2s_final(&state, out); | | 902 | blake2s_final(&state, out); |
903 | } | | 903 | } |
904 | | | 904 | |
905 | static void | | 905 | static void |
906 | wg_algo_generate_keypair(uint8_t pubkey[WG_EPHEMERAL_KEY_LEN], | | 906 | wg_algo_generate_keypair(uint8_t pubkey[WG_EPHEMERAL_KEY_LEN], |
907 | uint8_t privkey[WG_EPHEMERAL_KEY_LEN]) | | 907 | uint8_t privkey[WG_EPHEMERAL_KEY_LEN]) |
908 | { | | 908 | { |
909 | | | 909 | |
910 | CTASSERT(WG_EPHEMERAL_KEY_LEN == crypto_scalarmult_curve25519_BYTES); | | 910 | CTASSERT(WG_EPHEMERAL_KEY_LEN == crypto_scalarmult_curve25519_BYTES); |
911 | | | 911 | |
912 | cprng_strong(kern_cprng, privkey, WG_EPHEMERAL_KEY_LEN, 0); | | 912 | cprng_strong(kern_cprng, privkey, WG_EPHEMERAL_KEY_LEN, 0); |
913 | crypto_scalarmult_base(pubkey, privkey); | | 913 | crypto_scalarmult_base(pubkey, privkey); |
914 | } | | 914 | } |
915 | | | 915 | |
916 | static void | | 916 | static void |
917 | wg_algo_dh(uint8_t out[WG_DH_OUTPUT_LEN], | | 917 | wg_algo_dh(uint8_t out[WG_DH_OUTPUT_LEN], |
918 | const uint8_t privkey[WG_STATIC_KEY_LEN], | | 918 | const uint8_t privkey[WG_STATIC_KEY_LEN], |
919 | const uint8_t pubkey[WG_STATIC_KEY_LEN]) | | 919 | const uint8_t pubkey[WG_STATIC_KEY_LEN]) |
920 | { | | 920 | { |
921 | | | 921 | |
922 | CTASSERT(WG_STATIC_KEY_LEN == crypto_scalarmult_curve25519_BYTES); | | 922 | CTASSERT(WG_STATIC_KEY_LEN == crypto_scalarmult_curve25519_BYTES); |
923 | | | 923 | |
924 | int ret __diagused = crypto_scalarmult(out, privkey, pubkey); | | 924 | int ret __diagused = crypto_scalarmult(out, privkey, pubkey); |
925 | KASSERT(ret == 0); | | 925 | KASSERT(ret == 0); |
926 | } | | 926 | } |
927 | | | 927 | |
928 | static void | | 928 | static void |
929 | wg_algo_hmac(uint8_t out[], const size_t outlen, | | 929 | wg_algo_hmac(uint8_t out[], const size_t outlen, |
930 | const uint8_t key[], const size_t keylen, | | 930 | const uint8_t key[], const size_t keylen, |
931 | const uint8_t in[], const size_t inlen) | | 931 | const uint8_t in[], const size_t inlen) |
932 | { | | 932 | { |
933 | #define IPAD 0x36 | | 933 | #define IPAD 0x36 |
934 | #define OPAD 0x5c | | 934 | #define OPAD 0x5c |
935 | uint8_t hmackey[HMAC_BLOCK_LEN] = {0}; | | 935 | uint8_t hmackey[HMAC_BLOCK_LEN] = {0}; |
936 | uint8_t ipad[HMAC_BLOCK_LEN]; | | 936 | uint8_t ipad[HMAC_BLOCK_LEN]; |
937 | uint8_t opad[HMAC_BLOCK_LEN]; | | 937 | uint8_t opad[HMAC_BLOCK_LEN]; |
938 | int i; | | 938 | int i; |
939 | struct blake2s state; | | 939 | struct blake2s state; |
940 | | | 940 | |
941 | KASSERT(outlen == WG_HASH_LEN); | | 941 | KASSERT(outlen == WG_HASH_LEN); |
942 | KASSERT(keylen <= HMAC_BLOCK_LEN); | | 942 | KASSERT(keylen <= HMAC_BLOCK_LEN); |
943 | | | 943 | |
944 | memcpy(hmackey, key, keylen); | | 944 | memcpy(hmackey, key, keylen); |
945 | | | 945 | |
946 | for (i = 0; i < sizeof(hmackey); i++) { | | 946 | for (i = 0; i < sizeof(hmackey); i++) { |
947 | ipad[i] = hmackey[i] ^ IPAD; | | 947 | ipad[i] = hmackey[i] ^ IPAD; |
948 | opad[i] = hmackey[i] ^ OPAD; | | 948 | opad[i] = hmackey[i] ^ OPAD; |
949 | } | | 949 | } |
950 | | | 950 | |
951 | blake2s_init(&state, WG_HASH_LEN, NULL, 0); | | 951 | blake2s_init(&state, WG_HASH_LEN, NULL, 0); |
952 | blake2s_update(&state, ipad, sizeof(ipad)); | | 952 | blake2s_update(&state, ipad, sizeof(ipad)); |
953 | blake2s_update(&state, in, inlen); | | 953 | blake2s_update(&state, in, inlen); |
954 | blake2s_final(&state, out); | | 954 | blake2s_final(&state, out); |
955 | | | 955 | |
956 | blake2s_init(&state, WG_HASH_LEN, NULL, 0); | | 956 | blake2s_init(&state, WG_HASH_LEN, NULL, 0); |
957 | blake2s_update(&state, opad, sizeof(opad)); | | 957 | blake2s_update(&state, opad, sizeof(opad)); |
958 | blake2s_update(&state, out, WG_HASH_LEN); | | 958 | blake2s_update(&state, out, WG_HASH_LEN); |
959 | blake2s_final(&state, out); | | 959 | blake2s_final(&state, out); |
960 | #undef IPAD | | 960 | #undef IPAD |
961 | #undef OPAD | | 961 | #undef OPAD |
962 | } | | 962 | } |
963 | | | 963 | |
964 | static void | | 964 | static void |
965 | wg_algo_kdf(uint8_t out1[WG_KDF_OUTPUT_LEN], uint8_t out2[WG_KDF_OUTPUT_LEN], | | 965 | wg_algo_kdf(uint8_t out1[WG_KDF_OUTPUT_LEN], uint8_t out2[WG_KDF_OUTPUT_LEN], |
966 | uint8_t out3[WG_KDF_OUTPUT_LEN], const uint8_t ckey[WG_CHAINING_KEY_LEN], | | 966 | uint8_t out3[WG_KDF_OUTPUT_LEN], const uint8_t ckey[WG_CHAINING_KEY_LEN], |
967 | const uint8_t input[], const size_t inputlen) | | 967 | const uint8_t input[], const size_t inputlen) |
968 | { | | 968 | { |
969 | uint8_t tmp1[WG_KDF_OUTPUT_LEN], tmp2[WG_KDF_OUTPUT_LEN + 1]; | | 969 | uint8_t tmp1[WG_KDF_OUTPUT_LEN], tmp2[WG_KDF_OUTPUT_LEN + 1]; |
970 | uint8_t one[1]; | | 970 | uint8_t one[1]; |
971 | | | 971 | |
972 | /* | | 972 | /* |
973 | * [N] 4.3: "an input_key_material byte sequence with length | | 973 | * [N] 4.3: "an input_key_material byte sequence with length |
974 | * either zero bytes, 32 bytes, or DHLEN bytes." | | 974 | * either zero bytes, 32 bytes, or DHLEN bytes." |
975 | */ | | 975 | */ |
976 | KASSERT(inputlen == 0 || inputlen == 32 || inputlen == NOISE_DHLEN); | | 976 | KASSERT(inputlen == 0 || inputlen == 32 || inputlen == NOISE_DHLEN); |
977 | | | 977 | |
978 | WG_DUMP_HASH("ckey", ckey); | | 978 | WG_DUMP_HASH("ckey", ckey); |
979 | if (input != NULL) | | 979 | if (input != NULL) |
980 | WG_DUMP_HASH("input", input); | | 980 | WG_DUMP_HASH("input", input); |
981 | wg_algo_hmac(tmp1, sizeof(tmp1), ckey, WG_CHAINING_KEY_LEN, | | 981 | wg_algo_hmac(tmp1, sizeof(tmp1), ckey, WG_CHAINING_KEY_LEN, |
982 | input, inputlen); | | 982 | input, inputlen); |
983 | WG_DUMP_HASH("tmp1", tmp1); | | 983 | WG_DUMP_HASH("tmp1", tmp1); |
984 | one[0] = 1; | | 984 | one[0] = 1; |
985 | wg_algo_hmac(out1, WG_KDF_OUTPUT_LEN, tmp1, sizeof(tmp1), | | 985 | wg_algo_hmac(out1, WG_KDF_OUTPUT_LEN, tmp1, sizeof(tmp1), |
986 | one, sizeof(one)); | | 986 | one, sizeof(one)); |
987 | WG_DUMP_HASH("out1", out1); | | 987 | WG_DUMP_HASH("out1", out1); |
988 | if (out2 == NULL) | | 988 | if (out2 == NULL) |
989 | return; | | 989 | return; |
990 | memcpy(tmp2, out1, WG_KDF_OUTPUT_LEN); | | 990 | memcpy(tmp2, out1, WG_KDF_OUTPUT_LEN); |
991 | tmp2[WG_KDF_OUTPUT_LEN] = 2; | | 991 | tmp2[WG_KDF_OUTPUT_LEN] = 2; |
992 | wg_algo_hmac(out2, WG_KDF_OUTPUT_LEN, tmp1, sizeof(tmp1), | | 992 | wg_algo_hmac(out2, WG_KDF_OUTPUT_LEN, tmp1, sizeof(tmp1), |
993 | tmp2, sizeof(tmp2)); | | 993 | tmp2, sizeof(tmp2)); |
994 | WG_DUMP_HASH("out2", out2); | | 994 | WG_DUMP_HASH("out2", out2); |
995 | if (out3 == NULL) | | 995 | if (out3 == NULL) |
996 | return; | | 996 | return; |
997 | memcpy(tmp2, out2, WG_KDF_OUTPUT_LEN); | | 997 | memcpy(tmp2, out2, WG_KDF_OUTPUT_LEN); |
998 | tmp2[WG_KDF_OUTPUT_LEN] = 3; | | 998 | tmp2[WG_KDF_OUTPUT_LEN] = 3; |
999 | wg_algo_hmac(out3, WG_KDF_OUTPUT_LEN, tmp1, sizeof(tmp1), | | 999 | wg_algo_hmac(out3, WG_KDF_OUTPUT_LEN, tmp1, sizeof(tmp1), |
1000 | tmp2, sizeof(tmp2)); | | 1000 | tmp2, sizeof(tmp2)); |
1001 | WG_DUMP_HASH("out3", out3); | | 1001 | WG_DUMP_HASH("out3", out3); |
1002 | } | | 1002 | } |
1003 | | | 1003 | |
1004 | static void | | 1004 | static void |
1005 | wg_algo_dh_kdf(uint8_t ckey[WG_CHAINING_KEY_LEN], | | 1005 | wg_algo_dh_kdf(uint8_t ckey[WG_CHAINING_KEY_LEN], |
1006 | uint8_t cipher_key[WG_CIPHER_KEY_LEN], | | 1006 | uint8_t cipher_key[WG_CIPHER_KEY_LEN], |
1007 | const uint8_t local_key[WG_STATIC_KEY_LEN], | | 1007 | const uint8_t local_key[WG_STATIC_KEY_LEN], |
1008 | const uint8_t remote_key[WG_STATIC_KEY_LEN]) | | 1008 | const uint8_t remote_key[WG_STATIC_KEY_LEN]) |
1009 | { | | 1009 | { |
1010 | uint8_t dhout[WG_DH_OUTPUT_LEN]; | | 1010 | uint8_t dhout[WG_DH_OUTPUT_LEN]; |
1011 | | | 1011 | |
1012 | wg_algo_dh(dhout, local_key, remote_key); | | 1012 | wg_algo_dh(dhout, local_key, remote_key); |
1013 | wg_algo_kdf(ckey, cipher_key, NULL, ckey, dhout, sizeof(dhout)); | | 1013 | wg_algo_kdf(ckey, cipher_key, NULL, ckey, dhout, sizeof(dhout)); |
1014 | | | 1014 | |
1015 | WG_DUMP_HASH("dhout", dhout); | | 1015 | WG_DUMP_HASH("dhout", dhout); |
1016 | WG_DUMP_HASH("ckey", ckey); | | 1016 | WG_DUMP_HASH("ckey", ckey); |
1017 | if (cipher_key != NULL) | | 1017 | if (cipher_key != NULL) |
1018 | WG_DUMP_HASH("cipher_key", cipher_key); | | 1018 | WG_DUMP_HASH("cipher_key", cipher_key); |
1019 | } | | 1019 | } |
1020 | | | 1020 | |
1021 | static void | | 1021 | static void |
1022 | wg_algo_aead_enc(uint8_t out[], size_t expected_outsize, const uint8_t key[], | | 1022 | wg_algo_aead_enc(uint8_t out[], size_t expected_outsize, const uint8_t key[], |
1023 | const uint64_t counter, const uint8_t plain[], const size_t plainsize, | | 1023 | const uint64_t counter, const uint8_t plain[], const size_t plainsize, |
1024 | const uint8_t auth[], size_t authlen) | | 1024 | const uint8_t auth[], size_t authlen) |
1025 | { | | 1025 | { |
1026 | uint8_t nonce[(32 + 64) / 8] = {0}; | | 1026 | uint8_t nonce[(32 + 64) / 8] = {0}; |
1027 | long long unsigned int outsize; | | 1027 | long long unsigned int outsize; |
1028 | int error __diagused; | | 1028 | int error __diagused; |
1029 | | | 1029 | |
1030 | le64enc(&nonce[4], counter); | | 1030 | le64enc(&nonce[4], counter); |
1031 | | | 1031 | |
1032 | error = crypto_aead_chacha20poly1305_ietf_encrypt(out, &outsize, plain, | | 1032 | error = crypto_aead_chacha20poly1305_ietf_encrypt(out, &outsize, plain, |
1033 | plainsize, auth, authlen, NULL, nonce, key); | | 1033 | plainsize, auth, authlen, NULL, nonce, key); |
1034 | KASSERT(error == 0); | | 1034 | KASSERT(error == 0); |
1035 | KASSERT(outsize == expected_outsize); | | 1035 | KASSERT(outsize == expected_outsize); |
1036 | } | | 1036 | } |
1037 | | | 1037 | |
1038 | static int | | 1038 | static int |
1039 | wg_algo_aead_dec(uint8_t out[], size_t expected_outsize, const uint8_t key[], | | 1039 | wg_algo_aead_dec(uint8_t out[], size_t expected_outsize, const uint8_t key[], |
1040 | const uint64_t counter, const uint8_t encrypted[], | | 1040 | const uint64_t counter, const uint8_t encrypted[], |
1041 | const size_t encryptedsize, const uint8_t auth[], size_t authlen) | | 1041 | const size_t encryptedsize, const uint8_t auth[], size_t authlen) |
1042 | { | | 1042 | { |
1043 | uint8_t nonce[(32 + 64) / 8] = {0}; | | 1043 | uint8_t nonce[(32 + 64) / 8] = {0}; |
| @@ -3722,1043 +3722,1043 @@ wg_get_mbuf(size_t leading_len, size_t l | | | @@ -3722,1043 +3722,1043 @@ wg_get_mbuf(size_t leading_len, size_t l |
3722 | m = m_gethdr(M_DONTWAIT, MT_DATA); | | 3722 | m = m_gethdr(M_DONTWAIT, MT_DATA); |
3723 | if (m == NULL) | | 3723 | if (m == NULL) |
3724 | return NULL; | | 3724 | return NULL; |
3725 | if (len + leading_len > MHLEN) { | | 3725 | if (len + leading_len > MHLEN) { |
3726 | m_clget(m, M_DONTWAIT); | | 3726 | m_clget(m, M_DONTWAIT); |
3727 | if ((m->m_flags & M_EXT) == 0) { | | 3727 | if ((m->m_flags & M_EXT) == 0) { |
3728 | m_free(m); | | 3728 | m_free(m); |
3729 | return NULL; | | 3729 | return NULL; |
3730 | } | | 3730 | } |
3731 | } | | 3731 | } |
3732 | m->m_data += leading_len; | | 3732 | m->m_data += leading_len; |
3733 | m->m_pkthdr.len = m->m_len = len; | | 3733 | m->m_pkthdr.len = m->m_len = len; |
3734 | | | 3734 | |
3735 | return m; | | 3735 | return m; |
3736 | } | | 3736 | } |
3737 | | | 3737 | |
3738 | static int | | 3738 | static int |
3739 | wg_send_data_msg(struct wg_peer *wgp, struct wg_session *wgs, | | 3739 | wg_send_data_msg(struct wg_peer *wgp, struct wg_session *wgs, |
3740 | struct mbuf *m) | | 3740 | struct mbuf *m) |
3741 | { | | 3741 | { |
3742 | struct wg_softc *wg = wgp->wgp_sc; | | 3742 | struct wg_softc *wg = wgp->wgp_sc; |
3743 | int error; | | 3743 | int error; |
3744 | size_t inner_len, padded_len, encrypted_len; | | 3744 | size_t inner_len, padded_len, encrypted_len; |
3745 | char *padded_buf = NULL; | | 3745 | char *padded_buf = NULL; |
3746 | size_t mlen; | | 3746 | size_t mlen; |
3747 | struct wg_msg_data *wgmd; | | 3747 | struct wg_msg_data *wgmd; |
3748 | bool free_padded_buf = false; | | 3748 | bool free_padded_buf = false; |
3749 | struct mbuf *n; | | 3749 | struct mbuf *n; |
3750 | size_t leading_len = max_linkhdr + sizeof(struct ip6_hdr) + | | 3750 | size_t leading_len = max_linkhdr + sizeof(struct ip6_hdr) + |
3751 | sizeof(struct udphdr); | | 3751 | sizeof(struct udphdr); |
3752 | | | 3752 | |
3753 | mlen = m_length(m); | | 3753 | mlen = m_length(m); |
3754 | inner_len = mlen; | | 3754 | inner_len = mlen; |
3755 | padded_len = roundup(mlen, 16); | | 3755 | padded_len = roundup(mlen, 16); |
3756 | encrypted_len = padded_len + WG_AUTHTAG_LEN; | | 3756 | encrypted_len = padded_len + WG_AUTHTAG_LEN; |
3757 | WG_DLOG("inner=%lu, padded=%lu, encrypted_len=%lu\n", | | 3757 | WG_DLOG("inner=%lu, padded=%lu, encrypted_len=%lu\n", |
3758 | inner_len, padded_len, encrypted_len); | | 3758 | inner_len, padded_len, encrypted_len); |
3759 | if (mlen != 0) { | | 3759 | if (mlen != 0) { |
3760 | bool success; | | 3760 | bool success; |
3761 | success = m_ensure_contig(&m, padded_len); | | 3761 | success = m_ensure_contig(&m, padded_len); |
3762 | if (success) { | | 3762 | if (success) { |
3763 | padded_buf = mtod(m, char *); | | 3763 | padded_buf = mtod(m, char *); |
3764 | } else { | | 3764 | } else { |
3765 | padded_buf = kmem_intr_alloc(padded_len, KM_NOSLEEP); | | 3765 | padded_buf = kmem_intr_alloc(padded_len, KM_NOSLEEP); |
3766 | if (padded_buf == NULL) { | | 3766 | if (padded_buf == NULL) { |
3767 | error = ENOBUFS; | | 3767 | error = ENOBUFS; |
3768 | goto end; | | 3768 | goto end; |
3769 | } | | 3769 | } |
3770 | free_padded_buf = true; | | 3770 | free_padded_buf = true; |
3771 | m_copydata(m, 0, mlen, padded_buf); | | 3771 | m_copydata(m, 0, mlen, padded_buf); |
3772 | } | | 3772 | } |
3773 | memset(padded_buf + mlen, 0, padded_len - inner_len); | | 3773 | memset(padded_buf + mlen, 0, padded_len - inner_len); |
3774 | } | | 3774 | } |
3775 | | | 3775 | |
3776 | n = wg_get_mbuf(leading_len, sizeof(*wgmd) + encrypted_len); | | 3776 | n = wg_get_mbuf(leading_len, sizeof(*wgmd) + encrypted_len); |
3777 | if (n == NULL) { | | 3777 | if (n == NULL) { |
3778 | error = ENOBUFS; | | 3778 | error = ENOBUFS; |
3779 | goto end; | | 3779 | goto end; |
3780 | } | | 3780 | } |
3781 | KASSERT(n->m_len >= sizeof(*wgmd)); | | 3781 | KASSERT(n->m_len >= sizeof(*wgmd)); |
3782 | wgmd = mtod(n, struct wg_msg_data *); | | 3782 | wgmd = mtod(n, struct wg_msg_data *); |
3783 | wg_fill_msg_data(wg, wgp, wgs, wgmd); | | 3783 | wg_fill_msg_data(wg, wgp, wgs, wgmd); |
3784 | /* [W] 5.4.6: AEAD(Tm^send, Nm^send, P, e) */ | | 3784 | /* [W] 5.4.6: AEAD(Tm^send, Nm^send, P, e) */ |
3785 | wg_algo_aead_enc((char *)wgmd + sizeof(*wgmd), encrypted_len, | | 3785 | wg_algo_aead_enc((char *)wgmd + sizeof(*wgmd), encrypted_len, |
3786 | wgs->wgs_tkey_send, le64toh(wgmd->wgmd_counter), | | 3786 | wgs->wgs_tkey_send, le64toh(wgmd->wgmd_counter), |
3787 | padded_buf, padded_len, | | 3787 | padded_buf, padded_len, |
3788 | NULL, 0); | | 3788 | NULL, 0); |
3789 | | | 3789 | |
3790 | error = wg->wg_ops->send_data_msg(wgp, n); | | 3790 | error = wg->wg_ops->send_data_msg(wgp, n); |
3791 | if (error == 0) { | | 3791 | if (error == 0) { |
3792 | struct ifnet *ifp = &wg->wg_if; | | 3792 | struct ifnet *ifp = &wg->wg_if; |
3793 | if_statadd(ifp, if_obytes, mlen); | | 3793 | if_statadd(ifp, if_obytes, mlen); |
3794 | if_statinc(ifp, if_opackets); | | 3794 | if_statinc(ifp, if_opackets); |
3795 | if (wgs->wgs_is_initiator && | | 3795 | if (wgs->wgs_is_initiator && |
3796 | wgs->wgs_time_last_data_sent == 0) { | | 3796 | wgs->wgs_time_last_data_sent == 0) { |
3797 | /* | | 3797 | /* |
3798 | * [W] 6.2 Transport Message Limits | | 3798 | * [W] 6.2 Transport Message Limits |
3799 | * "if a peer is the initiator of a current secure | | 3799 | * "if a peer is the initiator of a current secure |
3800 | * session, WireGuard will send a handshake initiation | | 3800 | * session, WireGuard will send a handshake initiation |
3801 | * message to begin a new secure session if, after | | 3801 | * message to begin a new secure session if, after |
3802 | * transmitting a transport data message, the current | | 3802 | * transmitting a transport data message, the current |
3803 | * secure session is REKEY-AFTER-TIME seconds old," | | 3803 | * secure session is REKEY-AFTER-TIME seconds old," |
3804 | */ | | 3804 | */ |
3805 | wg_schedule_rekey_timer(wgp); | | 3805 | wg_schedule_rekey_timer(wgp); |
3806 | } | | 3806 | } |
3807 | wgs->wgs_time_last_data_sent = time_uptime; | | 3807 | wgs->wgs_time_last_data_sent = time_uptime; |
3808 | if (wg_session_get_send_counter(wgs) >= | | 3808 | if (wg_session_get_send_counter(wgs) >= |
3809 | wg_rekey_after_messages) { | | 3809 | wg_rekey_after_messages) { |
3810 | /* | | 3810 | /* |
3811 | * [W] 6.2 Transport Message Limits | | 3811 | * [W] 6.2 Transport Message Limits |
3812 | * "WireGuard will try to create a new session, by | | 3812 | * "WireGuard will try to create a new session, by |
3813 | * sending a handshake initiation message (section | | 3813 | * sending a handshake initiation message (section |
3814 | * 5.4.2), after it has sent REKEY-AFTER-MESSAGES | | 3814 | * 5.4.2), after it has sent REKEY-AFTER-MESSAGES |
3815 | * transport data messages..." | | 3815 | * transport data messages..." |
3816 | */ | | 3816 | */ |
3817 | wg_schedule_peer_task(wgp, WGP_TASK_SEND_INIT_MESSAGE); | | 3817 | wg_schedule_peer_task(wgp, WGP_TASK_SEND_INIT_MESSAGE); |
3818 | } | | 3818 | } |
3819 | } | | 3819 | } |
3820 | end: | | 3820 | end: |
3821 | m_freem(m); | | 3821 | m_freem(m); |
3822 | if (free_padded_buf) | | 3822 | if (free_padded_buf) |
3823 | kmem_intr_free(padded_buf, padded_len); | | 3823 | kmem_intr_free(padded_buf, padded_len); |
3824 | return error; | | 3824 | return error; |
3825 | } | | 3825 | } |
3826 | | | 3826 | |
3827 | static void | | 3827 | static void |
3828 | wg_input(struct ifnet *ifp, struct mbuf *m, const int af) | | 3828 | wg_input(struct ifnet *ifp, struct mbuf *m, const int af) |
3829 | { | | 3829 | { |
3830 | pktqueue_t *pktq; | | 3830 | pktqueue_t *pktq; |
3831 | size_t pktlen; | | 3831 | size_t pktlen; |
3832 | | | 3832 | |
3833 | KASSERT(af == AF_INET || af == AF_INET6); | | 3833 | KASSERT(af == AF_INET || af == AF_INET6); |
3834 | | | 3834 | |
3835 | WG_TRACE(""); | | 3835 | WG_TRACE(""); |
3836 | | | 3836 | |
3837 | m_set_rcvif(m, ifp); | | 3837 | m_set_rcvif(m, ifp); |
3838 | pktlen = m->m_pkthdr.len; | | 3838 | pktlen = m->m_pkthdr.len; |
3839 | | | 3839 | |
3840 | bpf_mtap_af(ifp, af, m, BPF_D_IN); | | 3840 | bpf_mtap_af(ifp, af, m, BPF_D_IN); |
3841 | | | 3841 | |
3842 | switch (af) { | | 3842 | switch (af) { |
3843 | case AF_INET: | | 3843 | case AF_INET: |
3844 | pktq = ip_pktq; | | 3844 | pktq = ip_pktq; |
3845 | break; | | 3845 | break; |
3846 | #ifdef INET6 | | 3846 | #ifdef INET6 |
3847 | case AF_INET6: | | 3847 | case AF_INET6: |
3848 | pktq = ip6_pktq; | | 3848 | pktq = ip6_pktq; |
3849 | break; | | 3849 | break; |
3850 | #endif | | 3850 | #endif |
3851 | default: | | 3851 | default: |
3852 | panic("invalid af=%d", af); | | 3852 | panic("invalid af=%d", af); |
3853 | } | | 3853 | } |
3854 | | | 3854 | |
3855 | const u_int h = curcpu()->ci_index; | | 3855 | const u_int h = curcpu()->ci_index; |
3856 | if (__predict_true(pktq_enqueue(pktq, m, h))) { | | 3856 | if (__predict_true(pktq_enqueue(pktq, m, h))) { |
3857 | if_statadd(ifp, if_ibytes, pktlen); | | 3857 | if_statadd(ifp, if_ibytes, pktlen); |
3858 | if_statinc(ifp, if_ipackets); | | 3858 | if_statinc(ifp, if_ipackets); |
3859 | } else { | | 3859 | } else { |
3860 | m_freem(m); | | 3860 | m_freem(m); |
3861 | } | | 3861 | } |
3862 | } | | 3862 | } |
3863 | | | 3863 | |
3864 | static void | | 3864 | static void |
3865 | wg_calc_pubkey(uint8_t pubkey[WG_STATIC_KEY_LEN], | | 3865 | wg_calc_pubkey(uint8_t pubkey[WG_STATIC_KEY_LEN], |
3866 | const uint8_t privkey[WG_STATIC_KEY_LEN]) | | 3866 | const uint8_t privkey[WG_STATIC_KEY_LEN]) |
3867 | { | | 3867 | { |
3868 | | | 3868 | |
3869 | crypto_scalarmult_base(pubkey, privkey); | | 3869 | crypto_scalarmult_base(pubkey, privkey); |
3870 | } | | 3870 | } |
3871 | | | 3871 | |
3872 | static int | | 3872 | static int |
3873 | wg_rtable_add_route(struct wg_softc *wg, struct wg_allowedip *wga) | | 3873 | wg_rtable_add_route(struct wg_softc *wg, struct wg_allowedip *wga) |
3874 | { | | 3874 | { |
3875 | struct radix_node_head *rnh; | | 3875 | struct radix_node_head *rnh; |
3876 | struct radix_node *rn; | | 3876 | struct radix_node *rn; |
3877 | int error = 0; | | 3877 | int error = 0; |
3878 | | | 3878 | |
3879 | rw_enter(wg->wg_rwlock, RW_WRITER); | | 3879 | rw_enter(wg->wg_rwlock, RW_WRITER); |
3880 | rnh = wg_rnh(wg, wga->wga_family); | | 3880 | rnh = wg_rnh(wg, wga->wga_family); |
3881 | KASSERT(rnh != NULL); | | 3881 | KASSERT(rnh != NULL); |
3882 | rn = rnh->rnh_addaddr(&wga->wga_sa_addr, &wga->wga_sa_mask, rnh, | | 3882 | rn = rnh->rnh_addaddr(&wga->wga_sa_addr, &wga->wga_sa_mask, rnh, |
3883 | wga->wga_nodes); | | 3883 | wga->wga_nodes); |
3884 | rw_exit(wg->wg_rwlock); | | 3884 | rw_exit(wg->wg_rwlock); |
3885 | | | 3885 | |
3886 | if (rn == NULL) | | 3886 | if (rn == NULL) |
3887 | error = EEXIST; | | 3887 | error = EEXIST; |
3888 | | | 3888 | |
3889 | return error; | | 3889 | return error; |
3890 | } | | 3890 | } |
3891 | | | 3891 | |
3892 | static int | | 3892 | static int |
3893 | wg_handle_prop_peer(struct wg_softc *wg, prop_dictionary_t peer, | | 3893 | wg_handle_prop_peer(struct wg_softc *wg, prop_dictionary_t peer, |
3894 | struct wg_peer **wgpp) | | 3894 | struct wg_peer **wgpp) |
3895 | { | | 3895 | { |
3896 | int error = 0; | | 3896 | int error = 0; |
3897 | const void *pubkey; | | 3897 | const void *pubkey; |
3898 | size_t pubkey_len; | | 3898 | size_t pubkey_len; |
3899 | const void *psk; | | 3899 | const void *psk; |
3900 | size_t psk_len; | | 3900 | size_t psk_len; |
3901 | const char *name = NULL; | | 3901 | const char *name = NULL; |
3902 | | | 3902 | |
3903 | if (prop_dictionary_get_string(peer, "name", &name)) { | | 3903 | if (prop_dictionary_get_string(peer, "name", &name)) { |
3904 | if (strlen(name) > WG_PEER_NAME_MAXLEN) { | | 3904 | if (strlen(name) > WG_PEER_NAME_MAXLEN) { |
3905 | error = EINVAL; | | 3905 | error = EINVAL; |
3906 | goto out; | | 3906 | goto out; |
3907 | } | | 3907 | } |
3908 | } | | 3908 | } |
3909 | | | 3909 | |
3910 | if (!prop_dictionary_get_data(peer, "public_key", | | 3910 | if (!prop_dictionary_get_data(peer, "public_key", |
3911 | &pubkey, &pubkey_len)) { | | 3911 | &pubkey, &pubkey_len)) { |
3912 | error = EINVAL; | | 3912 | error = EINVAL; |
3913 | goto out; | | 3913 | goto out; |
3914 | } | | 3914 | } |
3915 | #ifdef WG_DEBUG_DUMP | | 3915 | #ifdef WG_DEBUG_DUMP |
3916 | log(LOG_DEBUG, "pubkey=%p, pubkey_len=%lu\n", pubkey, pubkey_len); | | 3916 | log(LOG_DEBUG, "pubkey=%p, pubkey_len=%lu\n", pubkey, pubkey_len); |
3917 | for (int _i = 0; _i < pubkey_len; _i++) | | 3917 | for (int _i = 0; _i < pubkey_len; _i++) |
3918 | log(LOG_DEBUG, "%c", ((const char *)pubkey)[_i]); | | 3918 | log(LOG_DEBUG, "%c", ((const char *)pubkey)[_i]); |
3919 | log(LOG_DEBUG, "\n"); | | 3919 | log(LOG_DEBUG, "\n"); |
3920 | #endif | | 3920 | #endif |
3921 | | | 3921 | |
3922 | struct wg_peer *wgp = wg_alloc_peer(wg); | | 3922 | struct wg_peer *wgp = wg_alloc_peer(wg); |
3923 | memcpy(wgp->wgp_pubkey, pubkey, sizeof(wgp->wgp_pubkey)); | | 3923 | memcpy(wgp->wgp_pubkey, pubkey, sizeof(wgp->wgp_pubkey)); |
3924 | if (name != NULL) | | 3924 | if (name != NULL) |
3925 | strncpy(wgp->wgp_name, name, sizeof(wgp->wgp_name)); | | 3925 | strncpy(wgp->wgp_name, name, sizeof(wgp->wgp_name)); |
3926 | | | 3926 | |
3927 | if (prop_dictionary_get_data(peer, "preshared_key", &psk, &psk_len)) { | | 3927 | if (prop_dictionary_get_data(peer, "preshared_key", &psk, &psk_len)) { |
3928 | if (psk_len != sizeof(wgp->wgp_psk)) { | | 3928 | if (psk_len != sizeof(wgp->wgp_psk)) { |
3929 | error = EINVAL; | | 3929 | error = EINVAL; |
3930 | goto out; | | 3930 | goto out; |
3931 | } | | 3931 | } |
3932 | memcpy(wgp->wgp_psk, psk, sizeof(wgp->wgp_psk)); | | 3932 | memcpy(wgp->wgp_psk, psk, sizeof(wgp->wgp_psk)); |
3933 | } | | 3933 | } |
3934 | | | 3934 | |
3935 | const void *addr; | | 3935 | const void *addr; |
3936 | size_t addr_len; | | 3936 | size_t addr_len; |
3937 | struct wg_sockaddr *wgsa = wgp->wgp_endpoint; | | 3937 | struct wg_sockaddr *wgsa = wgp->wgp_endpoint; |
3938 | | | 3938 | |
3939 | if (!prop_dictionary_get_data(peer, "endpoint", &addr, &addr_len)) | | 3939 | if (!prop_dictionary_get_data(peer, "endpoint", &addr, &addr_len)) |
3940 | goto skip_endpoint; | | 3940 | goto skip_endpoint; |
3941 | if (addr_len < sizeof(*wgsatosa(wgsa)) || | | 3941 | if (addr_len < sizeof(*wgsatosa(wgsa)) || |
3942 | addr_len > sizeof(*wgsatoss(wgsa))) { | | 3942 | addr_len > sizeof(*wgsatoss(wgsa))) { |
3943 | error = EINVAL; | | 3943 | error = EINVAL; |
3944 | goto out; | | 3944 | goto out; |
3945 | } | | 3945 | } |
3946 | memcpy(wgsatoss(wgsa), addr, addr_len); | | 3946 | memcpy(wgsatoss(wgsa), addr, addr_len); |
3947 | switch (wgsa_family(wgsa)) { | | 3947 | switch (wgsa_family(wgsa)) { |
3948 | case AF_INET: | | 3948 | case AF_INET: |
3949 | #ifdef INET6 | | 3949 | #ifdef INET6 |
3950 | case AF_INET6: | | 3950 | case AF_INET6: |
3951 | #endif | | 3951 | #endif |
3952 | break; | | 3952 | break; |
3953 | default: | | 3953 | default: |
3954 | error = EPFNOSUPPORT; | | 3954 | error = EPFNOSUPPORT; |
3955 | goto out; | | 3955 | goto out; |
3956 | } | | 3956 | } |
3957 | if (addr_len != sockaddr_getsize_by_family(wgsa_family(wgsa))) { | | 3957 | if (addr_len != sockaddr_getsize_by_family(wgsa_family(wgsa))) { |
3958 | error = EINVAL; | | 3958 | error = EINVAL; |
3959 | goto out; | | 3959 | goto out; |
3960 | } | | 3960 | } |
3961 | { | | 3961 | { |
3962 | char addrstr[128]; | | 3962 | char addrstr[128]; |
3963 | sockaddr_format(wgsatosa(wgsa), addrstr, sizeof(addrstr)); | | 3963 | sockaddr_format(wgsatosa(wgsa), addrstr, sizeof(addrstr)); |
3964 | WG_DLOG("addr=%s\n", addrstr); | | 3964 | WG_DLOG("addr=%s\n", addrstr); |
3965 | } | | 3965 | } |
3966 | wgp->wgp_endpoint_available = true; | | 3966 | wgp->wgp_endpoint_available = true; |
3967 | | | 3967 | |
3968 | prop_array_t allowedips; | | 3968 | prop_array_t allowedips; |
3969 | skip_endpoint: | | 3969 | skip_endpoint: |
3970 | allowedips = prop_dictionary_get(peer, "allowedips"); | | 3970 | allowedips = prop_dictionary_get(peer, "allowedips"); |
3971 | if (allowedips == NULL) | | 3971 | if (allowedips == NULL) |
3972 | goto skip; | | 3972 | goto skip; |
3973 | | | 3973 | |
3974 | prop_object_iterator_t _it = prop_array_iterator(allowedips); | | 3974 | prop_object_iterator_t _it = prop_array_iterator(allowedips); |
3975 | prop_dictionary_t prop_allowedip; | | 3975 | prop_dictionary_t prop_allowedip; |
3976 | int j = 0; | | 3976 | int j = 0; |
3977 | while ((prop_allowedip = prop_object_iterator_next(_it)) != NULL) { | | 3977 | while ((prop_allowedip = prop_object_iterator_next(_it)) != NULL) { |
3978 | struct wg_allowedip *wga = &wgp->wgp_allowedips[j]; | | 3978 | struct wg_allowedip *wga = &wgp->wgp_allowedips[j]; |
3979 | | | 3979 | |
3980 | if (!prop_dictionary_get_int(prop_allowedip, "family", | | 3980 | if (!prop_dictionary_get_int(prop_allowedip, "family", |
3981 | &wga->wga_family)) | | 3981 | &wga->wga_family)) |
3982 | continue; | | 3982 | continue; |
3983 | if (!prop_dictionary_get_data(prop_allowedip, "ip", | | 3983 | if (!prop_dictionary_get_data(prop_allowedip, "ip", |
3984 | &addr, &addr_len)) | | 3984 | &addr, &addr_len)) |
3985 | continue; | | 3985 | continue; |
3986 | if (!prop_dictionary_get_uint8(prop_allowedip, "cidr", | | 3986 | if (!prop_dictionary_get_uint8(prop_allowedip, "cidr", |
3987 | &wga->wga_cidr)) | | 3987 | &wga->wga_cidr)) |
3988 | continue; | | 3988 | continue; |
3989 | | | 3989 | |
3990 | switch (wga->wga_family) { | | 3990 | switch (wga->wga_family) { |
3991 | case AF_INET: { | | 3991 | case AF_INET: { |
3992 | struct sockaddr_in sin; | | 3992 | struct sockaddr_in sin; |
3993 | char addrstr[128]; | | 3993 | char addrstr[128]; |
3994 | struct in_addr mask; | | 3994 | struct in_addr mask; |
3995 | struct sockaddr_in sin_mask; | | 3995 | struct sockaddr_in sin_mask; |
3996 | | | 3996 | |
3997 | if (addr_len != sizeof(struct in_addr)) | | 3997 | if (addr_len != sizeof(struct in_addr)) |
3998 | return EINVAL; | | 3998 | return EINVAL; |
3999 | memcpy(&wga->wga_addr4, addr, addr_len); | | 3999 | memcpy(&wga->wga_addr4, addr, addr_len); |
4000 | | | 4000 | |
4001 | sockaddr_in_init(&sin, (const struct in_addr *)addr, | | 4001 | sockaddr_in_init(&sin, (const struct in_addr *)addr, |
4002 | 0); | | 4002 | 0); |
4003 | sockaddr_copy(&wga->wga_sa_addr, | | 4003 | sockaddr_copy(&wga->wga_sa_addr, |
4004 | sizeof(sin), sintosa(&sin)); | | 4004 | sizeof(sin), sintosa(&sin)); |
4005 | | | 4005 | |
4006 | sockaddr_format(sintosa(&sin), | | 4006 | sockaddr_format(sintosa(&sin), |
4007 | addrstr, sizeof(addrstr)); | | 4007 | addrstr, sizeof(addrstr)); |
4008 | WG_DLOG("addr=%s/%d\n", addrstr, wga->wga_cidr); | | 4008 | WG_DLOG("addr=%s/%d\n", addrstr, wga->wga_cidr); |
4009 | | | 4009 | |
4010 | in_len2mask(&mask, wga->wga_cidr); | | 4010 | in_len2mask(&mask, wga->wga_cidr); |
4011 | sockaddr_in_init(&sin_mask, &mask, 0); | | 4011 | sockaddr_in_init(&sin_mask, &mask, 0); |
4012 | sockaddr_copy(&wga->wga_sa_mask, | | 4012 | sockaddr_copy(&wga->wga_sa_mask, |
4013 | sizeof(sin_mask), sintosa(&sin_mask)); | | 4013 | sizeof(sin_mask), sintosa(&sin_mask)); |
4014 | | | 4014 | |
4015 | break; | | 4015 | break; |
4016 | } | | 4016 | } |
4017 | #ifdef INET6 | | 4017 | #ifdef INET6 |
4018 | case AF_INET6: { | | 4018 | case AF_INET6: { |
4019 | struct sockaddr_in6 sin6; | | 4019 | struct sockaddr_in6 sin6; |
4020 | char addrstr[128]; | | 4020 | char addrstr[128]; |
4021 | struct in6_addr mask; | | 4021 | struct in6_addr mask; |
4022 | struct sockaddr_in6 sin6_mask; | | 4022 | struct sockaddr_in6 sin6_mask; |
4023 | | | 4023 | |
4024 | if (addr_len != sizeof(struct in6_addr)) | | 4024 | if (addr_len != sizeof(struct in6_addr)) |
4025 | return EINVAL; | | 4025 | return EINVAL; |
4026 | memcpy(&wga->wga_addr6, addr, addr_len); | | 4026 | memcpy(&wga->wga_addr6, addr, addr_len); |
4027 | | | 4027 | |
4028 | sockaddr_in6_init(&sin6, (const struct in6_addr *)addr, | | 4028 | sockaddr_in6_init(&sin6, (const struct in6_addr *)addr, |
4029 | 0, 0, 0); | | 4029 | 0, 0, 0); |
4030 | sockaddr_copy(&wga->wga_sa_addr, | | 4030 | sockaddr_copy(&wga->wga_sa_addr, |
4031 | sizeof(sin6), sin6tosa(&sin6)); | | 4031 | sizeof(sin6), sin6tosa(&sin6)); |
4032 | | | 4032 | |
4033 | sockaddr_format(sin6tosa(&sin6), | | 4033 | sockaddr_format(sin6tosa(&sin6), |
4034 | addrstr, sizeof(addrstr)); | | 4034 | addrstr, sizeof(addrstr)); |
4035 | WG_DLOG("addr=%s/%d\n", addrstr, wga->wga_cidr); | | 4035 | WG_DLOG("addr=%s/%d\n", addrstr, wga->wga_cidr); |
4036 | | | 4036 | |
4037 | in6_prefixlen2mask(&mask, wga->wga_cidr); | | 4037 | in6_prefixlen2mask(&mask, wga->wga_cidr); |
4038 | sockaddr_in6_init(&sin6_mask, &mask, 0, 0, 0); | | 4038 | sockaddr_in6_init(&sin6_mask, &mask, 0, 0, 0); |
4039 | sockaddr_copy(&wga->wga_sa_mask, | | 4039 | sockaddr_copy(&wga->wga_sa_mask, |
4040 | sizeof(sin6_mask), sin6tosa(&sin6_mask)); | | 4040 | sizeof(sin6_mask), sin6tosa(&sin6_mask)); |
4041 | | | 4041 | |
4042 | break; | | 4042 | break; |
4043 | } | | 4043 | } |
4044 | #endif | | 4044 | #endif |
4045 | default: | | 4045 | default: |
4046 | error = EINVAL; | | 4046 | error = EINVAL; |
4047 | goto out; | | 4047 | goto out; |
4048 | } | | 4048 | } |
4049 | wga->wga_peer = wgp; | | 4049 | wga->wga_peer = wgp; |
4050 | | | 4050 | |
4051 | error = wg_rtable_add_route(wg, wga); | | 4051 | error = wg_rtable_add_route(wg, wga); |
4052 | if (error != 0) | | 4052 | if (error != 0) |
4053 | goto out; | | 4053 | goto out; |
4054 | | | 4054 | |
4055 | j++; | | 4055 | j++; |
4056 | } | | 4056 | } |
4057 | wgp->wgp_n_allowedips = j; | | 4057 | wgp->wgp_n_allowedips = j; |
4058 | skip: | | 4058 | skip: |
4059 | *wgpp = wgp; | | 4059 | *wgpp = wgp; |
4060 | out: | | 4060 | out: |
4061 | return error; | | 4061 | return error; |
4062 | } | | 4062 | } |
4063 | | | 4063 | |
4064 | static int | | 4064 | static int |
4065 | wg_alloc_prop_buf(char **_buf, struct ifdrv *ifd) | | 4065 | wg_alloc_prop_buf(char **_buf, struct ifdrv *ifd) |
4066 | { | | 4066 | { |
4067 | int error; | | 4067 | int error; |
4068 | char *buf; | | 4068 | char *buf; |
4069 | | | 4069 | |
4070 | WG_DLOG("buf=%p, len=%lu\n", ifd->ifd_data, ifd->ifd_len); | | 4070 | WG_DLOG("buf=%p, len=%lu\n", ifd->ifd_data, ifd->ifd_len); |
4071 | buf = kmem_alloc(ifd->ifd_len + 1, KM_SLEEP); | | 4071 | buf = kmem_alloc(ifd->ifd_len + 1, KM_SLEEP); |
4072 | error = copyin(ifd->ifd_data, buf, ifd->ifd_len); | | 4072 | error = copyin(ifd->ifd_data, buf, ifd->ifd_len); |
4073 | if (error != 0) | | 4073 | if (error != 0) |
4074 | return error; | | 4074 | return error; |
4075 | buf[ifd->ifd_len] = '\0'; | | 4075 | buf[ifd->ifd_len] = '\0'; |
4076 | #ifdef WG_DEBUG_DUMP | | 4076 | #ifdef WG_DEBUG_DUMP |
4077 | for (int i = 0; i < ifd->ifd_len; i++) | | 4077 | for (int i = 0; i < ifd->ifd_len; i++) |
4078 | log(LOG_DEBUG, "%c", buf[i]); | | 4078 | log(LOG_DEBUG, "%c", buf[i]); |
4079 | log(LOG_DEBUG, "\n"); | | 4079 | log(LOG_DEBUG, "\n"); |
4080 | #endif | | 4080 | #endif |
4081 | *_buf = buf; | | 4081 | *_buf = buf; |
4082 | return 0; | | 4082 | return 0; |
4083 | } | | 4083 | } |
4084 | | | 4084 | |
4085 | static int | | 4085 | static int |
4086 | wg_ioctl_set_private_key(struct wg_softc *wg, struct ifdrv *ifd) | | 4086 | wg_ioctl_set_private_key(struct wg_softc *wg, struct ifdrv *ifd) |
4087 | { | | 4087 | { |
4088 | int error; | | 4088 | int error; |
4089 | prop_dictionary_t prop_dict; | | 4089 | prop_dictionary_t prop_dict; |
4090 | char *buf = NULL; | | 4090 | char *buf = NULL; |
4091 | const void *privkey; | | 4091 | const void *privkey; |
4092 | size_t privkey_len; | | 4092 | size_t privkey_len; |
4093 | | | 4093 | |
4094 | error = wg_alloc_prop_buf(&buf, ifd); | | 4094 | error = wg_alloc_prop_buf(&buf, ifd); |
4095 | if (error != 0) | | 4095 | if (error != 0) |
4096 | return error; | | 4096 | return error; |
4097 | error = EINVAL; | | 4097 | error = EINVAL; |
4098 | prop_dict = prop_dictionary_internalize(buf); | | 4098 | prop_dict = prop_dictionary_internalize(buf); |
4099 | if (prop_dict == NULL) | | 4099 | if (prop_dict == NULL) |
4100 | goto out; | | 4100 | goto out; |
4101 | if (!prop_dictionary_get_data(prop_dict, "private_key", | | 4101 | if (!prop_dictionary_get_data(prop_dict, "private_key", |
4102 | &privkey, &privkey_len)) | | 4102 | &privkey, &privkey_len)) |
4103 | goto out; | | 4103 | goto out; |
4104 | #ifdef WG_DEBUG_DUMP | | 4104 | #ifdef WG_DEBUG_DUMP |
4105 | log(LOG_DEBUG, "privkey=%p, privkey_len=%lu\n", privkey, privkey_len); | | 4105 | log(LOG_DEBUG, "privkey=%p, privkey_len=%lu\n", privkey, privkey_len); |
4106 | for (int i = 0; i < privkey_len; i++) | | 4106 | for (int i = 0; i < privkey_len; i++) |
4107 | log(LOG_DEBUG, "%c", ((const char *)privkey)[i]); | | 4107 | log(LOG_DEBUG, "%c", ((const char *)privkey)[i]); |
4108 | log(LOG_DEBUG, "\n"); | | 4108 | log(LOG_DEBUG, "\n"); |
4109 | #endif | | 4109 | #endif |
4110 | if (privkey_len != WG_STATIC_KEY_LEN) | | 4110 | if (privkey_len != WG_STATIC_KEY_LEN) |
4111 | goto out; | | 4111 | goto out; |
4112 | memcpy(wg->wg_privkey, privkey, WG_STATIC_KEY_LEN); | | 4112 | memcpy(wg->wg_privkey, privkey, WG_STATIC_KEY_LEN); |
4113 | wg_calc_pubkey(wg->wg_pubkey, wg->wg_privkey); | | 4113 | wg_calc_pubkey(wg->wg_pubkey, wg->wg_privkey); |
4114 | error = 0; | | 4114 | error = 0; |
4115 | | | 4115 | |
4116 | out: | | 4116 | out: |
4117 | kmem_free(buf, ifd->ifd_len + 1); | | 4117 | kmem_free(buf, ifd->ifd_len + 1); |
4118 | return error; | | 4118 | return error; |
4119 | } | | 4119 | } |
4120 | | | 4120 | |
4121 | static int | | 4121 | static int |
4122 | wg_ioctl_set_listen_port(struct wg_softc *wg, struct ifdrv *ifd) | | 4122 | wg_ioctl_set_listen_port(struct wg_softc *wg, struct ifdrv *ifd) |
4123 | { | | 4123 | { |
4124 | int error; | | 4124 | int error; |
4125 | prop_dictionary_t prop_dict; | | 4125 | prop_dictionary_t prop_dict; |
4126 | char *buf = NULL; | | 4126 | char *buf = NULL; |
4127 | uint16_t port; | | 4127 | uint16_t port; |
4128 | | | 4128 | |
4129 | error = wg_alloc_prop_buf(&buf, ifd); | | 4129 | error = wg_alloc_prop_buf(&buf, ifd); |
4130 | if (error != 0) | | 4130 | if (error != 0) |
4131 | return error; | | 4131 | return error; |
4132 | error = EINVAL; | | 4132 | error = EINVAL; |
4133 | prop_dict = prop_dictionary_internalize(buf); | | 4133 | prop_dict = prop_dictionary_internalize(buf); |
4134 | if (prop_dict == NULL) | | 4134 | if (prop_dict == NULL) |
4135 | goto out; | | 4135 | goto out; |
4136 | if (!prop_dictionary_get_uint16(prop_dict, "listen_port", &port)) | | 4136 | if (!prop_dictionary_get_uint16(prop_dict, "listen_port", &port)) |
4137 | goto out; | | 4137 | goto out; |
4138 | | | 4138 | |
4139 | error = wg->wg_ops->bind_port(wg, (uint16_t)port); | | 4139 | error = wg->wg_ops->bind_port(wg, (uint16_t)port); |
4140 | | | 4140 | |
4141 | out: | | 4141 | out: |
4142 | kmem_free(buf, ifd->ifd_len + 1); | | 4142 | kmem_free(buf, ifd->ifd_len + 1); |
4143 | return error; | | 4143 | return error; |
4144 | } | | 4144 | } |
4145 | | | 4145 | |
4146 | static int | | 4146 | static int |
4147 | wg_ioctl_add_peer(struct wg_softc *wg, struct ifdrv *ifd) | | 4147 | wg_ioctl_add_peer(struct wg_softc *wg, struct ifdrv *ifd) |
4148 | { | | 4148 | { |
4149 | int error; | | 4149 | int error; |
4150 | prop_dictionary_t prop_dict; | | 4150 | prop_dictionary_t prop_dict; |
4151 | char *buf = NULL; | | 4151 | char *buf = NULL; |
4152 | struct wg_peer *wgp = NULL, *wgp0 __diagused; | | 4152 | struct wg_peer *wgp = NULL, *wgp0 __diagused; |
4153 | | | 4153 | |
4154 | error = wg_alloc_prop_buf(&buf, ifd); | | 4154 | error = wg_alloc_prop_buf(&buf, ifd); |
4155 | if (error != 0) | | 4155 | if (error != 0) |
4156 | return error; | | 4156 | return error; |
4157 | error = EINVAL; | | 4157 | error = EINVAL; |
4158 | prop_dict = prop_dictionary_internalize(buf); | | 4158 | prop_dict = prop_dictionary_internalize(buf); |
4159 | if (prop_dict == NULL) | | 4159 | if (prop_dict == NULL) |
4160 | goto out; | | 4160 | goto out; |
4161 | | | 4161 | |
4162 | error = wg_handle_prop_peer(wg, prop_dict, &wgp); | | 4162 | error = wg_handle_prop_peer(wg, prop_dict, &wgp); |
4163 | if (error != 0) | | 4163 | if (error != 0) |
4164 | goto out; | | 4164 | goto out; |
4165 | | | 4165 | |
4166 | mutex_enter(wg->wg_lock); | | 4166 | mutex_enter(wg->wg_lock); |
4167 | if (thmap_get(wg->wg_peers_bypubkey, wgp->wgp_pubkey, | | 4167 | if (thmap_get(wg->wg_peers_bypubkey, wgp->wgp_pubkey, |
4168 | sizeof(wgp->wgp_pubkey)) != NULL || | | 4168 | sizeof(wgp->wgp_pubkey)) != NULL || |
4169 | (wgp->wgp_name[0] && | | 4169 | (wgp->wgp_name[0] && |
4170 | thmap_get(wg->wg_peers_byname, wgp->wgp_name, | | 4170 | thmap_get(wg->wg_peers_byname, wgp->wgp_name, |
4171 | strlen(wgp->wgp_name)) != NULL)) { | | 4171 | strlen(wgp->wgp_name)) != NULL)) { |
4172 | mutex_exit(wg->wg_lock); | | 4172 | mutex_exit(wg->wg_lock); |
4173 | wg_destroy_peer(wgp); | | 4173 | wg_destroy_peer(wgp); |
4174 | error = EEXIST; | | 4174 | error = EEXIST; |
4175 | goto out; | | 4175 | goto out; |
4176 | } | | 4176 | } |
4177 | wgp0 = thmap_put(wg->wg_peers_bypubkey, wgp->wgp_pubkey, | | 4177 | wgp0 = thmap_put(wg->wg_peers_bypubkey, wgp->wgp_pubkey, |
4178 | sizeof(wgp->wgp_pubkey), wgp); | | 4178 | sizeof(wgp->wgp_pubkey), wgp); |
4179 | KASSERT(wgp0 == wgp); | | 4179 | KASSERT(wgp0 == wgp); |
4180 | if (wgp->wgp_name[0]) { | | 4180 | if (wgp->wgp_name[0]) { |
4181 | wgp0 = thmap_put(wg->wg_peers_byname, wgp->wgp_name, | | 4181 | wgp0 = thmap_put(wg->wg_peers_byname, wgp->wgp_name, |
4182 | strlen(wgp->wgp_name), wgp); | | 4182 | strlen(wgp->wgp_name), wgp); |
4183 | KASSERT(wgp0 == wgp); | | 4183 | KASSERT(wgp0 == wgp); |
4184 | } | | 4184 | } |
4185 | WG_PEER_WRITER_INSERT_HEAD(wgp, wg); | | 4185 | WG_PEER_WRITER_INSERT_HEAD(wgp, wg); |
4186 | wg->wg_npeers++; | | 4186 | wg->wg_npeers++; |
4187 | mutex_exit(wg->wg_lock); | | 4187 | mutex_exit(wg->wg_lock); |
4188 | | | 4188 | |
4189 | out: | | 4189 | out: |
4190 | kmem_free(buf, ifd->ifd_len + 1); | | 4190 | kmem_free(buf, ifd->ifd_len + 1); |
4191 | return error; | | 4191 | return error; |
4192 | } | | 4192 | } |
4193 | | | 4193 | |
4194 | static int | | 4194 | static int |
4195 | wg_ioctl_delete_peer(struct wg_softc *wg, struct ifdrv *ifd) | | 4195 | wg_ioctl_delete_peer(struct wg_softc *wg, struct ifdrv *ifd) |
4196 | { | | 4196 | { |
4197 | int error; | | 4197 | int error; |
4198 | prop_dictionary_t prop_dict; | | 4198 | prop_dictionary_t prop_dict; |
4199 | char *buf = NULL; | | 4199 | char *buf = NULL; |
4200 | const char *name; | | 4200 | const char *name; |
4201 | | | 4201 | |
4202 | error = wg_alloc_prop_buf(&buf, ifd); | | 4202 | error = wg_alloc_prop_buf(&buf, ifd); |
4203 | if (error != 0) | | 4203 | if (error != 0) |
4204 | return error; | | 4204 | return error; |
4205 | error = EINVAL; | | 4205 | error = EINVAL; |
4206 | prop_dict = prop_dictionary_internalize(buf); | | 4206 | prop_dict = prop_dictionary_internalize(buf); |
4207 | if (prop_dict == NULL) | | 4207 | if (prop_dict == NULL) |
4208 | goto out; | | 4208 | goto out; |
4209 | | | 4209 | |
4210 | if (!prop_dictionary_get_string(prop_dict, "name", &name)) | | 4210 | if (!prop_dictionary_get_string(prop_dict, "name", &name)) |
4211 | goto out; | | 4211 | goto out; |
4212 | if (strlen(name) > WG_PEER_NAME_MAXLEN) | | 4212 | if (strlen(name) > WG_PEER_NAME_MAXLEN) |
4213 | goto out; | | 4213 | goto out; |
4214 | | | 4214 | |
4215 | error = wg_destroy_peer_name(wg, name); | | 4215 | error = wg_destroy_peer_name(wg, name); |
4216 | out: | | 4216 | out: |
4217 | kmem_free(buf, ifd->ifd_len + 1); | | 4217 | kmem_free(buf, ifd->ifd_len + 1); |
4218 | return error; | | 4218 | return error; |
4219 | } | | 4219 | } |
4220 | | | 4220 | |
4221 | static int | | 4221 | static int |
4222 | wg_ioctl_get(struct wg_softc *wg, struct ifdrv *ifd) | | 4222 | wg_ioctl_get(struct wg_softc *wg, struct ifdrv *ifd) |
4223 | { | | 4223 | { |
4224 | int error = ENOMEM; | | 4224 | int error = ENOMEM; |
4225 | prop_dictionary_t prop_dict; | | 4225 | prop_dictionary_t prop_dict; |
4226 | prop_array_t peers = NULL; | | 4226 | prop_array_t peers = NULL; |
4227 | char *buf; | | 4227 | char *buf; |
4228 | struct wg_peer *wgp; | | 4228 | struct wg_peer *wgp; |
4229 | int s, i; | | 4229 | int s, i; |
4230 | | | 4230 | |
4231 | prop_dict = prop_dictionary_create(); | | 4231 | prop_dict = prop_dictionary_create(); |
4232 | if (prop_dict == NULL) | | 4232 | if (prop_dict == NULL) |
4233 | goto error; | | 4233 | goto error; |
4234 | | | 4234 | |
4235 | if (!prop_dictionary_set_data(prop_dict, "private_key", wg->wg_privkey, | | 4235 | if (!prop_dictionary_set_data(prop_dict, "private_key", wg->wg_privkey, |
4236 | WG_STATIC_KEY_LEN)) | | 4236 | WG_STATIC_KEY_LEN)) |
4237 | goto error; | | 4237 | goto error; |
4238 | | | 4238 | |
4239 | if (wg->wg_listen_port != 0) { | | 4239 | if (wg->wg_listen_port != 0) { |
4240 | if (!prop_dictionary_set_uint16(prop_dict, "listen_port", | | 4240 | if (!prop_dictionary_set_uint16(prop_dict, "listen_port", |
4241 | wg->wg_listen_port)) | | 4241 | wg->wg_listen_port)) |
4242 | goto error; | | 4242 | goto error; |
4243 | } | | 4243 | } |
4244 | | | 4244 | |
4245 | if (wg->wg_npeers == 0) | | 4245 | if (wg->wg_npeers == 0) |
4246 | goto skip_peers; | | 4246 | goto skip_peers; |
4247 | | | 4247 | |
4248 | peers = prop_array_create(); | | 4248 | peers = prop_array_create(); |
4249 | if (peers == NULL) | | 4249 | if (peers == NULL) |
4250 | goto error; | | 4250 | goto error; |
4251 | | | 4251 | |
4252 | s = pserialize_read_enter(); | | 4252 | s = pserialize_read_enter(); |
4253 | i = 0; | | 4253 | i = 0; |
4254 | WG_PEER_READER_FOREACH(wgp, wg) { | | 4254 | WG_PEER_READER_FOREACH(wgp, wg) { |
4255 | struct wg_sockaddr *wgsa; | | 4255 | struct wg_sockaddr *wgsa; |
4256 | struct psref wgp_psref, wgsa_psref; | | 4256 | struct psref wgp_psref, wgsa_psref; |
4257 | prop_dictionary_t prop_peer; | | 4257 | prop_dictionary_t prop_peer; |
4258 | | | 4258 | |
4259 | wg_get_peer(wgp, &wgp_psref); | | 4259 | wg_get_peer(wgp, &wgp_psref); |
4260 | pserialize_read_exit(s); | | 4260 | pserialize_read_exit(s); |
4261 | | | 4261 | |
4262 | prop_peer = prop_dictionary_create(); | | 4262 | prop_peer = prop_dictionary_create(); |
4263 | if (prop_peer == NULL) | | 4263 | if (prop_peer == NULL) |
4264 | goto next; | | 4264 | goto next; |
4265 | | | 4265 | |
4266 | if (strlen(wgp->wgp_name) > 0) { | | 4266 | if (strlen(wgp->wgp_name) > 0) { |
4267 | if (!prop_dictionary_set_string(prop_peer, "name", | | 4267 | if (!prop_dictionary_set_string(prop_peer, "name", |
4268 | wgp->wgp_name)) | | 4268 | wgp->wgp_name)) |
4269 | goto next; | | 4269 | goto next; |
4270 | } | | 4270 | } |
4271 | | | 4271 | |
4272 | if (!prop_dictionary_set_data(prop_peer, "public_key", | | 4272 | if (!prop_dictionary_set_data(prop_peer, "public_key", |
4273 | wgp->wgp_pubkey, sizeof(wgp->wgp_pubkey))) | | 4273 | wgp->wgp_pubkey, sizeof(wgp->wgp_pubkey))) |
4274 | goto next; | | 4274 | goto next; |
4275 | | | 4275 | |
4276 | uint8_t psk_zero[WG_PRESHARED_KEY_LEN] = {0}; | | 4276 | uint8_t psk_zero[WG_PRESHARED_KEY_LEN] = {0}; |
4277 | if (!consttime_memequal(wgp->wgp_psk, psk_zero, | | 4277 | if (!consttime_memequal(wgp->wgp_psk, psk_zero, |
4278 | sizeof(wgp->wgp_psk))) { | | 4278 | sizeof(wgp->wgp_psk))) { |
4279 | if (!prop_dictionary_set_data(prop_peer, | | 4279 | if (!prop_dictionary_set_data(prop_peer, |
4280 | "preshared_key", | | 4280 | "preshared_key", |
4281 | wgp->wgp_psk, sizeof(wgp->wgp_psk))) | | 4281 | wgp->wgp_psk, sizeof(wgp->wgp_psk))) |
4282 | goto next; | | 4282 | goto next; |
4283 | } | | 4283 | } |
4284 | | | 4284 | |
4285 | wgsa = wg_get_endpoint_sa(wgp, &wgsa_psref); | | 4285 | wgsa = wg_get_endpoint_sa(wgp, &wgsa_psref); |
4286 | CTASSERT(AF_UNSPEC == 0); | | 4286 | CTASSERT(AF_UNSPEC == 0); |
4287 | if (wgsa_family(wgsa) != 0 /*AF_UNSPEC*/ && | | 4287 | if (wgsa_family(wgsa) != 0 /*AF_UNSPEC*/ && |
4288 | !prop_dictionary_set_data(prop_peer, "endpoint", | | 4288 | !prop_dictionary_set_data(prop_peer, "endpoint", |
4289 | wgsatoss(wgsa), | | 4289 | wgsatoss(wgsa), |
4290 | sockaddr_getsize_by_family(wgsa_family(wgsa)))) { | | 4290 | sockaddr_getsize_by_family(wgsa_family(wgsa)))) { |
4291 | wg_put_sa(wgp, wgsa, &wgsa_psref); | | 4291 | wg_put_sa(wgp, wgsa, &wgsa_psref); |
4292 | goto next; | | 4292 | goto next; |
4293 | } | | 4293 | } |
4294 | wg_put_sa(wgp, wgsa, &wgsa_psref); | | 4294 | wg_put_sa(wgp, wgsa, &wgsa_psref); |
4295 | | | 4295 | |
4296 | const struct timespec *t = &wgp->wgp_last_handshake_time; | | 4296 | const struct timespec *t = &wgp->wgp_last_handshake_time; |
4297 | | | 4297 | |
4298 | if (!prop_dictionary_set_uint64(prop_peer, | | 4298 | if (!prop_dictionary_set_uint64(prop_peer, |
4299 | "last_handshake_time_sec", t->tv_sec)) | | 4299 | "last_handshake_time_sec", t->tv_sec)) |
4300 | goto next; | | 4300 | goto next; |
4301 | if (!prop_dictionary_set_uint32(prop_peer, | | 4301 | if (!prop_dictionary_set_uint32(prop_peer, |
4302 | "last_handshake_time_nsec", t->tv_nsec)) | | 4302 | "last_handshake_time_nsec", t->tv_nsec)) |
4303 | goto next; | | 4303 | goto next; |
4304 | | | 4304 | |
4305 | if (wgp->wgp_n_allowedips == 0) | | 4305 | if (wgp->wgp_n_allowedips == 0) |
4306 | goto skip_allowedips; | | 4306 | goto skip_allowedips; |
4307 | | | 4307 | |
4308 | prop_array_t allowedips = prop_array_create(); | | 4308 | prop_array_t allowedips = prop_array_create(); |
4309 | if (allowedips == NULL) | | 4309 | if (allowedips == NULL) |
4310 | goto next; | | 4310 | goto next; |
4311 | for (int j = 0; j < wgp->wgp_n_allowedips; j++) { | | 4311 | for (int j = 0; j < wgp->wgp_n_allowedips; j++) { |
4312 | struct wg_allowedip *wga = &wgp->wgp_allowedips[j]; | | 4312 | struct wg_allowedip *wga = &wgp->wgp_allowedips[j]; |
4313 | prop_dictionary_t prop_allowedip; | | 4313 | prop_dictionary_t prop_allowedip; |
4314 | | | 4314 | |
4315 | prop_allowedip = prop_dictionary_create(); | | 4315 | prop_allowedip = prop_dictionary_create(); |
4316 | if (prop_allowedip == NULL) | | 4316 | if (prop_allowedip == NULL) |
4317 | break; | | 4317 | break; |
4318 | | | 4318 | |
4319 | if (!prop_dictionary_set_int(prop_allowedip, "family", | | 4319 | if (!prop_dictionary_set_int(prop_allowedip, "family", |
4320 | wga->wga_family)) | | 4320 | wga->wga_family)) |
4321 | goto _next; | | 4321 | goto _next; |
4322 | if (!prop_dictionary_set_uint8(prop_allowedip, "cidr", | | 4322 | if (!prop_dictionary_set_uint8(prop_allowedip, "cidr", |
4323 | wga->wga_cidr)) | | 4323 | wga->wga_cidr)) |
4324 | goto _next; | | 4324 | goto _next; |
4325 | | | 4325 | |
4326 | switch (wga->wga_family) { | | 4326 | switch (wga->wga_family) { |
4327 | case AF_INET: | | 4327 | case AF_INET: |
4328 | if (!prop_dictionary_set_data(prop_allowedip, | | 4328 | if (!prop_dictionary_set_data(prop_allowedip, |
4329 | "ip", &wga->wga_addr4, | | 4329 | "ip", &wga->wga_addr4, |
4330 | sizeof(wga->wga_addr4))) | | 4330 | sizeof(wga->wga_addr4))) |
4331 | goto _next; | | 4331 | goto _next; |
4332 | break; | | 4332 | break; |
4333 | #ifdef INET6 | | 4333 | #ifdef INET6 |
4334 | case AF_INET6: | | 4334 | case AF_INET6: |
4335 | if (!prop_dictionary_set_data(prop_allowedip, | | 4335 | if (!prop_dictionary_set_data(prop_allowedip, |
4336 | "ip", &wga->wga_addr6, | | 4336 | "ip", &wga->wga_addr6, |
4337 | sizeof(wga->wga_addr6))) | | 4337 | sizeof(wga->wga_addr6))) |
4338 | goto _next; | | 4338 | goto _next; |
4339 | break; | | 4339 | break; |
4340 | #endif | | 4340 | #endif |
4341 | default: | | 4341 | default: |
4342 | break; | | 4342 | break; |
4343 | } | | 4343 | } |
4344 | prop_array_set(allowedips, j, prop_allowedip); | | 4344 | prop_array_set(allowedips, j, prop_allowedip); |
4345 | _next: | | 4345 | _next: |
4346 | prop_object_release(prop_allowedip); | | 4346 | prop_object_release(prop_allowedip); |
4347 | } | | 4347 | } |
4348 | prop_dictionary_set(prop_peer, "allowedips", allowedips); | | 4348 | prop_dictionary_set(prop_peer, "allowedips", allowedips); |
4349 | prop_object_release(allowedips); | | 4349 | prop_object_release(allowedips); |
4350 | | | 4350 | |
4351 | skip_allowedips: | | 4351 | skip_allowedips: |
4352 | | | 4352 | |
4353 | prop_array_set(peers, i, prop_peer); | | 4353 | prop_array_set(peers, i, prop_peer); |
4354 | next: | | 4354 | next: |
4355 | if (prop_peer) | | 4355 | if (prop_peer) |
4356 | prop_object_release(prop_peer); | | 4356 | prop_object_release(prop_peer); |
4357 | i++; | | 4357 | i++; |
4358 | | | 4358 | |
4359 | s = pserialize_read_enter(); | | 4359 | s = pserialize_read_enter(); |
4360 | wg_put_peer(wgp, &wgp_psref); | | 4360 | wg_put_peer(wgp, &wgp_psref); |
4361 | } | | 4361 | } |
4362 | pserialize_read_exit(s); | | 4362 | pserialize_read_exit(s); |
4363 | | | 4363 | |
4364 | prop_dictionary_set(prop_dict, "peers", peers); | | 4364 | prop_dictionary_set(prop_dict, "peers", peers); |
4365 | prop_object_release(peers); | | 4365 | prop_object_release(peers); |
4366 | peers = NULL; | | 4366 | peers = NULL; |
4367 | | | 4367 | |
4368 | skip_peers: | | 4368 | skip_peers: |
4369 | buf = prop_dictionary_externalize(prop_dict); | | 4369 | buf = prop_dictionary_externalize(prop_dict); |
4370 | if (buf == NULL) | | 4370 | if (buf == NULL) |
4371 | goto error; | | 4371 | goto error; |
4372 | if (ifd->ifd_len < (strlen(buf) + 1)) { | | 4372 | if (ifd->ifd_len < (strlen(buf) + 1)) { |
4373 | error = EINVAL; | | 4373 | error = EINVAL; |
4374 | goto error; | | 4374 | goto error; |
4375 | } | | 4375 | } |
4376 | error = copyout(buf, ifd->ifd_data, strlen(buf) + 1); | | 4376 | error = copyout(buf, ifd->ifd_data, strlen(buf) + 1); |
4377 | | | 4377 | |
4378 | free(buf, 0); | | 4378 | free(buf, 0); |
4379 | error: | | 4379 | error: |
4380 | if (peers != NULL) | | 4380 | if (peers != NULL) |
4381 | prop_object_release(peers); | | 4381 | prop_object_release(peers); |
4382 | if (prop_dict != NULL) | | 4382 | if (prop_dict != NULL) |
4383 | prop_object_release(prop_dict); | | 4383 | prop_object_release(prop_dict); |
4384 | | | 4384 | |
4385 | return error; | | 4385 | return error; |
4386 | } | | 4386 | } |
4387 | | | 4387 | |
4388 | static int | | 4388 | static int |
4389 | wg_ioctl(struct ifnet *ifp, u_long cmd, void *data) | | 4389 | wg_ioctl(struct ifnet *ifp, u_long cmd, void *data) |
4390 | { | | 4390 | { |
4391 | struct wg_softc *wg = ifp->if_softc; | | 4391 | struct wg_softc *wg = ifp->if_softc; |
4392 | struct ifreq *ifr = data; | | 4392 | struct ifreq *ifr = data; |
4393 | struct ifaddr *ifa = data; | | 4393 | struct ifaddr *ifa = data; |
4394 | struct ifdrv *ifd = data; | | 4394 | struct ifdrv *ifd = data; |
4395 | int error = 0; | | 4395 | int error = 0; |
4396 | | | 4396 | |
4397 | switch (cmd) { | | 4397 | switch (cmd) { |
4398 | case SIOCINITIFADDR: | | 4398 | case SIOCINITIFADDR: |
4399 | if (ifa->ifa_addr->sa_family != AF_LINK && | | 4399 | if (ifa->ifa_addr->sa_family != AF_LINK && |
4400 | (ifp->if_flags & (IFF_UP | IFF_RUNNING)) != | | 4400 | (ifp->if_flags & (IFF_UP | IFF_RUNNING)) != |
4401 | (IFF_UP | IFF_RUNNING)) { | | 4401 | (IFF_UP | IFF_RUNNING)) { |
4402 | ifp->if_flags |= IFF_UP; | | 4402 | ifp->if_flags |= IFF_UP; |
4403 | error = ifp->if_init(ifp); | | 4403 | error = ifp->if_init(ifp); |
4404 | } | | 4404 | } |
4405 | return error; | | 4405 | return error; |
4406 | case SIOCADDMULTI: | | 4406 | case SIOCADDMULTI: |
4407 | case SIOCDELMULTI: | | 4407 | case SIOCDELMULTI: |
4408 | switch (ifr->ifr_addr.sa_family) { | | 4408 | switch (ifr->ifr_addr.sa_family) { |
4409 | case AF_INET: /* IP supports Multicast */ | | 4409 | case AF_INET: /* IP supports Multicast */ |
4410 | break; | | 4410 | break; |
4411 | #ifdef INET6 | | 4411 | #ifdef INET6 |
4412 | case AF_INET6: /* IP6 supports Multicast */ | | 4412 | case AF_INET6: /* IP6 supports Multicast */ |
4413 | break; | | 4413 | break; |
4414 | #endif | | 4414 | #endif |
4415 | default: /* Other protocols doesn't support Multicast */ | | 4415 | default: /* Other protocols doesn't support Multicast */ |
4416 | error = EAFNOSUPPORT; | | 4416 | error = EAFNOSUPPORT; |
4417 | break; | | 4417 | break; |
4418 | } | | 4418 | } |
4419 | return error; | | 4419 | return error; |
4420 | case SIOCSDRVSPEC: | | 4420 | case SIOCSDRVSPEC: |
4421 | switch (ifd->ifd_cmd) { | | 4421 | switch (ifd->ifd_cmd) { |
4422 | case WG_IOCTL_SET_PRIVATE_KEY: | | 4422 | case WG_IOCTL_SET_PRIVATE_KEY: |
4423 | error = wg_ioctl_set_private_key(wg, ifd); | | 4423 | error = wg_ioctl_set_private_key(wg, ifd); |
4424 | break; | | 4424 | break; |
4425 | case WG_IOCTL_SET_LISTEN_PORT: | | 4425 | case WG_IOCTL_SET_LISTEN_PORT: |
4426 | error = wg_ioctl_set_listen_port(wg, ifd); | | 4426 | error = wg_ioctl_set_listen_port(wg, ifd); |
4427 | break; | | 4427 | break; |
4428 | case WG_IOCTL_ADD_PEER: | | 4428 | case WG_IOCTL_ADD_PEER: |
4429 | error = wg_ioctl_add_peer(wg, ifd); | | 4429 | error = wg_ioctl_add_peer(wg, ifd); |
4430 | break; | | 4430 | break; |
4431 | case WG_IOCTL_DELETE_PEER: | | 4431 | case WG_IOCTL_DELETE_PEER: |
4432 | error = wg_ioctl_delete_peer(wg, ifd); | | 4432 | error = wg_ioctl_delete_peer(wg, ifd); |
4433 | break; | | 4433 | break; |
4434 | default: | | 4434 | default: |
4435 | error = EINVAL; | | 4435 | error = EINVAL; |
4436 | break; | | 4436 | break; |
4437 | } | | 4437 | } |
4438 | return error; | | 4438 | return error; |
4439 | case SIOCGDRVSPEC: | | 4439 | case SIOCGDRVSPEC: |
4440 | return wg_ioctl_get(wg, ifd); | | 4440 | return wg_ioctl_get(wg, ifd); |
4441 | case SIOCSIFFLAGS: | | 4441 | case SIOCSIFFLAGS: |
4442 | if ((error = ifioctl_common(ifp, cmd, data)) != 0) | | 4442 | if ((error = ifioctl_common(ifp, cmd, data)) != 0) |
4443 | break; | | 4443 | break; |
4444 | switch (ifp->if_flags & (IFF_UP|IFF_RUNNING)) { | | 4444 | switch (ifp->if_flags & (IFF_UP|IFF_RUNNING)) { |
4445 | case IFF_RUNNING: | | 4445 | case IFF_RUNNING: |
4446 | /* | | 4446 | /* |
4447 | * If interface is marked down and it is running, | | 4447 | * If interface is marked down and it is running, |
4448 | * then stop and disable it. | | 4448 | * then stop and disable it. |
4449 | */ | | 4449 | */ |
4450 | (*ifp->if_stop)(ifp, 1); | | 4450 | (*ifp->if_stop)(ifp, 1); |
4451 | break; | | 4451 | break; |
4452 | case IFF_UP: | | 4452 | case IFF_UP: |
4453 | /* | | 4453 | /* |
4454 | * If interface is marked up and it is stopped, then | | 4454 | * If interface is marked up and it is stopped, then |
4455 | * start it. | | 4455 | * start it. |
4456 | */ | | 4456 | */ |
4457 | error = (*ifp->if_init)(ifp); | | 4457 | error = (*ifp->if_init)(ifp); |
4458 | break; | | 4458 | break; |
4459 | default: | | 4459 | default: |
4460 | break; | | 4460 | break; |
4461 | } | | 4461 | } |
4462 | return error; | | 4462 | return error; |
4463 | #ifdef WG_RUMPKERNEL | | 4463 | #ifdef WG_RUMPKERNEL |
4464 | case SIOCSLINKSTR: | | 4464 | case SIOCSLINKSTR: |
4465 | error = wg_ioctl_linkstr(wg, ifd); | | 4465 | error = wg_ioctl_linkstr(wg, ifd); |
4466 | if (error == 0) | | 4466 | if (error == 0) |
4467 | wg->wg_ops = &wg_ops_rumpuser; | | 4467 | wg->wg_ops = &wg_ops_rumpuser; |
4468 | return error; | | 4468 | return error; |
4469 | #endif | | 4469 | #endif |
4470 | default: | | 4470 | default: |
4471 | break; | | 4471 | break; |
4472 | } | | 4472 | } |
4473 | | | 4473 | |
4474 | error = ifioctl_common(ifp, cmd, data); | | 4474 | error = ifioctl_common(ifp, cmd, data); |
4475 | | | 4475 | |
4476 | #ifdef WG_RUMPKERNEL | | 4476 | #ifdef WG_RUMPKERNEL |
4477 | if (!wg_user_mode(wg)) | | 4477 | if (!wg_user_mode(wg)) |
4478 | return error; | | 4478 | return error; |
4479 | | | 4479 | |
4480 | /* Do the same to the corresponding tun device on the host */ | | 4480 | /* Do the same to the corresponding tun device on the host */ |
4481 | /* | | 4481 | /* |
4482 | * XXX Actually the command has not been handled yet. It | | 4482 | * XXX Actually the command has not been handled yet. It |
4483 | * will be handled via pr_ioctl form doifioctl later. | | 4483 | * will be handled via pr_ioctl form doifioctl later. |
4484 | */ | | 4484 | */ |
4485 | switch (cmd) { | | 4485 | switch (cmd) { |
4486 | case SIOCAIFADDR: | | 4486 | case SIOCAIFADDR: |
4487 | case SIOCDIFADDR: { | | 4487 | case SIOCDIFADDR: { |
4488 | struct in_aliasreq _ifra = *(const struct in_aliasreq *)data; | | 4488 | struct in_aliasreq _ifra = *(const struct in_aliasreq *)data; |
4489 | struct in_aliasreq *ifra = &_ifra; | | 4489 | struct in_aliasreq *ifra = &_ifra; |
4490 | KASSERT(error == ENOTTY); | | 4490 | KASSERT(error == ENOTTY); |
4491 | strncpy(ifra->ifra_name, rumpuser_wg_get_tunname(wg->wg_user), | | 4491 | strncpy(ifra->ifra_name, rumpuser_wg_get_tunname(wg->wg_user), |
4492 | IFNAMSIZ); | | 4492 | IFNAMSIZ); |
4493 | error = rumpuser_wg_ioctl(wg->wg_user, cmd, ifra, AF_INET); | | 4493 | error = rumpuser_wg_ioctl(wg->wg_user, cmd, ifra, AF_INET); |
4494 | if (error == 0) | | 4494 | if (error == 0) |
4495 | error = ENOTTY; | | 4495 | error = ENOTTY; |
4496 | break; | | 4496 | break; |
4497 | } | | 4497 | } |
4498 | #ifdef INET6 | | 4498 | #ifdef INET6 |
4499 | case SIOCAIFADDR_IN6: | | 4499 | case SIOCAIFADDR_IN6: |
4500 | case SIOCDIFADDR_IN6: { | | 4500 | case SIOCDIFADDR_IN6: { |
4501 | struct in6_aliasreq _ifra = *(const struct in6_aliasreq *)data; | | 4501 | struct in6_aliasreq _ifra = *(const struct in6_aliasreq *)data; |
4502 | struct in6_aliasreq *ifra = &_ifra; | | 4502 | struct in6_aliasreq *ifra = &_ifra; |
4503 | KASSERT(error == ENOTTY); | | 4503 | KASSERT(error == ENOTTY); |
4504 | strncpy(ifra->ifra_name, rumpuser_wg_get_tunname(wg->wg_user), | | 4504 | strncpy(ifra->ifra_name, rumpuser_wg_get_tunname(wg->wg_user), |
4505 | IFNAMSIZ); | | 4505 | IFNAMSIZ); |
4506 | error = rumpuser_wg_ioctl(wg->wg_user, cmd, ifra, AF_INET6); | | 4506 | error = rumpuser_wg_ioctl(wg->wg_user, cmd, ifra, AF_INET6); |
4507 | if (error == 0) | | 4507 | if (error == 0) |
4508 | error = ENOTTY; | | 4508 | error = ENOTTY; |
4509 | break; | | 4509 | break; |
4510 | } | | 4510 | } |
4511 | #endif | | 4511 | #endif |
4512 | } | | 4512 | } |
4513 | #endif /* WG_RUMPKERNEL */ | | 4513 | #endif /* WG_RUMPKERNEL */ |
4514 | | | 4514 | |
4515 | return error; | | 4515 | return error; |
4516 | } | | 4516 | } |
4517 | | | 4517 | |
4518 | static int | | 4518 | static int |
4519 | wg_init(struct ifnet *ifp) | | 4519 | wg_init(struct ifnet *ifp) |
4520 | { | | 4520 | { |
4521 | | | 4521 | |
4522 | ifp->if_flags |= IFF_RUNNING; | | 4522 | ifp->if_flags |= IFF_RUNNING; |
4523 | | | 4523 | |
4524 | /* TODO flush pending packets. */ | | 4524 | /* TODO flush pending packets. */ |
4525 | return 0; | | 4525 | return 0; |
4526 | } | | 4526 | } |
4527 | | | 4527 | |
4528 | static void | | 4528 | static void |
4529 | wg_stop(struct ifnet *ifp, int disable) | | 4529 | wg_stop(struct ifnet *ifp, int disable) |
4530 | { | | 4530 | { |
4531 | | | 4531 | |
4532 | KASSERT((ifp->if_flags & IFF_RUNNING) != 0); | | 4532 | KASSERT((ifp->if_flags & IFF_RUNNING) != 0); |
4533 | ifp->if_flags &= ~IFF_RUNNING; | | 4533 | ifp->if_flags &= ~IFF_RUNNING; |
4534 | | | 4534 | |
4535 | /* Need to do something? */ | | 4535 | /* Need to do something? */ |
4536 | } | | 4536 | } |
4537 | | | 4537 | |
4538 | #ifdef WG_DEBUG_PARAMS | | 4538 | #ifdef WG_DEBUG_PARAMS |
4539 | SYSCTL_SETUP(sysctl_net_wg_setup, "sysctl net.wg setup") | | 4539 | SYSCTL_SETUP(sysctl_net_wg_setup, "sysctl net.wg setup") |
4540 | { | | 4540 | { |
4541 | const struct sysctlnode *node = NULL; | | 4541 | const struct sysctlnode *node = NULL; |
4542 | | | 4542 | |
4543 | sysctl_createv(clog, 0, NULL, &node, | | 4543 | sysctl_createv(clog, 0, NULL, &node, |
4544 | CTLFLAG_PERMANENT, | | 4544 | CTLFLAG_PERMANENT, |
4545 | CTLTYPE_NODE, "wg", | | 4545 | CTLTYPE_NODE, "wg", |
4546 | SYSCTL_DESCR("wg(4)"), | | 4546 | SYSCTL_DESCR("wg(4)"), |
4547 | NULL, 0, NULL, 0, | | 4547 | NULL, 0, NULL, 0, |
4548 | CTL_NET, CTL_CREATE, CTL_EOL); | | 4548 | CTL_NET, CTL_CREATE, CTL_EOL); |
4549 | sysctl_createv(clog, 0, &node, NULL, | | 4549 | sysctl_createv(clog, 0, &node, NULL, |
4550 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | | 4550 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
4551 | CTLTYPE_QUAD, "rekey_after_messages", | | 4551 | CTLTYPE_QUAD, "rekey_after_messages", |
4552 | SYSCTL_DESCR("session liftime by messages"), | | 4552 | SYSCTL_DESCR("session liftime by messages"), |
4553 | NULL, 0, &wg_rekey_after_messages, 0, CTL_CREATE, CTL_EOL); | | 4553 | NULL, 0, &wg_rekey_after_messages, 0, CTL_CREATE, CTL_EOL); |
4554 | sysctl_createv(clog, 0, &node, NULL, | | 4554 | sysctl_createv(clog, 0, &node, NULL, |
4555 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | | 4555 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
4556 | CTLTYPE_INT, "rekey_after_time", | | 4556 | CTLTYPE_INT, "rekey_after_time", |
4557 | SYSCTL_DESCR("session liftime"), | | 4557 | SYSCTL_DESCR("session liftime"), |
4558 | NULL, 0, &wg_rekey_after_time, 0, CTL_CREATE, CTL_EOL); | | 4558 | NULL, 0, &wg_rekey_after_time, 0, CTL_CREATE, CTL_EOL); |
4559 | sysctl_createv(clog, 0, &node, NULL, | | 4559 | sysctl_createv(clog, 0, &node, NULL, |
4560 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | | 4560 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
4561 | CTLTYPE_INT, "rekey_timeout", | | 4561 | CTLTYPE_INT, "rekey_timeout", |
4562 | SYSCTL_DESCR("session handshake retry time"), | | 4562 | SYSCTL_DESCR("session handshake retry time"), |
4563 | NULL, 0, &wg_rekey_timeout, 0, CTL_CREATE, CTL_EOL); | | 4563 | NULL, 0, &wg_rekey_timeout, 0, CTL_CREATE, CTL_EOL); |
4564 | sysctl_createv(clog, 0, &node, NULL, | | 4564 | sysctl_createv(clog, 0, &node, NULL, |
4565 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | | 4565 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
4566 | CTLTYPE_INT, "rekey_attempt_time", | | 4566 | CTLTYPE_INT, "rekey_attempt_time", |
4567 | SYSCTL_DESCR("session handshake timeout"), | | 4567 | SYSCTL_DESCR("session handshake timeout"), |
4568 | NULL, 0, &wg_rekey_attempt_time, 0, CTL_CREATE, CTL_EOL); | | 4568 | NULL, 0, &wg_rekey_attempt_time, 0, CTL_CREATE, CTL_EOL); |
4569 | sysctl_createv(clog, 0, &node, NULL, | | 4569 | sysctl_createv(clog, 0, &node, NULL, |
4570 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | | 4570 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
4571 | CTLTYPE_INT, "keepalive_timeout", | | 4571 | CTLTYPE_INT, "keepalive_timeout", |
4572 | SYSCTL_DESCR("keepalive timeout"), | | 4572 | SYSCTL_DESCR("keepalive timeout"), |
4573 | NULL, 0, &wg_keepalive_timeout, 0, CTL_CREATE, CTL_EOL); | | 4573 | NULL, 0, &wg_keepalive_timeout, 0, CTL_CREATE, CTL_EOL); |
4574 | sysctl_createv(clog, 0, &node, NULL, | | 4574 | sysctl_createv(clog, 0, &node, NULL, |
4575 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, | | 4575 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
4576 | CTLTYPE_BOOL, "force_underload", | | 4576 | CTLTYPE_BOOL, "force_underload", |
4577 | SYSCTL_DESCR("force to detemine under load"), | | 4577 | SYSCTL_DESCR("force to detemine under load"), |
4578 | NULL, 0, &wg_force_underload, 0, CTL_CREATE, CTL_EOL); | | 4578 | NULL, 0, &wg_force_underload, 0, CTL_CREATE, CTL_EOL); |
4579 | } | | 4579 | } |
4580 | #endif | | 4580 | #endif |
4581 | | | 4581 | |
4582 | #ifdef WG_RUMPKERNEL | | 4582 | #ifdef WG_RUMPKERNEL |
4583 | static bool | | 4583 | static bool |
4584 | wg_user_mode(struct wg_softc *wg) | | 4584 | wg_user_mode(struct wg_softc *wg) |
4585 | { | | 4585 | { |
4586 | | | 4586 | |
4587 | return wg->wg_user != NULL; | | 4587 | return wg->wg_user != NULL; |
4588 | } | | 4588 | } |
4589 | | | 4589 | |
4590 | static int | | 4590 | static int |
4591 | wg_ioctl_linkstr(struct wg_softc *wg, struct ifdrv *ifd) | | 4591 | wg_ioctl_linkstr(struct wg_softc *wg, struct ifdrv *ifd) |
4592 | { | | 4592 | { |
4593 | struct ifnet *ifp = &wg->wg_if; | | 4593 | struct ifnet *ifp = &wg->wg_if; |
4594 | int error; | | 4594 | int error; |
4595 | | | 4595 | |
4596 | if (ifp->if_flags & IFF_UP) | | 4596 | if (ifp->if_flags & IFF_UP) |
4597 | return EBUSY; | | 4597 | return EBUSY; |
4598 | | | 4598 | |
4599 | if (ifd->ifd_cmd == IFLINKSTR_UNSET) { | | 4599 | if (ifd->ifd_cmd == IFLINKSTR_UNSET) { |
4600 | /* XXX do nothing */ | | 4600 | /* XXX do nothing */ |
4601 | return 0; | | 4601 | return 0; |
4602 | } else if (ifd->ifd_cmd != 0) { | | 4602 | } else if (ifd->ifd_cmd != 0) { |
4603 | return EINVAL; | | 4603 | return EINVAL; |
4604 | } else if (wg->wg_user != NULL) { | | 4604 | } else if (wg->wg_user != NULL) { |
4605 | return EBUSY; | | 4605 | return EBUSY; |
4606 | } | | 4606 | } |
4607 | | | 4607 | |
4608 | /* Assume \0 included */ | | 4608 | /* Assume \0 included */ |
4609 | if (ifd->ifd_len > IFNAMSIZ) { | | 4609 | if (ifd->ifd_len > IFNAMSIZ) { |
4610 | return E2BIG; | | 4610 | return E2BIG; |
4611 | } else if (ifd->ifd_len < 1) { | | 4611 | } else if (ifd->ifd_len < 1) { |
4612 | return EINVAL; | | 4612 | return EINVAL; |
4613 | } | | 4613 | } |
4614 | | | 4614 | |
4615 | char tun_name[IFNAMSIZ]; | | 4615 | char tun_name[IFNAMSIZ]; |
4616 | error = copyinstr(ifd->ifd_data, tun_name, ifd->ifd_len, NULL); | | 4616 | error = copyinstr(ifd->ifd_data, tun_name, ifd->ifd_len, NULL); |
4617 | if (error != 0) | | 4617 | if (error != 0) |
4618 | return error; | | 4618 | return error; |
4619 | | | 4619 | |
4620 | if (strncmp(tun_name, "tun", 3) != 0) | | 4620 | if (strncmp(tun_name, "tun", 3) != 0) |
4621 | return EINVAL; | | 4621 | return EINVAL; |
4622 | | | 4622 | |
4623 | error = rumpuser_wg_create(tun_name, wg, &wg->wg_user); | | 4623 | error = rumpuser_wg_create(tun_name, wg, &wg->wg_user); |
4624 | | | 4624 | |
4625 | return error; | | 4625 | return error; |
4626 | } | | 4626 | } |
4627 | | | 4627 | |
4628 | static int | | 4628 | static int |
4629 | wg_send_user(struct wg_peer *wgp, struct mbuf *m) | | 4629 | wg_send_user(struct wg_peer *wgp, struct mbuf *m) |
4630 | { | | 4630 | { |
4631 | int error; | | 4631 | int error; |
4632 | struct psref psref; | | 4632 | struct psref psref; |
4633 | struct wg_sockaddr *wgsa; | | 4633 | struct wg_sockaddr *wgsa; |
4634 | struct wg_softc *wg = wgp->wgp_sc; | | 4634 | struct wg_softc *wg = wgp->wgp_sc; |
4635 | struct iovec iov[1]; | | 4635 | struct iovec iov[1]; |
4636 | | | 4636 | |
4637 | wgsa = wg_get_endpoint_sa(wgp, &psref); | | 4637 | wgsa = wg_get_endpoint_sa(wgp, &psref); |
4638 | | | 4638 | |
4639 | iov[0].iov_base = mtod(m, void *); | | 4639 | iov[0].iov_base = mtod(m, void *); |
4640 | iov[0].iov_len = m->m_len; | | 4640 | iov[0].iov_len = m->m_len; |
4641 | | | 4641 | |
4642 | /* Send messages to a peer via an ordinary socket. */ | | 4642 | /* Send messages to a peer via an ordinary socket. */ |
4643 | error = rumpuser_wg_send_peer(wg->wg_user, wgsatosa(wgsa), iov, 1); | | 4643 | error = rumpuser_wg_send_peer(wg->wg_user, wgsatosa(wgsa), iov, 1); |
4644 | | | 4644 | |
4645 | wg_put_sa(wgp, wgsa, &psref); | | 4645 | wg_put_sa(wgp, wgsa, &psref); |
4646 | | | 4646 | |
4647 | m_freem(m); | | 4647 | m_freem(m); |
4648 | | | 4648 | |
4649 | return error; | | 4649 | return error; |
4650 | } | | 4650 | } |
4651 | | | 4651 | |
4652 | static void | | 4652 | static void |
4653 | wg_input_user(struct ifnet *ifp, struct mbuf *m, const int af) | | 4653 | wg_input_user(struct ifnet *ifp, struct mbuf *m, const int af) |
4654 | { | | 4654 | { |
4655 | struct wg_softc *wg = ifp->if_softc; | | 4655 | struct wg_softc *wg = ifp->if_softc; |
4656 | struct iovec iov[2]; | | 4656 | struct iovec iov[2]; |
4657 | struct sockaddr_storage ss; | | 4657 | struct sockaddr_storage ss; |
4658 | | | 4658 | |
4659 | KASSERT(af == AF_INET || af == AF_INET6); | | 4659 | KASSERT(af == AF_INET || af == AF_INET6); |
4660 | | | 4660 | |
4661 | WG_TRACE(""); | | 4661 | WG_TRACE(""); |
4662 | | | 4662 | |
4663 | if (af == AF_INET) { | | 4663 | if (af == AF_INET) { |
4664 | struct sockaddr_in *sin = (struct sockaddr_in *)&ss; | | 4664 | struct sockaddr_in *sin = (struct sockaddr_in *)&ss; |
4665 | struct ip *ip; | | 4665 | struct ip *ip; |
4666 | | | 4666 | |
4667 | KASSERT(m->m_len >= sizeof(struct ip)); | | 4667 | KASSERT(m->m_len >= sizeof(struct ip)); |
4668 | ip = mtod(m, struct ip *); | | 4668 | ip = mtod(m, struct ip *); |
4669 | sockaddr_in_init(sin, &ip->ip_dst, 0); | | 4669 | sockaddr_in_init(sin, &ip->ip_dst, 0); |
4670 | } else { | | 4670 | } else { |
4671 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss; | | 4671 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss; |
4672 | struct ip6_hdr *ip6; | | 4672 | struct ip6_hdr *ip6; |
4673 | | | 4673 | |
4674 | KASSERT(m->m_len >= sizeof(struct ip6_hdr)); | | 4674 | KASSERT(m->m_len >= sizeof(struct ip6_hdr)); |
4675 | ip6 = mtod(m, struct ip6_hdr *); | | 4675 | ip6 = mtod(m, struct ip6_hdr *); |
4676 | sockaddr_in6_init(sin6, &ip6->ip6_dst, 0, 0, 0); | | 4676 | sockaddr_in6_init(sin6, &ip6->ip6_dst, 0, 0, 0); |
4677 | } | | 4677 | } |
4678 | | | 4678 | |
4679 | iov[0].iov_base = &ss; | | 4679 | iov[0].iov_base = &ss; |
4680 | iov[0].iov_len = ss.ss_len; | | 4680 | iov[0].iov_len = ss.ss_len; |
4681 | iov[1].iov_base = mtod(m, void *); | | 4681 | iov[1].iov_base = mtod(m, void *); |
4682 | iov[1].iov_len = m->m_len; | | 4682 | iov[1].iov_len = m->m_len; |
4683 | | | 4683 | |
4684 | WG_DUMP_BUF(iov[1].iov_base, iov[1].iov_len); | | 4684 | WG_DUMP_BUF(iov[1].iov_base, iov[1].iov_len); |
4685 | | | 4685 | |
4686 | /* Send decrypted packets to users via a tun. */ | | 4686 | /* Send decrypted packets to users via a tun. */ |
4687 | rumpuser_wg_send_user(wg->wg_user, iov, 2); | | 4687 | rumpuser_wg_send_user(wg->wg_user, iov, 2); |
4688 | | | 4688 | |
4689 | m_freem(m); | | 4689 | m_freem(m); |
4690 | } | | 4690 | } |
4691 | | | 4691 | |
4692 | static int | | 4692 | static int |
4693 | wg_bind_port_user(struct wg_softc *wg, const uint16_t port) | | 4693 | wg_bind_port_user(struct wg_softc *wg, const uint16_t port) |
4694 | { | | 4694 | { |
4695 | int error; | | 4695 | int error; |
4696 | uint16_t old_port = wg->wg_listen_port; | | 4696 | uint16_t old_port = wg->wg_listen_port; |
4697 | | | 4697 | |
4698 | if (port != 0 && old_port == port) | | 4698 | if (port != 0 && old_port == port) |
4699 | return 0; | | 4699 | return 0; |
4700 | | | 4700 | |
4701 | error = rumpuser_wg_sock_bind(wg->wg_user, port); | | 4701 | error = rumpuser_wg_sock_bind(wg->wg_user, port); |
4702 | if (error == 0) | | 4702 | if (error == 0) |
4703 | wg->wg_listen_port = port; | | 4703 | wg->wg_listen_port = port; |
4704 | return error; | | 4704 | return error; |
4705 | } | | 4705 | } |
4706 | | | 4706 | |
4707 | /* | | 4707 | /* |
4708 | * Receive user packets. | | 4708 | * Receive user packets. |
4709 | */ | | 4709 | */ |
4710 | void | | 4710 | void |
4711 | rumpkern_wg_recv_user(struct wg_softc *wg, struct iovec *iov, size_t iovlen) | | 4711 | rumpkern_wg_recv_user(struct wg_softc *wg, struct iovec *iov, size_t iovlen) |
4712 | { | | 4712 | { |
4713 | struct ifnet *ifp = &wg->wg_if; | | 4713 | struct ifnet *ifp = &wg->wg_if; |
4714 | struct mbuf *m; | | 4714 | struct mbuf *m; |
4715 | const struct sockaddr *dst; | | 4715 | const struct sockaddr *dst; |
4716 | | | 4716 | |
4717 | WG_TRACE(""); | | 4717 | WG_TRACE(""); |
4718 | | | 4718 | |
4719 | dst = iov[0].iov_base; | | 4719 | dst = iov[0].iov_base; |
4720 | | | 4720 | |
4721 | m = m_gethdr(M_NOWAIT, MT_DATA); | | 4721 | m = m_gethdr(M_DONTWAIT, MT_DATA); |
4722 | if (m == NULL) | | 4722 | if (m == NULL) |
4723 | return; | | 4723 | return; |
4724 | m->m_len = m->m_pkthdr.len = 0; | | 4724 | m->m_len = m->m_pkthdr.len = 0; |
4725 | m_copyback(m, 0, iov[1].iov_len, iov[1].iov_base); | | 4725 | m_copyback(m, 0, iov[1].iov_len, iov[1].iov_base); |
4726 | | | 4726 | |
4727 | WG_DLOG("iov_len=%lu\n", iov[1].iov_len); | | 4727 | WG_DLOG("iov_len=%lu\n", iov[1].iov_len); |
4728 | WG_DUMP_BUF(iov[1].iov_base, iov[1].iov_len); | | 4728 | WG_DUMP_BUF(iov[1].iov_base, iov[1].iov_len); |
4729 | | | 4729 | |
4730 | (void)wg_output(ifp, m, dst, NULL); | | 4730 | (void)wg_output(ifp, m, dst, NULL); |
4731 | } | | 4731 | } |
4732 | | | 4732 | |
4733 | /* | | 4733 | /* |
4734 | * Receive packets from a peer. | | 4734 | * Receive packets from a peer. |
4735 | */ | | 4735 | */ |
4736 | void | | 4736 | void |
4737 | rumpkern_wg_recv_peer(struct wg_softc *wg, struct iovec *iov, size_t iovlen) | | 4737 | rumpkern_wg_recv_peer(struct wg_softc *wg, struct iovec *iov, size_t iovlen) |
4738 | { | | 4738 | { |
4739 | struct mbuf *m; | | 4739 | struct mbuf *m; |
4740 | const struct sockaddr *src; | | 4740 | const struct sockaddr *src; |
4741 | | | 4741 | |
4742 | WG_TRACE(""); | | 4742 | WG_TRACE(""); |
4743 | | | 4743 | |
4744 | src = iov[0].iov_base; | | 4744 | src = iov[0].iov_base; |
4745 | | | 4745 | |
4746 | m = m_gethdr(M_NOWAIT, MT_DATA); | | 4746 | m = m_gethdr(M_DONTWAIT, MT_DATA); |
4747 | if (m == NULL) | | 4747 | if (m == NULL) |
4748 | return; | | 4748 | return; |
4749 | m->m_len = m->m_pkthdr.len = 0; | | 4749 | m->m_len = m->m_pkthdr.len = 0; |
4750 | m_copyback(m, 0, iov[1].iov_len, iov[1].iov_base); | | 4750 | m_copyback(m, 0, iov[1].iov_len, iov[1].iov_base); |
4751 | | | 4751 | |
4752 | WG_DLOG("iov_len=%lu\n", iov[1].iov_len); | | 4752 | WG_DLOG("iov_len=%lu\n", iov[1].iov_len); |
4753 | WG_DUMP_BUF(iov[1].iov_base, iov[1].iov_len); | | 4753 | WG_DUMP_BUF(iov[1].iov_base, iov[1].iov_len); |
4754 | | | 4754 | |
4755 | wg_handle_packet(wg, m, src); | | 4755 | wg_handle_packet(wg, m, src); |
4756 | } | | 4756 | } |
4757 | #endif /* WG_RUMPKERNEL */ | | 4757 | #endif /* WG_RUMPKERNEL */ |
4758 | | | 4758 | |
4759 | /* | | 4759 | /* |
4760 | * Module infrastructure | | 4760 | * Module infrastructure |
4761 | */ | | 4761 | */ |
4762 | #include "if_module.h" | | 4762 | #include "if_module.h" |
4763 | | | 4763 | |
4764 | IF_MODULE(MODULE_CLASS_DRIVER, wg, "") | | 4764 | IF_MODULE(MODULE_CLASS_DRIVER, wg, "") |