diff options
-rw-r--r--[-rwxr-xr-x] | src/plugins/wireguard/wireguard.c | 21 | ||||
-rwxr-xr-x | src/plugins/wireguard/wireguard.h | 12 | ||||
-rw-r--r-- | src/plugins/wireguard/wireguard_api.c | 35 | ||||
-rw-r--r--[-rwxr-xr-x] | src/plugins/wireguard/wireguard_cli.c | 12 | ||||
-rw-r--r--[-rwxr-xr-x] | src/plugins/wireguard/wireguard_cookie.c | 25 | ||||
-rw-r--r--[-rwxr-xr-x] | src/plugins/wireguard/wireguard_cookie.h | 12 | ||||
-rw-r--r-- | src/plugins/wireguard/wireguard_handoff.c | 102 | ||||
-rw-r--r-- | src/plugins/wireguard/wireguard_if.c | 29 | ||||
-rw-r--r-- | src/plugins/wireguard/wireguard_if.h | 2 | ||||
-rw-r--r-- | src/plugins/wireguard/wireguard_input.c | 117 | ||||
-rw-r--r--[-rwxr-xr-x] | src/plugins/wireguard/wireguard_output_tun.c | 134 | ||||
-rw-r--r-- | src/plugins/wireguard/wireguard_peer.c | 128 | ||||
-rw-r--r-- | src/plugins/wireguard/wireguard_peer.h | 16 | ||||
-rw-r--r--[-rwxr-xr-x] | src/plugins/wireguard/wireguard_send.c | 65 | ||||
-rwxr-xr-x | test/test_wireguard.py | 499 |
15 files changed, 965 insertions, 244 deletions
diff --git a/src/plugins/wireguard/wireguard.c b/src/plugins/wireguard/wireguard.c index 58422299069..8438cc126a6 100755..100644 --- a/src/plugins/wireguard/wireguard.c +++ b/src/plugins/wireguard/wireguard.c @@ -15,7 +15,6 @@ #include <vnet/vnet.h> #include <vnet/plugin/plugin.h> -#include <vnet/ipip/ipip.h> #include <vpp/app/version.h> #include <wireguard/wireguard_send.h> @@ -32,9 +31,12 @@ wg_init (vlib_main_t * vm) wmp->vlib_main = vm; - wmp->in_fq_index = vlib_frame_queue_main_init (wg_input_node.index, 0); - wmp->out_fq_index = - vlib_frame_queue_main_init (wg_output_tun_node.index, 0); + wmp->in4_fq_index = vlib_frame_queue_main_init (wg4_input_node.index, 0); + wmp->in6_fq_index = vlib_frame_queue_main_init (wg6_input_node.index, 0); + wmp->out4_fq_index = + vlib_frame_queue_main_init (wg4_output_tun_node.index, 0); + wmp->out6_fq_index = + vlib_frame_queue_main_init (wg6_output_tun_node.index, 0); vlib_thread_main_t *tm = vlib_get_thread_main (); @@ -50,13 +52,18 @@ VLIB_INIT_FUNCTION (wg_init); /* *INDENT-OFF* */ -VNET_FEATURE_INIT (wg_output_tun, static) = -{ +VNET_FEATURE_INIT (wg4_output_tun, static) = { .arc_name = "ip4-output", - .node_name = "wg-output-tun", + .node_name = "wg4-output-tun", .runs_after = VNET_FEATURES ("gso-ip4"), }; +VNET_FEATURE_INIT (wg6_output_tun, static) = { + .arc_name = "ip6-output", + .node_name = "wg6-output-tun", + .runs_after = VNET_FEATURES ("gso-ip6"), +}; + VLIB_PLUGIN_REGISTER () = { .version = VPP_BUILD_VER, diff --git a/src/plugins/wireguard/wireguard.h b/src/plugins/wireguard/wireguard.h index ef308c4c397..829c9e6f22b 100755 --- a/src/plugins/wireguard/wireguard.h +++ b/src/plugins/wireguard/wireguard.h @@ -21,8 +21,10 @@ #define WG_DEFAULT_DATA_SIZE 2048 -extern vlib_node_registration_t wg_input_node; -extern vlib_node_registration_t wg_output_tun_node; +extern vlib_node_registration_t wg4_input_node; +extern vlib_node_registration_t wg6_input_node; +extern vlib_node_registration_t wg4_output_tun_node; +extern vlib_node_registration_t wg6_output_tun_node; typedef struct wg_per_thread_data_t_ { @@ -37,8 +39,10 @@ typedef struct wg_index_table_t index_table; - u32 in_fq_index; - u32 out_fq_index; + u32 in4_fq_index; + u32 in6_fq_index; + u32 out4_fq_index; + u32 out6_fq_index; wg_per_thread_data_t *per_thread_data; u8 feature_init; diff --git a/src/plugins/wireguard/wireguard_api.c b/src/plugins/wireguard/wireguard_api.c index 3f17f658869..5dd4f86f910 100644 --- a/src/plugins/wireguard/wireguard_api.c +++ b/src/plugins/wireguard/wireguard_api.c @@ -47,19 +47,13 @@ static void ip_address_decode2 (&mp->interface.src_ip, &src); - if (AF_IP6 == ip_addr_version (&src)) - rv = VNET_API_ERROR_INVALID_PROTOCOL; + if (mp->generate_key) + curve25519_gen_secret (private_key); else - { - if (mp->generate_key) - curve25519_gen_secret (private_key); - else - clib_memcpy (private_key, mp->interface.private_key, - NOISE_PUBLIC_KEY_LEN); - - rv = wg_if_create (ntohl (mp->interface.user_instance), private_key, - ntohs (mp->interface.port), &src, &sw_if_index); - } + clib_memcpy (private_key, mp->interface.private_key, NOISE_PUBLIC_KEY_LEN); + + rv = wg_if_create (ntohl (mp->interface.user_instance), private_key, + ntohs (mp->interface.port), &src, &sw_if_index); /* *INDENT-OFF* */ REPLY_MACRO2(VL_API_WIREGUARD_INTERFACE_CREATE_REPLY, @@ -177,19 +171,10 @@ vl_api_wireguard_peer_add_t_handler (vl_api_wireguard_peer_add_t * mp) for (ii = 0; ii < mp->peer.n_allowed_ips; ii++) ip_prefix_decode (&mp->peer.allowed_ips[ii], &allowed_ips[ii]); - if (AF_IP6 == ip_addr_version (&endpoint) || - FIB_PROTOCOL_IP6 == allowed_ips[0].fp_proto) - /* ip6 currently not supported, but the API needs to support it - * else we'll need to change it later, and that's a PITA */ - rv = VNET_API_ERROR_INVALID_PROTOCOL; - else - rv = wg_peer_add (ntohl (mp->peer.sw_if_index), - mp->peer.public_key, - ntohl (mp->peer.table_id), - &ip_addr_46 (&endpoint), - allowed_ips, - ntohs (mp->peer.port), - ntohs (mp->peer.persistent_keepalive), &peeri); + rv = wg_peer_add (ntohl (mp->peer.sw_if_index), mp->peer.public_key, + ntohl (mp->peer.table_id), &ip_addr_46 (&endpoint), + allowed_ips, ntohs (mp->peer.port), + ntohs (mp->peer.persistent_keepalive), &peeri); vec_free (allowed_ips); done: diff --git a/src/plugins/wireguard/wireguard_cli.c b/src/plugins/wireguard/wireguard_cli.c index 3b4bf56a3dc..05275f74aad 100755..100644 --- a/src/plugins/wireguard/wireguard_cli.c +++ b/src/plugins/wireguard/wireguard_cli.c @@ -213,16 +213,8 @@ wg_peer_add_command_fn (vlib_main_t * vm, } } - if (AF_IP6 == ip_addr_version (&ip) || - FIB_PROTOCOL_IP6 == allowed_ip.fp_proto) - rv = VNET_API_ERROR_INVALID_PROTOCOL; - else - rv = wg_peer_add (tun_sw_if_index, - public_key, - table_id, - &ip_addr_46 (&ip), - allowed_ips, - portDst, persistent_keepalive, &peer_index); + rv = wg_peer_add (tun_sw_if_index, public_key, table_id, &ip_addr_46 (&ip), + allowed_ips, portDst, persistent_keepalive, &peer_index); switch (rv) { diff --git a/src/plugins/wireguard/wireguard_cookie.c b/src/plugins/wireguard/wireguard_cookie.c index f54ce715906..c4279b7407f 100755..100644 --- a/src/plugins/wireguard/wireguard_cookie.c +++ b/src/plugins/wireguard/wireguard_cookie.c @@ -29,9 +29,9 @@ static void cookie_macs_mac1 (message_macs_t *, const void *, size_t, const uint8_t[COOKIE_KEY_SIZE]); static void cookie_macs_mac2 (message_macs_t *, const void *, size_t, const uint8_t[COOKIE_COOKIE_SIZE]); -static void cookie_checker_make_cookie (vlib_main_t * vm, cookie_checker_t *, +static void cookie_checker_make_cookie (vlib_main_t *vm, cookie_checker_t *, uint8_t[COOKIE_COOKIE_SIZE], - ip4_address_t ip4, u16 udp_port); + ip46_address_t *ip, u16 udp_port); /* Public Functions */ void @@ -76,9 +76,9 @@ cookie_maker_mac (cookie_maker_t * cp, message_macs_t * cm, void *buf, } enum cookie_mac_state -cookie_checker_validate_macs (vlib_main_t * vm, cookie_checker_t * cc, - message_macs_t * cm, void *buf, size_t len, - bool busy, ip4_address_t ip4, u16 udp_port) +cookie_checker_validate_macs (vlib_main_t *vm, cookie_checker_t *cc, + message_macs_t *cm, void *buf, size_t len, + bool busy, ip46_address_t *ip, u16 udp_port) { message_macs_t our_cm; uint8_t cookie[COOKIE_COOKIE_SIZE]; @@ -93,7 +93,7 @@ cookie_checker_validate_macs (vlib_main_t * vm, cookie_checker_t * cc, if (!busy) return VALID_MAC_BUT_NO_COOKIE; - cookie_checker_make_cookie (vm, cc, cookie, ip4, udp_port); + cookie_checker_make_cookie (vm, cc, cookie, ip, udp_port); cookie_macs_mac2 (&our_cm, buf, len, cookie); /* If the mac2 is invalid, we want to send a cookie response */ @@ -139,9 +139,9 @@ cookie_macs_mac2 (message_macs_t * cm, const void *buf, size_t len, } static void -cookie_checker_make_cookie (vlib_main_t * vm, cookie_checker_t * cc, +cookie_checker_make_cookie (vlib_main_t *vm, cookie_checker_t *cc, uint8_t cookie[COOKIE_COOKIE_SIZE], - ip4_address_t ip4, u16 udp_port) + ip46_address_t *ip, u16 udp_port) { blake2s_state_t state; @@ -155,7 +155,14 @@ cookie_checker_make_cookie (vlib_main_t * vm, cookie_checker_t * cc, blake2s_init_key (&state, COOKIE_COOKIE_SIZE, cc->cc_secret, COOKIE_SECRET_SIZE); - blake2s_update (&state, ip4.as_u8, sizeof (ip4_address_t)); //TODO: IP6 + if (ip46_address_is_ip4 (ip)) + { + blake2s_update (&state, ip->ip4.as_u8, sizeof (ip4_address_t)); + } + else + { + blake2s_update (&state, ip->ip6.as_u8, sizeof (ip6_address_t)); + } blake2s_update (&state, (u8 *) & udp_port, sizeof (u16)); blake2s_final (&state, cookie, COOKIE_COOKIE_SIZE); } diff --git a/src/plugins/wireguard/wireguard_cookie.h b/src/plugins/wireguard/wireguard_cookie.h index 489cce81325..6ef418f55fa 100755..100644 --- a/src/plugins/wireguard/wireguard_cookie.h +++ b/src/plugins/wireguard/wireguard_cookie.h @@ -18,7 +18,7 @@ #ifndef __included_wg_cookie_h__ #define __included_wg_cookie_h__ -#include <vnet/ip/ip4_packet.h> +#include <vnet/ip/ip46_address.h> #include <wireguard/wireguard_noise.h> enum cookie_mac_state @@ -83,12 +83,10 @@ typedef struct cookie_checker void cookie_maker_init (cookie_maker_t *, const uint8_t[COOKIE_INPUT_SIZE]); void cookie_checker_update (cookie_checker_t *, uint8_t[COOKIE_INPUT_SIZE]); void cookie_maker_mac (cookie_maker_t *, message_macs_t *, void *, size_t); -enum cookie_mac_state cookie_checker_validate_macs (vlib_main_t * vm, - cookie_checker_t *, - message_macs_t *, void *, - size_t, bool, - ip4_address_t ip4, - u16 udp_port); +enum cookie_mac_state +cookie_checker_validate_macs (vlib_main_t *vm, cookie_checker_t *, + message_macs_t *, void *, size_t, bool, + ip46_address_t *ip, u16 udp_port); #endif /* __included_wg_cookie_h__ */ diff --git a/src/plugins/wireguard/wireguard_handoff.c b/src/plugins/wireguard/wireguard_handoff.c index d3e37b30c88..5f3dc14b412 100644 --- a/src/plugins/wireguard/wireguard_handoff.c +++ b/src/plugins/wireguard/wireguard_handoff.c @@ -129,40 +129,78 @@ wg_handoff (vlib_main_t * vm, return n_enq; } -VLIB_NODE_FN (wg_handshake_handoff) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * from_frame) +VLIB_NODE_FN (wg4_handshake_handoff) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *from_frame) { wg_main_t *wmp = &wg_main; - return wg_handoff (vm, node, from_frame, wmp->in_fq_index, + return wg_handoff (vm, node, from_frame, wmp->in4_fq_index, WG_HANDOFF_HANDSHAKE); } -VLIB_NODE_FN (wg_input_data_handoff) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * from_frame) +VLIB_NODE_FN (wg6_handshake_handoff) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *from_frame) { wg_main_t *wmp = &wg_main; - return wg_handoff (vm, node, from_frame, wmp->in_fq_index, + return wg_handoff (vm, node, from_frame, wmp->in6_fq_index, + WG_HANDOFF_HANDSHAKE); +} + +VLIB_NODE_FN (wg4_input_data_handoff) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *from_frame) +{ + wg_main_t *wmp = &wg_main; + + return wg_handoff (vm, node, from_frame, wmp->in4_fq_index, + WG_HANDOFF_INP_DATA); +} + +VLIB_NODE_FN (wg6_input_data_handoff) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *from_frame) +{ + wg_main_t *wmp = &wg_main; + + return wg_handoff (vm, node, from_frame, wmp->in6_fq_index, WG_HANDOFF_INP_DATA); } -VLIB_NODE_FN (wg_output_tun_handoff) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * from_frame) +VLIB_NODE_FN (wg4_output_tun_handoff) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *from_frame) { wg_main_t *wmp = &wg_main; - return wg_handoff (vm, node, from_frame, wmp->out_fq_index, + return wg_handoff (vm, node, from_frame, wmp->out4_fq_index, + WG_HANDOFF_OUT_TUN); +} + +VLIB_NODE_FN (wg6_output_tun_handoff) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *from_frame) +{ + wg_main_t *wmp = &wg_main; + + return wg_handoff (vm, node, from_frame, wmp->out6_fq_index, WG_HANDOFF_OUT_TUN); } /* *INDENT-OFF* */ -VLIB_REGISTER_NODE (wg_handshake_handoff) = +VLIB_REGISTER_NODE (wg4_handshake_handoff) = +{ + .name = "wg4-handshake-handoff", + .vector_size = sizeof (u32), + .format_trace = format_wg_handoff_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (wg_handoff_error_strings), + .error_strings = wg_handoff_error_strings, + .n_next_nodes = 1, + .next_nodes = { + [0] = "error-drop", + }, +}; + +VLIB_REGISTER_NODE (wg6_handshake_handoff) = { - .name = "wg-handshake-handoff", + .name = "wg6-handshake-handoff", .vector_size = sizeof (u32), .format_trace = format_wg_handoff_trace, .type = VLIB_NODE_TYPE_INTERNAL, @@ -174,9 +212,9 @@ VLIB_REGISTER_NODE (wg_handshake_handoff) = }, }; -VLIB_REGISTER_NODE (wg_input_data_handoff) = +VLIB_REGISTER_NODE (wg4_input_data_handoff) = { - .name = "wg-input-data-handoff", + .name = "wg4-input-data-handoff", .vector_size = sizeof (u32), .format_trace = format_wg_handoff_trace, .type = VLIB_NODE_TYPE_INTERNAL, @@ -188,9 +226,37 @@ VLIB_REGISTER_NODE (wg_input_data_handoff) = }, }; -VLIB_REGISTER_NODE (wg_output_tun_handoff) = +VLIB_REGISTER_NODE (wg6_input_data_handoff) = +{ + .name = "wg6-input-data-handoff", + .vector_size = sizeof (u32), + .format_trace = format_wg_handoff_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (wg_handoff_error_strings), + .error_strings = wg_handoff_error_strings, + .n_next_nodes = 1, + .next_nodes = { + [0] = "error-drop", + }, +}; + +VLIB_REGISTER_NODE (wg4_output_tun_handoff) = +{ + .name = "wg4-output-tun-handoff", + .vector_size = sizeof (u32), + .format_trace = format_wg_handoff_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (wg_handoff_error_strings), + .error_strings = wg_handoff_error_strings, + .n_next_nodes = 1, + .next_nodes = { + [0] = "error-drop", + }, +}; + +VLIB_REGISTER_NODE (wg6_output_tun_handoff) = { - .name = "wg-output-tun-handoff", + .name = "wg6-output-tun-handoff", .vector_size = sizeof (u32), .format_trace = format_wg_handoff_trace, .type = VLIB_NODE_TYPE_INTERNAL, diff --git a/src/plugins/wireguard/wireguard_if.c b/src/plugins/wireguard/wireguard_if.c index 0866d24e775..ad8f42c1969 100644 --- a/src/plugins/wireguard/wireguard_if.c +++ b/src/plugins/wireguard/wireguard_if.c @@ -49,7 +49,6 @@ format_wg_if (u8 * s, va_list * args) noise_local_t *local = noise_local_get (wgi->local_idx); u8 key[NOISE_KEY_LEN_BASE64]; - s = format (s, "[%d] %U src:%U port:%d", wgii, format_vnet_sw_if_index_name, vnet_get_main (), @@ -290,7 +289,12 @@ wg_if_create (u32 user_instance, vec_validate_init_empty (wg_if_indexes_by_port, port, NULL); if (vec_len (wg_if_indexes_by_port[port]) == 0) - udp_register_dst_port (vlib_get_main (), port, wg_input_node.index, 1); + { + udp_register_dst_port (vlib_get_main (), port, wg4_input_node.index, + UDP_IP4); + udp_register_dst_port (vlib_get_main (), port, wg6_input_node.index, + UDP_IP6); + } vec_add1 (wg_if_indexes_by_port[port], t_idx); @@ -350,7 +354,10 @@ wg_if_delete (u32 sw_if_index) } } if (vec_len (ifs) == 0) - udp_unregister_dst_port (vlib_get_main (), wg_if->port, 1); + { + udp_unregister_dst_port (vlib_get_main (), wg_if->port, 1); + udp_unregister_dst_port (vlib_get_main (), wg_if->port, 0); + } vnet_delete_hw_interface (vnm, hw->hw_if_index); pool_put_index (noise_local_pool, wg_if->local_idx); @@ -365,8 +372,12 @@ wg_if_peer_add (wg_if_t * wgi, index_t peeri) hash_set (wgi->peers, peeri, peeri); if (1 == hash_elts (wgi->peers)) - vnet_feature_enable_disable ("ip4-output", "wg-output-tun", - wgi->sw_if_index, 1, 0, 0); + { + vnet_feature_enable_disable ("ip4-output", "wg4-output-tun", + wgi->sw_if_index, 1, 0, 0); + vnet_feature_enable_disable ("ip6-output", "wg6-output-tun", + wgi->sw_if_index, 1, 0, 0); + } } void @@ -375,8 +386,12 @@ wg_if_peer_remove (wg_if_t * wgi, index_t peeri) hash_unset (wgi->peers, peeri); if (0 == hash_elts (wgi->peers)) - vnet_feature_enable_disable ("ip4-output", "wg-output-tun", - wgi->sw_if_index, 0, 0, 0); + { + vnet_feature_enable_disable ("ip4-output", "wg4-output-tun", + wgi->sw_if_index, 0, 0, 0); + vnet_feature_enable_disable ("ip6-output", "wg6-output-tun", + wgi->sw_if_index, 0, 0, 0); + } } void diff --git a/src/plugins/wireguard/wireguard_if.h b/src/plugins/wireguard/wireguard_if.h index 3153a380066..0a042cb2d9b 100644 --- a/src/plugins/wireguard/wireguard_if.h +++ b/src/plugins/wireguard/wireguard_if.h @@ -31,8 +31,6 @@ typedef struct wg_if_t_ cookie_checker_t cookie_checker; u16 port; - wg_index_table_t index_table; - /* Source IP address for originated packets */ ip_address_t src_ip; diff --git a/src/plugins/wireguard/wireguard_input.c b/src/plugins/wireguard/wireguard_input.c index ad002dcb3c2..6a0623e0a76 100644 --- a/src/plugins/wireguard/wireguard_input.c +++ b/src/plugins/wireguard/wireguard_input.c @@ -79,11 +79,11 @@ format_wg_input_trace (u8 * s, va_list * args) wg_input_trace_t *t = va_arg (*args, wg_input_trace_t *); - s = format (s, "WG input: \n"); - s = format (s, " Type: %U\n", format_wg_message_type, t->type); - s = format (s, " peer: %d\n", t->peer); - s = format (s, " Length: %d\n", t->current_length); - s = format (s, " Keepalive: %s", t->is_keepalive ? "true" : "false"); + s = format (s, "Wireguard input: \n"); + s = format (s, " Type: %U\n", format_wg_message_type, t->type); + s = format (s, " Peer: %d\n", t->peer); + s = format (s, " Length: %d\n", t->current_length); + s = format (s, " Keepalive: %s", t->is_keepalive ? "true" : "false"); return s; } @@ -93,6 +93,7 @@ typedef enum WG_INPUT_NEXT_HANDOFF_HANDSHAKE, WG_INPUT_NEXT_HANDOFF_DATA, WG_INPUT_NEXT_IP4_INPUT, + WG_INPUT_NEXT_IP6_INPUT, WG_INPUT_NEXT_PUNT, WG_INPUT_NEXT_ERROR, WG_INPUT_N_NEXT, @@ -108,8 +109,15 @@ typedef enum /* } */ /* } */ +static u8 +is_ip4_header (u8 *data) +{ + return (data[0] >> 4) == 0x4; +} + static wg_input_error_t -wg_handshake_process (vlib_main_t * vm, wg_main_t * wmp, vlib_buffer_t * b) +wg_handshake_process (vlib_main_t *vm, wg_main_t *wmp, vlib_buffer_t *b, + u32 node_idx, u8 is_ip4) { ASSERT (vm->thread_index == 0); @@ -122,10 +130,21 @@ wg_handshake_process (vlib_main_t * vm, wg_main_t * wmp, vlib_buffer_t * b) void *current_b_data = vlib_buffer_get_current (b); + ip46_address_t src_ip; + if (is_ip4) + { + ip4_header_t *iph4 = + current_b_data - sizeof (udp_header_t) - sizeof (ip4_header_t); + ip46_address_set_ip4 (&src_ip, &iph4->src_address); + } + else + { + ip6_header_t *iph6 = + current_b_data - sizeof (udp_header_t) - sizeof (ip6_header_t); + ip46_address_set_ip6 (&src_ip, &iph6->src_address); + } + udp_header_t *uhd = current_b_data - sizeof (udp_header_t); - ip4_header_t *iph = - current_b_data - sizeof (udp_header_t) - sizeof (ip4_header_t); - ip4_address_t ip4_src = iph->src_address; u16 udp_src_port = clib_host_to_net_u16 (uhd->src_port);; u16 udp_dst_port = clib_host_to_net_u16 (uhd->dst_port);; @@ -168,7 +187,7 @@ wg_handshake_process (vlib_main_t * vm, wg_main_t * wmp, vlib_buffer_t * b) mac_state = cookie_checker_validate_macs ( vm, &wg_if->cookie_checker, macs, current_b_data, len, under_load, - ip4_src, udp_src_port); + &src_ip, udp_src_port); if (mac_state == INVALID_MAC) { wg_if = NULL; @@ -214,7 +233,7 @@ wg_handshake_process (vlib_main_t * vm, wg_main_t * wmp, vlib_buffer_t * b) // set_peer_address (peer, ip4_src, udp_src_port); if (PREDICT_FALSE (!wg_send_handshake_response (vm, peer))) { - vlib_node_increment_counter (vm, wg_input_node.index, + vlib_node_increment_counter (vm, node_idx, WG_INPUT_ERROR_HANDSHAKE_SEND, 1); } break; @@ -254,9 +273,8 @@ wg_handshake_process (vlib_main_t * vm, wg_main_t * wmp, vlib_buffer_t * b) wg_timers_handshake_complete (peer); if (PREDICT_FALSE (!wg_send_keepalive (vm, peer))) { - vlib_node_increment_counter (vm, wg_input_node.index, - WG_INPUT_ERROR_KEEPALIVE_SEND, - 1); + vlib_node_increment_counter (vm, node_idx, + WG_INPUT_ERROR_KEEPALIVE_SEND, 1); } } break; @@ -270,9 +288,9 @@ wg_handshake_process (vlib_main_t * vm, wg_main_t * wmp, vlib_buffer_t * b) return WG_INPUT_ERROR_NONE; } -VLIB_NODE_FN (wg_input_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) +always_inline uword +wg_input_inline (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame, u8 is_ip4) { message_type_t header_type; u32 n_left_from; @@ -382,7 +400,20 @@ VLIB_NODE_FN (wg_input_node) (vlib_main_t * vm, wg_timers_data_received (peer); - ip4_header_t *iph = vlib_buffer_get_current (b[0]); + ip46_address_t src_ip; + u8 is_ip4_inner = is_ip4_header (vlib_buffer_get_current (b[0])); + if (is_ip4_inner) + { + ip46_address_set_ip4 ( + &src_ip, &((ip4_header_t *) vlib_buffer_get_current (b[0])) + ->src_address); + } + else + { + ip46_address_set_ip6 ( + &src_ip, &((ip6_header_t *) vlib_buffer_get_current (b[0])) + ->src_address); + } const fib_prefix_t *allowed_ip; bool allowed = false; @@ -392,9 +423,10 @@ VLIB_NODE_FN (wg_input_node) (vlib_main_t * vm, * is that there aren't many allowed IPs and thus a linear * walk is fater than an ACL */ + vec_foreach (allowed_ip, peer->allowed_ips) { - if (fib_prefix_is_cover_addr_4 (allowed_ip, &iph->src_address)) + if (fib_prefix_is_cover_addr_46 (allowed_ip, &src_ip)) { allowed = true; break; @@ -403,7 +435,8 @@ VLIB_NODE_FN (wg_input_node) (vlib_main_t * vm, if (allowed) { vnet_buffer (b[0])->sw_if_index[VLIB_RX] = peer->wg_sw_if_index; - next[0] = WG_INPUT_NEXT_IP4_INPUT; + next[0] = is_ip4_inner ? WG_INPUT_NEXT_IP4_INPUT : + WG_INPUT_NEXT_IP6_INPUT; } } else @@ -417,7 +450,8 @@ VLIB_NODE_FN (wg_input_node) (vlib_main_t * vm, goto next; } - wg_input_error_t ret = wg_handshake_process (vm, wmp, b[0]); + wg_input_error_t ret = + wg_handshake_process (vm, wmp, b[0], node->node_index, is_ip4); if (ret != WG_INPUT_ERROR_NONE) { next[0] = WG_INPUT_NEXT_ERROR; @@ -445,10 +479,42 @@ VLIB_NODE_FN (wg_input_node) (vlib_main_t * vm, return frame->n_vectors; } +VLIB_NODE_FN (wg4_input_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return wg_input_inline (vm, node, frame, /* is_ip4 */ 1); +} + +VLIB_NODE_FN (wg6_input_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return wg_input_inline (vm, node, frame, /* is_ip4 */ 0); +} + /* *INDENT-OFF* */ -VLIB_REGISTER_NODE (wg_input_node) = +VLIB_REGISTER_NODE (wg4_input_node) = +{ + .name = "wg4-input", + .vector_size = sizeof (u32), + .format_trace = format_wg_input_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (wg_input_error_strings), + .error_strings = wg_input_error_strings, + .n_next_nodes = WG_INPUT_N_NEXT, + /* edit / add dispositions here */ + .next_nodes = { + [WG_INPUT_NEXT_HANDOFF_HANDSHAKE] = "wg4-handshake-handoff", + [WG_INPUT_NEXT_HANDOFF_DATA] = "wg4-input-data-handoff", + [WG_INPUT_NEXT_IP4_INPUT] = "ip4-input-no-checksum", + [WG_INPUT_NEXT_IP6_INPUT] = "ip6-input", + [WG_INPUT_NEXT_PUNT] = "error-punt", + [WG_INPUT_NEXT_ERROR] = "error-drop", + }, +}; + +VLIB_REGISTER_NODE (wg6_input_node) = { - .name = "wg-input", + .name = "wg6-input", .vector_size = sizeof (u32), .format_trace = format_wg_input_trace, .type = VLIB_NODE_TYPE_INTERNAL, @@ -457,9 +523,10 @@ VLIB_REGISTER_NODE (wg_input_node) = .n_next_nodes = WG_INPUT_N_NEXT, /* edit / add dispositions here */ .next_nodes = { - [WG_INPUT_NEXT_HANDOFF_HANDSHAKE] = "wg-handshake-handoff", - [WG_INPUT_NEXT_HANDOFF_DATA] = "wg-input-data-handoff", + [WG_INPUT_NEXT_HANDOFF_HANDSHAKE] = "wg6-handshake-handoff", + [WG_INPUT_NEXT_HANDOFF_DATA] = "wg6-input-data-handoff", [WG_INPUT_NEXT_IP4_INPUT] = "ip4-input-no-checksum", + [WG_INPUT_NEXT_IP6_INPUT] = "ip6-input", [WG_INPUT_NEXT_PUNT] = "error-punt", [WG_INPUT_NEXT_ERROR] = "error-drop", }, diff --git a/src/plugins/wireguard/wireguard_output_tun.c b/src/plugins/wireguard/wireguard_output_tun.c index 53a8797c973..80ba9504f0e 100755..100644 --- a/src/plugins/wireguard/wireguard_output_tun.c +++ b/src/plugins/wireguard/wireguard_output_tun.c @@ -51,18 +51,28 @@ typedef enum typedef struct { - ip4_udp_header_t hdr; index_t peer; + u8 header[sizeof (ip6_udp_header_t)]; + u8 is_ip4; } wg_output_tun_trace_t; u8 * format_ip4_udp_header (u8 * s, va_list * args) { - ip4_udp_header_t *hdr = va_arg (*args, ip4_udp_header_t *); + ip4_udp_header_t *hdr4 = va_arg (*args, ip4_udp_header_t *); - s = format (s, "%U:$U", - format_ip4_header, &hdr->ip4, format_udp_header, &hdr->udp); + s = format (s, "%U:$U", format_ip4_header, &hdr4->ip4, format_udp_header, + &hdr4->udp); + return (s); +} + +u8 * +format_ip6_udp_header (u8 *s, va_list *args) +{ + ip6_udp_header_t *hdr6 = va_arg (*args, ip6_udp_header_t *); + s = format (s, "%U:$U", format_ip6_header, &hdr6->ip6, format_udp_header, + &hdr6->udp); return (s); } @@ -76,16 +86,22 @@ format_wg_output_tun_trace (u8 * s, va_list * args) wg_output_tun_trace_t *t = va_arg (*args, wg_output_tun_trace_t *); s = format (s, "peer: %d\n", t->peer); - s = format (s, " Encrypted packet: %U", format_ip4_udp_header, &t->hdr); + s = format (s, " Encrypted packet: "); + + s = t->is_ip4 ? format (s, "%U", format_ip4_udp_header, t->header) : + format (s, "%U", format_ip6_udp_header, t->header); return s; } -VLIB_NODE_FN (wg_output_tun_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) +/* is_ip4 - inner header flag */ +always_inline uword +wg_output_tun_inline (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame, u8 is_ip4) { u32 n_left_from; u32 *from; + ip4_udp_header_t *hdr4_out = NULL; + ip6_udp_header_t *hdr6_out = NULL; vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b; u16 nexts[VLIB_FRAME_SIZE], *next; u32 thread_index = vm->thread_index; @@ -102,12 +118,11 @@ VLIB_NODE_FN (wg_output_tun_node) (vlib_main_t * vm, while (n_left_from > 0) { - ip4_udp_header_t *hdr = vlib_buffer_get_current (b[0]); - u8 *plain_data = (vlib_buffer_get_current (b[0]) + - sizeof (ip4_udp_header_t)); - u16 plain_data_len = - clib_net_to_host_u16 (((ip4_header_t *) plain_data)->length); index_t peeri; + u8 iph_offset = 0; + u8 is_ip4_out = 1; + u8 *plain_data; + u16 plain_data_len; next[0] = WG_OUTPUT_NEXT_ERROR; peeri = @@ -119,7 +134,6 @@ VLIB_NODE_FN (wg_output_tun_node) (vlib_main_t * vm, b[0]->error = node->errors[WG_OUTPUT_ERROR_PEER]; goto out; } - if (PREDICT_FALSE (~0 == peer->output_thread_index)) { /* this is the first packet to use this peer, claim the peer @@ -141,6 +155,21 @@ VLIB_NODE_FN (wg_output_tun_node) (vlib_main_t * vm, b[0]->error = node->errors[WG_OUTPUT_ERROR_KEYPAIR]; goto out; } + + is_ip4_out = ip46_address_is_ip4 (&peer->src.addr); + if (is_ip4_out) + { + hdr4_out = vlib_buffer_get_current (b[0]); + } + else + { + hdr6_out = vlib_buffer_get_current (b[0]); + } + + 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; + size_t encrypted_packet_len = message_data_len (plain_data_len); /* @@ -159,13 +188,10 @@ VLIB_NODE_FN (wg_output_tun_node) (vlib_main_t * vm, (message_data_t *) wmp->per_thread_data[thread_index].data; enum noise_state_crypt state; - state = - noise_remote_encrypt (vm, - &peer->remote, - &encrypted_packet->receiver_index, - &encrypted_packet->counter, plain_data, - plain_data_len, - encrypted_packet->encrypted_data); + state = noise_remote_encrypt ( + vm, &peer->remote, &encrypted_packet->receiver_index, + &encrypted_packet->counter, plain_data, plain_data_len, + encrypted_packet->encrypted_data); if (PREDICT_FALSE (state == SC_KEEP_KEY_FRESH)) { @@ -184,12 +210,24 @@ VLIB_NODE_FN (wg_output_tun_node) (vlib_main_t * vm, clib_memcpy (plain_data, (u8 *) encrypted_packet, encrypted_packet_len); - hdr->udp.length = clib_host_to_net_u16 (encrypted_packet_len + - sizeof (udp_header_t)); - b[0]->current_length = (encrypted_packet_len + - sizeof (ip4_header_t) + sizeof (udp_header_t)); - ip4_header_set_len_w_chksum - (&hdr->ip4, clib_host_to_net_u16 (b[0]->current_length)); + if (is_ip4_out) + { + 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)); + } + else + { + 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)); + hdr6_out->ip6.payload_length = + clib_host_to_net_u16 (b[0]->current_length); + } wg_timers_any_authenticated_packet_sent (peer); wg_timers_data_sent (peer); @@ -201,9 +239,15 @@ VLIB_NODE_FN (wg_output_tun_node) (vlib_main_t * vm, { wg_output_tun_trace_t *t = vlib_add_trace (vm, node, b[0], sizeof (*t)); - t->hdr = *hdr; + t->peer = peeri; + t->is_ip4 = is_ip4_out; + if (hdr4_out) + clib_memcpy (t->header, hdr4_out, sizeof (*hdr4_out)); + else if (hdr6_out) + clib_memcpy (t->header, hdr6_out, sizeof (*hdr6_out)); } + next: n_left_from -= 1; next += 1; @@ -214,10 +258,38 @@ VLIB_NODE_FN (wg_output_tun_node) (vlib_main_t * vm, return frame->n_vectors; } +VLIB_NODE_FN (wg4_output_tun_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return wg_output_tun_inline (vm, node, frame, /* is_ip4 */ 1); +} + +VLIB_NODE_FN (wg6_output_tun_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return wg_output_tun_inline (vm, node, frame, /* is_ip4 */ 0); +} + /* *INDENT-OFF* */ -VLIB_REGISTER_NODE (wg_output_tun_node) = +VLIB_REGISTER_NODE (wg4_output_tun_node) = +{ + .name = "wg4-output-tun", + .vector_size = sizeof (u32), + .format_trace = format_wg_output_tun_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (wg_output_error_strings), + .error_strings = wg_output_error_strings, + .n_next_nodes = WG_OUTPUT_N_NEXT, + .next_nodes = { + [WG_OUTPUT_NEXT_HANDOFF] = "wg4-output-tun-handoff", + [WG_OUTPUT_NEXT_INTERFACE_OUTPUT] = "adj-midchain-tx", + [WG_OUTPUT_NEXT_ERROR] = "error-drop", + }, +}; + +VLIB_REGISTER_NODE (wg6_output_tun_node) = { - .name = "wg-output-tun", + .name = "wg6-output-tun", .vector_size = sizeof (u32), .format_trace = format_wg_output_tun_trace, .type = VLIB_NODE_TYPE_INTERNAL, @@ -225,7 +297,7 @@ VLIB_REGISTER_NODE (wg_output_tun_node) = .error_strings = wg_output_error_strings, .n_next_nodes = WG_OUTPUT_N_NEXT, .next_nodes = { - [WG_OUTPUT_NEXT_HANDOFF] = "wg-output-tun-handoff", + [WG_OUTPUT_NEXT_HANDOFF] = "wg6-output-tun-handoff", [WG_OUTPUT_NEXT_INTERFACE_OUTPUT] = "adj-midchain-tx", [WG_OUTPUT_NEXT_ERROR] = "error-drop", }, diff --git a/src/plugins/wireguard/wireguard_peer.c b/src/plugins/wireguard/wireguard_peer.c index d4a85a20cf6..fb540141e08 100644 --- a/src/plugins/wireguard/wireguard_peer.c +++ b/src/plugins/wireguard/wireguard_peer.c @@ -22,6 +22,7 @@ #include <wireguard/wireguard_key.h> #include <wireguard/wireguard_send.h> #include <wireguard/wireguard.h> +#include <vnet/tunnel/tunnel_dp.h> wg_peer_t *wg_peer_pool; @@ -91,25 +92,44 @@ wg_peer_init (vlib_main_t * vm, wg_peer_t * peer) } static u8 * -wg_peer_build_rewrite (const wg_peer_t * peer) +wg_peer_build_rewrite (const wg_peer_t *peer, u8 is_ip4) { - // v4 only for now - ip4_udp_header_t *hdr; u8 *rewrite = NULL; + if (is_ip4) + { + ip4_udp_header_t *hdr; + + vec_validate (rewrite, sizeof (*hdr) - 1); + hdr = (ip4_udp_header_t *) rewrite; + + hdr->ip4.ip_version_and_header_length = 0x45; + hdr->ip4.ttl = 64; + hdr->ip4.src_address = peer->src.addr.ip4; + hdr->ip4.dst_address = peer->dst.addr.ip4; + hdr->ip4.protocol = IP_PROTOCOL_UDP; + hdr->ip4.checksum = ip4_header_checksum (&hdr->ip4); + + hdr->udp.src_port = clib_host_to_net_u16 (peer->src.port); + hdr->udp.dst_port = clib_host_to_net_u16 (peer->dst.port); + hdr->udp.checksum = 0; + } + else + { + ip6_udp_header_t *hdr; - vec_validate (rewrite, sizeof (*hdr) - 1); - hdr = (ip4_udp_header_t *) rewrite; + vec_validate (rewrite, sizeof (*hdr) - 1); + hdr = (ip6_udp_header_t *) rewrite; - hdr->ip4.ip_version_and_header_length = 0x45; - hdr->ip4.ttl = 64; - hdr->ip4.src_address = peer->src.addr.ip4; - hdr->ip4.dst_address = peer->dst.addr.ip4; - hdr->ip4.protocol = IP_PROTOCOL_UDP; - hdr->ip4.checksum = ip4_header_checksum (&hdr->ip4); + hdr->ip6.ip_version_traffic_class_and_flow_label = 0x60; + ip6_address_copy (&hdr->ip6.src_address, &peer->src.addr.ip6); + ip6_address_copy (&hdr->ip6.dst_address, &peer->dst.addr.ip6); + hdr->ip6.protocol = IP_PROTOCOL_UDP; + hdr->ip6.hop_limit = 64; - hdr->udp.src_port = clib_host_to_net_u16 (peer->src.port); - hdr->udp.dst_port = clib_host_to_net_u16 (peer->dst.port); - hdr->udp.checksum = 0; + hdr->udp.src_port = clib_host_to_net_u16 (peer->src.port); + hdr->udp.dst_port = clib_host_to_net_u16 (peer->dst.port); + hdr->udp.checksum = 0; + } return (rewrite); } @@ -120,12 +140,15 @@ wg_peer_adj_stack (wg_peer_t *peer, adj_index_t ai) ip_adjacency_t *adj; u32 sw_if_index; wg_if_t *wgi; + fib_protocol_t fib_proto; if (!adj_is_valid (ai)) return; adj = adj_get (ai); sw_if_index = adj->rewrite_header.sw_if_index; + u8 is_ip4 = ip46_address_is_ip4 (&peer->src.addr); + fib_proto = is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6; wgi = wg_if_get (wg_if_find_by_sw_if_index (sw_if_index)); @@ -140,19 +163,76 @@ wg_peer_adj_stack (wg_peer_t *peer, adj_index_t ai) { /* *INDENT-OFF* */ fib_prefix_t dst = { - .fp_len = 32, - .fp_proto = FIB_PROTOCOL_IP4, - .fp_addr = peer->dst.addr, + .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_PROTOCOL_IP4, peer->table_id); + fib_index = fib_table_find (fib_proto, peer->table_id); adj_midchain_delegate_stack (ai, fib_index, &dst); } } +static void +wg_peer_66_fixup (vlib_main_t *vm, const ip_adjacency_t *adj, vlib_buffer_t *b, + const void *data) +{ + u8 iph_offset = 0; + ip6_header_t *ip6_out; + ip6_header_t *ip6_in; + + /* Must set locally originated otherwise we're not allowed to + fragment the packet later */ + b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED; + + ip6_out = vlib_buffer_get_current (b); + iph_offset = vnet_buffer (b)->ip.save_rewrite_length; + ip6_in = vlib_buffer_get_current (b) + iph_offset; + + ip6_out->ip_version_traffic_class_and_flow_label = + ip6_in->ip_version_traffic_class_and_flow_label; +} + +static void +wg_peer_46_fixup (vlib_main_t *vm, const ip_adjacency_t *adj, vlib_buffer_t *b, + const void *data) +{ + u8 iph_offset = 0; + ip6_header_t *ip6_out; + ip4_header_t *ip4_in; + + /* Must set locally originated otherwise we're not allowed to + fragment the packet later */ + b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED; + + ip6_out = vlib_buffer_get_current (b); + iph_offset = vnet_buffer (b)->ip.save_rewrite_length; + ip4_in = vlib_buffer_get_current (b) + iph_offset; + + u32 vtcfl = 0x6 << 28; + vtcfl |= ip4_in->tos << 20; + vtcfl |= vnet_buffer (b)->ip.flow_hash & 0x000fffff; + + ip6_out->ip_version_traffic_class_and_flow_label = + clib_host_to_net_u32 (vtcfl); +} + +static adj_midchain_fixup_t +wg_peer_get_fixup (wg_peer_t *peer, vnet_link_t lt) +{ + if (!ip46_address_is_ip4 (&peer->dst.addr)) + { + if (lt == VNET_LINK_IP4) + return (wg_peer_46_fixup); + if (lt == VNET_LINK_IP6) + return (wg_peer_66_fixup); + } + return (NULL); +} + walk_rc_t wg_peer_if_admin_state_change (index_t peeri, void *data) { @@ -170,6 +250,7 @@ walk_rc_t wg_peer_if_adj_change (index_t peeri, void *data) { adj_index_t *adj_index = data; + adj_midchain_fixup_t fixup; ip_adjacency_t *adj; wg_peer_t *peer; fib_prefix_t *allowed_ip; @@ -179,15 +260,16 @@ wg_peer_if_adj_change (index_t peeri, void *data) peer = wg_peer_get (peeri); vec_foreach (allowed_ip, peer->allowed_ips) { - if (fib_prefix_is_cover_addr_4 (allowed_ip, - &adj->sub_type.nbr.next_hop.ip4)) + if (fib_prefix_is_cover_addr_46 (allowed_ip, + &adj->sub_type.nbr.next_hop)) { vec_add1 (peer->adj_indices, *adj_index); vec_validate_init_empty (wg_peer_by_adj_index, *adj_index, INDEX_INVALID); wg_peer_by_adj_index[*adj_index] = peer - wg_peer_pool; - adj_nbr_midchain_update_rewrite (*adj_index, NULL, NULL, + fixup = wg_peer_get_fixup (peer, adj_get_link_type (*adj_index)); + adj_nbr_midchain_update_rewrite (*adj_index, fixup, NULL, ADJ_FLAG_MIDCHAIN_IP_STACK, vec_dup (peer->rewrite)); @@ -236,7 +318,9 @@ wg_peer_fill (vlib_main_t *vm, wg_peer_t *peer, u32 table_id, ip_address_to_46 (&wgi->src_ip, &peer->src.addr); peer->src.port = wgi->port; - peer->rewrite = wg_peer_build_rewrite (peer); + + u8 is_ip4 = ip46_address_is_ip4 (&peer->dst.addr); + peer->rewrite = wg_peer_build_rewrite (peer, is_ip4); u32 ii; vec_validate (peer->allowed_ips, vec_len (allowed_ips) - 1); diff --git a/src/plugins/wireguard/wireguard_peer.h b/src/plugins/wireguard/wireguard_peer.h index e23feb7866f..c719ac19511 100644 --- a/src/plugins/wireguard/wireguard_peer.h +++ b/src/plugins/wireguard/wireguard_peer.h @@ -31,7 +31,14 @@ typedef struct ip4_udp_header_t_ udp_header_t udp; } __clib_packed ip4_udp_header_t; +typedef struct ip6_udp_header_t_ +{ + ip6_header_t ip6; + udp_header_t udp; +} __clib_packed ip6_udp_header_t; + u8 *format_ip4_udp_header (u8 * s, va_list * va); +u8 *format_ip6_udp_header (u8 *s, va_list *va); typedef struct wg_peer_endpoint_t_ { @@ -141,15 +148,16 @@ wg_peer_assign_thread (u32 thread_id) } static_always_inline bool -fib_prefix_is_cover_addr_4 (const fib_prefix_t *p1, const ip4_address_t *ip4) +fib_prefix_is_cover_addr_46 (const fib_prefix_t *p1, const ip46_address_t *ip) { switch (p1->fp_proto) { case FIB_PROTOCOL_IP4: - return (ip4_destination_matches_route (&ip4_main, &p1->fp_addr.ip4, ip4, - p1->fp_len) != 0); + return (ip4_destination_matches_route (&ip4_main, &p1->fp_addr.ip4, + &ip->ip4, p1->fp_len) != 0); case FIB_PROTOCOL_IP6: - return (false); + return (ip6_destination_matches_route (&ip6_main, &p1->fp_addr.ip6, + &ip->ip6, p1->fp_len) != 0); case FIB_PROTOCOL_MPLS: break; } diff --git a/src/plugins/wireguard/wireguard_send.c b/src/plugins/wireguard/wireguard_send.c index f492e05c175..4451e000776 100755..100644 --- a/src/plugins/wireguard/wireguard_send.c +++ b/src/plugins/wireguard/wireguard_send.c @@ -22,11 +22,11 @@ #include <wireguard/wireguard_send.h> static int -ip46_enqueue_packet (vlib_main_t * vm, u32 bi0, int is_ip6) +ip46_enqueue_packet (vlib_main_t *vm, u32 bi0, int is_ip4) { vlib_frame_t *f = 0; u32 lookup_node_index = - is_ip6 ? ip6_lookup_node.index : ip4_lookup_node.index; + is_ip4 ? ip4_lookup_node.index : ip6_lookup_node.index; f = vlib_get_frame_to_node (vm, lookup_node_index); /* f can not be NULL here - frame allocation failure causes panic */ @@ -41,25 +41,41 @@ ip46_enqueue_packet (vlib_main_t * vm, u32 bi0, int is_ip6) } static void -wg_buffer_prepend_rewrite (vlib_buffer_t * b0, const wg_peer_t * peer) +wg_buffer_prepend_rewrite (vlib_buffer_t *b0, const wg_peer_t *peer, u8 is_ip4) { - ip4_udp_header_t *hdr; + if (is_ip4) + { + ip4_udp_header_t *hdr4; + + vlib_buffer_advance (b0, -sizeof (*hdr4)); - vlib_buffer_advance (b0, -sizeof (*hdr)); + hdr4 = vlib_buffer_get_current (b0); + clib_memcpy (hdr4, peer->rewrite, vec_len (peer->rewrite)); - hdr = vlib_buffer_get_current (b0); - clib_memcpy (hdr, peer->rewrite, vec_len (peer->rewrite)); + hdr4->udp.length = + clib_host_to_net_u16 (b0->current_length - sizeof (ip4_header_t)); + ip4_header_set_len_w_chksum (&hdr4->ip4, + clib_host_to_net_u16 (b0->current_length)); + } + else + { + ip6_udp_header_t *hdr6; - hdr->udp.length = - clib_host_to_net_u16 (b0->current_length - sizeof (ip4_header_t)); - ip4_header_set_len_w_chksum (&hdr->ip4, - clib_host_to_net_u16 (b0->current_length)); + vlib_buffer_advance (b0, -sizeof (*hdr6)); + + hdr6 = vlib_buffer_get_current (b0); + clib_memcpy (hdr6, peer->rewrite, vec_len (peer->rewrite)); + + hdr6->udp.length = + clib_host_to_net_u16 (b0->current_length - sizeof (ip6_header_t)); + + hdr6->ip6.payload_length = clib_host_to_net_u16 (b0->current_length); + } } static bool -wg_create_buffer (vlib_main_t * vm, - const wg_peer_t * peer, - const u8 * packet, u32 packet_len, u32 * bi) +wg_create_buffer (vlib_main_t *vm, const wg_peer_t *peer, const u8 *packet, + u32 packet_len, u32 *bi, u8 is_ip4) { u32 n_buf0 = 0; vlib_buffer_t *b0; @@ -75,7 +91,7 @@ wg_create_buffer (vlib_main_t * vm, b0->current_length = packet_len; - wg_buffer_prepend_rewrite (b0, peer); + wg_buffer_prepend_rewrite (b0, peer, is_ip4); return true; } @@ -113,11 +129,13 @@ wg_send_handshake (vlib_main_t * vm, wg_peer_t * peer, bool is_retry) else return false; + u8 is_ip4 = ip46_address_is_ip4 (&peer->dst.addr); u32 bi0 = 0; - if (!wg_create_buffer (vm, peer, (u8 *) & packet, sizeof (packet), &bi0)) + if (!wg_create_buffer (vm, peer, (u8 *) &packet, sizeof (packet), &bi0, + is_ip4)) return false; - ip46_enqueue_packet (vm, bi0, false); + ip46_enqueue_packet (vm, bi0, is_ip4); return true; } @@ -185,15 +203,17 @@ wg_send_keepalive (vlib_main_t * vm, wg_peer_t * peer) goto out; } + u8 is_ip4 = ip46_address_is_ip4 (&peer->dst.addr); packet->header.type = MESSAGE_DATA; - if (!wg_create_buffer (vm, peer, (u8 *) packet, size_of_packet, &bi0)) + if (!wg_create_buffer (vm, peer, (u8 *) packet, size_of_packet, &bi0, + is_ip4)) { ret = false; goto out; } - ip46_enqueue_packet (vm, bi0, false); + ip46_enqueue_packet (vm, bi0, is_ip4); wg_timers_any_authenticated_packet_sent (peer); wg_timers_any_authenticated_packet_traversal (peer); @@ -226,11 +246,12 @@ wg_send_handshake_response (vlib_main_t * vm, wg_peer_t * peer) peer->last_sent_handshake = vlib_time_now (vm); u32 bi0 = 0; - if (!wg_create_buffer (vm, peer, (u8 *) & packet, - sizeof (packet), &bi0)) + u8 is_ip4 = ip46_address_is_ip4 (&peer->dst.addr); + if (!wg_create_buffer (vm, peer, (u8 *) &packet, sizeof (packet), + &bi0, is_ip4)) return false; - ip46_enqueue_packet (vm, bi0, false); + ip46_enqueue_packet (vm, bi0, is_ip4); } else return false; diff --git a/test/test_wireguard.py b/test/test_wireguard.py index 96c1bc0f5fd..65ebd8d2c08 100755 --- a/test/test_wireguard.py +++ b/test/test_wireguard.py @@ -9,6 +9,7 @@ from scapy.packet import Packet from scapy.packet import Raw from scapy.layers.l2 import Ether, ARP from scapy.layers.inet import IP, UDP +from scapy.layers.inet6 import IPv6 from scapy.contrib.wireguard import Wireguard, WireguardResponse, \ WireguardInitiation, WireguardTransport from cryptography.hazmat.primitives.asymmetric.x25519 import \ @@ -98,8 +99,8 @@ class VppWgInterface(VppInterface): return "wireguard-%d" % self._sw_if_index -def find_route(test, prefix, table_id=0): - routes = test.vapi.ip_route_dump(table_id, False) +def find_route(test, prefix, is_ip6, table_id=0): + routes = test.vapi.ip_route_dump(table_id, is_ip6) for e in routes: if table_id == e.route.table_id \ @@ -134,7 +135,7 @@ class VppWgPeer(VppObject): self.noise = NoiseConnection.from_name(NOISE_HANDSHAKE_NAME) - def add_vpp_config(self): + def add_vpp_config(self, is_ip6=False): rv = self._test.vapi.wireguard_peer_add( peer={ 'public_key': self.public_key_bytes(), @@ -179,10 +180,15 @@ class VppWgPeer(VppObject): def set_responder(self): self.noise.set_as_responder() - def mk_tunnel_header(self, tx_itf): - return (Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac) / - IP(src=self.endpoint, dst=self.itf.src) / - UDP(sport=self.port, dport=self.itf.port)) + def mk_tunnel_header(self, tx_itf, is_ip6=False): + if is_ip6 is False: + return (Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac) / + IP(src=self.endpoint, dst=self.itf.src) / + UDP(sport=self.port, dport=self.itf.port)) + else: + return (Ether(dst=tx_itf.local_mac, src=tx_itf.remote_mac) / + IPv6(src=self.endpoint, dst=self.itf.src) / + UDP(sport=self.port, dport=self.itf.port)) def noise_init(self, public_key=None): self.noise.set_prologue(NOISE_IDENTIFIER_NAME) @@ -202,7 +208,7 @@ class VppWgPeer(VppObject): self.noise.start_handshake() - def mk_handshake(self, tx_itf, public_key=None): + def mk_handshake(self, tx_itf, is_ip6=False, public_key=None): self.noise.set_as_initiator() self.noise_init(public_key) @@ -232,21 +238,25 @@ class VppWgPeer(VppObject): key=mac_key).digest() p[WireguardInitiation].mac2 = bytearray(16) - p = (self.mk_tunnel_header(tx_itf) / p) + p = (self.mk_tunnel_header(tx_itf, is_ip6) / p) return p - def verify_header(self, p): - self._test.assertEqual(p[IP].src, self.itf.src) - self._test.assertEqual(p[IP].dst, self.endpoint) + def verify_header(self, p, is_ip6=False): + if is_ip6 is False: + self._test.assertEqual(p[IP].src, self.itf.src) + self._test.assertEqual(p[IP].dst, self.endpoint) + else: + self._test.assertEqual(p[IPv6].src, self.itf.src) + self._test.assertEqual(p[IPv6].dst, self.endpoint) self._test.assertEqual(p[UDP].sport, self.itf.port) self._test.assertEqual(p[UDP].dport, self.port) self._test.assert_packet_checksums_valid(p) - def consume_init(self, p, tx_itf): + def consume_init(self, p, tx_itf, is_ip6=False): self.noise.set_as_responder() self.noise_init(self.itf.public_key) - self.verify_header(p) + self.verify_header(p, is_ip6) init = Wireguard(p[Raw]) @@ -281,13 +291,13 @@ class VppWgPeer(VppObject): key=mac_key).digest() resp[WireguardResponse].mac1 = mac1 - resp = (self.mk_tunnel_header(tx_itf) / resp) + resp = (self.mk_tunnel_header(tx_itf, is_ip6) / resp) self._test.assertTrue(self.noise.handshake_finished) return resp - def consume_response(self, p): - self.verify_header(p) + def consume_response(self, p, is_ip6=False): + self.verify_header(p, is_ip6) resp = Wireguard(p[Raw]) @@ -302,8 +312,8 @@ class VppWgPeer(VppObject): self._test.assertEqual(payload, b'') self._test.assertTrue(self.noise.handshake_finished) - def decrypt_transport(self, p): - self.verify_header(p) + def decrypt_transport(self, p, is_ip6=False): + self.verify_header(p, is_ip6) p = Wireguard(p[Raw]) self._test.assertEqual(p[Wireguard].message_type, 4) @@ -318,13 +328,20 @@ class VppWgPeer(VppObject): def encrypt_transport(self, p): return self.noise.encrypt(bytes(p)) - def validate_encapped(self, rxs, tx): + def validate_encapped(self, rxs, tx, is_ip6=False): for rx in rxs: - rx = IP(self.decrypt_transport(rx)) + if is_ip6 is False: + rx = IP(self.decrypt_transport(rx)) - # chech the oringial packet is present - self._test.assertEqual(rx[IP].dst, tx[IP].dst) - self._test.assertEqual(rx[IP].ttl, tx[IP].ttl-1) + # chech the oringial packet is present + self._test.assertEqual(rx[IP].dst, tx[IP].dst) + self._test.assertEqual(rx[IP].ttl, tx[IP].ttl-1) + else: + rx = IPv6(self.decrypt_transport(rx)) + + # chech the oringial packet is present + self._test.assertEqual(rx[IPv6].dst, tx[IPv6].dst) + self._test.assertEqual(rx[IPv6].ttl, tx[IPv6].ttl-1) class TestWg(VppTestCase): @@ -332,6 +349,17 @@ class TestWg(VppTestCase): error_str = compile(r"Error") + wg4_output_node_name = '/err/wg4-output-tun/' + wg4_input_node_name = '/err/wg4-input/' + wg6_output_node_name = '/err/wg6-output-tun/' + wg6_input_node_name = '/err/wg6-input/' + kp4_error = wg4_output_node_name + "Keypair error" + mac4_error = wg4_input_node_name + "Invalid MAC handshake" + peer4_error = wg4_input_node_name + "Peer error" + kp6_error = wg6_output_node_name + "Keypair error" + mac6_error = wg6_input_node_name + "Invalid MAC handshake" + peer6_error = wg6_input_node_name + "Peer error" + @classmethod def setUpClass(cls): super(TestWg, cls).setUpClass() @@ -340,7 +368,9 @@ class TestWg(VppTestCase): for i in cls.pg_interfaces: i.admin_up() i.config_ip4() + i.config_ip6() i.resolve_arp() + i.resolve_ndp() except Exception: super(TestWg, cls).tearDownClass() @@ -350,6 +380,15 @@ class TestWg(VppTestCase): def tearDownClass(cls): super(TestWg, cls).tearDownClass() + def setUp(self): + super(VppTestCase, self).setUp() + self.base_kp4_err = self.statistics.get_err_counter(self.kp4_error) + self.base_mac4_err = self.statistics.get_err_counter(self.mac4_error) + self.base_peer4_err = self.statistics.get_err_counter(self.peer4_error) + self.base_kp6_err = self.statistics.get_err_counter(self.kp6_error) + self.base_mac6_err = self.statistics.get_err_counter(self.mac6_error) + self.base_peer6_err = self.statistics.get_err_counter(self.peer6_error) + def test_wg_interface(self): """ Simple interface creation """ port = 12312 @@ -409,9 +448,6 @@ class TestWg(VppTestCase): def test_wg_peer_resp(self): """ Send handshake response """ - wg_output_node_name = '/err/wg-output-tun/' - wg_input_node_name = '/err/wg-input/' - port = 12323 # Create interfaces @@ -481,10 +517,8 @@ class TestWg(VppTestCase): peer_1.remove_vpp_config() wg0.remove_vpp_config() - def test_wg_peer_init(self): - """ Send handshake init """ - wg_output_node_name = '/err/wg-output-tun/' - wg_input_node_name = '/err/wg-input/' + def test_wg_peer_v4o4(self): + """ Test v4o4""" port = 12333 @@ -514,23 +548,23 @@ class TestWg(VppTestCase): UDP(sport=555, dport=556) / Raw()) self.send_and_assert_no_replies(self.pg0, [p]) - - kp_error = wg_output_node_name + "Keypair error" - self.assertEqual(1, self.statistics.get_err_counter(kp_error)) + self.assertEqual(self.base_kp4_err + 1, + self.statistics.get_err_counter(self.kp4_error)) # send a handsake from the peer with an invalid MAC p = peer_1.mk_handshake(self.pg1) p[WireguardInitiation].mac1 = b'foobar' self.send_and_assert_no_replies(self.pg1, [p]) - self.assertEqual(1, self.statistics.get_err_counter( - wg_input_node_name + "Invalid MAC handshake")) + self.assertEqual(self.base_mac4_err + 1, + self.statistics.get_err_counter(self.mac4_error)) # send a handsake from the peer but signed by the wrong key. p = peer_1.mk_handshake(self.pg1, + False, X25519PrivateKey.generate().public_key()) self.send_and_assert_no_replies(self.pg1, [p]) - self.assertEqual(1, self.statistics.get_err_counter( - wg_input_node_name + "Peer error")) + self.assertEqual(self.base_peer4_err + 1, + self.statistics.get_err_counter(self.peer4_error)) # send a valid handsake init for which we expect a response p = peer_1.mk_handshake(self.pg1) @@ -546,7 +580,8 @@ class TestWg(VppTestCase): UDP(sport=555, dport=556) / Raw()) self.send_and_assert_no_replies(self.pg0, [p]) - self.assertEqual(2, self.statistics.get_err_counter(kp_error)) + self.assertEqual(self.base_kp4_err + 2, + self.statistics.get_err_counter(self.kp4_error)) # send a data packet from the peer through the tunnel # this completes the handshake @@ -602,9 +637,373 @@ class TestWg(VppTestCase): peer_1.remove_vpp_config() wg0.remove_vpp_config() + def test_wg_peer_v6o6(self): + """ Test v6o6""" + + port = 12343 + + # Create interfaces + wg0 = VppWgInterface(self, + self.pg1.local_ip6, + port).add_vpp_config() + wg0.admin_up() + wg0.config_ip6() + + peer_1 = VppWgPeer(self, + wg0, + self.pg1.remote_ip6, + port+1, + ["1::3:0/112"]).add_vpp_config(True) + self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1) + + r1 = VppIpRoute(self, "1::3:0", 112, + [VppRoutePath("1::3:1", + wg0.sw_if_index)]).add_vpp_config() + + # route a packet into the wg interface + # use the allowed-ip prefix + # this is dropped because the peer is not initiated + + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=self.pg0.remote_ip6, dst="1::3:2") / + UDP(sport=555, dport=556) / + Raw()) + self.send_and_assert_no_replies(self.pg0, [p]) + + self.assertEqual(self.base_kp6_err + 1, + self.statistics.get_err_counter(self.kp6_error)) + + # send a handsake from the peer with an invalid MAC + p = peer_1.mk_handshake(self.pg1, True) + p[WireguardInitiation].mac1 = b'foobar' + self.send_and_assert_no_replies(self.pg1, [p]) + + self.assertEqual(self.base_mac6_err + 1, + self.statistics.get_err_counter(self.mac6_error)) + + # send a handsake from the peer but signed by the wrong key. + p = peer_1.mk_handshake(self.pg1, + True, + X25519PrivateKey.generate().public_key()) + self.send_and_assert_no_replies(self.pg1, [p]) + self.assertEqual(self.base_peer6_err + 1, + self.statistics.get_err_counter(self.peer6_error)) + + # send a valid handsake init for which we expect a response + p = peer_1.mk_handshake(self.pg1, True) + + rx = self.send_and_expect(self.pg1, [p], self.pg1) + + peer_1.consume_response(rx[0], True) + + # route a packet into the wg interface + # this is dropped because the peer is still not initiated + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=self.pg0.remote_ip6, dst="1::3:2") / + UDP(sport=555, dport=556) / + Raw()) + self.send_and_assert_no_replies(self.pg0, [p]) + self.assertEqual(self.base_kp6_err + 2, + self.statistics.get_err_counter(self.kp6_error)) + + # send a data packet from the peer through the tunnel + # this completes the handshake + p = (IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20) / + UDP(sport=222, dport=223) / + Raw()) + d = peer_1.encrypt_transport(p) + p = (peer_1.mk_tunnel_header(self.pg1, True) / + (Wireguard(message_type=4, reserved_zero=0) / + WireguardTransport(receiver_index=peer_1.sender, + counter=0, + encrypted_encapsulated_packet=d))) + rxs = self.send_and_expect(self.pg1, [p], self.pg0) + + for rx in rxs: + self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6) + self.assertEqual(rx[IPv6].hlim, 19) + + # send a packets that are routed into the tunnel + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=self.pg0.remote_ip6, dst="1::3:2") / + UDP(sport=555, dport=556) / + Raw(b'\x00' * 80)) + + rxs = self.send_and_expect(self.pg0, p * 255, self.pg1) + + for rx in rxs: + rx = IPv6(peer_1.decrypt_transport(rx, True)) + + # chech the oringial packet is present + self.assertEqual(rx[IPv6].dst, p[IPv6].dst) + self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim-1) + + # send packets into the tunnel, expect to receive them on + # the other side + p = [(peer_1.mk_tunnel_header(self.pg1, True) / + Wireguard(message_type=4, reserved_zero=0) / + WireguardTransport( + receiver_index=peer_1.sender, + counter=ii+1, + encrypted_encapsulated_packet=peer_1.encrypt_transport( + (IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20) / + UDP(sport=222, dport=223) / + Raw())))) for ii in range(255)] + + rxs = self.send_and_expect(self.pg1, p, self.pg0) + + for rx in rxs: + self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6) + self.assertEqual(rx[IPv6].hlim, 19) + + r1.remove_vpp_config() + peer_1.remove_vpp_config() + wg0.remove_vpp_config() + + def test_wg_peer_v6o4(self): + """ Test v6o4""" + + port = 12353 + + # Create interfaces + wg0 = VppWgInterface(self, + self.pg1.local_ip4, + port).add_vpp_config() + wg0.admin_up() + wg0.config_ip6() + + peer_1 = VppWgPeer(self, + wg0, + self.pg1.remote_ip4, + port+1, + ["1::3:0/112"]).add_vpp_config(True) + self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1) + + r1 = VppIpRoute(self, "1::3:0", 112, + [VppRoutePath("1::3:1", + wg0.sw_if_index)]).add_vpp_config() + + # route a packet into the wg interface + # use the allowed-ip prefix + # this is dropped because the peer is not initiated + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=self.pg0.remote_ip6, dst="1::3:2") / + UDP(sport=555, dport=556) / + Raw()) + self.send_and_assert_no_replies(self.pg0, [p]) + self.assertEqual(self.base_kp6_err + 1, + self.statistics.get_err_counter(self.kp6_error)) + + # send a handsake from the peer with an invalid MAC + p = peer_1.mk_handshake(self.pg1) + p[WireguardInitiation].mac1 = b'foobar' + self.send_and_assert_no_replies(self.pg1, [p]) + + self.assertEqual(self.base_mac4_err + 1, + self.statistics.get_err_counter(self.mac4_error)) + + # send a handsake from the peer but signed by the wrong key. + p = peer_1.mk_handshake(self.pg1, + False, + X25519PrivateKey.generate().public_key()) + self.send_and_assert_no_replies(self.pg1, [p]) + self.assertEqual(self.base_peer4_err + 1, + self.statistics.get_err_counter(self.peer4_error)) + + # send a valid handsake init for which we expect a response + p = peer_1.mk_handshake(self.pg1) + + rx = self.send_and_expect(self.pg1, [p], self.pg1) + + peer_1.consume_response(rx[0]) + + # route a packet into the wg interface + # this is dropped because the peer is still not initiated + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=self.pg0.remote_ip6, dst="1::3:2") / + UDP(sport=555, dport=556) / + Raw()) + self.send_and_assert_no_replies(self.pg0, [p]) + self.assertEqual(self.base_kp6_err + 2, + self.statistics.get_err_counter(self.kp6_error)) + + # send a data packet from the peer through the tunnel + # this completes the handshake + p = (IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20) / + UDP(sport=222, dport=223) / + Raw()) + d = peer_1.encrypt_transport(p) + p = (peer_1.mk_tunnel_header(self.pg1) / + (Wireguard(message_type=4, reserved_zero=0) / + WireguardTransport(receiver_index=peer_1.sender, + counter=0, + encrypted_encapsulated_packet=d))) + rxs = self.send_and_expect(self.pg1, [p], self.pg0) + + for rx in rxs: + self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6) + self.assertEqual(rx[IPv6].hlim, 19) + + # send a packets that are routed into the tunnel + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=self.pg0.remote_ip6, dst="1::3:2") / + UDP(sport=555, dport=556) / + Raw(b'\x00' * 80)) + + rxs = self.send_and_expect(self.pg0, p * 255, self.pg1) + + for rx in rxs: + rx = IPv6(peer_1.decrypt_transport(rx)) + + # chech the oringial packet is present + self.assertEqual(rx[IPv6].dst, p[IPv6].dst) + self.assertEqual(rx[IPv6].hlim, p[IPv6].hlim-1) + + # send packets into the tunnel, expect to receive them on + # the other side + p = [(peer_1.mk_tunnel_header(self.pg1) / + Wireguard(message_type=4, reserved_zero=0) / + WireguardTransport( + receiver_index=peer_1.sender, + counter=ii+1, + encrypted_encapsulated_packet=peer_1.encrypt_transport( + (IPv6(src="1::3:1", dst=self.pg0.remote_ip6, hlim=20) / + UDP(sport=222, dport=223) / + Raw())))) for ii in range(255)] + + rxs = self.send_and_expect(self.pg1, p, self.pg0) + + for rx in rxs: + self.assertEqual(rx[IPv6].dst, self.pg0.remote_ip6) + self.assertEqual(rx[IPv6].hlim, 19) + + r1.remove_vpp_config() + peer_1.remove_vpp_config() + wg0.remove_vpp_config() + + def test_wg_peer_v4o6(self): + """ Test v4o6""" + + port = 12363 + + # Create interfaces + wg0 = VppWgInterface(self, + self.pg1.local_ip6, + port).add_vpp_config() + wg0.admin_up() + wg0.config_ip4() + + peer_1 = VppWgPeer(self, + wg0, + self.pg1.remote_ip6, + port+1, + ["10.11.3.0/24"]).add_vpp_config() + self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1) + + r1 = VppIpRoute(self, "10.11.3.0", 24, + [VppRoutePath("10.11.3.1", + wg0.sw_if_index)]).add_vpp_config() + + # route a packet into the wg interface + # use the allowed-ip prefix + # this is dropped because the peer is not initiated + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst="10.11.3.2") / + UDP(sport=555, dport=556) / + Raw()) + self.send_and_assert_no_replies(self.pg0, [p]) + self.assertEqual(self.base_kp4_err + 1, + self.statistics.get_err_counter(self.kp4_error)) + + # send a handsake from the peer with an invalid MAC + p = peer_1.mk_handshake(self.pg1, True) + p[WireguardInitiation].mac1 = b'foobar' + self.send_and_assert_no_replies(self.pg1, [p]) + self.assertEqual(self.base_mac6_err + 1, + self.statistics.get_err_counter(self.mac6_error)) + + # send a handsake from the peer but signed by the wrong key. + p = peer_1.mk_handshake(self.pg1, + True, + X25519PrivateKey.generate().public_key()) + self.send_and_assert_no_replies(self.pg1, [p]) + self.assertEqual(self.base_peer6_err + 1, + self.statistics.get_err_counter(self.peer6_error)) + + # send a valid handsake init for which we expect a response + p = peer_1.mk_handshake(self.pg1, True) + + rx = self.send_and_expect(self.pg1, [p], self.pg1) + + peer_1.consume_response(rx[0], True) + + # route a packet into the wg interface + # this is dropped because the peer is still not initiated + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst="10.11.3.2") / + UDP(sport=555, dport=556) / + Raw()) + self.send_and_assert_no_replies(self.pg0, [p]) + self.assertEqual(self.base_kp4_err + 2, + self.statistics.get_err_counter(self.kp4_error)) + + # send a data packet from the peer through the tunnel + # this completes the handshake + p = (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) / + UDP(sport=222, dport=223) / + Raw()) + d = peer_1.encrypt_transport(p) + p = (peer_1.mk_tunnel_header(self.pg1, True) / + (Wireguard(message_type=4, reserved_zero=0) / + WireguardTransport(receiver_index=peer_1.sender, + counter=0, + encrypted_encapsulated_packet=d))) + rxs = self.send_and_expect(self.pg1, [p], self.pg0) + + for rx in rxs: + self.assertEqual(rx[IP].dst, self.pg0.remote_ip4) + self.assertEqual(rx[IP].ttl, 19) + + # send a packets that are routed into the tunnel + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst="10.11.3.2") / + UDP(sport=555, dport=556) / + Raw(b'\x00' * 80)) + + rxs = self.send_and_expect(self.pg0, p * 255, self.pg1) + + for rx in rxs: + rx = IP(peer_1.decrypt_transport(rx, True)) + + # chech the oringial packet is present + self.assertEqual(rx[IP].dst, p[IP].dst) + self.assertEqual(rx[IP].ttl, p[IP].ttl-1) + + # send packets into the tunnel, expect to receive them on + # the other side + p = [(peer_1.mk_tunnel_header(self.pg1, True) / + Wireguard(message_type=4, reserved_zero=0) / + WireguardTransport( + receiver_index=peer_1.sender, + counter=ii+1, + encrypted_encapsulated_packet=peer_1.encrypt_transport( + (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) / + UDP(sport=222, dport=223) / + Raw())))) for ii in range(255)] + + rxs = self.send_and_expect(self.pg1, p, self.pg0) + + for rx in rxs: + self.assertEqual(rx[IP].dst, self.pg0.remote_ip4) + self.assertEqual(rx[IP].ttl, 19) + + r1.remove_vpp_config() + peer_1.remove_vpp_config() + wg0.remove_vpp_config() + def test_wg_multi_peer(self): """ multiple peer setup """ - port = 12343 + port = 12373 # Create interfaces wg0 = VppWgInterface(self, @@ -784,10 +1183,8 @@ class WireguardHandoffTests(TestWg): def test_wg_peer_init(self): """ Handoff """ - wg_output_node_name = '/err/wg-output-tun/' - wg_input_node_name = '/err/wg-input/' - port = 12353 + port = 12383 # Create interfaces wg0 = VppWgInterface(self, @@ -844,14 +1241,14 @@ class WireguardHandoffTests(TestWg): # send packets into the tunnel, from the other worker p = [(peer_1.mk_tunnel_header(self.pg1) / - Wireguard(message_type=4, reserved_zero=0) / - WireguardTransport( - receiver_index=peer_1.sender, - counter=ii+1, - encrypted_encapsulated_packet=peer_1.encrypt_transport( - (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) / - UDP(sport=222, dport=223) / - Raw())))) for ii in range(255)] + Wireguard(message_type=4, reserved_zero=0) / + WireguardTransport( + receiver_index=peer_1.sender, + counter=ii+1, + encrypted_encapsulated_packet=peer_1.encrypt_transport( + (IP(src="10.11.3.1", dst=self.pg0.remote_ip4, ttl=20) / + UDP(sport=222, dport=223) / + Raw())))) for ii in range(255)] rxs = self.send_and_expect(self.pg1, p, self.pg0, worker=1) |