FD.io VPP  v21.06-1-gbb7418cf9
Vector Packet Processing
wireguard_input.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 <vlib/vlib.h>
18 #include <vnet/vnet.h>
19 #include <vppinfra/error.h>
20 #include <wireguard/wireguard.h>
21 
23 #include <wireguard/wireguard_if.h>
24 
25 #define foreach_wg_input_error \
26  _ (NONE, "No error") \
27  _ (HANDSHAKE_MAC, "Invalid MAC handshake") \
28  _ (PEER, "Peer error") \
29  _ (INTERFACE, "Interface error") \
30  _ (DECRYPTION, "Failed during decryption") \
31  _ (KEEPALIVE_SEND, "Failed while sending Keepalive") \
32  _ (HANDSHAKE_SEND, "Failed while sending Handshake") \
33  _ (HANDSHAKE_RECEIVE, "Failed while receiving Handshake") \
34  _ (TOO_BIG, "Packet too big") \
35  _ (UNDEFINED, "Undefined error")
36 
37 typedef enum
38 {
39 #define _(sym,str) WG_INPUT_ERROR_##sym,
41 #undef _
44 
45 static char *wg_input_error_strings[] = {
46 #define _(sym,string) string,
48 #undef _
49 };
50 
51 typedef struct
52 {
58 
59 u8 *
60 format_wg_message_type (u8 * s, va_list * args)
61 {
62  message_type_t type = va_arg (*args, message_type_t);
63 
64  switch (type)
65  {
66 #define _(v,a) case MESSAGE_##v: return (format (s, "%s", a));
68 #undef _
69  }
70  return (format (s, "unknown"));
71 }
72 
73 /* packet trace format function */
74 static u8 *
75 format_wg_input_trace (u8 * s, va_list * args)
76 {
77  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
78  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
79 
80  wg_input_trace_t *t = va_arg (*args, wg_input_trace_t *);
81 
82  s = format (s, "WG input: \n");
83  s = format (s, " Type: %U\n", format_wg_message_type, t->type);
84  s = format (s, " peer: %d\n", t->peer);
85  s = format (s, " Length: %d\n", t->current_length);
86  s = format (s, " Keepalive: %s", t->is_keepalive ? "true" : "false");
87 
88  return s;
89 }
90 
91 typedef enum
92 {
100 
101 /* static void */
102 /* set_peer_address (wg_peer_t * peer, ip4_address_t ip4, u16 udp_port) */
103 /* { */
104 /* if (peer) */
105 /* { */
106 /* ip46_address_set_ip4 (&peer->dst.addr, &ip4); */
107 /* peer->dst.port = udp_port; */
108 /* } */
109 /* } */
110 
111 static wg_input_error_t
113 {
114  ASSERT (vm->thread_index == 0);
115 
116  enum cookie_mac_state mac_state;
117  bool packet_needs_cookie;
118  bool under_load;
119  wg_if_t *wg_if;
120  wg_peer_t *peer = NULL;
121 
122  void *current_b_data = vlib_buffer_get_current (b);
123 
124  udp_header_t *uhd = current_b_data - sizeof (udp_header_t);
125  ip4_header_t *iph =
126  current_b_data - sizeof (udp_header_t) - sizeof (ip4_header_t);
127  ip4_address_t ip4_src = iph->src_address;
128  u16 udp_src_port = clib_host_to_net_u16 (uhd->src_port);;
129  u16 udp_dst_port = clib_host_to_net_u16 (uhd->dst_port);;
130 
131  message_header_t *header = current_b_data;
132  under_load = false;
133 
134  wg_if = wg_if_get_by_port (udp_dst_port);
135 
136  if (NULL == wg_if)
137  return WG_INPUT_ERROR_INTERFACE;
138 
139  if (PREDICT_FALSE (header->type == MESSAGE_HANDSHAKE_COOKIE))
140  {
142  (message_handshake_cookie_t *) current_b_data;
143  u32 *entry =
145  if (entry)
146  peer = wg_peer_get (*entry);
147  else
148  return WG_INPUT_ERROR_PEER;
149 
150  // TODO: Implement cookie_maker_consume_payload
151 
152  return WG_INPUT_ERROR_NONE;
153  }
154 
155  u32 len = (header->type == MESSAGE_HANDSHAKE_INITIATION ?
158 
159  message_macs_t *macs = (message_macs_t *)
160  ((u8 *) current_b_data + len - sizeof (*macs));
161 
162  mac_state =
163  cookie_checker_validate_macs (vm, &wg_if->cookie_checker, macs,
164  current_b_data, len, under_load, ip4_src,
165  udp_src_port);
166 
167  if ((under_load && mac_state == VALID_MAC_WITH_COOKIE)
168  || (!under_load && mac_state == VALID_MAC_BUT_NO_COOKIE))
169  packet_needs_cookie = false;
170  else if (under_load && mac_state == VALID_MAC_BUT_NO_COOKIE)
171  packet_needs_cookie = true;
172  else
173  return WG_INPUT_ERROR_HANDSHAKE_MAC;
174 
175  switch (header->type)
176  {
177  case MESSAGE_HANDSHAKE_INITIATION:
178  {
179  message_handshake_initiation_t *message = current_b_data;
180 
181  if (packet_needs_cookie)
182  {
183  // TODO: Add processing
184  }
185  noise_remote_t *rp;
187  (vm, noise_local_get (wg_if->local_idx), &rp,
188  message->sender_index, message->unencrypted_ephemeral,
189  message->encrypted_static, message->encrypted_timestamp))
190  {
191  peer = wg_peer_get (rp->r_peer_idx);
192  }
193  else
194  {
195  return WG_INPUT_ERROR_PEER;
196  }
197 
198  // set_peer_address (peer, ip4_src, udp_src_port);
199  if (PREDICT_FALSE (!wg_send_handshake_response (vm, peer)))
200  {
202  WG_INPUT_ERROR_HANDSHAKE_SEND, 1);
203  }
204  break;
205  }
206  case MESSAGE_HANDSHAKE_RESPONSE:
207  {
208  message_handshake_response_t *resp = current_b_data;
209  u32 *entry =
211 
212  if (PREDICT_TRUE (entry != NULL))
213  {
214  peer = wg_peer_get (*entry);
215  if (peer->is_dead)
216  return WG_INPUT_ERROR_PEER;
217  }
218  else
219  return WG_INPUT_ERROR_PEER;
220 
222  (vm, &peer->remote, resp->sender_index,
224  resp->encrypted_nothing))
225  {
226  return WG_INPUT_ERROR_PEER;
227  }
228  if (packet_needs_cookie)
229  {
230  // TODO: Add processing
231  }
232 
233  // set_peer_address (peer, ip4_src, udp_src_port);
234  if (noise_remote_begin_session (vm, &peer->remote))
235  {
236 
239  if (PREDICT_FALSE (!wg_send_keepalive (vm, peer)))
240  {
242  WG_INPUT_ERROR_KEEPALIVE_SEND,
243  1);
244  }
245  }
246  break;
247  }
248  default:
249  return WG_INPUT_ERROR_HANDSHAKE_RECEIVE;
250  }
251 
254  return WG_INPUT_ERROR_NONE;
255 }
256 
259  const ip4_address_t * ip4)
260 {
261  switch (p1->fp_proto)
262  {
263  case FIB_PROTOCOL_IP4:
265  &p1->fp_addr.ip4,
266  ip4, p1->fp_len) != 0);
267  case FIB_PROTOCOL_IP6:
268  return (false);
269  case FIB_PROTOCOL_MPLS:
270  break;
271  }
272  return (false);
273 }
274 
278 {
279  message_type_t header_type;
281  u32 *from;
285 
286  from = vlib_frame_vector_args (frame);
287  n_left_from = frame->n_vectors;
288  b = bufs;
289  next = nexts;
290 
291  vlib_get_buffers (vm, from, bufs, n_left_from);
292 
293  wg_main_t *wmp = &wg_main;
294  wg_peer_t *peer = NULL;
295 
296  while (n_left_from > 0)
297  {
298  bool is_keepalive = false;
299  next[0] = WG_INPUT_NEXT_PUNT;
300  header_type =
302  u32 *peer_idx;
303 
304  if (PREDICT_TRUE (header_type == MESSAGE_DATA))
305  {
307 
308  peer_idx = wg_index_table_lookup (&wmp->index_table,
309  data->receiver_index);
310 
311  if (peer_idx)
312  {
313  peer = wg_peer_get (*peer_idx);
314  }
315  else
316  {
317  next[0] = WG_INPUT_NEXT_ERROR;
318  b[0]->error = node->errors[WG_INPUT_ERROR_PEER];
319  goto out;
320  }
321 
322  if (PREDICT_FALSE (~0 == peer->input_thread_index))
323  {
324  /* this is the first packet to use this peer, claim the peer
325  * for this thread.
326  */
328  wg_peer_assign_thread (thread_index));
329  }
330 
331  if (PREDICT_TRUE (thread_index != peer->input_thread_index))
332  {
333  next[0] = WG_INPUT_NEXT_HANDOFF_DATA;
334  goto next;
335  }
336 
337  u16 encr_len = b[0]->current_length - sizeof (message_data_t);
338  u16 decr_len = encr_len - NOISE_AUTHTAG_LEN;
339  if (PREDICT_FALSE (decr_len >= WG_DEFAULT_DATA_SIZE))
340  {
341  b[0]->error = node->errors[WG_INPUT_ERROR_TOO_BIG];
342  goto out;
343  }
344 
345  u8 *decr_data = wmp->per_thread_data[thread_index].data;
346 
347  enum noise_state_crypt state_cr = noise_remote_decrypt (vm,
348  &peer->remote,
349  data->receiver_index,
350  data->counter,
351  data->encrypted_data,
352  encr_len,
353  decr_data);
354 
355  if (PREDICT_FALSE (state_cr == SC_CONN_RESET))
356  {
358  }
359  else if (PREDICT_FALSE (state_cr == SC_KEEP_KEY_FRESH))
360  {
361  wg_send_handshake_from_mt (*peer_idx, false);
362  }
363  else if (PREDICT_FALSE (state_cr == SC_FAILED))
364  {
365  next[0] = WG_INPUT_NEXT_ERROR;
366  b[0]->error = node->errors[WG_INPUT_ERROR_DECRYPTION];
367  goto out;
368  }
369 
370  clib_memcpy (vlib_buffer_get_current (b[0]), decr_data, decr_len);
371  b[0]->current_length = decr_len;
373  VNET_BUFFER_OFFLOAD_F_UDP_CKSUM);
374 
377 
378  /* Keepalive packet has zero length */
379  if (decr_len == 0)
380  {
381  is_keepalive = true;
382  goto out;
383  }
384 
386 
388 
389  const wg_peer_allowed_ip_t *allowed_ip;
390  bool allowed = false;
391 
392  /*
393  * we could make this into an ACL, but the expectation
394  * is that there aren't many allowed IPs and thus a linear
395  * walk is fater than an ACL
396  */
397  vec_foreach (allowed_ip, peer->allowed_ips)
398  {
399  if (fib_prefix_is_cover_addr_4 (&allowed_ip->prefix,
400  &iph->src_address))
401  {
402  allowed = true;
403  break;
404  }
405  }
406  if (allowed)
407  {
408  vnet_buffer (b[0])->sw_if_index[VLIB_RX] = peer->wg_sw_if_index;
409  next[0] = WG_INPUT_NEXT_IP4_INPUT;
410  }
411  }
412  else
413  {
414  peer_idx = NULL;
415 
416  /* Handshake packets should be processed in main thread */
417  if (thread_index != 0)
418  {
420  goto next;
421  }
422 
423  wg_input_error_t ret = wg_handshake_process (vm, wmp, b[0]);
424  if (ret != WG_INPUT_ERROR_NONE)
425  {
426  next[0] = WG_INPUT_NEXT_ERROR;
427  b[0]->error = node->errors[ret];
428  }
429  }
430 
431  out:
432  if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
433  && (b[0]->flags & VLIB_BUFFER_IS_TRACED)))
434  {
435  wg_input_trace_t *t = vlib_add_trace (vm, node, b[0], sizeof (*t));
436  t->type = header_type;
437  t->current_length = b[0]->current_length;
438  t->is_keepalive = is_keepalive;
439  t->peer = peer_idx ? *peer_idx : INDEX_INVALID;
440  }
441  next:
442  n_left_from -= 1;
443  next += 1;
444  b += 1;
445  }
446  vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
447 
448  return frame->n_vectors;
449 }
450 
451 /* *INDENT-OFF* */
453 {
454  .name = "wg-input",
455  .vector_size = sizeof (u32),
456  .format_trace = format_wg_input_trace,
458  .n_errors = ARRAY_LEN (wg_input_error_strings),
459  .error_strings = wg_input_error_strings,
460  .n_next_nodes = WG_INPUT_N_NEXT,
461  /* edit / add dispositions here */
462  .next_nodes = {
463  [WG_INPUT_NEXT_HANDOFF_HANDSHAKE] = "wg-handshake-handoff",
464  [WG_INPUT_NEXT_HANDOFF_DATA] = "wg-input-data-handoff",
465  [WG_INPUT_NEXT_IP4_INPUT] = "ip4-input-no-checksum",
466  [WG_INPUT_NEXT_PUNT] = "error-punt",
467  [WG_INPUT_NEXT_ERROR] = "error-drop",
468  },
469 };
470 /* *INDENT-ON* */
471 
472 /*
473  * fd.io coding-style-patch-verification: ON
474  *
475  * Local Variables:
476  * eval: (c-set-style "gnu")
477  * End:
478  */
#define WG_DEFAULT_DATA_SIZE
Definition: wireguard.h:22
wg_per_thread_data_t * per_thread_data
Definition: wireguard.h:43
u32 flags
buffer flags: VLIB_BUFFER_FREE_LIST_INDEX_MASK: bits used to store free list index, VLIB_BUFFER_IS_TRACED: trace this buffer.
Definition: buffer.h:133
fib_protocol_t fp_proto
protocol type
Definition: fib_types.h:211
u8 encrypted_static[noise_encrypted_len(NOISE_PUBLIC_KEY_LEN)]
wg_peer_allowed_ip_t * allowed_ips
struct message_handshake_initiation message_handshake_initiation_t
#define CLIB_UNUSED(x)
Definition: clib.h:90
noise_state_crypt
ip4_address_t src_address
Definition: ip4_packet.h:125
#define NOISE_AUTHTAG_LEN
u32 thread_index
#define PREDICT_TRUE(x)
Definition: clib.h:125
u16 nexts[VLIB_FRAME_SIZE]
u32 thread_index
Definition: main.h:213
u16 current_length
Nbytes between current data and the end of this buffer.
Definition: buffer.h:122
vlib_main_t vlib_node_runtime_t vlib_frame_t * frame
Definition: nat44_ei.c:3048
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
void wg_timers_any_authenticated_packet_traversal(wg_peer_t *peer)
message_type_t type
static uword ip4_destination_matches_route(const ip4_main_t *im, const ip4_address_t *key, const ip4_address_t *dest, uword dest_length)
Definition: ip4.h:188
#define VLIB_NODE_FN(node)
Definition: node.h:202
unsigned char u8
Definition: types.h:56
vlib_buffer_t ** b
u8 * format_wg_message_type(u8 *s, va_list *args)
u8 data[128]
Definition: ipsec_types.api:92
static wg_input_error_t wg_handshake_process(vlib_main_t *vm, wg_main_t *wmp, vlib_buffer_t *b)
static u8 * format_wg_input_trace(u8 *s, va_list *args)
unsigned int u32
Definition: types.h:88
#define clib_memcpy(d, s, n)
Definition: string.h:197
bool noise_remote_begin_session(vlib_main_t *vm, noise_remote_t *r)
#define static_always_inline
Definition: clib.h:112
noise_remote_t remote
vlib_get_buffers(vm, from, b, n_left_from)
bool wg_send_handshake_response(vlib_main_t *vm, wg_peer_t *peer)
description fragment has unexpected format
Definition: map.api:433
Aggregate type for a prefix.
Definition: fib_types.h:202
void wg_send_handshake_from_mt(u32 peer_idx, bool is_retry)
vlib_buffer_enqueue_to_next(vm, node, from,(u16 *) nexts, frame->n_vectors)
u16 fp_len
The mask length.
Definition: fib_types.h:206
u32 local_idx
Definition: wireguard_if.h:30
#define VLIB_FRAME_SIZE
Definition: node.h:369
void wg_timers_any_authenticated_packet_received(wg_peer_t *peer)
vl_api_fib_path_type_t type
Definition: fib_types.api:123
vlib_error_t error
Error code for buffers to be enqueued to error handler.
Definition: buffer.h:145
u16 * next
u16 udp_src_port[default=4500]
u8 encrypted_nothing[noise_encrypted_len(0)]
ip46_address_t fp_addr
The address type is not deriveable from the fp_addr member.
Definition: fib_types.h:225
wg_input_error_t
unsigned short u16
Definition: types.h:57
description malformed packet
Definition: map.api:445
static void * vlib_buffer_get_current(vlib_buffer_t *b)
Get pointer to current data to process.
Definition: buffer.h:257
#define PREDICT_FALSE(x)
Definition: clib.h:124
vl_api_ip4_address_t ip4
Definition: one.api:376
vlib_main_t * vm
X-connect all packets from the HOST to the PHY.
Definition: nat44_ei.c:3047
vl_api_address_t peer
Definition: teib.api:28
static void vlib_node_increment_counter(vlib_main_t *vm, u32 node_index, u32 counter_index, u64 increment)
Definition: node_funcs.h:1244
u8 len
Definition: ip_types.api:103
void wg_timers_data_received(wg_peer_t *peer)
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:169
wg_input_next_t
u8 data[WG_DEFAULT_DATA_SIZE]
Definition: wireguard.h:29
vlib_node_registration_t wg_input_node
(constructor) VLIB_REGISTER_NODE (wg_input_node)
#define ARRAY_LEN(x)
Definition: clib.h:70
wg_index_table_t index_table
Definition: wireguard.h:38
#define clib_atomic_cmp_and_swap(addr, old, new)
Definition: atomics.h:37
bool is_dead
struct message_data message_data_t
bool noise_consume_response(vlib_main_t *vm, noise_remote_t *r, uint32_t s_idx, uint32_t r_idx, uint8_t ue[NOISE_PUBLIC_KEY_LEN], uint8_t en[0+NOISE_AUTHTAG_LEN])
static_always_inline bool fib_prefix_is_cover_addr_4(const fib_prefix_t *p1, const ip4_address_t *ip4)
bool noise_consume_initiation(vlib_main_t *vm, noise_local_t *l, noise_remote_t **rp, uint32_t s_idx, uint8_t ue[NOISE_PUBLIC_KEY_LEN], uint8_t es[NOISE_PUBLIC_KEY_LEN+NOISE_AUTHTAG_LEN], uint8_t ets[NOISE_TIMESTAMP_LEN+NOISE_AUTHTAG_LEN])
u16 udp_dst_port[default=4500]
#define ASSERT(truth)
uint32_t r_peer_idx
wg_main_t wg_main
Definition: wireguard.c:26
static char * wg_input_error_strings[]
u8 encrypted_timestamp[noise_encrypted_len(NOISE_TIMESTAMP_LEN)]
u8 unencrypted_ephemeral[NOISE_PUBLIC_KEY_LEN]
u32 wg_sw_if_index
static_always_inline wg_if_t * wg_if_get_by_port(u16 port)
Definition: wireguard_if.h:78
vlib_main_t vlib_node_runtime_t * node
Definition: nat44_ei.c:3047
#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 buffer representation.
Definition: buffer.h:111
static void * vlib_frame_vector_args(vlib_frame_t *f)
Get pointer to frame vector data.
Definition: node_funcs.h:301
void wg_timers_session_derived(wg_peer_t *peer)
static u32 wg_peer_assign_thread(u32 thread_id)
#define foreach_wg_input_error
static_always_inline void vnet_buffer_offload_flags_clear(vlib_buffer_t *b, vnet_buffer_oflags_t oflags)
Definition: buffer.h:538
#define vnet_buffer(b)
Definition: buffer.h:437
u32 input_thread_index
ip4_main_t ip4_main
Global ip4 main structure.
Definition: ip4_forward.c:1105
bool wg_send_keepalive(vlib_main_t *vm, wg_peer_t *peer)
static_always_inline noise_local_t * noise_local_get(uint32_t locali)
message_type_t type
enum noise_state_crypt noise_remote_decrypt(vlib_main_t *vm, noise_remote_t *r, uint32_t r_idx, uint64_t nonce, uint8_t *src, size_t srclen, uint8_t *dst)
#define vec_foreach(var, vec)
Vector iterator.
u8 unencrypted_ephemeral[NOISE_PUBLIC_KEY_LEN]
void * vlib_add_trace(vlib_main_t *vm, vlib_node_runtime_t *r, vlib_buffer_t *b, u32 n_data_bytes)
Definition: trace.c:628
cookie_checker_t cookie_checker
Definition: wireguard_if.h:31
enum message_type message_type_t
#define VLIB_NODE_FLAG_TRACE
Definition: node.h:292
static wg_peer_t * wg_peer_get(index_t peeri)
vlib_buffer_t * bufs[VLIB_FRAME_SIZE]
u32 * wg_index_table_lookup(const wg_index_table_t *table, u32 key)
Definition: defs.h:46
void wg_timers_handshake_complete(wg_peer_t *peer)