FD.io VPP  v21.06-1-gbb7418cf9
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  peer->timers_dispatched[i] = 0;
85  }
86 
87  peer->last_sent_handshake = vlib_time_now (vm) - (REKEY_TIMEOUT + 1);
88 
89  clib_memset (&peer->cookie_maker, 0, sizeof (peer->cookie_maker));
90 
91  wg_peer_endpoint_reset (&peer->src);
92  wg_peer_endpoint_reset (&peer->dst);
93 
94  if (INDEX_INVALID != peer->adj_index)
95  {
96  adj_unlock (peer->adj_index);
98  }
99  wg_peer_fib_flush (peer);
100 
101  peer->input_thread_index = ~0;
102  peer->output_thread_index = ~0;
103  peer->adj_index = INDEX_INVALID;
104  peer->timer_wheel = 0;
106  peer->timer_handshake_attempts = 0;
107  peer->last_sent_packet = 0;
108  peer->last_received_packet = 0;
109  peer->session_derived = 0;
110  peer->rehandshake_started = 0;
111  peer->new_handshake_interval_tick = 0;
112  peer->rehandshake_interval_tick = 0;
113  peer->timer_need_another_keepalive = false;
114  peer->is_dead = true;
115  vec_free (peer->allowed_ips);
116 }
117 
118 static void
120 {
121  peer->adj_index = INDEX_INVALID;
122  wg_peer_clear (vm, peer);
123 }
124 
125 static u8 *
127 {
128  // v4 only for now
129  ip4_udp_header_t *hdr;
130  u8 *rewrite = NULL;
131 
132  vec_validate (rewrite, sizeof (*hdr) - 1);
133  hdr = (ip4_udp_header_t *) rewrite;
134 
135  hdr->ip4.ip_version_and_header_length = 0x45;
136  hdr->ip4.ttl = 64;
137  hdr->ip4.src_address = peer->src.addr.ip4;
138  hdr->ip4.dst_address = peer->dst.addr.ip4;
139  hdr->ip4.protocol = IP_PROTOCOL_UDP;
140  hdr->ip4.checksum = ip4_header_checksum (&hdr->ip4);
141 
142  hdr->udp.src_port = clib_host_to_net_u16 (peer->src.port);
143  hdr->udp.dst_port = clib_host_to_net_u16 (peer->dst.port);
144  hdr->udp.checksum = 0;
145 
146  return (rewrite);
147 }
148 
149 static void
151 {
152  ip_adjacency_t *adj;
154  wg_if_t *wgi;
155 
156  adj = adj_get (peer->adj_index);
157  sw_if_index = adj->rewrite_header.sw_if_index;
158 
159  wgi = wg_if_get (wg_if_find_by_sw_if_index (sw_if_index));
160 
161  if (!wgi)
162  return;
163 
165  {
167  }
168  else
169  {
170  /* *INDENT-OFF* */
171  fib_prefix_t dst = {
172  .fp_len = 32,
173  .fp_proto = FIB_PROTOCOL_IP4,
174  .fp_addr = peer->dst.addr,
175  };
176  /* *INDENT-ON* */
177  u32 fib_index;
178 
179  fib_index = fib_table_find (FIB_PROTOCOL_IP4, peer->table_id);
180 
181  adj_midchain_delegate_stack (peer->adj_index, fib_index, &dst);
182  }
183 }
184 
185 walk_rc_t
187 {
188  wg_peer_adj_stack (wg_peer_get (peeri));
189 
190  return (WALK_CONTINUE);
191 }
192 
193 walk_rc_t
195 {
197  wg_peer_t *peer;
198 
199  peer = wg_peer_get (peeri);
200 
201  wg_peer_fib_flush (peer);
202  wg_peer_fib_populate (peer, ctx->new_fib_index);
203 
204  return (WALK_CONTINUE);
205 }
206 
207 static int
209  u32 table_id,
210  const ip46_address_t * dst,
211  u16 port,
212  u16 persistent_keepalive_interval,
213  const fib_prefix_t * allowed_ips, u32 wg_sw_if_index)
214 {
215  wg_peer_endpoint_init (&peer->dst, dst, port);
216 
217  peer->table_id = table_id;
218  peer->wg_sw_if_index = wg_sw_if_index;
220  peer->persistent_keepalive_interval = persistent_keepalive_interval;
221  peer->last_sent_handshake = vlib_time_now (vm) - (REKEY_TIMEOUT + 1);
222  peer->is_dead = false;
223 
224  const wg_if_t *wgi = wg_if_get (wg_if_find_by_sw_if_index (wg_sw_if_index));
225 
226  if (NULL == wgi)
227  return (VNET_API_ERROR_INVALID_INTERFACE);
228 
229  ip_address_to_46 (&wgi->src_ip, &peer->src.addr);
230  peer->src.port = wgi->port;
231 
232  /*
233  * and an adjacency for the endpoint address in the overlay
234  * on the wg interface
235  */
236  peer->rewrite = wg_peer_build_rewrite (peer);
237 
240  &peer->dst.addr, wgi->sw_if_index);
241 
243  peer->adj_index, INDEX_INVALID);
245 
247  NULL,
248  NULL,
250  vec_dup (peer->rewrite));
251  wg_peer_adj_stack (peer);
252 
253  /*
254  * add a route in the overlay to each of the allowed-ips
255  */
256  u32 ii;
257 
258  vec_validate (peer->allowed_ips, vec_len (allowed_ips) - 1);
259 
260  vec_foreach_index (ii, allowed_ips)
261  {
262  peer->allowed_ips[ii].prefix = allowed_ips[ii];
263  }
264 
265  wg_peer_fib_populate (peer,
268 
269  return (0);
270 }
271 
272 int
273 wg_peer_add (u32 tun_sw_if_index,
275  u32 table_id,
276  const ip46_address_t * endpoint,
277  const fib_prefix_t * allowed_ips,
278  u16 port, u16 persistent_keepalive, u32 * peer_index)
279 {
280  wg_if_t *wg_if;
281  wg_peer_t *peer;
282  int rv;
283 
285 
286  if (tun_sw_if_index == ~0)
287  return (VNET_API_ERROR_INVALID_SW_IF_INDEX);
288 
289  wg_if = wg_if_get (wg_if_find_by_sw_if_index (tun_sw_if_index));
290  if (!wg_if)
291  return (VNET_API_ERROR_INVALID_SW_IF_INDEX);
292 
293  /* *INDENT-OFF* */
294  pool_foreach (peer, wg_peer_pool)
295  {
296  if (!memcmp (peer->remote.r_public, public_key, NOISE_PUBLIC_KEY_LEN))
297  {
298  return (VNET_API_ERROR_ENTRY_ALREADY_EXISTS);
299  }
300  }
301  /* *INDENT-ON* */
302 
303  if (pool_elts (wg_peer_pool) > MAX_PEERS)
304  return (VNET_API_ERROR_LIMIT_EXCEEDED);
305 
306  pool_get (wg_peer_pool, peer);
307 
308  wg_peer_init (vm, peer);
309 
310  rv = wg_peer_fill (vm, peer, table_id, endpoint, (u16) port,
311  persistent_keepalive, allowed_ips, tun_sw_if_index);
312 
313  if (rv)
314  {
315  wg_peer_clear (vm, peer);
316  pool_put (wg_peer_pool, peer);
317  return (rv);
318  }
319 
320  noise_remote_init (&peer->remote, peer - wg_peer_pool, public_key,
321  wg_if->local_idx);
323 
324  if (peer->persistent_keepalive_interval != 0)
325  {
326  wg_send_keepalive (vm, peer);
327  }
328 
329  *peer_index = peer - wg_peer_pool;
330  wg_if_peer_add (wg_if, *peer_index);
331 
332  return (0);
333 }
334 
335 int
337 {
338  wg_main_t *wmp = &wg_main;
339  wg_peer_t *peer = NULL;
340  wg_if_t *wgi;
341 
342  if (pool_is_free_index (wg_peer_pool, peeri))
343  return VNET_API_ERROR_NO_SUCH_ENTRY;
344 
345  peer = pool_elt_at_index (wg_peer_pool, peeri);
346 
348  wg_if_peer_remove (wgi, peeri);
349 
350  vnet_feature_enable_disable ("ip4-output", "wg-output-tun",
351  peer->wg_sw_if_index, 0, 0, 0);
352 
353  noise_remote_clear (wmp->vlib_main, &peer->remote);
354  wg_peer_clear (wmp->vlib_main, peer);
355  pool_put (wg_peer_pool, peer);
356 
357  return (0);
358 }
359 
360 index_t
362 {
363  index_t peeri;
364 
365  /* *INDENT-OFF* */
366  pool_foreach_index (peeri, wg_peer_pool)
367  {
368  if (WALK_STOP == fn(peeri, data))
369  return peeri;
370  }
371  /* *INDENT-ON* */
372  return INDEX_INVALID;
373 }
374 
375 static u8 *
376 format_wg_peer_endpoint (u8 * s, va_list * args)
377 {
378  wg_peer_endpoint_t *ep = va_arg (*args, wg_peer_endpoint_t *);
379 
380  s = format (s, "%U:%d",
382 
383  return (s);
384 }
385 
386 u8 *
387 format_wg_peer (u8 * s, va_list * va)
388 {
389  index_t peeri = va_arg (*va, index_t);
390  wg_peer_allowed_ip_t *allowed_ip;
392  wg_peer_t *peer;
393 
394  peer = wg_peer_get (peeri);
396 
397  s = format (s, "[%d] endpoint:[%U->%U] %U keep-alive:%d adj:%d",
398  peeri,
402  peer->wg_sw_if_index,
404  s = format (s, "\n key:%=s %U",
405  key, format_hex_bytes, peer->remote.r_public,
407  s = format (s, "\n allowed-ips:");
408  vec_foreach (allowed_ip, peer->allowed_ips)
409  {
410  s = format (s, " %U", format_fib_prefix, &allowed_ip->prefix);
411  }
412 
413  return s;
414 }
415 
416 static clib_error_t *
418 {
419  /*
420  * use a priority better than interface source, so that
421  * if the same subnet is added to the wg interface and is
422  * used as an allowed IP, then the wireguard soueced prefix
423  * wins and traffic is routed to the endpoint rather than dropped
424  */
426 
427  return (NULL);
428 }
429 
431 
432 /*
433  * fd.io coding-style-patch-verification: ON
434  *
435  * Local Variables:
436  * eval: (c-set-style "gnu")
437  * End:
438  */
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:524
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:576
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
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:534
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:325
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:43
u8 timers_dispatched[WG_N_TIMERS]
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:146
#define pool_get(P, E)
Allocate an object E from a pool P (unspecified alignment).
Definition: pool.h:255
u32 timers[WG_N_TIMERS]
vhost_vring_addr_t addr
Definition: vhost_user.h:130
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: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
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:470
ip4_header_t ip4
noise_remote_t remote
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:172
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
vnet_main_t * vnet_get_main(void)
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:358
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
int __clib_unused rv
Definition: application.c:491
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:553
vl_api_interface_index_t sw_if_index
Definition: wireguard.api:34
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:305
#define vec_dup(V)
Return copy of vector (no header, no alignment)
Definition: vec.h:444
add paths with [mpls] path extensions
Definition: fib_source.h:208
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)
vlib_main_t * vm
X-connect all packets from the HOST to the PHY.
Definition: nat44_ei.c:3047
fib_protocol_t ip_address_to_46(const ip_address_t *addr, ip46_address_t *a)
Definition: ip_types.c:252
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:261
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:395
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:213
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:302
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:38
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:88
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:49
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
vl_api_ip4_address_t dst
Definition: pnat.api:41
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:257
#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:571
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
u32 output_thread_index
rewrite
Definition: pnat.api:158
static uword pool_elts(void *v)
Number of active elements in a pool.
Definition: pool.h:127