2 #include <vnet/adj/adj_midchain.h>
3 #include <vnet/udp/udp.h>
5 #include <wireguard/wireguard_messages.h>
6 #include <wireguard/wireguard_if.h>
7 #include <wireguard/wireguard.h>
9 /* pool of interfaces */
12 /* bitmap of Allocated WG_ITF instances */
13 static uword *wg_if_instances;
15 /* vector of interfaces key'd on their sw_if_index */
16 static index_t *wg_if_index_by_sw_if_index;
18 /* vector of interfaces key'd on their UDP port (in network order) */
19 index_t *wg_if_index_by_port;
22 format_wg_if_name (u8 * s, va_list * args)
24 u32 dev_instance = va_arg (*args, u32);
25 return format (s, "wg%d", dev_instance);
29 format_wg_if (u8 * s, va_list * args)
31 index_t wgii = va_arg (*args, u32);
32 wg_if_t *wgi = wg_if_get (wgii);
33 u8 key[NOISE_KEY_LEN_BASE64];
35 key_to_base64 (wgi->local.l_private, NOISE_PUBLIC_KEY_LEN, key);
37 s = format (s, "[%d] %U src:%U port:%d",
39 format_vnet_sw_if_index_name, vnet_get_main (),
40 wgi->sw_if_index, format_ip_address, &wgi->src_ip, wgi->port);
42 key_to_base64 (wgi->local.l_private, NOISE_PUBLIC_KEY_LEN, key);
44 s = format (s, " private-key:%s", key);
46 format (s, " %U", format_hex_bytes, wgi->local.l_private,
47 NOISE_PUBLIC_KEY_LEN);
49 key_to_base64 (wgi->local.l_public, NOISE_PUBLIC_KEY_LEN, key);
51 s = format (s, " public-key:%s", key);
54 format (s, " %U", format_hex_bytes, wgi->local.l_public,
55 NOISE_PUBLIC_KEY_LEN);
57 s = format (s, " mac-key: %U", format_hex_bytes,
58 &wgi->cookie_checker.cc_mac1_key, NOISE_PUBLIC_KEY_LEN);
64 wg_if_find_by_sw_if_index (u32 sw_if_index)
66 if (vec_len (wg_if_index_by_sw_if_index) <= sw_if_index)
68 u32 ti = wg_if_index_by_sw_if_index[sw_if_index];
75 static noise_remote_t *
76 wg_remote_get (uint8_t public[NOISE_PUBLIC_KEY_LEN])
78 wg_main_t *wmp = &wg_main;
79 wg_peer_t *peer = NULL;
82 pool_foreach (peer_iter, wmp->peers,
84 if (!memcmp (peer_iter->remote.r_public, public, NOISE_PUBLIC_KEY_LEN))
91 return peer ? &peer->remote : NULL;
95 wg_index_set (noise_remote_t * remote)
97 wg_main_t *wmp = &wg_main;
98 u32 rnd_seed = (u32) (vlib_time_now (wmp->vlib_main) * 1e6);
100 wg_index_table_add (&wmp->index_table, remote->r_peer_idx, rnd_seed);
105 wg_index_drop (uint32_t key)
107 wg_main_t *wmp = &wg_main;
108 wg_index_table_del (&wmp->index_table, key);
111 static clib_error_t *
112 wg_if_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
114 vnet_hw_interface_t *hi;
118 hi = vnet_get_hw_interface (vnm, hw_if_index);
119 hw_flags = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP ?
120 VNET_HW_INTERFACE_FLAG_LINK_UP : 0);
121 vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags);
123 wgii = wg_if_find_by_sw_if_index (hi->sw_if_index);
125 wg_if_peer_walk (wg_if_get (wgii), wg_peer_if_admin_state_change, NULL);
131 wg_if_update_adj (vnet_main_t * vnm, u32 sw_if_index, adj_index_t ai)
133 /* The peers manage the adjacencies */
138 VNET_DEVICE_CLASS (wg_if_device_class) = {
139 .name = "Wireguard Tunnel",
140 .format_device_name = format_wg_if_name,
141 .admin_up_down_function = wg_if_admin_up_down,
144 VNET_HW_INTERFACE_CLASS(wg_hw_interface_class) = {
146 .update_adjacency = wg_if_update_adj,
147 .flags = VNET_HW_INTERFACE_CLASS_FLAG_NBMA,
152 * Maintain a bitmap of allocated wg_if instance numbers.
154 #define WG_ITF_MAX_INSTANCE (16 * 1024)
157 wg_if_instance_alloc (u32 want)
160 * Check for dynamically allocated instance number.
166 bit = clib_bitmap_first_clear (wg_if_instances);
167 if (bit >= WG_ITF_MAX_INSTANCE)
171 wg_if_instances = clib_bitmap_set (wg_if_instances, bit, 1);
178 if (want >= WG_ITF_MAX_INSTANCE)
186 if (clib_bitmap_get (wg_if_instances, want))
192 * Grant allocation request.
194 wg_if_instances = clib_bitmap_set (wg_if_instances, want, 1);
200 wg_if_instance_free (u32 instance)
202 if (instance >= WG_ITF_MAX_INSTANCE)
207 if (clib_bitmap_get (wg_if_instances, instance) == 0)
212 wg_if_instances = clib_bitmap_set (wg_if_instances, instance, 0);
218 wg_if_create (u32 user_instance,
219 const u8 private_key[NOISE_PUBLIC_KEY_LEN],
220 u16 port, const ip_address_t * src_ip, u32 * sw_if_indexp)
222 vnet_main_t *vnm = vnet_get_main ();
223 u32 instance, hw_if_index;
224 vnet_hw_interface_t *hi;
227 ASSERT (sw_if_indexp);
229 *sw_if_indexp = (u32) ~ 0;
232 * Allocate a wg_if instance. Either select on dynamically
233 * or try to use the desired user_instance number.
235 instance = wg_if_instance_alloc (user_instance);
237 return VNET_API_ERROR_INVALID_REGISTRATION;
239 pool_get (wg_if_pool, wg_if);
241 /* tunnel index (or instance) */
242 u32 t_idx = wg_if - wg_if_pool;
244 wg_if->user_instance = instance;
245 if (~0 == wg_if->user_instance)
246 wg_if->user_instance = t_idx;
248 udp_register_dst_port (vlib_get_main (), port, wg_input_node.index, 1);
250 vec_validate_init_empty (wg_if_index_by_port, port, INDEX_INVALID);
251 wg_if_index_by_port[port] = wg_if - wg_if_pool;
256 struct noise_upcall upcall = {
257 .u_remote_get = wg_remote_get,
258 .u_index_set = wg_index_set,
259 .u_index_drop = wg_index_drop,
263 noise_local_init (&wg_if->local, &upcall);
264 noise_local_set_private (&wg_if->local, private_key);
265 cookie_checker_update (&wg_if->cookie_checker, wg_if->local.l_public);
267 hw_if_index = vnet_register_interface (vnm,
268 wg_if_device_class.index,
270 wg_hw_interface_class.index, t_idx);
272 hi = vnet_get_hw_interface (vnm, hw_if_index);
274 vec_validate_init_empty (wg_if_index_by_sw_if_index, hi->sw_if_index,
276 wg_if_index_by_sw_if_index[hi->sw_if_index] = t_idx;
278 ip_address_copy (&wg_if->src_ip, src_ip);
279 wg_if->sw_if_index = *sw_if_indexp = hi->sw_if_index;
285 wg_if_delete (u32 sw_if_index)
287 vnet_main_t *vnm = vnet_get_main ();
289 if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
290 return VNET_API_ERROR_INVALID_SW_IF_INDEX;
292 vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index);
293 if (hw == 0 || hw->dev_class_index != wg_if_device_class.index)
294 return VNET_API_ERROR_INVALID_VALUE;
297 wg_if = wg_if_get (wg_if_find_by_sw_if_index (sw_if_index));
299 return VNET_API_ERROR_INVALID_SW_IF_INDEX_2;
301 if (wg_if_instance_free (wg_if->user_instance) < 0)
302 return VNET_API_ERROR_INVALID_VALUE_2;
304 udp_unregister_dst_port (vlib_get_main (), wg_if->port, 1);
305 wg_if_index_by_port[wg_if->port] = INDEX_INVALID;
306 vnet_delete_hw_interface (vnm, hw->hw_if_index);
307 pool_put (wg_if_pool, wg_if);
313 wg_if_peer_add (wg_if_t * wgi, index_t peeri)
315 hash_set (wgi->peers, peeri, peeri);
317 if (1 == hash_elts (wgi->peers))
318 vnet_feature_enable_disable ("ip4-output", "wg-output-tun",
319 wgi->sw_if_index, 1, 0, 0);
323 wg_if_peer_remove (wg_if_t * wgi, index_t peeri)
325 hash_unset (wgi->peers, peeri);
327 if (0 == hash_elts (wgi->peers))
328 vnet_feature_enable_disable ("ip4-output", "wg-output-tun",
329 wgi->sw_if_index, 0, 0, 0);
333 wg_if_walk (wg_if_walk_cb_t fn, void *data)
338 pool_foreach_index (wgii, wg_if_pool,
340 if (WALK_STOP == fn(wgii, data))
347 wg_if_peer_walk (wg_if_t * wgi, wg_if_peer_walk_cb_t fn, void *data)
352 hash_foreach (peeri, val, wgi->peers,
354 if (WALK_STOP == fn(wgi, peeri, data))
362 wg_if_table_bind_v4 (ip4_main_t * im,
364 u32 sw_if_index, u32 new_fib_index, u32 old_fib_index)
368 wg_if = wg_if_get (wg_if_find_by_sw_if_index (sw_if_index));
372 wg_peer_table_bind_ctx_t ctx = {
374 .old_fib_index = old_fib_index,
375 .new_fib_index = new_fib_index,
378 wg_if_peer_walk (wg_if, wg_peer_if_table_change, &ctx);
382 wg_if_table_bind_v6 (ip6_main_t * im,
384 u32 sw_if_index, u32 new_fib_index, u32 old_fib_index)
388 wg_if = wg_if_get (wg_if_find_by_sw_if_index (sw_if_index));
392 wg_peer_table_bind_ctx_t ctx = {
394 .old_fib_index = old_fib_index,
395 .new_fib_index = new_fib_index,
398 wg_if_peer_walk (wg_if, wg_peer_if_table_change, &ctx);
401 static clib_error_t *
402 wg_if_module_init (vlib_main_t * vm)
405 ip4_table_bind_callback_t cb = {
406 .function = wg_if_table_bind_v4,
408 vec_add1 (ip4_main.table_bind_callbacks, cb);
411 ip6_table_bind_callback_t cb = {
412 .function = wg_if_table_bind_v6,
414 vec_add1 (ip6_main.table_bind_callbacks, cb);
421 VLIB_INIT_FUNCTION (wg_if_module_init) =
423 .runs_after = VLIB_INITS("ip_main_init"),
429 * fd.io coding-style-patch-verification: ON
432 * eval: (c-set-style "gnu")