FD.io VPP  v21.01.1
Vector Packet Processing
wireguard_peer.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Doc.ai and/or its affiliates.
3  * Copyright (c) 2020 Cisco 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/fib/fib_table.h>
20 #include <wireguard/wireguard_if.h>
24 #include <wireguard/wireguard.h>
25 
28 
30 
31 static void
33 {
34  ip46_address_reset (&ep->addr);
35  ep->port = 0;
36 }
37 
38 static void
40  const ip46_address_t * addr, u16 port)
41 {
42  ip46_address_copy (&ep->addr, addr);
43  ep->port = port;
44 }
45 
46 static void
48 {
49  wg_peer_allowed_ip_t *allowed_ip;
50 
51  vec_foreach (allowed_ip, peer->allowed_ips)
52  {
55  }
56 }
57 
58 static void
60 {
61  wg_peer_allowed_ip_t *allowed_ip;
62 
63  vec_foreach (allowed_ip, peer->allowed_ips)
64  {
65  allowed_ip->fib_entry_index =
66  fib_table_entry_path_add (fib_index,
67  &allowed_ip->prefix,
70  fib_proto_to_dpo (allowed_ip->
71  prefix.fp_proto),
72  &peer->dst.addr, peer->wg_sw_if_index, ~0, 1,
74  }
75 }
76 
77 static void
79 {
80  wg_timers_stop (peer);
81  for (int i = 0; i < WG_N_TIMERS; i++)
82  {
83  peer->timers[i] = ~0;
84  }
85 
86  peer->last_sent_handshake = vlib_time_now (vm) - (REKEY_TIMEOUT + 1);
87 
88  clib_memset (&peer->cookie_maker, 0, sizeof (peer->cookie_maker));
89 
90  wg_peer_endpoint_reset (&peer->src);
91  wg_peer_endpoint_reset (&peer->dst);
92 
93  if (INDEX_INVALID != peer->adj_index)
94  {
95  adj_unlock (peer->adj_index);
97  }
98  wg_peer_fib_flush (peer);
99 
100  peer->input_thread_index = ~0;
101  peer->output_thread_index = ~0;
102  peer->adj_index = INDEX_INVALID;
103  peer->timer_wheel = 0;
105  peer->timer_handshake_attempts = 0;
106  peer->last_sent_packet = 0;
107  peer->last_received_packet = 0;
108  peer->session_derived = 0;
109  peer->rehandshake_started = 0;
110  peer->new_handshake_interval_tick = 0;
111  peer->rehandshake_interval_tick = 0;
112  peer->timer_need_another_keepalive = false;
113  peer->is_dead = true;
114  vec_free (peer->allowed_ips);
115 }
116 
117 static void
119 {
120  peer->adj_index = INDEX_INVALID;
121  wg_peer_clear (vm, peer);
122 }
123 
124 static u8 *
126 {
127  // v4 only for now
128  ip4_udp_header_t *hdr;
129  u8 *rewrite = NULL;
130 
131  vec_validate (rewrite, sizeof (*hdr) - 1);
132  hdr = (ip4_udp_header_t *) rewrite;
133 
134  hdr->ip4.ip_version_and_header_length = 0x45;
135  hdr->ip4.ttl = 64;
136  hdr->ip4.src_address = peer->src.addr.ip4;
137  hdr->ip4.dst_address = peer->dst.addr.ip4;
138  hdr->ip4.protocol = IP_PROTOCOL_UDP;
139  hdr->ip4.checksum = ip4_header_checksum (&hdr->ip4);
140 
141  hdr->udp.src_port = clib_host_to_net_u16 (peer->src.port);
142  hdr->udp.dst_port = clib_host_to_net_u16 (peer->dst.port);
143  hdr->udp.checksum = 0;
144 
145  return (rewrite);
146 }
147 
148 static void
150 {
151  ip_adjacency_t *adj;
153  wg_if_t *wgi;
154 
155  adj = adj_get (peer->adj_index);
156  sw_if_index = adj->rewrite_header.sw_if_index;
157 
158  wgi = wg_if_get (wg_if_find_by_sw_if_index (sw_if_index));
159 
160  if (!wgi)
161  return;
162 
164  {
166  }
167  else
168  {
169  /* *INDENT-OFF* */
170  fib_prefix_t dst = {
171  .fp_len = 32,
172  .fp_proto = FIB_PROTOCOL_IP4,
173  .fp_addr = peer->dst.addr,
174  };
175  /* *INDENT-ON* */
176  u32 fib_index;
177 
178  fib_index = fib_table_find (FIB_PROTOCOL_IP4, peer->table_id);
179 
180  adj_midchain_delegate_stack (peer->adj_index, fib_index, &dst);
181  }
182 }
183 
184 walk_rc_t
186 {
187  wg_peer_adj_stack (wg_peer_get (peeri));
188 
189  return (WALK_CONTINUE);
190 }
191 
192 walk_rc_t
194 {
196  wg_peer_t *peer;
197 
198  peer = wg_peer_get (peeri);
199 
200  wg_peer_fib_flush (peer);
201  wg_peer_fib_populate (peer, ctx->new_fib_index);
202 
203  return (WALK_CONTINUE);
204 }
205 
206 static int
208  u32 table_id,
209  const ip46_address_t * dst,
210  u16 port,
211  u16 persistent_keepalive_interval,
212  const fib_prefix_t * allowed_ips, u32 wg_sw_if_index)
213 {
214  wg_peer_endpoint_init (&peer->dst, dst, port);
215 
216  peer->table_id = table_id;
217  peer->wg_sw_if_index = wg_sw_if_index;
219  peer->persistent_keepalive_interval = persistent_keepalive_interval;
220  peer->last_sent_handshake = vlib_time_now (vm) - (REKEY_TIMEOUT + 1);
221  peer->is_dead = false;
222 
223  const wg_if_t *wgi = wg_if_get (wg_if_find_by_sw_if_index (wg_sw_if_index));
224 
225  if (NULL == wgi)
226  return (VNET_API_ERROR_INVALID_INTERFACE);
227 
228  ip_address_to_46 (&wgi->src_ip, &peer->src.addr);
229  peer->src.port = wgi->port;
230 
231  /*
232  * and an adjacency for the endpoint address in the overlay
233  * on the wg interface
234  */
235  peer->rewrite = wg_peer_build_rewrite (peer);
236 
239  &peer->dst.addr, wgi->sw_if_index);
240 
242  peer->adj_index, INDEX_INVALID);
244 
246  NULL,
247  NULL,
249  vec_dup (peer->rewrite));
250  wg_peer_adj_stack (peer);
251 
252  /*
253  * add a route in the overlay to each of the allowed-ips
254  */
255  u32 ii;
256 
257  vec_validate (peer->allowed_ips, vec_len (allowed_ips) - 1);
258 
259  vec_foreach_index (ii, allowed_ips)
260  {
261  peer->allowed_ips[ii].prefix = allowed_ips[ii];
262  }
263 
264  wg_peer_fib_populate (peer,
267 
268  return (0);
269 }
270 
271 int
272 wg_peer_add (u32 tun_sw_if_index,
274  u32 table_id,
275  const ip46_address_t * endpoint,
276  const fib_prefix_t * allowed_ips,
277  u16 port, u16 persistent_keepalive, u32 * peer_index)
278 {
279  wg_if_t *wg_if;
280  wg_peer_t *peer;
281  int rv;
282 
284 
285  if (tun_sw_if_index == ~0)
286  return (VNET_API_ERROR_INVALID_SW_IF_INDEX);
287 
288  wg_if = wg_if_get (wg_if_find_by_sw_if_index (tun_sw_if_index));
289  if (!wg_if)
290  return (VNET_API_ERROR_INVALID_SW_IF_INDEX);
291 
292  /* *INDENT-OFF* */
293  pool_foreach (peer, wg_peer_pool)
294  {
295  if (!memcmp (peer->remote.r_public, public_key, NOISE_PUBLIC_KEY_LEN))
296  {
297  return (VNET_API_ERROR_ENTRY_ALREADY_EXISTS);
298  }
299  }
300  /* *INDENT-ON* */
301 
302  if (pool_elts (wg_peer_pool) > MAX_PEERS)
303  return (VNET_API_ERROR_LIMIT_EXCEEDED);
304 
305  pool_get (wg_peer_pool, peer);
306 
307  wg_peer_init (vm, peer);
308 
309  rv = wg_peer_fill (vm, peer, table_id, endpoint, (u16) port,
310  persistent_keepalive, allowed_ips, tun_sw_if_index);
311 
312  if (rv)
313  {
314  wg_peer_clear (vm, peer);
315  pool_put (wg_peer_pool, peer);
316  return (rv);
317  }
318 
319  noise_remote_init (&peer->remote, peer - wg_peer_pool, public_key,
320  wg_if->local_idx);
322 
323  if (peer->persistent_keepalive_interval != 0)
324  {
325  wg_send_keepalive (vm, peer);
326  }
327 
328  *peer_index = peer - wg_peer_pool;
329  wg_if_peer_add (wg_if, *peer_index);
330 
331  return (0);
332 }
333 
334 int
336 {
337  wg_main_t *wmp = &wg_main;
338  wg_peer_t *peer = NULL;
339  wg_if_t *wgi;
340 
341  if (pool_is_free_index (wg_peer_pool, peeri))
342  return VNET_API_ERROR_NO_SUCH_ENTRY;
343 
344  peer = pool_elt_at_index (wg_peer_pool, peeri);
345 
347  wg_if_peer_remove (wgi, peeri);
348 
349  vnet_feature_enable_disable ("ip4-output", "wg-output-tun",
350  peer->wg_sw_if_index, 0, 0, 0);
351 
352  noise_remote_clear (wmp->vlib_main, &peer->remote);
353  wg_peer_clear (wmp->vlib_main, peer);
354  pool_put (wg_peer_pool, peer);
355 
356  return (0);
357 }
358 
359 index_t
361 {
362  index_t peeri;
363 
364  /* *INDENT-OFF* */
365  pool_foreach_index (peeri, wg_peer_pool)
366  {
367  if (WALK_STOP == fn(peeri, data))
368  return peeri;
369  }
370  /* *INDENT-ON* */
371  return INDEX_INVALID;
372 }
373 
374 static u8 *
375 format_wg_peer_endpoint (u8 * s, va_list * args)
376 {
377  wg_peer_endpoint_t *ep = va_arg (*args, wg_peer_endpoint_t *);
378 
379  s = format (s, "%U:%d",
381 
382  return (s);
383 }
384 
385 u8 *
386 format_wg_peer (u8 * s, va_list * va)
387 {
388  index_t peeri = va_arg (*va, index_t);
389  wg_peer_allowed_ip_t *allowed_ip;
391  wg_peer_t *peer;
392 
393  peer = wg_peer_get (peeri);
395 
396  s = format (s, "[%d] endpoint:[%U->%U] %U keep-alive:%d adj:%d",
397  peeri,
401  peer->wg_sw_if_index,
403  s = format (s, "\n key:%=s %U",
404  key, format_hex_bytes, peer->remote.r_public,
406  s = format (s, "\n allowed-ips:");
407  vec_foreach (allowed_ip, peer->allowed_ips)
408  {
409  s = format (s, " %U", format_fib_prefix, &allowed_ip->prefix);
410  }
411 
412  return s;
413 }
414 
415 static clib_error_t *
417 {
418  /*
419  * use a priority better than interface source, so that
420  * if the same subnet is added to the wg interface and is
421  * used as an allowed IP, then the wireguard soueced prefix
422  * wins and traffic is routed to the endpoint rather than dropped
423  */
425 
426  return (NULL);
427 }
428 
430 
431 /*
432  * fd.io coding-style-patch-verification: ON
433  *
434  * Local Variables:
435  * eval: (c-set-style "gnu")
436  * End:
437  */
static u8 * wg_peer_build_rewrite(const wg_peer_t *peer)
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:509
enum fib_source_t_ fib_source_t
The different sources that can create a route.
#define NOISE_KEY_LEN_BASE64
fib_node_index_t fib_table_entry_path_add(u32 fib_index, const fib_prefix_t *prefix, fib_source_t source, fib_entry_flag_t flags, dpo_proto_t next_hop_proto, const ip46_address_t *next_hop, u32 next_hop_sw_if_index, u32 next_hop_fib_index, u32 next_hop_weight, fib_mpls_label_t *next_hop_labels, fib_route_path_flags_t path_flags)
Add one path to an entry (aka route) in the FIB.
Definition: fib_table.c:558
wg_peer_allowed_ip_t * allowed_ips
#define vec_foreach_index(var, v)
Iterate over vector indices.
static void wg_peer_fib_flush(wg_peer_t *peer)
#define pool_foreach_index(i, v)
Definition: pool.h:569
u16 persistent_keepalive_interval
f64 rehandshake_started
static int wg_peer_fill(vlib_main_t *vm, wg_peer_t *peer, u32 table_id, const ip46_address_t *dst, u16 port, u16 persistent_keepalive_interval, const fib_prefix_t *allowed_ips, u32 wg_sw_if_index)
ip4_address_t src_address
Definition: ip4_packet.h:125
vnet_main_t * vnet_get_main(void)
Definition: misc.c:46
void wg_if_peer_add(wg_if_t *wgi, index_t peeri)
Definition: wireguard_if.c:351
static fib_source_t wg_fib_source
#define pool_foreach(VAR, POOL)
Iterate through pool.
Definition: pool.h:527
static_always_inline wg_if_t * wg_if_get(index_t wgii)
Definition: wireguard_if.h:68
void adj_midchain_delegate_stack(adj_index_t ai, u32 fib_index, const fib_prefix_t *pfx)
create/attach a midchain delegate and stack it on the prefix passed
clib_memset(h->entries, 0, sizeof(h->entries[0]) *entries)
static f64 vlib_time_now(vlib_main_t *vm)
Definition: main.h:334
IP unicast adjacency.
Definition: adj.h:235
u32 fib_table_get_index_for_sw_if_index(fib_protocol_t proto, u32 sw_if_index)
Get the index of the FIB bound to the interface.
Definition: fib_table.c:998
tw_timer_wheel_16t_2w_512sl_t * timer_wheel
walk_rc_t(* wg_peer_walk_cb_t)(index_t peeri, void *arg)
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:41
vlib_main_t * vm
Definition: in2out_ed.c:1580
int wg_peer_add(u32 tun_sw_if_index, const u8 public_key[NOISE_PUBLIC_KEY_LEN], u32 table_id, const ip46_address_t *endpoint, const fib_prefix_t *allowed_ips, u16 port, u16 persistent_keepalive, u32 *peer_index)
vl_api_prefix_t prefix
Definition: ip.api:144
#define pool_get(P, E)
Allocate an object E from a pool P (unspecified alignment).
Definition: pool.h:251
u32 timers[WG_N_TIMERS]
vhost_vring_addr_t addr
Definition: vhost_user.h:111
f64 session_derived
uint8_t r_public[NOISE_PUBLIC_KEY_LEN]
bool key_to_base64(const u8 *src, size_t src_len, u8 *out)
void noise_remote_init(noise_remote_t *r, uint32_t peer_pool_idx, const uint8_t public[NOISE_PUBLIC_KEY_LEN], u32 noise_local_idx)
format_function_t format_vnet_sw_if_index_name
unsigned char u8
Definition: types.h:56
void wg_timers_stop(wg_peer_t *peer)
u8 data[128]
Definition: ipsec_types.api:90
index_t wg_if_find_by_sw_if_index(u32 sw_if_index)
Definition: wireguard_if.c:80
f64 last_sent_packet
static void wg_peer_adj_stack(wg_peer_t *peer)
enum walk_rc_t_ walk_rc_t
Walk return code.
static void ip46_address_reset(ip46_address_t *ip46)
Definition: ip46_address.h:74
static ip_adjacency_t * adj_get(adj_index_t adj_index)
Get a pointer to an adjacency object from its index.
Definition: adj.h:467
ip4_header_t ip4
noise_remote_t remote
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:173
ip4_address_t dst_address
Definition: ip4_packet.h:125
vl_api_prefix_t allowed_ips[n_allowed_ips]
Definition: wireguard.api:107
description fragment has unexpected format
Definition: map.api:433
u8 * format_fib_prefix(u8 *s, va_list *args)
Definition: fib_types.c:283
Aggregate type for a prefix.
Definition: fib_types.h:202
u8 * format_hex_bytes(u8 *s, va_list *va)
Definition: std-formats.c:84
static void wg_peer_endpoint_init(wg_peer_endpoint_t *ep, const ip46_address_t *addr, u16 port)
void noise_remote_clear(vlib_main_t *vm, noise_remote_t *r)
void adj_unlock(adj_index_t adj_index)
Release a reference counting lock on the adjacency.
Definition: adj.c:347
unsigned int u32
Definition: types.h:88
u32 fib_table_find(fib_protocol_t proto, u32 table_id)
Get the index of the FIB for a Table-ID.
Definition: fib_table.c:1106
u32 local_idx
Definition: wireguard_if.h:30
tw_timer_wheel_16t_2w_512sl_t timer_wheel
Definition: wireguard.h:46
#define NOISE_PUBLIC_KEY_LEN
fib_source_t fib_source_allocate(const char *name, fib_source_priority_t prio, fib_source_behaviour_t bh)
Definition: fib_source.c:118
Definition: fib_entry.h:112
#define pool_elt_at_index(p, i)
Returns pointer to element at given index.
Definition: pool.h:546
static_always_inline void ip46_address_copy(ip46_address_t *dst, const ip46_address_t *src)
Definition: ip46_address.h:123
static void wg_peer_endpoint_reset(wg_peer_endpoint_t *ep)
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
#define pool_put(P, E)
Free an object E in pool P.
Definition: pool.h:301
#define vec_dup(V)
Return copy of vector (no header, no alignment)
Definition: vec.h:429
add paths with [mpls] path extensions
Definition: fib_source.h:203
fib_protocol_t ip_address_to_46(const ip_address_t *addr, ip46_address_t *a)
Definition: ip_types.c:253
vl_api_address_t dst
Definition: gre.api:55
vl_api_address_t peer
Definition: teib.api:28
format_function_t format_ip46_address
Definition: ip46_address.h:50
u8 public_key[32]
Definition: wireguard.api:36
vl_api_address_t endpoint
Definition: wireguard.api:103
static void wg_peer_fib_populate(wg_peer_t *peer, u32 fib_index)
wg_peer_endpoint_t dst
ip_address_t src_ip
Definition: wireguard_if.h:37
void fib_table_entry_delete_index(fib_node_index_t fib_entry_index, fib_source_t source)
Delete a FIB entry.
Definition: fib_table.c:919
sll srl srl sll sra u16x4 i
Definition: vector_sse42.h:317
static u8 * format_wg_peer_endpoint(u8 *s, va_list *args)
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:380
u32 new_handshake_interval_tick
void adj_nbr_midchain_update_rewrite(adj_index_t adj_index, adj_midchain_fixup_t fixup, const void *fixup_data, adj_flags_t flags, u8 *rewrite)
adj_nbr_midchain_update_rewrite
Definition: adj_midchain.c:440
wg_peer_t * wg_peer_pool
cookie_maker_t cookie_maker
#define pool_is_free_index(P, I)
Use free bitmap to query whether given index is free.
Definition: pool.h:298
static void wg_peer_init(vlib_main_t *vm, wg_peer_t *peer)
void adj_midchain_delegate_unstack(adj_index_t ai)
unstack a midchain delegate (this stacks it on a drop)
fib_node_index_t fib_entry_index
bool is_dead
adj_index_t adj_index
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)
u8 * rewrite
index_t * wg_peer_by_adj_index
u16 persistent_keepalive
Definition: wireguard.api:101
bool timer_need_another_keepalive
dpo_proto_t fib_proto_to_dpo(fib_protocol_t fib_proto)
Definition: fib_types.c:343
f64 last_sent_handshake
static vlib_main_t * vlib_get_main(void)
Definition: global_funcs.h:23
wg_peer_endpoint_t src
static uword vnet_sw_interface_is_admin_up(vnet_main_t *vnm, u32 sw_if_index)
u32 rehandshake_interval_tick
typedef key
Definition: ipsec_types.api:86
u32 wg_sw_if_index
#define FIB_NODE_INDEX_INVALID
Definition: fib_types.h:30
#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)
#define INDEX_INVALID
Invalid index - used when no index is known blazoned capitals INVALID speak volumes where ~0 does not...
Definition: dpo.h:47
vlib_main_t * vlib_main
Definition: wireguard.h:34
u32 table_id
Definition: wireguard.api:102
udp_header_t udp
u16 port
Definition: lb_types.api:73
u8 * format_wg_peer(u8 *s, va_list *va)
f64 last_received_packet
ip46_address_t addr
u32 input_thread_index
bool wg_send_keepalive(vlib_main_t *vm, wg_peer_t *peer)
int wg_peer_remove(index_t peeri)
adj_index_t adj_nbr_add_or_lock(fib_protocol_t nh_proto, vnet_link_t link_type, const ip46_address_t *nh_addr, u32 sw_if_index)
Neighbour Adjacency sub-type.
Definition: adj_nbr.c:236
#define vec_foreach(var, vec)
Vector iterator.
static void wg_peer_clear(vlib_main_t *vm, wg_peer_t *peer)
u8 ip_version_and_header_length
Definition: ip4_packet.h:93
#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:556
static wg_peer_t * wg_peer_get(index_t peeri)
u32 sw_if_index
Definition: wireguard_if.h:26
u32 timer_handshake_attempts
u32 table_id
static clib_error_t * wg_peer_module_init(vlib_main_t *vm)
static u16 ip4_header_checksum(ip4_header_t *i)
Definition: ip4_packet.h:314
vl_api_interface_index_t sw_if_index
Definition: wireguard.api:34
u32 output_thread_index
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)
Definition: feature.c:303
static uword pool_elts(void *v)
Number of active elements in a pool.
Definition: pool.h:127