FD.io VPP  v21.06-1-gbb7418cf9
Vector Packet Processing
wireguard_if.c
Go to the documentation of this file.
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 
21 #include <wireguard/wireguard_if.h>
22 #include <wireguard/wireguard.h>
24 
25 /* pool of interfaces */
27 
28 /* bitmap of Allocated WG_ITF instances */
30 
31 /* vector of interfaces key'd on their sw_if_index */
33 
34 /* vector of interfaces key'd on their UDP port (in network order) */
36 
37 static u8 *
38 format_wg_if_name (u8 * s, va_list * args)
39 {
40  u32 dev_instance = va_arg (*args, u32);
41  return format (s, "wg%d", dev_instance);
42 }
43 
44 u8 *
45 format_wg_if (u8 * s, va_list * args)
46 {
47  index_t wgii = va_arg (*args, u32);
48  wg_if_t *wgi = wg_if_get (wgii);
49  noise_local_t *local = noise_local_get (wgi->local_idx);
51 
52 
53  s = format (s, "[%d] %U src:%U port:%d",
54  wgii,
56  wgi->sw_if_index, format_ip_address, &wgi->src_ip, wgi->port);
57 
59 
60  s = format (s, " private-key:%s", key);
61  s =
62  format (s, " %U", format_hex_bytes, local->l_private,
64 
66 
67  s = format (s, " public-key:%s", key);
68 
69  s =
70  format (s, " %U", format_hex_bytes, local->l_public,
72 
73  s = format (s, " mac-key: %U", format_hex_bytes,
75 
76  return (s);
77 }
78 
79 index_t
81 {
82  if (vec_len (wg_if_index_by_sw_if_index) <= sw_if_index)
83  return INDEX_INVALID;
85  if (ti == ~0)
86  return INDEX_INVALID;
87 
88  return (ti);
89 }
90 
91 static walk_rc_t
93 {
94  uint8_t *public = data;
95  wg_peer_t *peer = wg_peer_get (peeri);
96 
97  if (!memcmp (peer->remote.r_public, public, NOISE_PUBLIC_KEY_LEN))
98  return (WALK_STOP);
99  return (WALK_CONTINUE);
100 }
101 
102 static noise_remote_t *
103 wg_remote_get (const uint8_t public[NOISE_PUBLIC_KEY_LEN])
104 {
105  index_t peeri;
106 
107  peeri = wg_peer_walk (wg_if_find_peer_by_public_key, (void *) public);
108 
109  if (INDEX_INVALID != peeri)
110  return &wg_peer_get (peeri)->remote;
111 
112  return NULL;
113 }
114 
115 static uint32_t
117 {
118  wg_main_t *wmp = &wg_main;
119  u32 rnd_seed = (u32) (vlib_time_now (wmp->vlib_main) * 1e6);
120  u32 ret =
121  wg_index_table_add (&wmp->index_table, remote->r_peer_idx, rnd_seed);
122  return ret;
123 }
124 
125 static void
126 wg_index_drop (uint32_t key)
127 {
128  wg_main_t *wmp = &wg_main;
129  wg_index_table_del (&wmp->index_table, key);
130 }
131 
132 static clib_error_t *
134 {
136  index_t wgii;
137  u32 hw_flags;
138 
139  hi = vnet_get_hw_interface (vnm, hw_if_index);
140  hw_flags = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP ?
142  vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags);
143 
145 
147 
148  return (NULL);
149 }
150 
151 void
153 {
154  /* The peers manage the adjacencies */
155 }
156 
157 
158 /* *INDENT-OFF* */
159 VNET_DEVICE_CLASS (wg_if_device_class) = {
160  .name = "Wireguard Tunnel",
161  .format_device_name = format_wg_if_name,
162  .admin_up_down_function = wg_if_admin_up_down,
163 };
164 
165 VNET_HW_INTERFACE_CLASS(wg_hw_interface_class) = {
166  .name = "Wireguard",
167  .update_adjacency = wg_if_update_adj,
169 };
170 /* *INDENT-ON* */
171 
172 /*
173  * Maintain a bitmap of allocated wg_if instance numbers.
174  */
175 #define WG_ITF_MAX_INSTANCE (16 * 1024)
176 
177 static u32
179 {
180  /*
181  * Check for dynamically allocated instance number.
182  */
183  if (~0 == want)
184  {
185  u32 bit;
186 
188  if (bit >= WG_ITF_MAX_INSTANCE)
189  {
190  return ~0;
191  }
193  return bit;
194  }
195 
196  /*
197  * In range?
198  */
199  if (want >= WG_ITF_MAX_INSTANCE)
200  {
201  return ~0;
202  }
203 
204  /*
205  * Already in use?
206  */
207  if (clib_bitmap_get (wg_if_instances, want))
208  {
209  return ~0;
210  }
211 
212  /*
213  * Grant allocation request.
214  */
216 
217  return want;
218 }
219 
220 static int
222 {
223  if (instance >= WG_ITF_MAX_INSTANCE)
224  {
225  return -1;
226  }
227 
228  if (clib_bitmap_get (wg_if_instances, instance) == 0)
229  {
230  return -1;
231  }
232 
234  return 0;
235 }
236 
237 
238 int
239 wg_if_create (u32 user_instance,
241  u16 port, const ip_address_t * src_ip, u32 * sw_if_indexp)
242 {
243  vnet_main_t *vnm = vnet_get_main ();
244  u32 instance, hw_if_index;
246  wg_if_t *wg_if;
247  noise_local_t *local;
248 
249  ASSERT (sw_if_indexp);
250 
251  *sw_if_indexp = (u32) ~ 0;
252 
253  /*
254  * Check if the required port is already in use
255  */
257  if (pi)
258  return VNET_API_ERROR_UDP_PORT_TAKEN;
259 
260  /*
261  * Allocate a wg_if instance. Either select on dynamically
262  * or try to use the desired user_instance number.
263  */
264  instance = wg_if_instance_alloc (user_instance);
265  if (instance == ~0)
266  return VNET_API_ERROR_INVALID_REGISTRATION;
267 
268  /* *INDENT-OFF* */
269  struct noise_upcall upcall = {
270  .u_remote_get = wg_remote_get,
271  .u_index_set = wg_index_set,
272  .u_index_drop = wg_index_drop,
273  };
274  /* *INDENT-ON* */
275 
276  pool_get (noise_local_pool, local);
277 
278  noise_local_init (local, &upcall);
279  if (!noise_local_set_private (local, private_key))
280  {
281  pool_put (noise_local_pool, local);
282  wg_if_instance_free (instance);
283  return VNET_API_ERROR_INVALID_REGISTRATION;
284  }
285 
286  pool_get (wg_if_pool, wg_if);
287 
288  /* tunnel index (or instance) */
289  u32 t_idx = wg_if - wg_if_pool;
290 
291  wg_if->user_instance = instance;
292  if (~0 == wg_if->user_instance)
293  wg_if->user_instance = t_idx;
294 
296 
299 
300  wg_if->port = port;
301  wg_if->local_idx = local - noise_local_pool;
303 
304  hw_if_index = vnet_register_interface (vnm,
305  wg_if_device_class.index,
306  t_idx,
307  wg_hw_interface_class.index, t_idx);
308 
309  hi = vnet_get_hw_interface (vnm, hw_if_index);
310 
312  INDEX_INVALID);
314 
315  ip_address_copy (&wg_if->src_ip, src_ip);
316  wg_if->sw_if_index = *sw_if_indexp = hi->sw_if_index;
317 
318  return 0;
319 }
320 
321 int
323 {
324  vnet_main_t *vnm = vnet_get_main ();
325 
326  if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
327  return VNET_API_ERROR_INVALID_SW_IF_INDEX;
328 
329  vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index);
330  if (hw == 0 || hw->dev_class_index != wg_if_device_class.index)
331  return VNET_API_ERROR_INVALID_VALUE;
332 
333  wg_if_t *wg_if;
334  wg_if = wg_if_get (wg_if_find_by_sw_if_index (sw_if_index));
335  if (NULL == wg_if)
336  return VNET_API_ERROR_INVALID_SW_IF_INDEX_2;
337 
338  if (wg_if_instance_free (wg_if->user_instance) < 0)
339  return VNET_API_ERROR_INVALID_VALUE_2;
340 
345  pool_put (wg_if_pool, wg_if);
346 
347  return 0;
348 }
349 
350 void
352 {
353  hash_set (wgi->peers, peeri, peeri);
354 
355  if (1 == hash_elts (wgi->peers))
356  vnet_feature_enable_disable ("ip4-output", "wg-output-tun",
357  wgi->sw_if_index, 1, 0, 0);
358 }
359 
360 void
362 {
363  hash_unset (wgi->peers, peeri);
364 
365  if (0 == hash_elts (wgi->peers))
366  vnet_feature_enable_disable ("ip4-output", "wg-output-tun",
367  wgi->sw_if_index, 0, 0, 0);
368 }
369 
370 void
372 {
373  index_t wgii;
374 
375  /* *INDENT-OFF* */
376  pool_foreach_index (wgii, wg_if_pool)
377  {
378  if (WALK_STOP == fn(wgii, data))
379  break;
380  }
381  /* *INDENT-ON* */
382 }
383 
384 index_t
386 {
387  index_t peeri, val;
388 
389  /* *INDENT-OFF* */
390  hash_foreach (peeri, val, wgi->peers,
391  {
392  if (WALK_STOP == fn(wgi, peeri, data))
393  return peeri;
394  });
395  /* *INDENT-ON* */
396 
397  return INDEX_INVALID;
398 }
399 
400 
401 static void
403  uword opaque,
404  u32 sw_if_index, u32 new_fib_index, u32 old_fib_index)
405 {
406  wg_if_t *wg_if;
407 
408  wg_if = wg_if_get (wg_if_find_by_sw_if_index (sw_if_index));
409  if (NULL == wg_if)
410  return;
411 
413  .af = AF_IP4,
414  .old_fib_index = old_fib_index,
415  .new_fib_index = new_fib_index,
416  };
417 
419 }
420 
421 static void
423  uword opaque,
424  u32 sw_if_index, u32 new_fib_index, u32 old_fib_index)
425 {
426  wg_if_t *wg_if;
427 
428  wg_if = wg_if_get (wg_if_find_by_sw_if_index (sw_if_index));
429  if (NULL == wg_if)
430  return;
431 
433  .af = AF_IP6,
434  .old_fib_index = old_fib_index,
435  .new_fib_index = new_fib_index,
436  };
437 
439 }
440 
441 static clib_error_t *
443 {
444  {
447  };
449  }
450  {
453  };
455  }
456 
457  return (NULL);
458 }
459 
460 /* *INDENT-OFF* */
462 {
463  .runs_after = VLIB_INITS("ip_main_init"),
464 };
465 /* *INDENT-ON* */
466 
467 
468 /*
469  * fd.io coding-style-patch-verification: ON
470  *
471  * Local Variables:
472  * eval: (c-set-style "gnu")
473  * End:
474  */
void udp_unregister_dst_port(vlib_main_t *vm, udp_dst_port_t dst_port, u8 is_ip4)
Definition: udp_local.c:506
udp_main_t udp_main
Definition: udp.c:23
#define NOISE_KEY_LEN_BASE64
uword * peers
Definition: wireguard_if.h:40
#define hash_set(h, key, value)
Definition: hash.h:255
ip4_table_bind_function_t * function
Definition: ip4.h:94
#define pool_foreach_index(i, v)
Definition: pool.h:576
#define hash_unset(h, key)
Definition: hash.h:261
vl_api_wireguard_peer_flags_t flags
Definition: wireguard.api:105
void noise_local_init(noise_local_t *l, struct noise_upcall *upcall)
static vnet_hw_interface_t * vnet_get_sup_hw_interface(vnet_main_t *vnm, u32 sw_if_index)
void wg_if_peer_add(wg_if_t *wgi, index_t peeri)
Definition: wireguard_if.c:351
vnet_interface_main_t interface_main
Definition: vnet.h:81
static_always_inline wg_if_t * wg_if_get(index_t wgii)
Definition: wireguard_if.h:68
static f64 vlib_time_now(vlib_main_t *vm)
Definition: main.h:325
static uword * wg_if_instances
Definition: wireguard_if.c:29
a non-broadcast multiple access interface
Definition: interface.h:398
static vnet_hw_interface_t * vnet_get_hw_interface(vnet_main_t *vnm, u32 hw_if_index)
index_t wg_peer_walk(wg_peer_walk_cb_t fn, void *data)
u32 index_t
A Data-Path Object is an object that represents actions that are applied to packets are they are swit...
Definition: dpo.h:43
#define vec_add1(V, E)
Add 1 element to end of vector (unspecified alignment).
Definition: vec.h:607
wg_if_t * wg_if_pool
Data-plane exposed functions.
Definition: wireguard_if.c:26
void udp_register_dst_port(vlib_main_t *vm, udp_dst_port_t dst_port, u32 node_index, u8 is_ip4)
Definition: udp_local.c:468
static int wg_if_instance_free(u32 instance)
Definition: wireguard_if.c:221
static uword * clib_bitmap_set(uword *ai, uword i, uword value)
Sets the ith bit of a bitmap to new_value Removes trailing zeros from the bitmap. ...
Definition: bitmap.h:167
static udp_dst_port_info_t * udp_get_dst_port_info(udp_main_t *um, udp_dst_port_t dst_port, u8 is_ip4)
Definition: udp.h:228
walk_rc_t(* wg_if_walk_cb_t)(index_t wgi, void *data)
Definition: wireguard_if.h:52
u32 vnet_register_interface(vnet_main_t *vnm, u32 dev_class_index, u32 dev_instance, u32 hw_class_index, u32 hw_instance)
Definition: interface.c:812
clib_error_t * vnet_hw_interface_set_flags(vnet_main_t *vnm, u32 hw_if_index, vnet_hw_interface_flags_t flags)
Definition: interface.c:513
#define pool_get(P, E)
Allocate an object E from a pool P (unspecified alignment).
Definition: pool.h:255
uint8_t r_public[NOISE_PUBLIC_KEY_LEN]
bool key_to_base64(const u8 *src, size_t src_len, u8 *out)
format_function_t format_vnet_sw_if_index_name
unsigned char u8
Definition: types.h:56
u8 data[128]
Definition: ipsec_types.api:92
unsigned int u32
Definition: types.h:88
index_t wg_if_find_by_sw_if_index(u32 sw_if_index)
Definition: wireguard_if.c:80
enum walk_rc_t_ walk_rc_t
Walk return code.
static u32 wg_if_instance_alloc(u32 want)
Definition: wireguard_if.c:178
noise_remote_t remote
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:172
description fragment has unexpected format
Definition: map.api:433
#define hash_foreach(key_var, value_var, h, body)
Definition: hash.h:442
u8 * format_hex_bytes(u8 *s, va_list *va)
Definition: std-formats.c:84
vnet_main_t * vnet_get_main(void)
index_t wg_if_peer_walk(wg_if_t *wgi, wg_if_peer_walk_cb_t fn, void *data)
Definition: wireguard_if.c:385
int wg_if_delete(u32 sw_if_index)
Definition: wireguard_if.c:322
index_t * wg_if_index_by_port
Definition: wireguard_if.c:35
static u8 * format_wg_if_name(u8 *s, va_list *args)
Definition: wireguard_if.c:38
u32 local_idx
Definition: wireguard_if.h:30
static void wg_if_table_bind_v6(ip6_main_t *im, uword opaque, u32 sw_if_index, u32 new_fib_index, u32 old_fib_index)
Definition: wireguard_if.c:422
#define NOISE_PUBLIC_KEY_LEN
vlib_node_registration_t wg_input_node
(constructor) VLIB_REGISTER_NODE (wg_input_node)
vl_api_interface_index_t sw_if_index
Definition: wireguard.api:34
u8 * format_wg_if(u8 *s, va_list *args)
Definition: wireguard_if.c:45
u8 private_key[32]
Definition: wireguard.api:35
void wg_if_peer_remove(wg_if_t *wgi, index_t peeri)
Definition: wireguard_if.c:361
long ctx[MAX_CONNS]
Definition: main.c:144
unsigned short u16
Definition: types.h:57
static noise_remote_t * wg_remote_get(const uint8_t public[NOISE_PUBLIC_KEY_LEN])
Definition: wireguard_if.c:103
void wg_index_table_del(wg_index_table_t *table, u32 key)
#define pool_put(P, E)
Free an object E in pool P.
Definition: pool.h:305
ip6_table_bind_callback_t * table_bind_callbacks
Functions to call when interface to table biding changes.
Definition: ip6.h:151
int vnet_feature_enable_disable(const char *arc_name, const char *node_name, u32 sw_if_index, int enable_disable, void *feature_config, u32 n_feature_config_bytes)
ip6_main_t ip6_main
Definition: ip6_forward.c:2787
vlib_main_t * vm
X-connect all packets from the HOST to the PHY.
Definition: nat44_ei.c:3047
bool noise_local_set_private(noise_local_t *l, const uint8_t private[NOISE_PUBLIC_KEY_LEN])
uint8_t l_public[NOISE_PUBLIC_KEY_LEN]
vl_api_address_t peer
Definition: teib.api:28
static uint32_t wg_index_set(noise_remote_t *remote)
Definition: wireguard_if.c:116
static clib_error_t * wg_if_module_init(vlib_main_t *vm)
Definition: wireguard_if.c:442
int user_instance
Definition: wireguard_if.h:25
walk_rc_t(* wg_if_peer_walk_cb_t)(wg_if_t *wgi, index_t peeri, void *data)
Definition: wireguard_if.h:55
u32 ti
ip_address_t src_ip
Definition: wireguard_if.h:37
u8 * format_ip_address(u8 *s, va_list *args)
Definition: ip_types.c:21
vnet_interface_main_t * im
static clib_error_t * wg_if_admin_up_down(vnet_main_t *vnm, u32 hw_if_index, u32 flags)
Definition: wireguard_if.c:133
#define pool_is_free_index(P, I)
Use free bitmap to query whether given index is free.
Definition: pool.h:302
u32 adj_index_t
An index for adjacencies.
Definition: adj_types.h:30
static uword clib_bitmap_get(uword *ai, uword i)
Gets the ith bit value from a bitmap.
Definition: bitmap.h:197
wg_index_table_t index_table
Definition: wireguard.h:38
static index_t * wg_if_index_by_sw_if_index
Definition: wireguard_if.c:32
#define pool_put_index(p, i)
Free pool element with given index.
Definition: pool.h:337
static uword hash_elts(void *v)
Definition: hash.h:118
#define ASSERT(truth)
uint32_t r_peer_idx
wg_main_t wg_main
Definition: wireguard.c:26
walk_rc_t wg_peer_if_table_change(wg_if_t *wgi, index_t peeri, void *data)
void vnet_delete_hw_interface(vnet_main_t *vnm, u32 hw_if_index)
Definition: interface.c:1052
IPv4 main type.
Definition: ip4.h:107
u32 wg_index_table_add(wg_index_table_t *table, u32 peer_pool_idx, u32 rnd_seed)
VNET_HW_INTERFACE_CLASS(wg_hw_interface_class)
VNET_DEVICE_CLASS(wg_if_device_class)
void wg_if_walk(wg_if_walk_cb_t fn, void *data)
Definition: wireguard_if.c:371
void wg_if_update_adj(vnet_main_t *vnm, u32 sw_if_index, adj_index_t ai)
Definition: wireguard_if.c:152
static vlib_main_t * vlib_get_main(void)
Definition: global_funcs.h:38
vl_api_ip4_address_t hi
Definition: arp.api:37
ip4_table_bind_callback_t * table_bind_callbacks
Functions to call when interface to table biding changes.
Definition: ip4.h:148
typedef key
Definition: ipsec_types.api:88
uint8_t l_private[NOISE_PUBLIC_KEY_LEN]
static void wg_index_drop(uint32_t key)
Definition: wireguard_if.c:126
static void wg_if_table_bind_v4(ip4_main_t *im, uword opaque, u32 sw_if_index, u32 new_fib_index, u32 old_fib_index)
Definition: wireguard_if.c:402
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
walk_rc_t wg_peer_if_admin_state_change(wg_if_t *wgi, index_t peeri, void *data)
static walk_rc_t wg_if_find_peer_by_public_key(index_t peeri, void *data)
Definition: wireguard_if.c:92
u32 instance
Definition: gre.api:51
#define INDEX_INVALID
Invalid index - used when no index is known blazoned capitals INVALID speak volumes where ~0 does not...
Definition: dpo.h:49
u64 uword
Definition: types.h:112
vnet_sw_interface_t * sw_interfaces
Definition: interface.h:1014
vl_api_address_t src_ip
Definition: wireguard.api:38
vlib_main_t * vlib_main
Definition: wireguard.h:34
ip6_table_bind_function_t * function
Definition: ip6.h:106
u16 port
Definition: lb_types.api:73
#define WG_ITF_MAX_INSTANCE
Definition: wireguard_if.c:175
void ip_address_copy(ip_address_t *dst, const ip_address_t *src)
Definition: ip_types.c:133
ip4_main_t ip4_main
Global ip4 main structure.
Definition: ip4_forward.c:1105
static_always_inline noise_local_t * noise_local_get(uint32_t locali)
int wg_if_create(u32 user_instance, const u8 private_key[NOISE_PUBLIC_KEY_LEN], u16 port, const ip_address_t *src_ip, u32 *sw_if_indexp)
Definition: wireguard_if.c:239
uint8_t cc_mac1_key[COOKIE_KEY_SIZE]
cookie_checker_t cookie_checker
Definition: wireguard_if.h:31
static uword clib_bitmap_first_clear(uword *ai)
Return the lowest numbered clear bit in a bitmap.
Definition: bitmap.h:451
#define vec_validate_init_empty(V, I, INIT)
Make sure vector is long enough for given index and initialize empty space (no header, unspecified alignment)
Definition: vec.h:571
noise_local_t * noise_local_pool
static wg_peer_t * wg_peer_get(index_t peeri)
u32 sw_if_index
Definition: wireguard_if.h:26
#define VLIB_INITS(...)
Definition: init.h:352
Definition: udp.h:93