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