wireguard: coverity fixes
[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
248   /* *INDENT-OFF* */
249   struct noise_upcall upcall =  {
250     .u_remote_get = wg_remote_get,
251     .u_index_set = wg_index_set,
252     .u_index_drop = wg_index_drop,
253   };
254   /* *INDENT-ON* */
255
256   noise_local_init (&wg_if->local, &upcall);
257   noise_local_set_private (&wg_if->local, private_key);
258   cookie_checker_update (&wg_if->cookie_checker, wg_if->local.l_public);
259
260   hw_if_index = vnet_register_interface (vnm,
261                                          wg_if_device_class.index,
262                                          t_idx,
263                                          wg_hw_interface_class.index, t_idx);
264
265   hi = vnet_get_hw_interface (vnm, hw_if_index);
266
267   vec_validate_init_empty (wg_if_index_by_sw_if_index, hi->sw_if_index,
268                            INDEX_INVALID);
269   wg_if_index_by_sw_if_index[hi->sw_if_index] = t_idx;
270
271   ip_address_copy (&wg_if->src_ip, src_ip);
272   wg_if->sw_if_index = *sw_if_indexp = hi->sw_if_index;
273
274   return 0;
275 }
276
277 int
278 wg_if_delete (u32 sw_if_index)
279 {
280   vnet_main_t *vnm = vnet_get_main ();
281
282   if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
283     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
284
285   vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index);
286   if (hw == 0 || hw->dev_class_index != wg_if_device_class.index)
287     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
288
289   wg_if_t *wg_if;
290   wg_if = wg_if_get (wg_if_find_by_sw_if_index (sw_if_index));
291   if (NULL == wg_if)
292     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
293
294   if (wg_if_instance_free (hw->dev_instance) < 0)
295     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
296
297   wg_if_index_by_port[wg_if->port] = INDEX_INVALID;
298   vnet_delete_hw_interface (vnm, hw->hw_if_index);
299   pool_put (wg_if_pool, wg_if);
300
301   return 0;
302 }
303
304 void
305 wg_if_peer_add (wg_if_t * wgi, index_t peeri)
306 {
307   hash_set (wgi->peers, peeri, peeri);
308
309   if (1 == hash_elts (wgi->peers))
310     vnet_feature_enable_disable ("ip4-output", "wg-output-tun",
311                                  wgi->sw_if_index, 1, 0, 0);
312 }
313
314 void
315 wg_if_peer_remove (wg_if_t * wgi, index_t peeri)
316 {
317   hash_unset (wgi->peers, peeri);
318
319   if (0 == hash_elts (wgi->peers))
320     vnet_feature_enable_disable ("ip4-output", "wg-output-tun",
321                                  wgi->sw_if_index, 0, 0, 0);
322 }
323
324 void
325 wg_if_walk (wg_if_walk_cb_t fn, void *data)
326 {
327   index_t wgii;
328
329   /* *INDENT-OFF* */
330   pool_foreach_index (wgii, wg_if_pool,
331   {
332     if (WALK_STOP == fn(wgii, data))
333       break;
334   });
335   /* *INDENT-ON* */
336 }
337
338 void
339 wg_if_peer_walk (wg_if_t * wgi, wg_if_peer_walk_cb_t fn, void *data)
340 {
341   index_t peeri, val;
342
343   /* *INDENT-OFF* */
344   hash_foreach (peeri, val, wgi->peers,
345   {
346     if (WALK_STOP == fn(wgi, peeri, data))
347       break;
348   });
349   /* *INDENT-ON* */
350 }
351
352
353 static void
354 wg_if_table_bind_v4 (ip4_main_t * im,
355                      uword opaque,
356                      u32 sw_if_index, u32 new_fib_index, u32 old_fib_index)
357 {
358   wg_if_t *wg_if;
359
360   wg_if = wg_if_get (wg_if_find_by_sw_if_index (sw_if_index));
361   if (NULL == wg_if)
362     return;
363
364   wg_peer_table_bind_ctx_t ctx = {
365     .af = AF_IP4,
366     .old_fib_index = old_fib_index,
367     .new_fib_index = new_fib_index,
368   };
369
370   wg_if_peer_walk (wg_if, wg_peer_if_table_change, &ctx);
371 }
372
373 static void
374 wg_if_table_bind_v6 (ip6_main_t * im,
375                      uword opaque,
376                      u32 sw_if_index, u32 new_fib_index, u32 old_fib_index)
377 {
378   wg_if_t *wg_if;
379
380   wg_if = wg_if_get (wg_if_find_by_sw_if_index (sw_if_index));
381   if (NULL == wg_if)
382     return;
383
384   wg_peer_table_bind_ctx_t ctx = {
385     .af = AF_IP6,
386     .old_fib_index = old_fib_index,
387     .new_fib_index = new_fib_index,
388   };
389
390   wg_if_peer_walk (wg_if, wg_peer_if_table_change, &ctx);
391 }
392
393 static clib_error_t *
394 wg_if_module_init (vlib_main_t * vm)
395 {
396   {
397     ip4_table_bind_callback_t cb = {
398       .function = wg_if_table_bind_v4,
399     };
400     vec_add1 (ip4_main.table_bind_callbacks, cb);
401   }
402   {
403     ip6_table_bind_callback_t cb = {
404       .function = wg_if_table_bind_v6,
405     };
406     vec_add1 (ip6_main.table_bind_callbacks, cb);
407   }
408
409   return (NULL);
410 }
411
412 /* *INDENT-OFF* */
413 VLIB_INIT_FUNCTION (wg_if_module_init) =
414 {
415   .runs_after = VLIB_INITS("ip_main_init"),
416 };
417 /* *INDENT-ON* */
418
419
420 /*
421  * fd.io coding-style-patch-verification: ON
422  *
423  * Local Variables:
424  * eval: (c-set-style "gnu")
425  * End:
426  */