wireguard: add ipv6 support 09/32009/12
authorArtem Glazychev <artem.glazychev@xored.com>
Thu, 3 Jun 2021 13:11:54 +0000 (20:11 +0700)
committerEd Warnicke <hagbard@gmail.com>
Wed, 6 Oct 2021 17:57:46 +0000 (17:57 +0000)
Type: improvement
Signed-off-by: Artem Glazychev <artem.glazychev@xored.com>
Change-Id: If1a7e82ce163c4c4acaa5acf45ad2b88371396f6

15 files changed:
src/plugins/wireguard/wireguard.c [changed mode: 0755->0644]
src/plugins/wireguard/wireguard.h
src/plugins/wireguard/wireguard_api.c
src/plugins/wireguard/wireguard_cli.c [changed mode: 0755->0644]
src/plugins/wireguard/wireguard_cookie.c [changed mode: 0755->0644]
src/plugins/wireguard/wireguard_cookie.h [changed mode: 0755->0644]
src/plugins/wireguard/wireguard_handoff.c
src/plugins/wireguard/wireguard_if.c
src/plugins/wireguard/wireguard_if.h
src/plugins/wireguard/wireguard_input.c
src/plugins/wireguard/wireguard_output_tun.c [changed mode: 0755->0644]
src/plugins/wireguard/wireguard_peer.c
src/plugins/wireguard/wireguard_peer.h
src/plugins/wireguard/wireguard_send.c [changed mode: 0755->0644]
test/test_wireguard.py

old mode 100755 (executable)
new mode 100644 (file)
index 5842229..8438cc1
@@ -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,
index ef308c4..829c9e6 100755 (executable)
 
 #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;
index 3f17f65..5dd4f86 100644 (file)
@@ -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:
old mode 100755 (executable)
new mode 100644 (file)
index 3b4bf56..05275f7
@@ -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)
     {
old mode 100755 (executable)
new mode 100644 (file)
index f54ce71..c4279b7
@@ -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);
 }
old mode 100755 (executable)
new mode 100644 (file)
index 489cce8..6ef418f
@@ -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__ */
 
index d3e37b3..5f3dc14 100644 (file)
@@ -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,
index 0866d24..ad8f42c 100644 (file)
@@ -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
index 3153a38..0a042cb 100644 (file)
@@ -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;
 
index ad002dc..6a0623e 100644 (file)
@@ -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",
   },
old mode 100755 (executable)
new mode 100644 (file)
index 53a8797..80ba950
@@ -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",
   },
index d4a85a2..fb54014 100644 (file)
@@ -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);
index e23feb7..c719ac1 100644 (file)
@@ -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;
     }
old mode 100755 (executable)
new mode 100644 (file)
index f492e05..4451e00
 #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;
index 96c1bc0..65ebd8d 100755 (executable)
@@ -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)