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