FD.io VPP  v20.09-64-g4f7b92f0a
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  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <vnet/adj/adj_midchain.h>
17 #include <vnet/fib/fib_table.h>
19 #include <wireguard/wireguard_if.h>
23 #include <wireguard/wireguard.h>
24 
27 
29 
30 static void
32 {
33  ip46_address_reset (&ep->addr);
34  ep->port = 0;
35 }
36 
37 static void
39  const ip46_address_t * addr, u16 port)
40 {
41  ip46_address_copy (&ep->addr, addr);
42  ep->port = port;
43 }
44 
45 static void
47 {
48  wg_peer_allowed_ip_t *allowed_ip;
49 
50  vec_foreach (allowed_ip, peer->allowed_ips)
51  {
54  }
55 }
56 
57 static void
59 {
60  wg_peer_allowed_ip_t *allowed_ip;
61 
62  vec_foreach (allowed_ip, peer->allowed_ips)
63  {
64  allowed_ip->fib_entry_index =
65  fib_table_entry_path_add (fib_index,
66  &allowed_ip->prefix,
69  fib_proto_to_dpo (allowed_ip->
70  prefix.fp_proto),
71  &peer->dst.addr, peer->wg_sw_if_index, ~0, 1,
73  }
74 }
75 
76 static void
78 {
79  wg_timers_stop (peer);
80  for (int i = 0; i < WG_N_TIMERS; i++)
81  {
82  peer->timers[i] = ~0;
83  }
84 
85  peer->last_sent_handshake = vlib_time_now (vm) - (REKEY_TIMEOUT + 1);
86 
87  clib_memset (&peer->cookie_maker, 0, sizeof (peer->cookie_maker));
88 
89  wg_peer_endpoint_reset (&peer->src);
90  wg_peer_endpoint_reset (&peer->dst);
91 
92  if (INDEX_INVALID != peer->adj_index)
93  {
94  adj_unlock (peer->adj_index);
96  }
97  wg_peer_fib_flush (peer);
98 
99  peer->input_thread_index = ~0;
100  peer->output_thread_index = ~0;
101  peer->adj_index = INDEX_INVALID;
102  peer->timer_wheel = 0;
104  peer->timer_handshake_attempts = 0;
105  peer->last_sent_packet = 0;
106  peer->last_received_packet = 0;
107  peer->session_derived = 0;
108  peer->rehandshake_started = 0;
109  peer->new_handshake_interval_tick = 0;
110  peer->rehandshake_interval_tick = 0;
111  peer->timer_need_another_keepalive = false;
112  peer->is_dead = true;
113  vec_free (peer->allowed_ips);
114 }
115 
116 static void
118 {
119  peer->adj_index = INDEX_INVALID;
120  wg_peer_clear (vm, peer);
121 }
122 
123 static u8 *
125 {
126  // v4 only for now
127  ip4_udp_header_t *hdr;
128  u8 *rewrite = NULL;
129 
130  vec_validate (rewrite, sizeof (*hdr) - 1);
131  hdr = (ip4_udp_header_t *) rewrite;
132 
133  hdr->ip4.ip_version_and_header_length = 0x45;
134  hdr->ip4.ttl = 64;
135  hdr->ip4.src_address = peer->src.addr.ip4;
136  hdr->ip4.dst_address = peer->dst.addr.ip4;
137  hdr->ip4.protocol = IP_PROTOCOL_UDP;
138  hdr->ip4.checksum = ip4_header_checksum (&hdr->ip4);
139 
140  hdr->udp.src_port = clib_host_to_net_u16 (peer->src.port);
141  hdr->udp.dst_port = clib_host_to_net_u16 (peer->dst.port);
142  hdr->udp.checksum = 0;
143 
144  return (rewrite);
145 }
146 
147 static void
149 {
150  ip_adjacency_t *adj;
152  wg_if_t *wgi;
153 
154  adj = adj_get (peer->adj_index);
155  sw_if_index = adj->rewrite_header.sw_if_index;
156 
157  wgi = wg_if_get (wg_if_find_by_sw_if_index (sw_if_index));
158 
159  if (!wgi)
160  return;
161 
163  {
165  }
166  else
167  {
168  /* *INDENT-OFF* */
169  fib_prefix_t dst = {
170  .fp_len = 32,
171  .fp_proto = FIB_PROTOCOL_IP4,
172  .fp_addr = peer->dst.addr,
173  };
174  /* *INDENT-ON* */
175  u32 fib_index;
176 
177  fib_index = fib_table_find (FIB_PROTOCOL_IP4, peer->table_id);
178 
179  adj_midchain_delegate_stack (peer->adj_index, fib_index, &dst);
180  }
181 }
182 
183 walk_rc_t
185 {
186  wg_peer_adj_stack (wg_peer_get (peeri));
187 
188  return (WALK_CONTINUE);
189 }
190 
191 walk_rc_t
193 {
195  wg_peer_t *peer;
196 
197  peer = wg_peer_get (peeri);
198 
199  wg_peer_fib_flush (peer);
200  wg_peer_fib_populate (peer, ctx->new_fib_index);
201 
202  return (WALK_CONTINUE);
203 }
204 
205 static int
207  u32 table_id,
208  const ip46_address_t * dst,
209  u16 port,
210  u16 persistent_keepalive_interval,
211  const fib_prefix_t * allowed_ips, u32 wg_sw_if_index)
212 {
213  wg_peer_endpoint_init (&peer->dst, dst, port);
214 
215  peer->table_id = table_id;
216  peer->wg_sw_if_index = wg_sw_if_index;
218  peer->persistent_keepalive_interval = persistent_keepalive_interval;
219  peer->last_sent_handshake = vlib_time_now (vm) - (REKEY_TIMEOUT + 1);
220  peer->is_dead = false;
221 
222  const wg_if_t *wgi = wg_if_get (wg_if_find_by_sw_if_index (wg_sw_if_index));
223 
224  if (NULL == wgi)
225  return (VNET_API_ERROR_INVALID_INTERFACE);
226 
227  ip_address_to_46 (&wgi->src_ip, &peer->src.addr);
228  peer->src.port = wgi->port;
229 
230  /*
231  * and an adjacency for the endpoint address in the overlay
232  * on the wg interface
233  */
234  peer->rewrite = wg_peer_build_rewrite (peer);
235 
238  &peer->dst.addr, wgi->sw_if_index);
239 
241  peer->adj_index, INDEX_INVALID);
243 
245  NULL,
246  NULL,
248  vec_dup (peer->rewrite));
249  wg_peer_adj_stack (peer);
250 
251  /*
252  * add a route in the overlay to each of the allowed-ips
253  */
254  u32 ii;
255 
256  vec_validate (peer->allowed_ips, vec_len (allowed_ips) - 1);
257 
258  vec_foreach_index (ii, allowed_ips)
259  {
260  peer->allowed_ips[ii].prefix = allowed_ips[ii];
261  }
262 
263  wg_peer_fib_populate (peer,
266 
267  return (0);
268 }
269 
270 int
271 wg_peer_add (u32 tun_sw_if_index,
272  const u8 public_key[NOISE_PUBLIC_KEY_LEN],
273  u32 table_id,
274  const ip46_address_t * endpoint,
275  const fib_prefix_t * allowed_ips,
276  u16 port, u16 persistent_keepalive, u32 * peer_index)
277 {
278  wg_if_t *wg_if;
279  wg_peer_t *peer;
280  int rv;
281 
283 
284  if (tun_sw_if_index == ~0)
285  return (VNET_API_ERROR_INVALID_SW_IF_INDEX);
286 
287  wg_if = wg_if_get (wg_if_find_by_sw_if_index (tun_sw_if_index));
288  if (!wg_if)
289  return (VNET_API_ERROR_INVALID_SW_IF_INDEX);
290 
291  /* *INDENT-OFF* */
292  pool_foreach (peer, wg_peer_pool,
293  ({
294  if (!memcmp (peer->remote.r_public, public_key, NOISE_PUBLIC_KEY_LEN))
295  {
296  return (VNET_API_ERROR_ENTRY_ALREADY_EXISTS);
297  }
298  }));
299  /* *INDENT-ON* */
300 
301  if (pool_elts (wg_peer_pool) > MAX_PEERS)
302  return (VNET_API_ERROR_LIMIT_EXCEEDED);
303 
304  pool_get (wg_peer_pool, peer);
305 
306  wg_peer_init (vm, peer);
307 
308  rv = wg_peer_fill (vm, peer, table_id, endpoint, (u16) port,
309  persistent_keepalive, allowed_ips, tun_sw_if_index);
310 
311  if (rv)
312  {
313  wg_peer_clear (vm, peer);
314  pool_put (wg_peer_pool, peer);
315  return (rv);
316  }
317 
318  noise_remote_init (&peer->remote, peer - wg_peer_pool, public_key,
319  wg_if->local_idx);
320  cookie_maker_init (&peer->cookie_maker, public_key);
321 
322  if (peer->persistent_keepalive_interval != 0)
323  {
324  wg_send_keepalive (vm, peer);
325  }
326 
327  *peer_index = peer - wg_peer_pool;
328  wg_if_peer_add (wg_if, *peer_index);
329 
330  return (0);
331 }
332 
333 int
335 {
336  wg_main_t *wmp = &wg_main;
337  wg_peer_t *peer = NULL;
338  wg_if_t *wgi;
339 
340  if (pool_is_free_index (wg_peer_pool, peeri))
341  return VNET_API_ERROR_NO_SUCH_ENTRY;
342 
343  peer = pool_elt_at_index (wg_peer_pool, peeri);
344 
346  wg_if_peer_remove (wgi, peeri);
347 
348  vnet_feature_enable_disable ("ip4-output", "wg-output-tun",
349  peer->wg_sw_if_index, 0, 0, 0);
350 
351  noise_remote_clear (wmp->vlib_main, &peer->remote);
352  wg_peer_clear (wmp->vlib_main, peer);
353  pool_put (wg_peer_pool, peer);
354 
355  return (0);
356 }
357 
358 index_t
360 {
361  index_t peeri;
362 
363  /* *INDENT-OFF* */
364  pool_foreach_index(peeri, wg_peer_pool,
365  {
366  if (WALK_STOP == fn(peeri, data))
367  return peeri;
368  });
369  /* *INDENT-ON* */
370  return INDEX_INVALID;
371 }
372 
373 static u8 *
374 format_wg_peer_endpoint (u8 * s, va_list * args)
375 {
376  wg_peer_endpoint_t *ep = va_arg (*args, wg_peer_endpoint_t *);
377 
378  s = format (s, "%U:%d",
380 
381  return (s);
382 }
383 
384 u8 *
385 format_wg_peer (u8 * s, va_list * va)
386 {
387  index_t peeri = va_arg (*va, index_t);
388  wg_peer_allowed_ip_t *allowed_ip;
390  wg_peer_t *peer;
391 
392  peer = wg_peer_get (peeri);
394 
395  s = format (s, "[%d] endpoint:[%U->%U] %U keep-alive:%d adj:%d",
396  peeri,
400  peer->wg_sw_if_index,
402  s = format (s, "\n key:%=s %U",
403  key, format_hex_bytes, peer->remote.r_public,
405  s = format (s, "\n allowed-ips:");
406  vec_foreach (allowed_ip, peer->allowed_ips)
407  {
408  s = format (s, " %U", format_fib_prefix, &allowed_ip->prefix);
409  }
410 
411  return s;
412 }
413 
414 static clib_error_t *
416 {
417  /*
418  * use a priority better than interface source, so that
419  * if the same subnet is added to the wg interface and is
420  * used as an allowed IP, then the wireguard soueced prefix
421  * wins and traffic is routed to the endpoint rather than dropped
422  */
424 
425  return (NULL);
426 }
427 
429 
430 /*
431  * fd.io coding-style-patch-verification: ON
432  *
433  * Local Variables:
434  * eval: (c-set-style "gnu")
435  * End:
436  */
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:549
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)
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:329
static fib_source_t wg_fib_source
static_always_inline wg_if_t * wg_if_get(index_t wgii)
Definition: wireguard_if.h:67
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:333
IP unicast adjacency.
Definition: adj.h:227
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:989
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:1582
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:424
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:252
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:89
index_t wg_if_find_by_sw_if_index(u32 sw_if_index)
Definition: wireguard_if.c:65
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:459
ip4_header_t ip4
#define pool_foreach(VAR, POOL, BODY)
Iterate through pool.
Definition: pool.h:513
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:105
u8 * format_fib_prefix(u8 *s, va_list *args)
Definition: fib_types.c:264
Aggregate type for a prefix.
Definition: fib_types.h:203
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:348
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:1097
u32 local_idx
Definition: wireguard_if.h:29
tw_timer_wheel_16t_2w_512sl_t timer_wheel
Definition: wireguard.h:45
#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:534
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:339
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:302
#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:250
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
vl_api_address_t endpoint
Definition: wireguard.api:101
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:36
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:910
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:434
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:299
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:27
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:99
bool timer_need_another_keepalive
dpo_proto_t fib_proto_to_dpo(fib_protocol_t fib_proto)
Definition: fib_types.c:324
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:85
u32 wg_sw_if_index
#define FIB_NODE_INDEX_INVALID
Definition: fib_types.h:31
#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:100
udp_header_t udp
u16 port
Definition: lb_types.api:72
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 pool_foreach_index(i, v, body)
Iterate pool by index.
Definition: pool.h:558
#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:25
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:33
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:128