Line data Source code
1 : /*
2 : * Copyright (c) 2020 Cisco and/or its affiliates.
3 : * Copyright (c) 2020 Doc.ai and/or its affiliates.
4 : * Licensed under the Apache License, Version 2.0 (the "License");
5 : * you may not use this file except in compliance with the License.
6 : * You may obtain a copy of the License at:
7 : *
8 : * http://www.apache.org/licenses/LICENSE-2.0
9 : *
10 : * Unless required by applicable law or agreed to in writing, software
11 : * distributed under the License is distributed on an "AS IS" BASIS,
12 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 : * See the License for the specific language governing permissions and
14 : * limitations under the License.
15 : */
16 :
17 : #include <vnet/adj/adj_midchain.h>
18 : #include <vnet/udp/udp.h>
19 :
20 : #include <wireguard/wireguard_messages.h>
21 : #include <wireguard/wireguard_if.h>
22 : #include <wireguard/wireguard.h>
23 : #include <wireguard/wireguard_peer.h>
24 :
25 : /* pool of interfaces */
26 : wg_if_t *wg_if_pool;
27 :
28 : /* bitmap of Allocated WG_ITF instances */
29 : static uword *wg_if_instances;
30 :
31 : /* vector of interfaces key'd on their sw_if_index */
32 : static index_t *wg_if_index_by_sw_if_index;
33 :
34 : /* vector of interfaces key'd on their UDP port (in network order) */
35 : index_t **wg_if_indexes_by_port;
36 :
37 : /* pool of ratelimit entries */
38 : static ratelimit_entry_t *wg_ratelimit_pool;
39 :
40 : static u8 *
41 82 : format_wg_if_name (u8 * s, va_list * args)
42 : {
43 82 : u32 dev_instance = va_arg (*args, u32);
44 82 : wg_if_t *wgi = wg_if_get (dev_instance);
45 82 : return format (s, "wg%d", wgi->user_instance);
46 : }
47 :
48 : u8 *
49 4 : format_wg_if (u8 * s, va_list * args)
50 : {
51 4 : index_t wgii = va_arg (*args, u32);
52 4 : wg_if_t *wgi = wg_if_get (wgii);
53 4 : noise_local_t *local = noise_local_get (wgi->local_idx);
54 : u8 key[NOISE_KEY_LEN_BASE64];
55 :
56 4 : s = format (s, "[%d] %U src:%U port:%d",
57 : wgii,
58 : format_vnet_sw_if_index_name, vnet_get_main (),
59 4 : wgi->sw_if_index, format_ip_address, &wgi->src_ip, wgi->port);
60 :
61 4 : key_to_base64 (local->l_private, NOISE_PUBLIC_KEY_LEN, key);
62 :
63 4 : s = format (s, " private-key:%s", key);
64 : s =
65 4 : format (s, " %U", format_hex_bytes, local->l_private,
66 : NOISE_PUBLIC_KEY_LEN);
67 :
68 4 : key_to_base64 (local->l_public, NOISE_PUBLIC_KEY_LEN, key);
69 :
70 4 : s = format (s, " public-key:%s", key);
71 :
72 : s =
73 4 : format (s, " %U", format_hex_bytes, local->l_public,
74 : NOISE_PUBLIC_KEY_LEN);
75 :
76 4 : s = format (s, " mac-key: %U", format_hex_bytes,
77 : &wgi->cookie_checker.cc_mac1_key, NOISE_PUBLIC_KEY_LEN);
78 :
79 4 : return (s);
80 : }
81 :
82 : index_t
83 856 : wg_if_find_by_sw_if_index (u32 sw_if_index)
84 : {
85 856 : if (vec_len (wg_if_index_by_sw_if_index) <= sw_if_index)
86 0 : return INDEX_INVALID;
87 856 : u32 ti = wg_if_index_by_sw_if_index[sw_if_index];
88 856 : if (ti == ~0)
89 0 : return INDEX_INVALID;
90 :
91 856 : return (ti);
92 : }
93 :
94 : static walk_rc_t
95 813 : wg_if_find_peer_by_public_key (index_t peeri, void *data)
96 : {
97 813 : uint8_t *public = data;
98 813 : wg_peer_t *peer = wg_peer_get (peeri);
99 :
100 813 : if (!memcmp (peer->remote.r_public, public, NOISE_PUBLIC_KEY_LEN))
101 627 : return (WALK_STOP);
102 186 : return (WALK_CONTINUE);
103 : }
104 :
105 : static noise_remote_t *
106 627 : wg_remote_get (const uint8_t public[NOISE_PUBLIC_KEY_LEN])
107 : {
108 : index_t peeri;
109 :
110 627 : peeri = wg_peer_walk (wg_if_find_peer_by_public_key, (void *) public);
111 :
112 627 : if (INDEX_INVALID != peeri)
113 627 : return &wg_peer_get (peeri)->remote;
114 :
115 0 : return NULL;
116 : }
117 :
118 : static uint32_t
119 214 : wg_index_set (vlib_main_t *vm, noise_remote_t *remote)
120 : {
121 214 : wg_main_t *wmp = &wg_main;
122 214 : u32 rnd_seed = (u32) (vlib_time_now (wmp->vlib_main) * 1e6);
123 : u32 ret =
124 214 : wg_index_table_add (vm, &wmp->index_table, remote->r_peer_idx, rnd_seed);
125 214 : return ret;
126 : }
127 :
128 : static void
129 214 : wg_index_drop (vlib_main_t *vm, uint32_t key)
130 : {
131 214 : wg_main_t *wmp = &wg_main;
132 214 : wg_index_table_del (vm, &wmp->index_table, key);
133 214 : }
134 :
135 : static clib_error_t *
136 164 : wg_if_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
137 : {
138 : vnet_hw_interface_t *hi;
139 : index_t wgii;
140 : u32 hw_flags;
141 :
142 164 : hi = vnet_get_hw_interface (vnm, hw_if_index);
143 164 : hw_flags = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP ?
144 : VNET_HW_INTERFACE_FLAG_LINK_UP : 0);
145 164 : vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags);
146 :
147 164 : wgii = wg_if_find_by_sw_if_index (hi->sw_if_index);
148 :
149 164 : wg_if_peer_walk (wg_if_get (wgii), wg_peer_if_admin_state_change, NULL);
150 :
151 164 : return (NULL);
152 : }
153 :
154 : void
155 116 : wg_if_update_adj (vnet_main_t * vnm, u32 sw_if_index, adj_index_t ai)
156 : {
157 : index_t wgii;
158 :
159 : /* Convert any neighbour adjacency that has a next-hop reachable through
160 : * the wg interface into a midchain. This is to avoid sending ARP/ND to
161 : * resolve the next-hop address via the wg interface. Then, if one of the
162 : * peers has matching prefix among allowed prefixes, the midchain will be
163 : * updated to the corresponding one.
164 : */
165 116 : adj_nbr_midchain_update_rewrite (ai, NULL, NULL, ADJ_FLAG_NONE, NULL);
166 :
167 116 : wgii = wg_if_find_by_sw_if_index (sw_if_index);
168 116 : wg_if_peer_walk (wg_if_get (wgii), wg_peer_if_adj_change, &ai);
169 116 : }
170 :
171 :
172 : /* *INDENT-OFF* */
173 1119 : VNET_DEVICE_CLASS (wg_if_device_class) = {
174 : .name = "Wireguard Tunnel",
175 : .format_device_name = format_wg_if_name,
176 : .admin_up_down_function = wg_if_admin_up_down,
177 : };
178 :
179 1119 : VNET_HW_INTERFACE_CLASS(wg_hw_interface_class) = {
180 : .name = "Wireguard",
181 : .update_adjacency = wg_if_update_adj,
182 : .flags = VNET_HW_INTERFACE_CLASS_FLAG_NBMA,
183 : };
184 : /* *INDENT-ON* */
185 :
186 : /*
187 : * Maintain a bitmap of allocated wg_if instance numbers.
188 : */
189 : #define WG_ITF_MAX_INSTANCE (16 * 1024)
190 :
191 : static u32
192 82 : wg_if_instance_alloc (u32 want)
193 : {
194 : /*
195 : * Check for dynamically allocated instance number.
196 : */
197 82 : if (~0 == want)
198 : {
199 : u32 bit;
200 :
201 82 : bit = clib_bitmap_first_clear (wg_if_instances);
202 82 : if (bit >= WG_ITF_MAX_INSTANCE)
203 : {
204 0 : return ~0;
205 : }
206 82 : wg_if_instances = clib_bitmap_set (wg_if_instances, bit, 1);
207 82 : return bit;
208 : }
209 :
210 : /*
211 : * In range?
212 : */
213 0 : if (want >= WG_ITF_MAX_INSTANCE)
214 : {
215 0 : return ~0;
216 : }
217 :
218 : /*
219 : * Already in use?
220 : */
221 0 : if (clib_bitmap_get (wg_if_instances, want))
222 : {
223 0 : return ~0;
224 : }
225 :
226 : /*
227 : * Grant allocation request.
228 : */
229 0 : wg_if_instances = clib_bitmap_set (wg_if_instances, want, 1);
230 :
231 0 : return want;
232 : }
233 :
234 : static int
235 82 : wg_if_instance_free (u32 instance)
236 : {
237 82 : if (instance >= WG_ITF_MAX_INSTANCE)
238 : {
239 0 : return -1;
240 : }
241 :
242 82 : if (clib_bitmap_get (wg_if_instances, instance) == 0)
243 : {
244 0 : return -1;
245 : }
246 :
247 82 : wg_if_instances = clib_bitmap_set (wg_if_instances, instance, 0);
248 82 : return 0;
249 : }
250 :
251 :
252 : int
253 82 : wg_if_create (u32 user_instance,
254 : const u8 private_key[NOISE_PUBLIC_KEY_LEN],
255 : u16 port, const ip_address_t * src_ip, u32 * sw_if_indexp)
256 : {
257 82 : vnet_main_t *vnm = vnet_get_main ();
258 : u32 instance, hw_if_index;
259 : vnet_hw_interface_t *hi;
260 : wg_if_t *wg_if;
261 : noise_local_t *local;
262 :
263 82 : ASSERT (sw_if_indexp);
264 :
265 82 : *sw_if_indexp = (u32) ~ 0;
266 :
267 : /*
268 : * Allocate a wg_if instance. Either select on dynamically
269 : * or try to use the desired user_instance number.
270 : */
271 82 : instance = wg_if_instance_alloc (user_instance);
272 82 : if (instance == ~0)
273 0 : return VNET_API_ERROR_INVALID_REGISTRATION;
274 :
275 : /* *INDENT-OFF* */
276 82 : struct noise_upcall upcall = {
277 : .u_remote_get = wg_remote_get,
278 : .u_index_set = wg_index_set,
279 : .u_index_drop = wg_index_drop,
280 : };
281 : /* *INDENT-ON* */
282 :
283 82 : pool_get (noise_local_pool, local);
284 :
285 82 : noise_local_init (local, &upcall);
286 82 : if (!noise_local_set_private (local, private_key))
287 : {
288 0 : pool_put (noise_local_pool, local);
289 0 : wg_if_instance_free (instance);
290 0 : return VNET_API_ERROR_INVALID_REGISTRATION;
291 : }
292 :
293 82 : pool_get_zero (wg_if_pool, wg_if);
294 :
295 : /* tunnel index (or instance) */
296 82 : u32 t_idx = wg_if - wg_if_pool;
297 :
298 82 : wg_if->user_instance = instance;
299 82 : if (~0 == wg_if->user_instance)
300 0 : wg_if->user_instance = t_idx;
301 :
302 37610 : vec_validate_init_empty (wg_if_indexes_by_port, port, NULL);
303 82 : if (vec_len (wg_if_indexes_by_port[port]) == 0)
304 : {
305 79 : udp_register_dst_port (vlib_get_main (), port, wg4_input_node.index,
306 : UDP_IP4);
307 79 : udp_register_dst_port (vlib_get_main (), port, wg6_input_node.index,
308 : UDP_IP6);
309 : }
310 :
311 82 : vec_add1 (wg_if_indexes_by_port[port], t_idx);
312 :
313 82 : wg_if->port = port;
314 82 : wg_if->local_idx = local - noise_local_pool;
315 82 : cookie_checker_init (&wg_if->cookie_checker, wg_ratelimit_pool);
316 82 : cookie_checker_update (&wg_if->cookie_checker, local->l_public);
317 :
318 82 : hw_if_index = vnet_register_interface (vnm,
319 : wg_if_device_class.index,
320 : t_idx,
321 : wg_hw_interface_class.index, t_idx);
322 :
323 82 : hi = vnet_get_hw_interface (vnm, hw_if_index);
324 :
325 100 : vec_validate_init_empty (wg_if_index_by_sw_if_index, hi->sw_if_index,
326 : INDEX_INVALID);
327 82 : wg_if_index_by_sw_if_index[hi->sw_if_index] = t_idx;
328 :
329 82 : ip_address_copy (&wg_if->src_ip, src_ip);
330 82 : wg_if->sw_if_index = *sw_if_indexp = hi->sw_if_index;
331 82 : vnet_set_interface_l3_output_node (vnm->vlib_main, hi->sw_if_index,
332 : (u8 *) "tunnel-output");
333 :
334 82 : return 0;
335 : }
336 :
337 : int
338 82 : wg_if_delete (u32 sw_if_index)
339 : {
340 82 : vnet_main_t *vnm = vnet_get_main ();
341 :
342 82 : if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
343 0 : return VNET_API_ERROR_INVALID_SW_IF_INDEX;
344 :
345 82 : vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index);
346 82 : if (hw == 0 || hw->dev_class_index != wg_if_device_class.index)
347 0 : return VNET_API_ERROR_INVALID_VALUE;
348 :
349 : wg_if_t *wg_if;
350 82 : index_t wgii = wg_if_find_by_sw_if_index (sw_if_index);
351 82 : wg_if = wg_if_get (wgii);
352 82 : if (NULL == wg_if)
353 0 : return VNET_API_ERROR_INVALID_SW_IF_INDEX_2;
354 :
355 82 : if (wg_if_instance_free (wg_if->user_instance) < 0)
356 0 : return VNET_API_ERROR_INVALID_VALUE_2;
357 :
358 : // Remove peers before interface deletion
359 82 : wg_if_peer_walk (wg_if, wg_peer_if_delete, NULL);
360 :
361 82 : hash_free (wg_if->peers);
362 :
363 : index_t *ii;
364 82 : index_t *ifs = wg_if_indexes_get_by_port (wg_if->port);
365 85 : vec_foreach (ii, ifs)
366 : {
367 84 : if (*ii == wgii)
368 : {
369 81 : vec_del1 (ifs, ifs - ii);
370 81 : break;
371 : }
372 : }
373 82 : if (vec_len (ifs) == 0)
374 : {
375 78 : udp_unregister_dst_port (vlib_get_main (), wg_if->port, 1);
376 78 : udp_unregister_dst_port (vlib_get_main (), wg_if->port, 0);
377 : }
378 :
379 82 : cookie_checker_deinit (&wg_if->cookie_checker);
380 :
381 82 : vnet_reset_interface_l3_output_node (vnm->vlib_main, sw_if_index);
382 82 : vnet_delete_hw_interface (vnm, hw->hw_if_index);
383 82 : pool_put_index (noise_local_pool, wg_if->local_idx);
384 82 : pool_put (wg_if_pool, wg_if);
385 :
386 82 : return 0;
387 : }
388 :
389 : void
390 146 : wg_if_peer_add (wg_if_t * wgi, index_t peeri)
391 : {
392 146 : hash_set (wgi->peers, peeri, peeri);
393 :
394 146 : if (1 == hash_elts (wgi->peers))
395 : {
396 80 : vnet_feature_enable_disable ("ip4-output", "wg4-output-tun",
397 : wgi->sw_if_index, 1, 0, 0);
398 80 : vnet_feature_enable_disable ("ip6-output", "wg6-output-tun",
399 : wgi->sw_if_index, 1, 0, 0);
400 : }
401 146 : }
402 :
403 : void
404 146 : wg_if_peer_remove (wg_if_t * wgi, index_t peeri)
405 : {
406 146 : hash_unset (wgi->peers, peeri);
407 :
408 146 : if (0 == hash_elts (wgi->peers))
409 : {
410 80 : vnet_feature_enable_disable ("ip4-output", "wg4-output-tun",
411 : wgi->sw_if_index, 0, 0, 0);
412 80 : vnet_feature_enable_disable ("ip6-output", "wg6-output-tun",
413 : wgi->sw_if_index, 0, 0, 0);
414 : }
415 146 : }
416 :
417 : void
418 84 : wg_if_walk (wg_if_walk_cb_t fn, void *data)
419 : {
420 : index_t wgii;
421 :
422 : /* *INDENT-OFF* */
423 88 : pool_foreach_index (wgii, wg_if_pool)
424 : {
425 4 : if (WALK_STOP == fn(wgii, data))
426 0 : break;
427 : }
428 : /* *INDENT-ON* */
429 84 : }
430 :
431 : index_t
432 362 : wg_if_peer_walk (wg_if_t * wgi, wg_if_peer_walk_cb_t fn, void *data)
433 : {
434 : index_t peeri, val;
435 :
436 : /* *INDENT-OFF* */
437 12732 : hash_foreach (peeri, val, wgi->peers, {
438 : if (WALK_STOP == fn (peeri, data))
439 : return peeri;
440 : });
441 : /* *INDENT-ON* */
442 :
443 322 : return INDEX_INVALID;
444 : }
445 :
446 : /*
447 : * fd.io coding-style-patch-verification: ON
448 : *
449 : * Local Variables:
450 : * eval: (c-set-style "gnu")
451 : * End:
452 : */
|