diff options
Diffstat (limited to 'src/plugins/wireguard')
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); |