aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/wireguard
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/wireguard')
-rw-r--r--src/plugins/wireguard/CMakeLists.txt8
-rw-r--r--src/plugins/wireguard/wireguard.api2
-rw-r--r--src/plugins/wireguard/wireguard.c4
-rw-r--r--src/plugins/wireguard/wireguard.h3
-rw-r--r--src/plugins/wireguard/wireguard_api.c28
-rw-r--r--src/plugins/wireguard/wireguard_chachapoly.c8
-rw-r--r--src/plugins/wireguard/wireguard_cli.c12
-rw-r--r--src/plugins/wireguard/wireguard_handoff.c2
-rw-r--r--src/plugins/wireguard/wireguard_if.c16
-rw-r--r--src/plugins/wireguard/wireguard_index_table.c14
-rw-r--r--src/plugins/wireguard/wireguard_index_table.h7
-rw-r--r--src/plugins/wireguard/wireguard_input.c276
-rw-r--r--src/plugins/wireguard/wireguard_noise.c42
-rw-r--r--src/plugins/wireguard/wireguard_noise.h10
-rw-r--r--src/plugins/wireguard/wireguard_output_tun.c306
-rw-r--r--src/plugins/wireguard/wireguard_peer.c70
-rw-r--r--src/plugins/wireguard/wireguard_peer.h3
-rw-r--r--src/plugins/wireguard/wireguard_send.c30
-rw-r--r--src/plugins/wireguard/wireguard_timer.c12
-rw-r--r--src/plugins/wireguard/wireguard_timer.h1
20 files changed, 650 insertions, 204 deletions
diff --git a/src/plugins/wireguard/CMakeLists.txt b/src/plugins/wireguard/CMakeLists.txt
index 31f09f1d8e3..710b6a3b04a 100644
--- a/src/plugins/wireguard/CMakeLists.txt
+++ b/src/plugins/wireguard/CMakeLists.txt
@@ -12,7 +12,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+if(NOT OPENSSL_FOUND)
+ message(WARNING "OpenSSL not found - wireguard plugin disabled")
+ return()
+endif()
+
if (OPENSSL_VERSION VERSION_LESS 1.1.0)
+ message(WARNING "OpenSSL too old - wireguard plugin disabled")
return()
endif()
@@ -54,7 +60,7 @@ add_vpp_plugin(wireguard
wireguard_index_table.h
wireguard_api.c
- LINK_LIBRARIES ${OPENSSL_LIBRARIES}
+ LINK_LIBRARIES ${OPENSSL_CRYPTO_LIBRARIES}
API_FILES
wireguard.api
diff --git a/src/plugins/wireguard/wireguard.api b/src/plugins/wireguard/wireguard.api
index 508c0cf7981..55a36c6f6e5 100644
--- a/src/plugins/wireguard/wireguard.api
+++ b/src/plugins/wireguard/wireguard.api
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-option version = "0.3.0";
+option version = "1.3.0";
import "vnet/interface_types.api";
import "vnet/ip/ip_types.api";
diff --git a/src/plugins/wireguard/wireguard.c b/src/plugins/wireguard/wireguard.c
index 5d73638f8f9..b1c8bc79870 100644
--- a/src/plugins/wireguard/wireguard.c
+++ b/src/plugins/wireguard/wireguard.c
@@ -30,8 +30,6 @@ wg_async_post_next_t wg_decrypt_async_next;
void
wg_set_async_mode (u32 is_enabled)
{
- vnet_crypto_request_async_mode (is_enabled);
-
if (is_enabled)
wg_op_mode_set_ASYNC ();
else
@@ -94,7 +92,6 @@ wg_init (vlib_main_t * vm)
VLIB_INIT_FUNCTION (wg_init);
-/* *INDENT-OFF* */
VNET_FEATURE_INIT (wg4_output_tun, static) = {
.arc_name = "ip4-output",
@@ -113,7 +110,6 @@ VLIB_PLUGIN_REGISTER () =
.version = VPP_BUILD_VER,
.description = "Wireguard Protocol",
};
-/* *INDENT-ON* */
/*
* fd.io coding-style-patch-verification: ON
diff --git a/src/plugins/wireguard/wireguard.h b/src/plugins/wireguard/wireguard.h
index 3a6248ba6b5..05cefc4f073 100644
--- a/src/plugins/wireguard/wireguard.h
+++ b/src/plugins/wireguard/wireguard.h
@@ -31,9 +31,12 @@ typedef struct wg_per_thread_data_t_
{
CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
vnet_crypto_op_t *crypto_ops;
+ vnet_crypto_op_t *chained_crypto_ops;
+ vnet_crypto_op_chunk_t *chunks;
vnet_crypto_async_frame_t **async_frames;
u8 data[WG_DEFAULT_DATA_SIZE];
} wg_per_thread_data_t;
+
typedef struct
{
/* convenience */
diff --git a/src/plugins/wireguard/wireguard_api.c b/src/plugins/wireguard/wireguard_api.c
index c5334e0ecd4..e736efcd6c0 100644
--- a/src/plugins/wireguard/wireguard_api.c
+++ b/src/plugins/wireguard/wireguard_api.c
@@ -346,7 +346,7 @@ vl_api_want_wireguard_peer_events_t_handler (
REPLY_MACRO (VL_API_WANT_WIREGUARD_PEER_EVENTS_REPLY);
}
-void
+static void
wg_api_send_peer_event (vl_api_registration_t *rp, index_t peer_index,
wg_peer_flags flags)
{
@@ -360,10 +360,16 @@ wg_api_send_peer_event (vl_api_registration_t *rp, index_t peer_index,
vl_api_send_msg (rp, (u8 *) mp);
}
-void
-wg_api_peer_event (index_t peeri, wg_peer_flags flags)
+typedef struct
{
- wg_peer_t *peer = wg_peer_get (peeri);
+ index_t peeri;
+ wg_peer_flags flags;
+} wg_api_peer_event_args_t;
+
+static void
+wg_api_peer_event_cb (wg_api_peer_event_args_t *args)
+{
+ wg_peer_t *peer = wg_peer_get (args->peeri);
vpe_client_registration_t *api_client;
vl_api_registration_t *rp;
@@ -372,11 +378,23 @@ wg_api_peer_event (index_t peeri, wg_peer_flags flags)
rp = vl_api_client_index_to_registration (api_client->client_index);
if (rp)
{
- wg_api_send_peer_event (rp, peeri, flags);
+ wg_api_send_peer_event (rp, args->peeri, args->flags);
}
};
}
+void
+wg_api_peer_event (index_t peeri, wg_peer_flags flags)
+{
+ wg_api_peer_event_args_t args = {
+ .peeri = peeri,
+ .flags = flags,
+ };
+
+ vl_api_rpc_call_main_thread (wg_api_peer_event_cb, (u8 *) &args,
+ sizeof (args));
+}
+
static void
vl_api_wg_set_async_mode_t_handler (vl_api_wg_set_async_mode_t *mp)
{
diff --git a/src/plugins/wireguard/wireguard_chachapoly.c b/src/plugins/wireguard/wireguard_chachapoly.c
index 0dd7908d2e2..ad644ff6cb8 100644
--- a/src/plugins/wireguard/wireguard_chachapoly.c
+++ b/src/plugins/wireguard/wireguard_chachapoly.c
@@ -72,11 +72,11 @@ wg_xchacha20poly1305_encrypt (vlib_main_t *vm, u8 *src, u32 src_len, u8 *dst,
u64 h_nonce;
clib_memcpy (&h_nonce, nonce + 16, sizeof (h_nonce));
- h_nonce = le64toh (h_nonce);
+ h_nonce = clib_little_to_host_u64 (h_nonce);
hchacha20 (derived_key, nonce, key);
for (i = 0; i < (sizeof (derived_key) / sizeof (derived_key[0])); i++)
- (derived_key[i]) = htole32 ((derived_key[i]));
+ (derived_key[i]) = clib_host_to_little_u32 ((derived_key[i]));
uint32_t key_idx;
@@ -102,11 +102,11 @@ wg_xchacha20poly1305_decrypt (vlib_main_t *vm, u8 *src, u32 src_len, u8 *dst,
u64 h_nonce;
clib_memcpy (&h_nonce, nonce + 16, sizeof (h_nonce));
- h_nonce = le64toh (h_nonce);
+ h_nonce = clib_little_to_host_u64 (h_nonce);
hchacha20 (derived_key, nonce, key);
for (i = 0; i < (sizeof (derived_key) / sizeof (derived_key[0])); i++)
- (derived_key[i]) = htole32 ((derived_key[i]));
+ (derived_key[i]) = clib_host_to_little_u32 ((derived_key[i]));
uint32_t key_idx;
diff --git a/src/plugins/wireguard/wireguard_cli.c b/src/plugins/wireguard/wireguard_cli.c
index 5fa620507d6..e412fa36c44 100644
--- a/src/plugins/wireguard/wireguard_cli.c
+++ b/src/plugins/wireguard/wireguard_cli.c
@@ -94,14 +94,12 @@ wg_if_create_cli (vlib_main_t * vm,
/*?
* Create a Wireguard interface.
?*/
-/* *INDENT-OFF* */
VLIB_CLI_COMMAND (wg_if_create_command, static) = {
.path = "wireguard create",
.short_help = "wireguard create listen-port <port> "
"private-key <key> src <IP> [generate-key]",
.function = wg_if_create_cli,
};
-/* *INDENT-ON* */
static clib_error_t *
wg_if_delete_cli (vlib_main_t * vm,
@@ -143,13 +141,11 @@ wg_if_delete_cli (vlib_main_t * vm,
/*?
* Delete a Wireguard interface.
?*/
-/* *INDENT-OFF* */
VLIB_CLI_COMMAND (wg_if_delete_command, static) = {
.path = "wireguard delete",
.short_help = "wireguard delete <interface>",
.function = wg_if_delete_cli,
};
-/* *INDENT-ON* */
static clib_error_t *
@@ -251,7 +247,6 @@ done:
return error;
}
-/* *INDENT-OFF* */
VLIB_CLI_COMMAND (wg_peer_add_command, static) = {
.path = "wireguard peer add",
.short_help =
@@ -260,7 +255,6 @@ VLIB_CLI_COMMAND (wg_peer_add_command, static) = {
"dst-port [port_dst] persistent-keepalive [keepalive_interval]",
.function = wg_peer_add_command_fn,
};
-/* *INDENT-ON* */
static clib_error_t *
wg_peer_remove_command_fn (vlib_main_t * vm,
@@ -299,14 +293,12 @@ done:
return error;
}
-/* *INDENT-OFF* */
VLIB_CLI_COMMAND (wg_peer_remove_command, static) =
{
.path = "wireguard peer remove",
.short_help = "wireguard peer remove <index>",
.function = wg_peer_remove_command_fn,
};
-/* *INDENT-ON* */
static walk_rc_t
wg_peer_show_one (index_t peeri, void *arg)
@@ -325,14 +317,12 @@ wg_show_peer_command_fn (vlib_main_t * vm,
return NULL;
}
-/* *INDENT-OFF* */
VLIB_CLI_COMMAND (wg_show_peers_command, static) =
{
.path = "show wireguard peer",
.short_help = "show wireguard peer",
.function = wg_show_peer_command_fn,
};
-/* *INDENT-ON* */
static walk_rc_t
wg_if_show_one (index_t itfi, void *arg)
@@ -355,7 +345,6 @@ wg_show_if_command_fn (vlib_main_t * vm,
return NULL;
}
-/* *INDENT-OFF* */
VLIB_CLI_COMMAND (wg_show_itfs_command, static) =
{
.path = "show wireguard interface",
@@ -417,7 +406,6 @@ VLIB_CLI_COMMAND (wg_show_modemode_command, static) = {
.function = wg_show_mode_command_fn,
};
-/* *INDENT-ON* */
/*
* fd.io coding-style-patch-verification: ON
diff --git a/src/plugins/wireguard/wireguard_handoff.c b/src/plugins/wireguard/wireguard_handoff.c
index 5f3dc14b412..195baf209a0 100644
--- a/src/plugins/wireguard/wireguard_handoff.c
+++ b/src/plugins/wireguard/wireguard_handoff.c
@@ -183,7 +183,6 @@ VLIB_NODE_FN (wg6_output_tun_handoff)
WG_HANDOFF_OUT_TUN);
}
-/* *INDENT-OFF* */
VLIB_REGISTER_NODE (wg4_handshake_handoff) =
{
.name = "wg4-handshake-handoff",
@@ -267,7 +266,6 @@ VLIB_REGISTER_NODE (wg6_output_tun_handoff) =
[0] = "error-drop",
},
};
-/* *INDENT-ON* */
/*
* fd.io coding-style-patch-verification: ON
diff --git a/src/plugins/wireguard/wireguard_if.c b/src/plugins/wireguard/wireguard_if.c
index a869df08ce2..afeeda1dd2b 100644
--- a/src/plugins/wireguard/wireguard_if.c
+++ b/src/plugins/wireguard/wireguard_if.c
@@ -116,20 +116,20 @@ wg_remote_get (const uint8_t public[NOISE_PUBLIC_KEY_LEN])
}
static uint32_t
-wg_index_set (noise_remote_t * remote)
+wg_index_set (vlib_main_t *vm, noise_remote_t *remote)
{
wg_main_t *wmp = &wg_main;
u32 rnd_seed = (u32) (vlib_time_now (wmp->vlib_main) * 1e6);
u32 ret =
- wg_index_table_add (&wmp->index_table, remote->r_peer_idx, rnd_seed);
+ wg_index_table_add (vm, &wmp->index_table, remote->r_peer_idx, rnd_seed);
return ret;
}
static void
-wg_index_drop (uint32_t key)
+wg_index_drop (vlib_main_t *vm, uint32_t key)
{
wg_main_t *wmp = &wg_main;
- wg_index_table_del (&wmp->index_table, key);
+ wg_index_table_del (vm, &wmp->index_table, key);
}
static clib_error_t *
@@ -169,7 +169,6 @@ wg_if_update_adj (vnet_main_t * vnm, u32 sw_if_index, adj_index_t ai)
}
-/* *INDENT-OFF* */
VNET_DEVICE_CLASS (wg_if_device_class) = {
.name = "Wireguard Tunnel",
.format_device_name = format_wg_if_name,
@@ -181,7 +180,6 @@ VNET_HW_INTERFACE_CLASS(wg_hw_interface_class) = {
.update_adjacency = wg_if_update_adj,
.flags = VNET_HW_INTERFACE_CLASS_FLAG_NBMA,
};
-/* *INDENT-ON* */
/*
* Maintain a bitmap of allocated wg_if instance numbers.
@@ -272,13 +270,11 @@ wg_if_create (u32 user_instance,
if (instance == ~0)
return VNET_API_ERROR_INVALID_REGISTRATION;
- /* *INDENT-OFF* */
struct noise_upcall upcall = {
.u_remote_get = wg_remote_get,
.u_index_set = wg_index_set,
.u_index_drop = wg_index_drop,
};
- /* *INDENT-ON* */
pool_get (noise_local_pool, local);
@@ -419,13 +415,11 @@ wg_if_walk (wg_if_walk_cb_t fn, void *data)
{
index_t wgii;
- /* *INDENT-OFF* */
pool_foreach_index (wgii, wg_if_pool)
{
if (WALK_STOP == fn(wgii, data))
break;
}
- /* *INDENT-ON* */
}
index_t
@@ -433,12 +427,10 @@ wg_if_peer_walk (wg_if_t * wgi, wg_if_peer_walk_cb_t fn, void *data)
{
index_t peeri, val;
- /* *INDENT-OFF* */
hash_foreach (peeri, val, wgi->peers, {
if (WALK_STOP == fn (peeri, data))
return peeri;
});
- /* *INDENT-ON* */
return INDEX_INVALID;
}
diff --git a/src/plugins/wireguard/wireguard_index_table.c b/src/plugins/wireguard/wireguard_index_table.c
index 5f81204b4c0..da53bfd75f1 100644
--- a/src/plugins/wireguard/wireguard_index_table.c
+++ b/src/plugins/wireguard/wireguard_index_table.c
@@ -13,13 +13,15 @@
* limitations under the License.
*/
+#include <vlib/vlib.h>
#include <vppinfra/hash.h>
#include <vppinfra/pool.h>
#include <vppinfra/random.h>
#include <wireguard/wireguard_index_table.h>
u32
-wg_index_table_add (wg_index_table_t * table, u32 peer_pool_idx, u32 rnd_seed)
+wg_index_table_add (vlib_main_t *vm, wg_index_table_t *table,
+ u32 peer_pool_idx, u32 rnd_seed)
{
u32 key;
@@ -29,19 +31,25 @@ wg_index_table_add (wg_index_table_t * table, u32 peer_pool_idx, u32 rnd_seed)
if (hash_get (table->hash, key))
continue;
+ vlib_worker_thread_barrier_sync (vm);
hash_set (table->hash, key, peer_pool_idx);
+ vlib_worker_thread_barrier_release (vm);
break;
}
return key;
}
void
-wg_index_table_del (wg_index_table_t * table, u32 key)
+wg_index_table_del (vlib_main_t *vm, wg_index_table_t *table, u32 key)
{
uword *p;
p = hash_get (table->hash, key);
if (p)
- hash_unset (table->hash, key);
+ {
+ vlib_worker_thread_barrier_sync (vm);
+ hash_unset (table->hash, key);
+ vlib_worker_thread_barrier_release (vm);
+ }
}
u32 *
diff --git a/src/plugins/wireguard/wireguard_index_table.h b/src/plugins/wireguard/wireguard_index_table.h
index 67cae1f49d5..e9aa374c0ca 100644
--- a/src/plugins/wireguard/wireguard_index_table.h
+++ b/src/plugins/wireguard/wireguard_index_table.h
@@ -16,6 +16,7 @@
#ifndef __included_wg_index_table_h__
#define __included_wg_index_table_h__
+#include <vlib/vlib.h>
#include <vppinfra/types.h>
typedef struct
@@ -23,9 +24,9 @@ typedef struct
uword *hash;
} wg_index_table_t;
-u32 wg_index_table_add (wg_index_table_t * table, u32 peer_pool_idx,
- u32 rnd_seed);
-void wg_index_table_del (wg_index_table_t * table, u32 key);
+u32 wg_index_table_add (vlib_main_t *vm, wg_index_table_t *table,
+ u32 peer_pool_idx, u32 rnd_seed);
+void wg_index_table_del (vlib_main_t *vm, wg_index_table_t *table, u32 key);
u32 *wg_index_table_lookup (const wg_index_table_t * table, u32 key);
#endif //__included_wg_index_table_h__
diff --git a/src/plugins/wireguard/wireguard_input.c b/src/plugins/wireguard/wireguard_input.c
index f4d9132d948..1eb7fbfed0b 100644
--- a/src/plugins/wireguard/wireguard_input.c
+++ b/src/plugins/wireguard/wireguard_input.c
@@ -34,7 +34,7 @@
_ (HANDSHAKE_RECEIVE, "Failed while receiving Handshake") \
_ (COOKIE_DECRYPTION, "Failed during Cookie decryption") \
_ (COOKIE_SEND, "Failed during sending Cookie") \
- _ (TOO_BIG, "Packet too big") \
+ _ (NO_BUFFERS, "No buffers") \
_ (UNDEFINED, "Undefined error") \
_ (CRYPTO_ENGINE_ERROR, "crypto engine error (packet dropped)")
@@ -266,10 +266,6 @@ wg_handshake_process (vlib_main_t *vm, wg_main_t *wmp, vlib_buffer_t *b,
vlib_node_increment_counter (vm, node_idx,
WG_INPUT_ERROR_HANDSHAKE_SEND, 1);
}
- else
- {
- wg_peer_update_flags (rp->r_peer_idx, WG_PEER_ESTABLISHED, true);
- }
break;
}
case MESSAGE_HANDSHAKE_RESPONSE:
@@ -343,27 +339,35 @@ wg_input_post_process (vlib_main_t *vm, vlib_buffer_t *b, u16 *next,
bool *is_keepalive)
{
next[0] = WG_INPUT_NEXT_PUNT;
+ noise_keypair_t *kp;
+ vlib_buffer_t *lb;
- noise_keypair_t *kp =
- wg_get_active_keypair (&peer->remote, data->receiver_index);
+ if ((kp = wg_get_active_keypair (&peer->remote, data->receiver_index)) ==
+ NULL)
+ return -1;
if (!noise_counter_recv (&kp->kp_ctr, data->counter))
{
return -1;
}
- u16 encr_len = b->current_length - sizeof (message_data_t);
+ lb = b;
+ /* Find last buffer in the chain */
+ while (lb->flags & VLIB_BUFFER_NEXT_PRESENT)
+ lb = vlib_get_buffer (vm, lb->next_buffer);
+
+ u16 encr_len = vlib_buffer_length_in_chain (vm, b) - sizeof (message_data_t);
u16 decr_len = encr_len - NOISE_AUTHTAG_LEN;
vlib_buffer_advance (b, sizeof (message_data_t));
- b->current_length = decr_len;
+ vlib_buffer_chain_increase_length (b, lb, -NOISE_AUTHTAG_LEN);
vnet_buffer_offload_flags_clear (b, VNET_BUFFER_OFFLOAD_F_UDP_CKSUM);
/* Keepalive packet has zero length */
if (decr_len == 0)
{
*is_keepalive = true;
- return -1;
+ return 0;
}
wg_timers_data_received (peer);
@@ -435,9 +439,75 @@ wg_input_process_ops (vlib_main_t *vm, vlib_node_runtime_t *node,
}
}
+static_always_inline void
+wg_input_process_chained_ops (vlib_main_t *vm, vlib_node_runtime_t *node,
+ vnet_crypto_op_t *ops, vlib_buffer_t *b[],
+ u16 *nexts, vnet_crypto_op_chunk_t *chunks,
+ u16 drop_next)
+{
+ u32 n_fail, n_ops = vec_len (ops);
+ vnet_crypto_op_t *op = ops;
+
+ if (n_ops == 0)
+ return;
+
+ n_fail = n_ops - vnet_crypto_process_chained_ops (vm, op, chunks, n_ops);
+
+ while (n_fail)
+ {
+ ASSERT (op - ops < n_ops);
+
+ if (op->status != VNET_CRYPTO_OP_STATUS_COMPLETED)
+ {
+ u32 bi = op->user_data;
+ b[bi]->error = node->errors[WG_INPUT_ERROR_DECRYPTION];
+ nexts[bi] = drop_next;
+ n_fail--;
+ }
+ op++;
+ }
+}
+
+static_always_inline void
+wg_input_chain_crypto (vlib_main_t *vm, wg_per_thread_data_t *ptd,
+ vlib_buffer_t *b, vlib_buffer_t *lb, u8 *start,
+ u32 start_len, u16 *n_ch)
+{
+ vnet_crypto_op_chunk_t *ch;
+ vlib_buffer_t *cb = b;
+ u32 n_chunks = 1;
+
+ vec_add2 (ptd->chunks, ch, 1);
+ ch->len = start_len;
+ ch->src = ch->dst = start;
+ cb = vlib_get_buffer (vm, cb->next_buffer);
+
+ while (1)
+ {
+ vec_add2 (ptd->chunks, ch, 1);
+ n_chunks += 1;
+ if (lb == cb)
+ ch->len = cb->current_length - NOISE_AUTHTAG_LEN;
+ else
+ ch->len = cb->current_length;
+
+ ch->src = ch->dst = vlib_buffer_get_current (cb);
+
+ if (!(cb->flags & VLIB_BUFFER_NEXT_PRESENT))
+ break;
+
+ cb = vlib_get_buffer (vm, cb->next_buffer);
+ }
+
+ if (n_ch)
+ *n_ch = n_chunks;
+}
+
always_inline void
-wg_prepare_sync_dec_op (vlib_main_t *vm, vnet_crypto_op_t **crypto_ops,
- u8 *src, u32 src_len, u8 *dst, u8 *aad, u32 aad_len,
+wg_prepare_sync_dec_op (vlib_main_t *vm, wg_per_thread_data_t *ptd,
+ vlib_buffer_t *b, vlib_buffer_t *lb,
+ vnet_crypto_op_t **crypto_ops, u8 *src, u32 src_len,
+ u8 *dst, u8 *aad, u32 aad_len,
vnet_crypto_key_index_t key_index, u32 bi, u8 *iv)
{
vnet_crypto_op_t _op, *op = &_op;
@@ -447,16 +517,28 @@ wg_prepare_sync_dec_op (vlib_main_t *vm, vnet_crypto_op_t **crypto_ops,
vnet_crypto_op_init (op, VNET_CRYPTO_OP_CHACHA20_POLY1305_DEC);
op->tag_len = NOISE_AUTHTAG_LEN;
- op->tag = src + src_len;
- op->src = !src ? src_ : src;
- op->len = src_len;
- op->dst = dst;
+ op->tag = vlib_buffer_get_tail (lb) - NOISE_AUTHTAG_LEN;
op->key_index = key_index;
op->aad = aad;
op->aad_len = aad_len;
op->iv = iv;
op->user_data = bi;
op->flags |= VNET_CRYPTO_OP_FLAG_HMAC_CHECK;
+
+ if (b != lb)
+ {
+ /* Chained buffers */
+ op->flags |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS;
+ op->chunk_index = vec_len (ptd->chunks);
+ wg_input_chain_crypto (vm, ptd, b, lb, src, src_len + NOISE_AUTHTAG_LEN,
+ &op->n_chunks);
+ }
+ else
+ {
+ op->src = !src ? src_ : src;
+ op->len = src_len;
+ op->dst = dst;
+ }
}
static_always_inline void
@@ -487,10 +569,10 @@ static_always_inline enum noise_state_crypt
wg_input_process (vlib_main_t *vm, wg_per_thread_data_t *ptd,
vnet_crypto_op_t **crypto_ops,
vnet_crypto_async_frame_t **async_frame, vlib_buffer_t *b,
- u32 buf_idx, noise_remote_t *r, uint32_t r_idx,
- uint64_t nonce, uint8_t *src, size_t srclen, uint8_t *dst,
- u32 from_idx, u8 *iv, f64 time, u8 is_async,
- u16 async_next_node)
+ vlib_buffer_t *lb, u32 buf_idx, noise_remote_t *r,
+ uint32_t r_idx, uint64_t nonce, uint8_t *src, size_t srclen,
+ size_t srclen_total, uint8_t *dst, u32 from_idx, u8 *iv,
+ f64 time, u8 is_async, u16 async_next_node)
{
noise_keypair_t *kp;
enum noise_state_crypt ret = SC_FAILED;
@@ -518,23 +600,31 @@ wg_input_process (vlib_main_t *vm, wg_per_thread_data_t *ptd,
if (is_async)
{
+ u8 flags = VNET_CRYPTO_OP_FLAG_HMAC_CHECK;
+ u8 *tag = vlib_buffer_get_tail (lb) - NOISE_AUTHTAG_LEN;
+
+ if (b != lb)
+ flags |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS;
+
if (NULL == *async_frame ||
vnet_crypto_async_frame_is_full (*async_frame))
{
*async_frame = vnet_crypto_async_get_frame (
vm, VNET_CRYPTO_OP_CHACHA20_POLY1305_TAG16_AAD0_DEC);
+ if (PREDICT_FALSE (NULL == *async_frame))
+ goto error;
/* Save the frame to the list we'll submit at the end */
vec_add1 (ptd->async_frames, *async_frame);
}
- wg_input_add_to_frame (vm, *async_frame, kp->kp_recv_index, srclen,
- src - b->data, buf_idx, async_next_node, iv,
- src + srclen, VNET_CRYPTO_OP_FLAG_HMAC_CHECK);
+ wg_input_add_to_frame (vm, *async_frame, kp->kp_recv_index, srclen_total,
+ src - b->data, buf_idx, async_next_node, iv, tag,
+ flags);
}
else
{
- wg_prepare_sync_dec_op (vm, crypto_ops, src, srclen, dst, NULL, 0,
- kp->kp_recv_index, from_idx, iv);
+ wg_prepare_sync_dec_op (vm, ptd, b, lb, crypto_ops, src, srclen, dst,
+ NULL, 0, kp->kp_recv_index, from_idx, iv);
}
/* If we've received the handshake confirming data packet then move the
@@ -607,8 +697,9 @@ wg_input_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
u32 n_left_from = frame->n_vectors;
vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
+ vlib_buffer_t *lb;
u32 thread_index = vm->thread_index;
- vnet_crypto_op_t **crypto_ops = &ptd->crypto_ops;
+ vnet_crypto_op_t **crypto_ops;
const u16 drop_next = WG_INPUT_NEXT_PUNT;
message_type_t header_type;
vlib_buffer_t *data_bufs[VLIB_FRAME_SIZE];
@@ -622,6 +713,8 @@ wg_input_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
vlib_get_buffers (vm, from, bufs, n_left_from);
vec_reset_length (ptd->crypto_ops);
+ vec_reset_length (ptd->chained_crypto_ops);
+ vec_reset_length (ptd->chunks);
vec_reset_length (ptd->async_frames);
f64 time = clib_time_now (&vm->clib_time) + vm->time_offset;
@@ -632,6 +725,7 @@ wg_input_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
bool is_keepalive = false;
u32 *peer_idx = NULL;
+ index_t peeri = INDEX_INVALID;
while (n_left_from > 0)
{
@@ -656,6 +750,7 @@ wg_input_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
message_data_t *data = vlib_buffer_get_current (b[0]);
u8 *iv_data = b[0]->pre_data;
u32 buf_idx = from[b - bufs];
+ u32 n_bufs;
peer_idx = wg_index_table_lookup (&wmp->index_table,
data->receiver_index);
@@ -665,9 +760,15 @@ wg_input_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
data->receiver_index);
if (PREDICT_TRUE (peer_idx != NULL))
{
- peer = wg_peer_get (*peer_idx);
+ peeri = *peer_idx;
+ peer = wg_peer_get (peeri);
+ last_rec_idx = data->receiver_index;
+ }
+ else
+ {
+ peer = NULL;
+ last_rec_idx = ~0;
}
- last_rec_idx = data->receiver_index;
}
if (PREDICT_FALSE (!peer_idx))
@@ -696,21 +797,63 @@ wg_input_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
goto next;
}
- u16 encr_len = b[0]->current_length - sizeof (message_data_t);
- u16 decr_len = encr_len - NOISE_AUTHTAG_LEN;
- if (PREDICT_FALSE (decr_len >= WG_DEFAULT_DATA_SIZE))
+ lb = b[0];
+ n_bufs = vlib_buffer_chain_linearize (vm, b[0]);
+ if (n_bufs == 0)
{
- b[0]->error = node->errors[WG_INPUT_ERROR_TOO_BIG];
+ other_next[n_other] = WG_INPUT_NEXT_ERROR;
+ b[0]->error = node->errors[WG_INPUT_ERROR_NO_BUFFERS];
other_bi[n_other] = buf_idx;
n_other += 1;
goto out;
}
- enum noise_state_crypt state_cr = wg_input_process (
- vm, ptd, crypto_ops, &async_frame, b[0], buf_idx, &peer->remote,
- data->receiver_index, data->counter, data->encrypted_data,
- decr_len, data->encrypted_data, n_data, iv_data, time, is_async,
- async_next_node);
+ if (n_bufs > 1)
+ {
+ vlib_buffer_t *before_last = b[0];
+
+ /* Find last and before last buffer in the chain */
+ while (lb->flags & VLIB_BUFFER_NEXT_PRESENT)
+ {
+ before_last = lb;
+ lb = vlib_get_buffer (vm, lb->next_buffer);
+ }
+
+ /* Ensure auth tag is contiguous and not splitted into two last
+ * buffers */
+ if (PREDICT_FALSE (lb->current_length < NOISE_AUTHTAG_LEN))
+ {
+ u32 len_diff = NOISE_AUTHTAG_LEN - lb->current_length;
+
+ before_last->current_length -= len_diff;
+ if (before_last == b[0])
+ before_last->flags &= ~VLIB_BUFFER_TOTAL_LENGTH_VALID;
+
+ vlib_buffer_advance (lb, (signed) -len_diff);
+
+ clib_memcpy_fast (vlib_buffer_get_current (lb),
+ vlib_buffer_get_tail (before_last),
+ len_diff);
+ }
+ }
+
+ u16 encr_len = b[0]->current_length - sizeof (message_data_t);
+ u16 decr_len = encr_len - NOISE_AUTHTAG_LEN;
+ u16 encr_len_total =
+ vlib_buffer_length_in_chain (vm, b[0]) - sizeof (message_data_t);
+ u16 decr_len_total = encr_len_total - NOISE_AUTHTAG_LEN;
+
+ if (lb != b[0])
+ crypto_ops = &ptd->chained_crypto_ops;
+ else
+ crypto_ops = &ptd->crypto_ops;
+
+ enum noise_state_crypt state_cr =
+ wg_input_process (vm, ptd, crypto_ops, &async_frame, b[0], lb,
+ buf_idx, &peer->remote, data->receiver_index,
+ data->counter, data->encrypted_data, decr_len,
+ decr_len_total, data->encrypted_data, n_data,
+ iv_data, time, is_async, async_next_node);
if (PREDICT_FALSE (state_cr == SC_FAILED))
{
@@ -739,7 +882,7 @@ wg_input_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
}
else if (PREDICT_FALSE (state_cr == SC_KEEP_KEY_FRESH))
{
- wg_send_handshake_from_mt (*peer_idx, false);
+ wg_send_handshake_from_mt (peeri, false);
goto next;
}
else if (PREDICT_TRUE (state_cr == SC_OK))
@@ -780,7 +923,7 @@ wg_input_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
t->type = header_type;
t->current_length = b[0]->current_length;
t->is_keepalive = is_keepalive;
- t->peer = peer_idx ? *peer_idx : INDEX_INVALID;
+ t->peer = peer_idx ? peeri : INDEX_INVALID;
}
next:
@@ -791,6 +934,8 @@ wg_input_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
/* decrypt packets */
wg_input_process_ops (vm, node, ptd->crypto_ops, data_bufs, data_nexts,
drop_next);
+ wg_input_process_chained_ops (vm, node, ptd->chained_crypto_ops, data_bufs,
+ data_nexts, ptd->chunks, drop_next);
/* process after decryption */
b = data_bufs;
@@ -827,23 +972,41 @@ wg_input_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
{
peer_idx =
wg_index_table_lookup (&wmp->index_table, data->receiver_index);
- peer = wg_peer_get (*peer_idx);
- last_rec_idx = data->receiver_index;
+ if (PREDICT_TRUE (peer_idx != NULL))
+ {
+ peeri = *peer_idx;
+ peer = wg_peer_get (peeri);
+ last_rec_idx = data->receiver_index;
+ }
+ else
+ {
+ peer = NULL;
+ last_rec_idx = ~0;
+ }
}
- if (PREDICT_FALSE (wg_input_post_process (vm, b[0], data_next, peer,
- data, &is_keepalive) < 0))
- goto trace;
+ if (PREDICT_TRUE (peer != NULL))
+ {
+ if (PREDICT_FALSE (wg_input_post_process (vm, b[0], data_next, peer,
+ data, &is_keepalive) < 0))
+ goto trace;
+ }
+ else
+ {
+ data_next[0] = WG_INPUT_NEXT_PUNT;
+ goto trace;
+ }
if (PREDICT_FALSE (peer_idx && (last_peer_time_idx != peer_idx)))
{
if (PREDICT_FALSE (
!ip46_address_is_equal (&peer->dst.addr, &out_src_ip) ||
peer->dst.port != out_udp_src_port))
- wg_peer_update_endpoint_from_mt (*peer_idx, &out_src_ip,
+ wg_peer_update_endpoint_from_mt (peeri, &out_src_ip,
out_udp_src_port);
wg_timers_any_authenticated_packet_received_opt (peer, time);
wg_timers_any_authenticated_packet_traversal (peer);
+ wg_peer_update_flags (*peer_idx, WG_PEER_ESTABLISHED, true);
last_peer_time_idx = peer_idx;
}
@@ -860,7 +1023,7 @@ wg_input_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
t->type = header_type;
t->current_length = b[0]->current_length;
t->is_keepalive = is_keepalive;
- t->peer = peer_idx ? *peer_idx : INDEX_INVALID;
+ t->peer = peer_idx ? peeri : INDEX_INVALID;
}
b += 1;
@@ -922,6 +1085,7 @@ wg_input_post (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame,
wg_peer_t *peer = NULL;
u32 *peer_idx = NULL;
u32 *last_peer_time_idx = NULL;
+ index_t peeri = INDEX_INVALID;
u32 last_rec_idx = ~0;
f64 time = clib_time_now (&vm->clib_time) + vm->time_offset;
@@ -955,8 +1119,17 @@ wg_input_post (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame,
peer_idx =
wg_index_table_lookup (&wmp->index_table, data->receiver_index);
- peer = wg_peer_get (*peer_idx);
- last_rec_idx = data->receiver_index;
+ if (PREDICT_TRUE (peer_idx != NULL))
+ {
+ peeri = *peer_idx;
+ peer = wg_peer_get (peeri);
+ last_rec_idx = data->receiver_index;
+ }
+ else
+ {
+ peer = NULL;
+ last_rec_idx = ~0;
+ }
}
if (PREDICT_TRUE (peer != NULL))
@@ -976,10 +1149,11 @@ wg_input_post (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame,
if (PREDICT_FALSE (
!ip46_address_is_equal (&peer->dst.addr, &out_src_ip) ||
peer->dst.port != out_udp_src_port))
- wg_peer_update_endpoint_from_mt (*peer_idx, &out_src_ip,
+ wg_peer_update_endpoint_from_mt (peeri, &out_src_ip,
out_udp_src_port);
wg_timers_any_authenticated_packet_received_opt (peer, time);
wg_timers_any_authenticated_packet_traversal (peer);
+ wg_peer_update_flags (*peer_idx, WG_PEER_ESTABLISHED, true);
last_peer_time_idx = peer_idx;
}
@@ -995,7 +1169,7 @@ wg_input_post (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame,
wg_input_post_trace_t *t =
vlib_add_trace (vm, node, b[0], sizeof (*t));
t->next = next[0];
- t->peer = peer_idx ? *peer_idx : INDEX_INVALID;
+ t->peer = peer_idx ? peeri : INDEX_INVALID;
}
b += 1;
@@ -1033,7 +1207,6 @@ VLIB_NODE_FN (wg6_input_post_node)
return wg_input_post (vm, node, from_frame, /* is_ip4 */ 0);
}
-/* *INDENT-OFF* */
VLIB_REGISTER_NODE (wg4_input_node) =
{
.name = "wg4-input",
@@ -1096,7 +1269,6 @@ VLIB_REGISTER_NODE (wg6_input_post_node) = {
.error_strings = wg_input_error_strings,
};
-/* *INDENT-ON* */
/*
* fd.io coding-style-patch-verification: ON
diff --git a/src/plugins/wireguard/wireguard_noise.c b/src/plugins/wireguard/wireguard_noise.c
index c9d8e31061a..c3f28f442f5 100644
--- a/src/plugins/wireguard/wireguard_noise.c
+++ b/src/plugins/wireguard/wireguard_noise.c
@@ -33,8 +33,10 @@ noise_local_t *noise_local_pool;
static noise_keypair_t *noise_remote_keypair_allocate (noise_remote_t *);
static void noise_remote_keypair_free (vlib_main_t * vm, noise_remote_t *,
noise_keypair_t **);
-static uint32_t noise_remote_handshake_index_get (noise_remote_t *);
-static void noise_remote_handshake_index_drop (noise_remote_t *);
+static uint32_t noise_remote_handshake_index_get (vlib_main_t *vm,
+ noise_remote_t *);
+static void noise_remote_handshake_index_drop (vlib_main_t *vm,
+ noise_remote_t *);
static uint64_t noise_counter_send (noise_counter_t *);
bool noise_counter_recv (noise_counter_t *, uint64_t);
@@ -86,7 +88,7 @@ noise_local_set_private (noise_local_t * l,
}
void
-noise_remote_init (noise_remote_t * r, uint32_t peer_pool_idx,
+noise_remote_init (vlib_main_t *vm, noise_remote_t *r, uint32_t peer_pool_idx,
const uint8_t public[NOISE_PUBLIC_KEY_LEN],
u32 noise_local_idx)
{
@@ -97,18 +99,18 @@ noise_remote_init (noise_remote_t * r, uint32_t peer_pool_idx,
r->r_local_idx = noise_local_idx;
r->r_handshake.hs_state = HS_ZEROED;
- noise_remote_precompute (r);
+ noise_remote_precompute (vm, r);
}
void
-noise_remote_precompute (noise_remote_t * r)
+noise_remote_precompute (vlib_main_t *vm, noise_remote_t *r)
{
noise_local_t *l = noise_local_get (r->r_local_idx);
if (!curve25519_gen_shared (r->r_ss, l->l_private, r->r_public))
clib_memset (r->r_ss, 0, NOISE_PUBLIC_KEY_LEN);
- noise_remote_handshake_index_drop (r);
+ noise_remote_handshake_index_drop (vm, r);
wg_secure_zero_memory (&r->r_handshake, sizeof (r->r_handshake));
}
@@ -142,6 +144,7 @@ noise_create_initiation (vlib_main_t * vm, noise_remote_t * r,
/* es */
if (!noise_mix_dh (hs->hs_ck, key, hs->hs_e, r->r_public))
goto error;
+ vnet_crypto_key_update (vm, key_idx);
/* s */
noise_msg_encrypt (vm, es, l->l_public, NOISE_PUBLIC_KEY_LEN, key_idx,
@@ -150,13 +153,14 @@ noise_create_initiation (vlib_main_t * vm, noise_remote_t * r,
/* ss */
if (!noise_mix_ss (hs->hs_ck, key, r->r_ss))
goto error;
+ vnet_crypto_key_update (vm, key_idx);
/* {t} */
noise_tai64n_now (ets);
noise_msg_encrypt (vm, ets, ets, NOISE_TIMESTAMP_LEN, key_idx, hs->hs_hash);
- noise_remote_handshake_index_drop (r);
+ noise_remote_handshake_index_drop (vm, r);
hs->hs_state = CREATED_INITIATION;
- hs->hs_local_index = noise_remote_handshake_index_get (r);
+ hs->hs_local_index = noise_remote_handshake_index_get (vm, r);
*s_idx = hs->hs_local_index;
ret = true;
error:
@@ -196,6 +200,7 @@ noise_consume_initiation (vlib_main_t * vm, noise_local_t * l,
/* es */
if (!noise_mix_dh (hs.hs_ck, key, l->l_private, ue))
goto error;
+ vnet_crypto_key_update (vm, key_idx);
/* s */
@@ -211,6 +216,7 @@ noise_consume_initiation (vlib_main_t * vm, noise_local_t * l,
/* ss */
if (!noise_mix_ss (hs.hs_ck, key, r->r_ss))
goto error;
+ vnet_crypto_key_update (vm, key_idx);
/* {t} */
if (!noise_msg_decrypt (vm, timestamp, ets,
@@ -237,7 +243,7 @@ noise_consume_initiation (vlib_main_t * vm, noise_local_t * l,
goto error;
/* Ok, we're happy to accept this initiation now */
- noise_remote_handshake_index_drop (r);
+ noise_remote_handshake_index_drop (vm, r);
r->r_handshake = hs;
*rp = r;
ret = true;
@@ -285,13 +291,14 @@ noise_create_response (vlib_main_t * vm, noise_remote_t * r, uint32_t * s_idx,
/* psk */
noise_mix_psk (hs->hs_ck, hs->hs_hash, key, r->r_psk);
+ vnet_crypto_key_update (vm, key_idx);
/* {} */
noise_msg_encrypt (vm, en, NULL, 0, key_idx, hs->hs_hash);
hs->hs_state = CREATED_RESPONSE;
- hs->hs_local_index = noise_remote_handshake_index_get (r);
+ hs->hs_local_index = noise_remote_handshake_index_get (vm, r);
*r_idx = hs->hs_remote_index;
*s_idx = hs->hs_local_index;
ret = true;
@@ -339,6 +346,7 @@ noise_consume_response (vlib_main_t * vm, noise_remote_t * r, uint32_t s_idx,
/* psk */
noise_mix_psk (hs.hs_ck, hs.hs_hash, key, preshared_key);
+ vnet_crypto_key_update (vm, key_idx);
/* {} */
@@ -451,7 +459,7 @@ noise_remote_begin_session (vlib_main_t * vm, noise_remote_t * r)
void
noise_remote_clear (vlib_main_t * vm, noise_remote_t * r)
{
- noise_remote_handshake_index_drop (r);
+ noise_remote_handshake_index_drop (vm, r);
wg_secure_zero_memory (&r->r_handshake, sizeof (r->r_handshake));
clib_rwlock_writer_lock (&r->r_keypair_lock);
@@ -555,21 +563,21 @@ noise_remote_keypair_allocate (noise_remote_t * r)
}
static uint32_t
-noise_remote_handshake_index_get (noise_remote_t * r)
+noise_remote_handshake_index_get (vlib_main_t *vm, noise_remote_t *r)
{
noise_local_t *local = noise_local_get (r->r_local_idx);
struct noise_upcall *u = &local->l_upcall;
- return u->u_index_set (r);
+ return u->u_index_set (vm, r);
}
static void
-noise_remote_handshake_index_drop (noise_remote_t * r)
+noise_remote_handshake_index_drop (vlib_main_t *vm, noise_remote_t *r)
{
noise_handshake_t *hs = &r->r_handshake;
noise_local_t *local = noise_local_get (r->r_local_idx);
struct noise_upcall *u = &local->l_upcall;
if (hs->hs_state != HS_ZEROED)
- u->u_index_drop (hs->hs_local_index);
+ u->u_index_drop (vm, hs->hs_local_index);
}
static void
@@ -743,8 +751,8 @@ noise_tai64n_now (uint8_t output[NOISE_TIMESTAMP_LEN])
unix_nanosec &= REJECT_INTERVAL_MASK;
/* https://cr.yp.to/libtai/tai64.html */
- sec = htobe64 (0x400000000000000aULL + unix_sec);
- nsec = htobe32 (unix_nanosec);
+ sec = clib_host_to_big_u64 (0x400000000000000aULL + unix_sec);
+ nsec = clib_host_to_big_u32 (unix_nanosec);
/* memcpy to output buffer, assuming output could be unaligned. */
clib_memcpy (output, &sec, sizeof (sec));
diff --git a/src/plugins/wireguard/wireguard_noise.h b/src/plugins/wireguard/wireguard_noise.h
index e95211b8884..fd2c09ebfa5 100644
--- a/src/plugins/wireguard/wireguard_noise.h
+++ b/src/plugins/wireguard/wireguard_noise.h
@@ -121,8 +121,8 @@ typedef struct noise_local
{
void *u_arg;
noise_remote_t *(*u_remote_get) (const uint8_t[NOISE_PUBLIC_KEY_LEN]);
- uint32_t (*u_index_set) (noise_remote_t *);
- void (*u_index_drop) (uint32_t);
+ uint32_t (*u_index_set) (vlib_main_t *, noise_remote_t *);
+ void (*u_index_drop) (vlib_main_t *, uint32_t);
} l_upcall;
} noise_local_t;
@@ -148,11 +148,11 @@ void noise_local_init (noise_local_t *, struct noise_upcall *);
bool noise_local_set_private (noise_local_t *,
const uint8_t[NOISE_PUBLIC_KEY_LEN]);
-void noise_remote_init (noise_remote_t *, uint32_t,
+void noise_remote_init (vlib_main_t *, noise_remote_t *, uint32_t,
const uint8_t[NOISE_PUBLIC_KEY_LEN], uint32_t);
/* Should be called anytime noise_local_set_private is called */
-void noise_remote_precompute (noise_remote_t *);
+void noise_remote_precompute (vlib_main_t *, noise_remote_t *);
/* Cryptographic functions */
bool noise_create_initiation (vlib_main_t * vm, noise_remote_t *,
@@ -266,7 +266,7 @@ noise_remote_keypair_free (vlib_main_t *vm, noise_remote_t *r,
struct noise_upcall *u = &local->l_upcall;
if (*kp)
{
- u->u_index_drop ((*kp)->kp_local_index);
+ u->u_index_drop (vm, (*kp)->kp_local_index);
vnet_crypto_key_del (vm, (*kp)->kp_send_index);
vnet_crypto_key_del (vm, (*kp)->kp_recv_index);
clib_mem_free (*kp);
diff --git a/src/plugins/wireguard/wireguard_output_tun.c b/src/plugins/wireguard/wireguard_output_tun.c
index d1b1d6bb8f0..c9411f6ff20 100644
--- a/src/plugins/wireguard/wireguard_output_tun.c
+++ b/src/plugins/wireguard/wireguard_output_tun.c
@@ -25,7 +25,7 @@
_ (NONE, "No error") \
_ (PEER, "Peer error") \
_ (KEYPAIR, "Keypair error") \
- _ (TOO_BIG, "packet too big") \
+ _ (NO_BUFFERS, "No buffers") \
_ (CRYPTO_ENGINE_ERROR, "crypto engine error (packet dropped)")
typedef enum
@@ -115,10 +115,46 @@ format_wg_output_tun_post_trace (u8 *s, va_list *args)
}
static_always_inline void
-wg_prepare_sync_enc_op (vlib_main_t *vm, vnet_crypto_op_t **crypto_ops,
- u8 *src, u32 src_len, u8 *dst, u8 *aad, u32 aad_len,
- u64 nonce, vnet_crypto_key_index_t key_index, u32 bi,
- u8 *iv)
+wg_output_chain_crypto (vlib_main_t *vm, wg_per_thread_data_t *ptd,
+ vlib_buffer_t *b, vlib_buffer_t *lb, u8 *start,
+ u32 start_len, u16 *n_ch)
+{
+ vnet_crypto_op_chunk_t *ch;
+ vlib_buffer_t *cb = b;
+ u32 n_chunks = 1;
+
+ vec_add2 (ptd->chunks, ch, 1);
+ ch->len = start_len;
+ ch->src = ch->dst = start;
+ cb = vlib_get_buffer (vm, cb->next_buffer);
+
+ while (1)
+ {
+ vec_add2 (ptd->chunks, ch, 1);
+ n_chunks += 1;
+ if (lb == cb)
+ ch->len = cb->current_length - NOISE_AUTHTAG_LEN;
+ else
+ ch->len = cb->current_length;
+
+ ch->src = ch->dst = vlib_buffer_get_current (cb);
+
+ if (!(cb->flags & VLIB_BUFFER_NEXT_PRESENT))
+ break;
+
+ cb = vlib_get_buffer (vm, cb->next_buffer);
+ }
+
+ if (n_ch)
+ *n_ch = n_chunks;
+}
+
+static_always_inline void
+wg_prepare_sync_enc_op (vlib_main_t *vm, wg_per_thread_data_t *ptd,
+ vlib_buffer_t *b, vlib_buffer_t *lb,
+ vnet_crypto_op_t **crypto_ops, u8 *src, u32 src_len,
+ u8 *dst, u8 *aad, u32 aad_len, u64 nonce,
+ vnet_crypto_key_index_t key_index, u32 bi, u8 *iv)
{
vnet_crypto_op_t _op, *op = &_op;
u8 src_[] = {};
@@ -130,15 +166,55 @@ wg_prepare_sync_enc_op (vlib_main_t *vm, vnet_crypto_op_t **crypto_ops,
vnet_crypto_op_init (op, VNET_CRYPTO_OP_CHACHA20_POLY1305_ENC);
op->tag_len = NOISE_AUTHTAG_LEN;
- op->tag = dst + src_len;
- op->src = !src ? src_ : src;
- op->len = src_len;
- op->dst = dst;
+ op->tag = vlib_buffer_get_tail (lb) - NOISE_AUTHTAG_LEN;
op->key_index = key_index;
op->aad = aad;
op->aad_len = aad_len;
op->iv = iv;
op->user_data = bi;
+
+ if (b != lb)
+ {
+ /* Chained buffers */
+ op->flags |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS;
+ op->chunk_index = vec_len (ptd->chunks);
+ wg_output_chain_crypto (vm, ptd, b, lb, src, src_len, &op->n_chunks);
+ }
+ else
+ {
+ op->src = !src ? src_ : src;
+ op->len = src_len;
+ op->dst = dst;
+ }
+}
+
+static_always_inline void
+wg_output_process_chained_ops (vlib_main_t *vm, vlib_node_runtime_t *node,
+ vnet_crypto_op_t *ops, vlib_buffer_t *b[],
+ u16 *nexts, vnet_crypto_op_chunk_t *chunks,
+ u16 drop_next)
+{
+ u32 n_fail, n_ops = vec_len (ops);
+ vnet_crypto_op_t *op = ops;
+
+ if (n_ops == 0)
+ return;
+
+ n_fail = n_ops - vnet_crypto_process_chained_ops (vm, op, chunks, n_ops);
+
+ while (n_fail)
+ {
+ ASSERT (op - ops < n_ops);
+
+ if (op->status != VNET_CRYPTO_OP_STATUS_COMPLETED)
+ {
+ u32 bi = op->user_data;
+ b[bi]->error = node->errors[WG_OUTPUT_ERROR_CRYPTO_ENGINE_ERROR];
+ nexts[bi] = drop_next;
+ n_fail--;
+ }
+ op++;
+ }
}
static_always_inline void
@@ -194,10 +270,11 @@ wg_output_tun_add_to_frame (vlib_main_t *vm, vnet_crypto_async_frame_t *f,
}
static_always_inline enum noise_state_crypt
-wq_output_tun_process (vlib_main_t *vm, vnet_crypto_op_t **crypto_ops,
- noise_remote_t *r, uint32_t *r_idx, uint64_t *nonce,
- uint8_t *src, size_t srclen, uint8_t *dst, u32 bi,
- u8 *iv, f64 time)
+wg_output_tun_process (vlib_main_t *vm, wg_per_thread_data_t *ptd,
+ vlib_buffer_t *b, vlib_buffer_t *lb,
+ vnet_crypto_op_t **crypto_ops, noise_remote_t *r,
+ uint32_t *r_idx, uint64_t *nonce, uint8_t *src,
+ size_t srclen, uint8_t *dst, u32 bi, u8 *iv, f64 time)
{
noise_keypair_t *kp;
enum noise_state_crypt ret = SC_FAILED;
@@ -223,8 +300,8 @@ wq_output_tun_process (vlib_main_t *vm, vnet_crypto_op_t **crypto_ops,
* are passed back out to the caller through the provided data pointer. */
*r_idx = kp->kp_remote_index;
- wg_prepare_sync_enc_op (vm, crypto_ops, src, srclen, dst, NULL, 0, *nonce,
- kp->kp_send_index, bi, iv);
+ wg_prepare_sync_enc_op (vm, ptd, b, lb, crypto_ops, src, srclen, dst, NULL,
+ 0, *nonce, kp->kp_send_index, bi, iv);
/* If our values are still within tolerances, but we are approaching
* the tolerances, we notify the caller with ESTALE that they should
@@ -246,13 +323,15 @@ error:
static_always_inline enum noise_state_crypt
wg_add_to_async_frame (vlib_main_t *vm, wg_per_thread_data_t *ptd,
- vnet_crypto_async_frame_t *async_frame,
- vlib_buffer_t *b, u8 *payload, u32 payload_len, u32 bi,
- u16 next, u16 async_next, noise_remote_t *r,
- uint32_t *r_idx, uint64_t *nonce, u8 *iv, f64 time)
+ vnet_crypto_async_frame_t **async_frame,
+ vlib_buffer_t *b, vlib_buffer_t *lb, u8 *payload,
+ u32 payload_len, u32 bi, u16 next, u16 async_next,
+ noise_remote_t *r, uint32_t *r_idx, uint64_t *nonce,
+ u8 *iv, f64 time)
{
wg_post_data_t *post = wg_post_data (b);
u8 flag = 0;
+ u8 *tag;
noise_keypair_t *kp;
post->next_index = next;
@@ -284,10 +363,26 @@ wg_add_to_async_frame (vlib_main_t *vm, wg_per_thread_data_t *ptd,
clib_memset (iv, 0, 4);
clib_memcpy (iv + 4, nonce, sizeof (*nonce));
+ /* get a frame for this op if we don't yet have one or it's full */
+ if (NULL == *async_frame || vnet_crypto_async_frame_is_full (*async_frame))
+ {
+ *async_frame = vnet_crypto_async_get_frame (
+ vm, VNET_CRYPTO_OP_CHACHA20_POLY1305_TAG16_AAD0_ENC);
+ if (PREDICT_FALSE (NULL == *async_frame))
+ goto error;
+ /* Save the frame to the list we'll submit at the end */
+ vec_add1 (ptd->async_frames, *async_frame);
+ }
+
+ if (b != lb)
+ flag |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS;
+
+ tag = vlib_buffer_get_tail (lb) - NOISE_AUTHTAG_LEN;
+
/* this always succeeds because we know the frame is not full */
- wg_output_tun_add_to_frame (vm, async_frame, kp->kp_send_index, payload_len,
- payload - b->data, bi, async_next, iv,
- payload + payload_len, flag);
+ wg_output_tun_add_to_frame (vm, *async_frame, kp->kp_send_index, payload_len,
+ payload - b->data, bi, async_next, iv, tag,
+ flag);
/* If our values are still within tolerances, but we are approaching
* the tolerances, we notify the caller with ESTALE that they should
@@ -307,6 +402,22 @@ error:
return ret;
}
+static_always_inline void
+wg_calc_checksum (vlib_main_t *vm, vlib_buffer_t *b)
+{
+ int bogus = 0;
+ u8 ip_ver_out = (*((u8 *) vlib_buffer_get_current (b)) >> 4);
+
+ /* IPv6 UDP checksum is mandatory */
+ if (ip_ver_out == 6)
+ {
+ ip6_header_t *ip6 =
+ (ip6_header_t *) ((u8 *) vlib_buffer_get_current (b));
+ udp_header_t *udp = ip6_next_header (ip6);
+ udp->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b, ip6, &bogus);
+ }
+}
+
/* is_ip4 - inner header flag */
always_inline uword
wg_output_tun_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
@@ -321,7 +432,8 @@ wg_output_tun_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
ip6_udp_wg_header_t *hdr6_out = NULL;
message_data_t *message_data_wg = NULL;
vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
- vnet_crypto_op_t **crypto_ops = &ptd->crypto_ops;
+ vlib_buffer_t *lb;
+ vnet_crypto_op_t **crypto_ops;
u16 nexts[VLIB_FRAME_SIZE], *next = nexts;
vlib_buffer_t *sync_bufs[VLIB_FRAME_SIZE];
u32 thread_index = vm->thread_index;
@@ -337,6 +449,8 @@ wg_output_tun_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
vlib_get_buffers (vm, from, bufs, n_left_from);
vec_reset_length (ptd->crypto_ops);
+ vec_reset_length (ptd->chained_crypto_ops);
+ vec_reset_length (ptd->chunks);
vec_reset_length (ptd->async_frames);
wg_peer_t *peer = NULL;
@@ -352,6 +466,10 @@ wg_output_tun_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
u8 is_ip4_out = 1;
u8 *plain_data;
u16 plain_data_len;
+ u16 plain_data_len_total;
+ u16 n_bufs;
+ u16 b_space_left_at_beginning;
+ u32 bi = from[b - bufs];
if (n_left_from > 2)
{
@@ -407,35 +525,83 @@ wg_output_tun_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
goto out;
}
- is_ip4_out = ip46_address_is_ip4 (&peer->src.addr);
- if (is_ip4_out)
+ lb = b[0];
+ n_bufs = vlib_buffer_chain_linearize (vm, b[0]);
+ if (n_bufs == 0)
{
- hdr4_out = vlib_buffer_get_current (b[0]);
- message_data_wg = &hdr4_out->wg;
+ b[0]->error = node->errors[WG_OUTPUT_ERROR_NO_BUFFERS];
+ goto out;
}
- else
+
+ if (n_bufs > 1)
{
- hdr6_out = vlib_buffer_get_current (b[0]);
- message_data_wg = &hdr6_out->wg;
+ /* Find last buffer in the chain */
+ while (lb->flags & VLIB_BUFFER_NEXT_PRESENT)
+ lb = vlib_get_buffer (vm, lb->next_buffer);
+ }
+
+ /* Ensure there is enough free space at the beginning of the first buffer
+ * to write ethernet header (e.g. IPv6 VxLAN over IPv6 Wireguard will
+ * trigger this)
+ */
+ ASSERT ((signed) b[0]->current_data >=
+ (signed) -VLIB_BUFFER_PRE_DATA_SIZE);
+ b_space_left_at_beginning =
+ b[0]->current_data + VLIB_BUFFER_PRE_DATA_SIZE;
+ if (PREDICT_FALSE (b_space_left_at_beginning <
+ sizeof (ethernet_header_t)))
+ {
+ u32 size_diff =
+ sizeof (ethernet_header_t) - b_space_left_at_beginning;
+
+ /* Can only move buffer when it's single and has enough free space*/
+ if (lb == b[0] &&
+ vlib_buffer_space_left_at_end (vm, b[0]) >= size_diff)
+ {
+ vlib_buffer_move (vm, b[0],
+ b[0]->current_data + (signed) size_diff);
+ }
+ else
+ {
+ b[0]->error = node->errors[WG_OUTPUT_ERROR_NO_BUFFERS];
+ goto out;
+ }
+ }
+
+ /*
+ * Ensure there is enough free space at the end of the last buffer to
+ * write auth tag */
+ if (PREDICT_FALSE (vlib_buffer_space_left_at_end (vm, lb) <
+ NOISE_AUTHTAG_LEN))
+ {
+ u32 tmp_bi = 0;
+ if (vlib_buffer_alloc (vm, &tmp_bi, 1) != 1)
+ {
+ b[0]->error = node->errors[WG_OUTPUT_ERROR_NO_BUFFERS];
+ goto out;
+ }
+ lb = vlib_buffer_chain_buffer (vm, lb, tmp_bi);
}
iph_offset = vnet_buffer (b[0])->ip.save_rewrite_length;
plain_data = vlib_buffer_get_current (b[0]) + iph_offset;
- plain_data_len = vlib_buffer_length_in_chain (vm, b[0]) - iph_offset;
+ plain_data_len = b[0]->current_length - iph_offset;
+ plain_data_len_total =
+ vlib_buffer_length_in_chain (vm, b[0]) - iph_offset;
+ size_t encrypted_packet_len = message_data_len (plain_data_len_total);
+ vlib_buffer_chain_increase_length (b[0], lb, NOISE_AUTHTAG_LEN);
u8 *iv_data = b[0]->pre_data;
- size_t encrypted_packet_len = message_data_len (plain_data_len);
-
- /*
- * Ensure there is enough space to write the encrypted data
- * into the packet
- */
- if (PREDICT_FALSE (encrypted_packet_len >= WG_DEFAULT_DATA_SIZE) ||
- PREDICT_FALSE ((b[0]->current_data + encrypted_packet_len) >=
- vlib_buffer_get_default_data_size (vm)))
+ is_ip4_out = ip46_address_is_ip4 (&peer->src.addr);
+ if (is_ip4_out)
{
- b[0]->error = node->errors[WG_OUTPUT_ERROR_TOO_BIG];
- goto out;
+ hdr4_out = vlib_buffer_get_current (b[0]);
+ message_data_wg = &hdr4_out->wg;
+ }
+ else
+ {
+ hdr6_out = vlib_buffer_get_current (b[0]);
+ message_data_wg = &hdr6_out->wg;
}
if (PREDICT_FALSE (last_adj_index != adj_index))
@@ -449,31 +615,27 @@ wg_output_tun_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
/* Here we are sure that can send packet to next node */
next[0] = WG_OUTPUT_NEXT_INTERFACE_OUTPUT;
+ if (lb != b[0])
+ crypto_ops = &ptd->chained_crypto_ops;
+ else
+ crypto_ops = &ptd->crypto_ops;
+
enum noise_state_crypt state;
if (is_async)
{
- /* get a frame for this op if we don't yet have one or it's full */
- if (NULL == async_frame ||
- vnet_crypto_async_frame_is_full (async_frame))
- {
- async_frame = vnet_crypto_async_get_frame (
- vm, VNET_CRYPTO_OP_CHACHA20_POLY1305_TAG16_AAD0_ENC);
- /* Save the frame to the list we'll submit at the end */
- vec_add1 (ptd->async_frames, async_frame);
- }
state = wg_add_to_async_frame (
- vm, ptd, async_frame, b[0], plain_data, plain_data_len,
- from[b - bufs], next[0], async_next_node, &peer->remote,
+ vm, ptd, &async_frame, b[0], lb, plain_data, plain_data_len_total,
+ bi, next[0], async_next_node, &peer->remote,
&message_data_wg->receiver_index, &message_data_wg->counter,
iv_data, time);
}
else
{
- state = wq_output_tun_process (
- vm, crypto_ops, &peer->remote, &message_data_wg->receiver_index,
- &message_data_wg->counter, plain_data, plain_data_len, plain_data,
- n_sync, iv_data, time);
+ state = wg_output_tun_process (
+ vm, ptd, b[0], lb, crypto_ops, &peer->remote,
+ &message_data_wg->receiver_index, &message_data_wg->counter,
+ plain_data, plain_data_len, plain_data, n_sync, iv_data, time);
}
if (PREDICT_FALSE (state == SC_KEEP_KEY_FRESH))
@@ -496,10 +658,9 @@ wg_output_tun_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
hdr4_out->wg.header.type = MESSAGE_DATA;
hdr4_out->udp.length = clib_host_to_net_u16 (encrypted_packet_len +
sizeof (udp_header_t));
- b[0]->current_length =
- (encrypted_packet_len + sizeof (ip4_udp_header_t));
ip4_header_set_len_w_chksum (
- &hdr4_out->ip4, clib_host_to_net_u16 (b[0]->current_length));
+ &hdr4_out->ip4, clib_host_to_net_u16 (encrypted_packet_len +
+ sizeof (ip4_udp_header_t)));
}
else
{
@@ -507,8 +668,6 @@ wg_output_tun_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
hdr6_out->ip6.payload_length = hdr6_out->udp.length =
clib_host_to_net_u16 (encrypted_packet_len +
sizeof (udp_header_t));
- b[0]->current_length =
- (encrypted_packet_len + sizeof (ip6_udp_header_t));
}
out:
@@ -529,14 +688,14 @@ wg_output_tun_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
next:
if (PREDICT_FALSE (err != WG_OUTPUT_NEXT_INTERFACE_OUTPUT))
{
- noop_bi[n_noop] = from[b - bufs];
+ noop_bi[n_noop] = bi;
n_noop++;
noop_next++;
goto next_left;
}
if (!is_async)
{
- sync_bi[n_sync] = from[b - bufs];
+ sync_bi[n_sync] = bi;
sync_bufs[n_sync] = b[0];
n_sync += 1;
next += 1;
@@ -555,6 +714,16 @@ wg_output_tun_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
/* wg-output-process-ops */
wg_output_process_ops (vm, node, ptd->crypto_ops, sync_bufs, nexts,
drop_next);
+ wg_output_process_chained_ops (vm, node, ptd->chained_crypto_ops,
+ sync_bufs, nexts, ptd->chunks, drop_next);
+
+ int n_left_from_sync_bufs = n_sync;
+ while (n_left_from_sync_bufs > 0)
+ {
+ n_left_from_sync_bufs--;
+ wg_calc_checksum (vm, sync_bufs[n_left_from_sync_bufs]);
+ }
+
vlib_buffer_enqueue_to_next (vm, node, sync_bi, nexts, n_sync);
}
if (n_async)
@@ -627,6 +796,11 @@ wg_output_tun_post (vlib_main_t *vm, vlib_node_runtime_t *node,
next[2] = (wg_post_data (b[2]))->next_index;
next[3] = (wg_post_data (b[3]))->next_index;
+ wg_calc_checksum (vm, b[0]);
+ wg_calc_checksum (vm, b[1]);
+ wg_calc_checksum (vm, b[2]);
+ wg_calc_checksum (vm, b[3]);
+
if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
{
if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
@@ -671,6 +845,8 @@ wg_output_tun_post (vlib_main_t *vm, vlib_node_runtime_t *node,
while (n_left > 0)
{
+ wg_calc_checksum (vm, b[0]);
+
next[0] = (wg_post_data (b[0]))->next_index;
if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
(b[0]->flags & VLIB_BUFFER_IS_TRACED)))
@@ -737,7 +913,6 @@ VLIB_NODE_FN (wg6_output_tun_node)
wg_encrypt_async_next.wg6_post_next);
}
-/* *INDENT-OFF* */
VLIB_REGISTER_NODE (wg4_output_tun_node) =
{
.name = "wg4-output-tun",
@@ -769,7 +944,6 @@ VLIB_REGISTER_NODE (wg6_output_tun_node) =
[WG_OUTPUT_NEXT_ERROR] = "error-drop",
},
};
-/* *INDENT-ON* */
/*
* fd.io coding-style-patch-verification: ON
diff --git a/src/plugins/wireguard/wireguard_peer.c b/src/plugins/wireguard/wireguard_peer.c
index b22110af8f6..e71db86de0b 100644
--- a/src/plugins/wireguard/wireguard_peer.c
+++ b/src/plugins/wireguard/wireguard_peer.c
@@ -83,6 +83,7 @@ wg_peer_clear (vlib_main_t * vm, wg_peer_t * peer)
peer->new_handshake_interval_tick = 0;
peer->rehandshake_interval_tick = 0;
peer->timer_need_another_keepalive = false;
+ peer->handshake_is_sent = false;
vec_free (peer->rewrite);
vec_free (peer->allowed_ips);
vec_free (peer->adj_indices);
@@ -124,13 +125,11 @@ wg_peer_adj_stack (wg_peer_t *peer, adj_index_t ai)
}
else
{
- /* *INDENT-OFF* */
fib_prefix_t dst = {
.fp_len = is_ip4 ? 32 : 128,
.fp_proto = fib_proto,
.fp_addr = peer->dst.addr,
};
- /* *INDENT-ON* */
u32 fib_index;
fib_index = fib_table_find (fib_proto, peer->table_id);
@@ -202,16 +201,72 @@ wg_peer_get_fixup (wg_peer_t *peer, vnet_link_t lt)
return (NULL);
}
+static void
+wg_peer_disable (vlib_main_t *vm, wg_peer_t *peer)
+{
+ index_t peeri = peer - wg_peer_pool;
+
+ wg_timers_stop (peer);
+ wg_peer_update_flags (peeri, WG_PEER_ESTABLISHED, false);
+
+ for (int i = 0; i < WG_N_TIMERS; i++)
+ {
+ peer->timers[i] = ~0;
+ peer->timers_dispatched[i] = 0;
+ }
+ peer->timer_handshake_attempts = 0;
+
+ peer->last_sent_handshake = vlib_time_now (vm) - (REKEY_TIMEOUT + 1);
+ peer->last_sent_packet = 0;
+ peer->last_received_packet = 0;
+ peer->session_derived = 0;
+ peer->rehandshake_started = 0;
+
+ peer->new_handshake_interval_tick = 0;
+ peer->rehandshake_interval_tick = 0;
+
+ peer->timer_need_another_keepalive = false;
+
+ noise_remote_clear (vm, &peer->remote);
+}
+
+static void
+wg_peer_enable (vlib_main_t *vm, wg_peer_t *peer)
+{
+ index_t peeri = peer - wg_peer_pool;
+ wg_if_t *wg_if;
+ u8 public_key[NOISE_PUBLIC_KEY_LEN];
+
+ wg_if = wg_if_get (wg_if_find_by_sw_if_index (peer->wg_sw_if_index));
+ clib_memcpy (public_key, peer->remote.r_public, NOISE_PUBLIC_KEY_LEN);
+
+ noise_remote_init (vm, &peer->remote, peeri, public_key, wg_if->local_idx);
+
+ wg_timers_send_first_handshake (peer);
+}
+
walk_rc_t
wg_peer_if_admin_state_change (index_t peeri, void *data)
{
wg_peer_t *peer;
adj_index_t *adj_index;
+ vlib_main_t *vm = vlib_get_main ();
+
peer = wg_peer_get (peeri);
vec_foreach (adj_index, peer->adj_indices)
{
wg_peer_adj_stack (peer, *adj_index);
}
+
+ if (vnet_sw_interface_is_admin_up (vnet_get_main (), peer->wg_sw_if_index))
+ {
+ wg_peer_enable (vm, peer);
+ }
+ else
+ {
+ wg_peer_disable (vm, peer);
+ }
+
return (WALK_CONTINUE);
}
@@ -400,7 +455,6 @@ wg_peer_add (u32 tun_sw_if_index, const u8 public_key[NOISE_PUBLIC_KEY_LEN],
if (!wg_if)
return (VNET_API_ERROR_INVALID_SW_IF_INDEX);
- /* *INDENT-OFF* */
pool_foreach (peer, wg_peer_pool)
{
if (!memcmp (peer->remote.r_public, public_key, NOISE_PUBLIC_KEY_LEN))
@@ -408,7 +462,6 @@ wg_peer_add (u32 tun_sw_if_index, const u8 public_key[NOISE_PUBLIC_KEY_LEN],
return (VNET_API_ERROR_ENTRY_ALREADY_EXISTS);
}
}
- /* *INDENT-ON* */
if (pool_elts (wg_peer_pool) > MAX_PEERS)
return (VNET_API_ERROR_LIMIT_EXCEEDED);
@@ -427,14 +480,13 @@ wg_peer_add (u32 tun_sw_if_index, const u8 public_key[NOISE_PUBLIC_KEY_LEN],
return (rv);
}
- noise_remote_init (&peer->remote, peer - wg_peer_pool, public_key,
+ noise_remote_init (vm, &peer->remote, peer - wg_peer_pool, public_key,
wg_if->local_idx);
cookie_maker_init (&peer->cookie_maker, public_key);
- wg_send_handshake (vm, peer, false);
- if (peer->persistent_keepalive_interval != 0)
+ if (vnet_sw_interface_is_admin_up (vnet_get_main (), tun_sw_if_index))
{
- wg_send_keepalive (vm, peer);
+ wg_timers_send_first_handshake (peer);
}
*peer_index = peer - wg_peer_pool;
@@ -470,13 +522,11 @@ wg_peer_walk (wg_peer_walk_cb_t fn, void *data)
{
index_t peeri;
- /* *INDENT-OFF* */
pool_foreach_index (peeri, wg_peer_pool)
{
if (WALK_STOP == fn(peeri, data))
return peeri;
}
- /* *INDENT-ON* */
return INDEX_INVALID;
}
diff --git a/src/plugins/wireguard/wireguard_peer.h b/src/plugins/wireguard/wireguard_peer.h
index 85df0727902..613c2640ad1 100644
--- a/src/plugins/wireguard/wireguard_peer.h
+++ b/src/plugins/wireguard/wireguard_peer.h
@@ -115,6 +115,9 @@ typedef struct wg_peer
u32 rehandshake_interval_tick;
bool timer_need_another_keepalive;
+
+ /* Handshake is sent to main thread? */
+ bool handshake_is_sent;
} wg_peer_t;
typedef struct wg_peer_table_bind_ctx_t_
diff --git a/src/plugins/wireguard/wireguard_send.c b/src/plugins/wireguard/wireguard_send.c
index 91d993bee15..41b2e7706a1 100644
--- a/src/plugins/wireguard/wireguard_send.c
+++ b/src/plugins/wireguard/wireguard_send.c
@@ -41,7 +41,8 @@ ip46_enqueue_packet (vlib_main_t *vm, u32 bi0, int is_ip4)
}
static void
-wg_buffer_prepend_rewrite (vlib_buffer_t *b0, const u8 *rewrite, u8 is_ip4)
+wg_buffer_prepend_rewrite (vlib_main_t *vm, vlib_buffer_t *b0,
+ const u8 *rewrite, u8 is_ip4)
{
if (is_ip4)
{
@@ -72,6 +73,13 @@ wg_buffer_prepend_rewrite (vlib_buffer_t *b0, const u8 *rewrite, u8 is_ip4)
hdr6->ip6.payload_length = hdr6->udp.length =
clib_host_to_net_u16 (b0->current_length - sizeof (ip6_header_t));
+
+ /* IPv6 UDP checksum is mandatory */
+ int bogus = 0;
+ ip6_header_t *ip6_0 = &(hdr6->ip6);
+ hdr6->udp.checksum =
+ ip6_tcp_udp_icmp_compute_checksum (vm, b0, ip6_0, &bogus);
+ ASSERT (bogus == 0);
}
}
@@ -93,7 +101,7 @@ wg_create_buffer (vlib_main_t *vm, const u8 *rewrite, const u8 *packet,
b0->current_length = packet_len;
- wg_buffer_prepend_rewrite (b0, rewrite, is_ip4);
+ wg_buffer_prepend_rewrite (vm, b0, rewrite, is_ip4);
return true;
}
@@ -206,8 +214,11 @@ wg_send_handshake_thread_fn (void *arg)
wg_main_t *wmp = &wg_main;
wg_peer_t *peer = wg_peer_get (a->peer_idx);
+ bool handshake;
wg_send_handshake (wmp->vlib_main, peer, a->is_retry);
+ handshake = false;
+ __atomic_store_n (&peer->handshake_is_sent, handshake, __ATOMIC_RELEASE);
return 0;
}
@@ -219,8 +230,18 @@ wg_send_handshake_from_mt (u32 peer_idx, bool is_retry)
.is_retry = is_retry,
};
- vl_api_rpc_call_main_thread (wg_send_handshake_thread_fn,
- (u8 *) & a, sizeof (a));
+ wg_peer_t *peer = wg_peer_get (peer_idx);
+
+ bool handshake =
+ __atomic_load_n (&peer->handshake_is_sent, __ATOMIC_ACQUIRE);
+
+ if (handshake == false)
+ {
+ handshake = true;
+ __atomic_store_n (&peer->handshake_is_sent, handshake, __ATOMIC_RELEASE);
+ vl_api_rpc_call_main_thread (wg_send_handshake_thread_fn, (u8 *) &a,
+ sizeof (a));
+ }
}
bool
@@ -304,7 +325,6 @@ wg_send_handshake_response (vlib_main_t * vm, wg_peer_t * peer)
wg_timers_session_derived (peer);
wg_timers_any_authenticated_packet_sent (peer);
wg_timers_any_authenticated_packet_traversal (peer);
- peer->last_sent_handshake = vlib_time_now (vm);
u32 bi0 = 0;
u8 is_ip4 = ip46_address_is_ip4 (&peer->dst.addr);
diff --git a/src/plugins/wireguard/wireguard_timer.c b/src/plugins/wireguard/wireguard_timer.c
index b95801122fc..237e67c1f06 100644
--- a/src/plugins/wireguard/wireguard_timer.c
+++ b/src/plugins/wireguard/wireguard_timer.c
@@ -239,6 +239,16 @@ wg_timers_handshake_initiated (wg_peer_t * peer)
}
void
+wg_timers_send_first_handshake (wg_peer_t *peer)
+{
+ // zero value is not allowed
+ peer->new_handshake_interval_tick =
+ get_random_u32_max (REKEY_TIMEOUT_JITTER) + 1;
+ start_timer_from_mt (peer - wg_peer_pool, WG_TIMER_NEW_HANDSHAKE,
+ peer->new_handshake_interval_tick);
+}
+
+void
wg_timers_session_derived (wg_peer_t * peer)
{
peer->session_derived = vlib_time_now (vlib_get_main ());
@@ -424,14 +434,12 @@ wg_timers_stop (wg_peer_t * peer)
}
}
-/* *INDENT-OFF* */
VLIB_REGISTER_NODE (wg_timer_mngr_node, static) = {
.function = wg_timer_mngr_fn,
.type = VLIB_NODE_TYPE_PROCESS,
.name =
"wg-timer-manager",
};
-/* *INDENT-ON* */
void
wg_feature_init (wg_main_t * wmp)
diff --git a/src/plugins/wireguard/wireguard_timer.h b/src/plugins/wireguard/wireguard_timer.h
index ebde47e9067..47638bfd74d 100644
--- a/src/plugins/wireguard/wireguard_timer.h
+++ b/src/plugins/wireguard/wireguard_timer.h
@@ -50,6 +50,7 @@ void wg_timers_any_authenticated_packet_received_opt (wg_peer_t *peer,
f64 time);
void wg_timers_handshake_initiated (wg_peer_t * peer);
void wg_timers_handshake_complete (wg_peer_t * peer);
+void wg_timers_send_first_handshake (wg_peer_t *peer);
void wg_timers_session_derived (wg_peer_t * peer);
void wg_timers_any_authenticated_packet_traversal (wg_peer_t * peer);