wireguard: add handshake rate limiting support
[vpp.git] / src / plugins / wireguard / wireguard_if.c
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 format_wg_if_name (u8 * s, va_list * args)
42 {
43   u32 dev_instance = va_arg (*args, u32);
44   wg_if_t *wgi = wg_if_get (dev_instance);
45   return format (s, "wg%d", wgi->user_instance);
46 }
47
48 u8 *
49 format_wg_if (u8 * s, va_list * args)
50 {
51   index_t wgii = va_arg (*args, u32);
52   wg_if_t *wgi = wg_if_get (wgii);
53   noise_local_t *local = noise_local_get (wgi->local_idx);
54   u8 key[NOISE_KEY_LEN_BASE64];
55
56   s = format (s, "[%d] %U src:%U port:%d",
57               wgii,
58               format_vnet_sw_if_index_name, vnet_get_main (),
59               wgi->sw_if_index, format_ip_address, &wgi->src_ip, wgi->port);
60
61   key_to_base64 (local->l_private, NOISE_PUBLIC_KEY_LEN, key);
62
63   s = format (s, " private-key:%s", key);
64   s =
65     format (s, " %U", format_hex_bytes, local->l_private,
66             NOISE_PUBLIC_KEY_LEN);
67
68   key_to_base64 (local->l_public, NOISE_PUBLIC_KEY_LEN, key);
69
70   s = format (s, " public-key:%s", key);
71
72   s =
73     format (s, " %U", format_hex_bytes, local->l_public,
74             NOISE_PUBLIC_KEY_LEN);
75
76   s = format (s, " mac-key: %U", format_hex_bytes,
77               &wgi->cookie_checker.cc_mac1_key, NOISE_PUBLIC_KEY_LEN);
78
79   return (s);
80 }
81
82 index_t
83 wg_if_find_by_sw_if_index (u32 sw_if_index)
84 {
85   if (vec_len (wg_if_index_by_sw_if_index) <= sw_if_index)
86     return INDEX_INVALID;
87   u32 ti = wg_if_index_by_sw_if_index[sw_if_index];
88   if (ti == ~0)
89     return INDEX_INVALID;
90
91   return (ti);
92 }
93
94 static walk_rc_t
95 wg_if_find_peer_by_public_key (index_t peeri, void *data)
96 {
97   uint8_t *public = data;
98   wg_peer_t *peer = wg_peer_get (peeri);
99
100   if (!memcmp (peer->remote.r_public, public, NOISE_PUBLIC_KEY_LEN))
101     return (WALK_STOP);
102   return (WALK_CONTINUE);
103 }
104
105 static noise_remote_t *
106 wg_remote_get (const uint8_t public[NOISE_PUBLIC_KEY_LEN])
107 {
108   index_t peeri;
109
110   peeri = wg_peer_walk (wg_if_find_peer_by_public_key, (void *) public);
111
112   if (INDEX_INVALID != peeri)
113     return &wg_peer_get (peeri)->remote;
114
115   return NULL;
116 }
117
118 static uint32_t
119 wg_index_set (noise_remote_t * remote)
120 {
121   wg_main_t *wmp = &wg_main;
122   u32 rnd_seed = (u32) (vlib_time_now (wmp->vlib_main) * 1e6);
123   u32 ret =
124     wg_index_table_add (&wmp->index_table, remote->r_peer_idx, rnd_seed);
125   return ret;
126 }
127
128 static void
129 wg_index_drop (uint32_t key)
130 {
131   wg_main_t *wmp = &wg_main;
132   wg_index_table_del (&wmp->index_table, key);
133 }
134
135 static clib_error_t *
136 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   hi = vnet_get_hw_interface (vnm, hw_if_index);
143   hw_flags = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP ?
144               VNET_HW_INTERFACE_FLAG_LINK_UP : 0);
145   vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags);
146
147   wgii = wg_if_find_by_sw_if_index (hi->sw_if_index);
148
149   wg_if_peer_walk (wg_if_get (wgii), wg_peer_if_admin_state_change, NULL);
150
151   return (NULL);
152 }
153
154 void
155 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   adj_nbr_midchain_update_rewrite (ai, NULL, NULL, ADJ_FLAG_NONE, NULL);
166
167   wgii = wg_if_find_by_sw_if_index (sw_if_index);
168   wg_if_peer_walk (wg_if_get (wgii), wg_peer_if_adj_change, &ai);
169 }
170
171
172 /* *INDENT-OFF* */
173 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 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 wg_if_instance_alloc (u32 want)
193 {
194   /*
195    * Check for dynamically allocated instance number.
196    */
197   if (~0 == want)
198     {
199       u32 bit;
200
201       bit = clib_bitmap_first_clear (wg_if_instances);
202       if (bit >= WG_ITF_MAX_INSTANCE)
203         {
204           return ~0;
205         }
206       wg_if_instances = clib_bitmap_set (wg_if_instances, bit, 1);
207       return bit;
208     }
209
210   /*
211    * In range?
212    */
213   if (want >= WG_ITF_MAX_INSTANCE)
214     {
215       return ~0;
216     }
217
218   /*
219    * Already in use?
220    */
221   if (clib_bitmap_get (wg_if_instances, want))
222     {
223       return ~0;
224     }
225
226   /*
227    * Grant allocation request.
228    */
229   wg_if_instances = clib_bitmap_set (wg_if_instances, want, 1);
230
231   return want;
232 }
233
234 static int
235 wg_if_instance_free (u32 instance)
236 {
237   if (instance >= WG_ITF_MAX_INSTANCE)
238     {
239       return -1;
240     }
241
242   if (clib_bitmap_get (wg_if_instances, instance) == 0)
243     {
244       return -1;
245     }
246
247   wg_if_instances = clib_bitmap_set (wg_if_instances, instance, 0);
248   return 0;
249 }
250
251
252 int
253 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   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   ASSERT (sw_if_indexp);
264
265   *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   instance = wg_if_instance_alloc (user_instance);
272   if (instance == ~0)
273     return VNET_API_ERROR_INVALID_REGISTRATION;
274
275   /* *INDENT-OFF* */
276   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   pool_get (noise_local_pool, local);
284
285   noise_local_init (local, &upcall);
286   if (!noise_local_set_private (local, private_key))
287     {
288       pool_put (noise_local_pool, local);
289       wg_if_instance_free (instance);
290       return VNET_API_ERROR_INVALID_REGISTRATION;
291     }
292
293   pool_get_zero (wg_if_pool, wg_if);
294
295   /* tunnel index (or instance) */
296   u32 t_idx = wg_if - wg_if_pool;
297
298   wg_if->user_instance = instance;
299   if (~0 == wg_if->user_instance)
300     wg_if->user_instance = t_idx;
301
302   vec_validate_init_empty (wg_if_indexes_by_port, port, NULL);
303   if (vec_len (wg_if_indexes_by_port[port]) == 0)
304     {
305       udp_register_dst_port (vlib_get_main (), port, wg4_input_node.index,
306                              UDP_IP4);
307       udp_register_dst_port (vlib_get_main (), port, wg6_input_node.index,
308                              UDP_IP6);
309     }
310
311   vec_add1 (wg_if_indexes_by_port[port], t_idx);
312
313   wg_if->port = port;
314   wg_if->local_idx = local - noise_local_pool;
315   cookie_checker_init (&wg_if->cookie_checker, wg_ratelimit_pool);
316   cookie_checker_update (&wg_if->cookie_checker, local->l_public);
317
318   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   hi = vnet_get_hw_interface (vnm, hw_if_index);
324
325   vec_validate_init_empty (wg_if_index_by_sw_if_index, hi->sw_if_index,
326                            INDEX_INVALID);
327   wg_if_index_by_sw_if_index[hi->sw_if_index] = t_idx;
328
329   ip_address_copy (&wg_if->src_ip, src_ip);
330   wg_if->sw_if_index = *sw_if_indexp = hi->sw_if_index;
331   vnet_set_interface_l3_output_node (vnm->vlib_main, hi->sw_if_index,
332                                      (u8 *) "tunnel-output");
333
334   return 0;
335 }
336
337 int
338 wg_if_delete (u32 sw_if_index)
339 {
340   vnet_main_t *vnm = vnet_get_main ();
341
342   if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
343     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
344
345   vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index);
346   if (hw == 0 || hw->dev_class_index != wg_if_device_class.index)
347     return VNET_API_ERROR_INVALID_VALUE;
348
349   wg_if_t *wg_if;
350   index_t wgii = wg_if_find_by_sw_if_index (sw_if_index);
351   wg_if = wg_if_get (wgii);
352   if (NULL == wg_if)
353     return VNET_API_ERROR_INVALID_SW_IF_INDEX_2;
354
355   if (wg_if_instance_free (wg_if->user_instance) < 0)
356     return VNET_API_ERROR_INVALID_VALUE_2;
357
358   // Remove peers before interface deletion
359   wg_if_peer_walk (wg_if, wg_peer_if_delete, NULL);
360
361   hash_free (wg_if->peers);
362
363   index_t *ii;
364   index_t *ifs = wg_if_indexes_get_by_port (wg_if->port);
365   vec_foreach (ii, ifs)
366     {
367       if (*ii == wgii)
368         {
369           vec_del1 (ifs, ifs - ii);
370           break;
371         }
372     }
373   if (vec_len (ifs) == 0)
374     {
375       udp_unregister_dst_port (vlib_get_main (), wg_if->port, 1);
376       udp_unregister_dst_port (vlib_get_main (), wg_if->port, 0);
377     }
378
379   cookie_checker_deinit (&wg_if->cookie_checker);
380
381   vnet_reset_interface_l3_output_node (vnm->vlib_main, sw_if_index);
382   vnet_delete_hw_interface (vnm, hw->hw_if_index);
383   pool_put_index (noise_local_pool, wg_if->local_idx);
384   pool_put (wg_if_pool, wg_if);
385
386   return 0;
387 }
388
389 void
390 wg_if_peer_add (wg_if_t * wgi, index_t peeri)
391 {
392   hash_set (wgi->peers, peeri, peeri);
393
394   if (1 == hash_elts (wgi->peers))
395     {
396       vnet_feature_enable_disable ("ip4-output", "wg4-output-tun",
397                                    wgi->sw_if_index, 1, 0, 0);
398       vnet_feature_enable_disable ("ip6-output", "wg6-output-tun",
399                                    wgi->sw_if_index, 1, 0, 0);
400     }
401 }
402
403 void
404 wg_if_peer_remove (wg_if_t * wgi, index_t peeri)
405 {
406   hash_unset (wgi->peers, peeri);
407
408   if (0 == hash_elts (wgi->peers))
409     {
410       vnet_feature_enable_disable ("ip4-output", "wg4-output-tun",
411                                    wgi->sw_if_index, 0, 0, 0);
412       vnet_feature_enable_disable ("ip6-output", "wg6-output-tun",
413                                    wgi->sw_if_index, 0, 0, 0);
414     }
415 }
416
417 void
418 wg_if_walk (wg_if_walk_cb_t fn, void *data)
419 {
420   index_t wgii;
421
422   /* *INDENT-OFF* */
423   pool_foreach_index (wgii, wg_if_pool)
424   {
425     if (WALK_STOP == fn(wgii, data))
426       break;
427   }
428   /* *INDENT-ON* */
429 }
430
431 index_t
432 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   hash_foreach (peeri, val, wgi->peers, {
438     if (WALK_STOP == fn (peeri, data))
439       return peeri;
440   });
441   /* *INDENT-ON* */
442
443   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  */