ikev2: handoff packets 00/40400/8
authorStanislav Zaikin <[email protected]>
Fri, 8 Sep 2023 08:27:15 +0000 (10:27 +0200)
committerBeno�t Ganne <[email protected]>
Wed, 7 Aug 2024 12:07:13 +0000 (12:07 +0000)
current approach saves state in per-thread data structure. in
multi-worker + nat-t cases udp/500 and udp/4500 might be dispatched on
different workers. this patch adds hands off packet to 1 explicit thread
- 1st worker (or main thread in case there're no workers) or to thread
  that was explicitly set by user via configuration

Type: improvement

Change-Id: Ib5cd9a4b8612dfaa63b276035709524f7a492d4f
Signed-off-by: Stanislav Zaikin <[email protected]>
src/plugins/ikev2/CMakeLists.txt
src/plugins/ikev2/ikev2.api
src/plugins/ikev2/ikev2.c
src/plugins/ikev2/ikev2_api.c
src/plugins/ikev2/ikev2_handoff.c [new file with mode: 0644]
src/plugins/ikev2/ikev2_priv.h
test/test_ikev2.py

index 568271e..dd2b49d 100644 (file)
@@ -27,6 +27,7 @@ add_vpp_plugin(ikev2
   ikev2_crypto.c
   ikev2_format.c
   ikev2_payload.c
+  ikev2_handoff.c
 
   API_FILES
   ikev2_types.api
index de276e7..e2ff8fb 100644 (file)
@@ -658,6 +658,12 @@ counters ikev2 {
     units "packets";
     description "IKE AUTH SA requests received";
   };
+  handoff {
+    severity info;
+    type counter64;
+    units "packets";
+    description "IKE packets handoff";
+  };
 };
 paths {
   "/err/ikev2-ip4" "ike";
index 9bea2c9..f66469a 100644 (file)
@@ -97,6 +97,7 @@ format_ikev2_gen_sa_error (u8 * s, va_list * args)
 typedef enum
 {
   IKEV2_NEXT_IP4_LOOKUP,
+  IKEV2_NEXT_IP4_HANDOFF,
   IKEV2_NEXT_IP4_ERROR_DROP,
   IKEV2_IP4_N_NEXT,
 } ikev2_ip4_next_t;
@@ -104,6 +105,7 @@ typedef enum
 typedef enum
 {
   IKEV2_NEXT_IP6_LOOKUP,
+  IKEV2_NEXT_IP6_HANDOFF,
   IKEV2_NEXT_IP6_ERROR_DROP,
   IKEV2_IP6_N_NEXT,
 } ikev2_ip6_next_t;
@@ -3187,6 +3189,7 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node,
   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
   u16 nexts[VLIB_FRAME_SIZE], *next = nexts;
   ikev2_main_per_thread_data_t *ptd = ikev2_get_per_thread_data ();
+  u32 thread_index = vm->thread_index;
   ikev2_stats_t _stats, *stats = &_stats;
   int res;
 
@@ -3213,6 +3216,14 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node,
       int ip_hdr_sz = 0;
       int is_req = 0;
 
+      if (PREDICT_TRUE (thread_index != km->handoff_thread))
+      {
+         vlib_node_increment_counter (vm, node->node_index,
+                                      IKEV2_ERROR_HANDOFF, 1);
+
+         next[0] = is_ip4 ? IKEV2_NEXT_IP4_HANDOFF : IKEV2_NEXT_IP6_HANDOFF;
+         goto out;
+      }
       if (natt)
        {
          u8 *ptr = vlib_buffer_get_current (b0);
@@ -3723,6 +3734,8 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node,
 
          ikev2_delete_sa (ptd, sa0);
        }
+
+    out:
       if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
                         && (b0->flags & VLIB_BUFFER_IS_TRACED)))
        {
@@ -3775,6 +3788,7 @@ VLIB_REGISTER_NODE (ikev2_node_ip4,static) = {
   .n_next_nodes = IKEV2_IP4_N_NEXT,
   .next_nodes = {
     [IKEV2_NEXT_IP4_LOOKUP] = "ip4-lookup",
+    [IKEV2_NEXT_IP4_HANDOFF] = "ikev2-ip4-handoff",
     [IKEV2_NEXT_IP4_ERROR_DROP] = "error-drop",
   },
 };
@@ -3792,6 +3806,7 @@ VLIB_REGISTER_NODE (ikev2_node_ip4_natt,static) = {
   .n_next_nodes = IKEV2_IP4_N_NEXT,
   .next_nodes = {
     [IKEV2_NEXT_IP4_LOOKUP] = "ip4-lookup",
+    [IKEV2_NEXT_IP4_HANDOFF] = "ikev2-ip4-natt-handoff",
     [IKEV2_NEXT_IP4_ERROR_DROP] = "error-drop",
   },
 };
@@ -3809,6 +3824,7 @@ VLIB_REGISTER_NODE (ikev2_node_ip6,static) = {
   .n_next_nodes = IKEV2_IP6_N_NEXT,
   .next_nodes = {
     [IKEV2_NEXT_IP6_LOOKUP] = "ip6-lookup",
+    [IKEV2_NEXT_IP4_HANDOFF] = "ikev2-ip6-handoff",
     [IKEV2_NEXT_IP6_ERROR_DROP] = "error-drop",
   },
 };
@@ -5126,6 +5142,8 @@ ikev2_init (vlib_main_t * vm)
   km->liveness_period = IKEV2_LIVENESS_PERIOD_CHECK;
   km->liveness_max_retries = IKEV2_LIVENESS_RETRIES;
 
+  km->handoff_thread = vlib_num_workers () ? 1 : 0;
+
   return 0;
 }
 
@@ -5133,6 +5151,31 @@ VLIB_INIT_FUNCTION (ikev2_init) = {
   .runs_after = VLIB_INITS ("ipsec_init", "ipsec_punt_init"),
 };
 
+static clib_error_t *
+ikev2_config (vlib_main_t *vm, unformat_input_t *input)
+{
+  ikev2_main_t *km = &ikev2_main;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+  {
+    if (unformat (input, "handoff-thread %d", &km->handoff_thread))
+      {
+      if (km->handoff_thread > vlib_num_workers ())
+       {
+         return clib_error_return (0, "wrong handoff-thread %d",
+                                   km->handoff_thread);
+       }
+      }
+    else
+      return clib_error_return (0, "unknown input `%U'", format_unformat_error,
+                               input);
+  }
+
+  return 0;
+}
+
+VLIB_CONFIG_FUNCTION (ikev2_config, "ikev2");
+
 static u8
 ikev2_mngr_process_child_sa (ikev2_sa_t * sa, ikev2_child_sa_t * csa,
                             u8 del_old_ids)
@@ -5447,6 +5490,7 @@ ikev2_send_informational_request (ikev2_sa_t * sa)
     }
 
   dp = sa->dst_port ? sa->dst_port : ikev2_get_port (sa);
+
   ikev2_send_ike (km->vlib_main, src, dst, bi0, len, ikev2_get_port (sa), dp,
                  sa->sw_if_index);
 }
@@ -5625,6 +5669,15 @@ ikev2_lazy_init (ikev2_main_t *km)
   if (!km->dns_resolve_name_ptr)
     ikev2_log_error ("cannot load symbols from dns plugin");
 
+  km->handoff_ip4_fq_index =
+    vlib_frame_queue_main_init (ikev2_node_ip4.index, 0);
+
+  km->handoff_ip4_natt_fq_index =
+    vlib_frame_queue_main_init (ikev2_node_ip4_natt.index, 0);
+
+  km->handoff_ip6_fq_index =
+    vlib_frame_queue_main_init (ikev2_node_ip6.index, 0);
+
   /* wake up ikev2 process */
   vlib_process_signal_event (vlib_get_first_main (),
                             ikev2_mngr_process_node.index, 0, 0);
index c9608aa..e09bde3 100644 (file)
@@ -577,6 +577,7 @@ vl_api_ikev2_child_sa_dump_t_handler (vl_api_ikev2_child_sa_dump_t * mp)
   vec_foreach (child, sa->childs)
   {
     u32 child_sa_index = child - sa->childs;
+    sai = ikev2_encode_sa_index (sai, tkm - im->per_thread_data);
     send_child_sa (child, mp, child_sa_index, sai);
   }
 }
diff --git a/src/plugins/ikev2/ikev2_handoff.c b/src/plugins/ikev2/ikev2_handoff.c
new file mode 100644 (file)
index 0000000..8f55985
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <ikev2/ikev2_priv.h>
+
+extern ikev2_main_t ikev2_main;
+
+#define foreach_ikev2_handoff_error _ (CONGESTION_DROP, "congestion drop")
+
+typedef enum
+{
+#define _(sym, str) IKEV2_HANDOFF_ERROR_##sym,
+  foreach_ikev2_handoff_error
+#undef _
+    IKEV2_HANDOFF_N_ERROR,
+} ikev2_handoff_error_t;
+
+static char *ikev2_handoff_error_strings[] = {
+#define _(sym, string) string,
+  foreach_ikev2_handoff_error
+#undef _
+};
+
+typedef struct ikev2_handoff_trace_t_
+{
+  u32 current_worker_index;
+  u32 next_worker_index;
+} ikev2_handoff_trace_t;
+
+u8 *
+format_ikev2_handoff_trace (u8 *s, va_list *args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+
+  ikev2_handoff_trace_t *t = va_arg (*args, ikev2_handoff_trace_t *);
+  s = format (s, "ikev2 handoff  %d to %d", t->current_worker_index,
+             t->next_worker_index);
+  return s;
+}
+
+static_always_inline uword
+ikev2_handoff_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
+                     vlib_frame_t *frame, u32 fq_index)
+{
+  ikev2_main_t *km = &ikev2_main;
+  vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
+  u16 thread_indices[VLIB_FRAME_SIZE], *ti;
+  u32 n_enq, n_left_from, *from;
+  u32 this_thread;
+
+  this_thread = vm->thread_index;
+
+  from = vlib_frame_vector_args (frame);
+  n_left_from = frame->n_vectors;
+  vlib_get_buffers (vm, from, bufs, n_left_from);
+
+  b = bufs;
+  ti = thread_indices;
+
+  while (n_left_from > 0)
+    {
+      ti[0] = km->handoff_thread;
+
+      if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
+                        b[0]->flags & VLIB_BUFFER_IS_TRACED))
+       {
+         ikev2_handoff_trace_t *t =
+           vlib_add_trace (vm, node, b[0], sizeof (*t));
+         t->current_worker_index = this_thread;
+         t->next_worker_index = ti[0];
+       }
+      n_left_from--;
+      ti++;
+      b++;
+    }
+
+  n_enq = vlib_buffer_enqueue_to_thread (vm, node, fq_index, from,
+                                        thread_indices, frame->n_vectors, 1);
+
+  if (n_enq < frame->n_vectors)
+    vlib_node_increment_counter (vm, node->node_index,
+                                IKEV2_HANDOFF_ERROR_CONGESTION_DROP,
+                                frame->n_vectors - n_enq);
+  return n_enq;
+}
+
+/* Do worker handoff based on the ikev2's thread_index */
+VLIB_NODE_FN (ikev2_ip4_handoff)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *from_frame)
+{
+  ikev2_main_t *km = &ikev2_main;
+
+  return ikev2_handoff_inline (vm, node, from_frame, km->handoff_ip4_fq_index);
+}
+
+VLIB_NODE_FN (ikev2_ip4_natt_handoff)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *from_frame)
+{
+  ikev2_main_t *km = &ikev2_main;
+
+  return ikev2_handoff_inline (vm, node, from_frame,
+                              km->handoff_ip4_natt_fq_index);
+}
+
+VLIB_NODE_FN (ikev2_ip6_handoff)
+(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *from_frame)
+{
+  ikev2_main_t *km = &ikev2_main;
+
+  return ikev2_handoff_inline (vm, node, from_frame, km->handoff_ip6_fq_index);
+}
+
+VLIB_REGISTER_NODE (ikev2_ip4_handoff) = {
+  .name = "ikev2-ip4-handoff",
+  .vector_size = sizeof (u32),
+  .format_trace = format_ikev2_handoff_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .n_errors = ARRAY_LEN(ikev2_handoff_error_strings),
+  .error_strings = ikev2_handoff_error_strings,
+  .n_next_nodes = 1,
+  .next_nodes = {
+    [0] = "error-drop",
+  },
+};
+
+VLIB_REGISTER_NODE (ikev2_ip4_natt_handoff) = {
+  .name = "ikev2-ip4-natt-handoff",
+  .vector_size = sizeof (u32),
+  .format_trace = format_ikev2_handoff_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .n_errors = ARRAY_LEN(ikev2_handoff_error_strings),
+  .error_strings = ikev2_handoff_error_strings,
+  .n_next_nodes = 1,
+  .next_nodes = {
+    [0] = "error-drop",
+  },
+};
+
+VLIB_REGISTER_NODE (ikev2_ip6_handoff) = {
+  .name = "ikev2-ip6-handoff",
+  .vector_size = sizeof (u32),
+  .format_trace = format_ikev2_handoff_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .n_errors = ARRAY_LEN(ikev2_handoff_error_strings),
+  .error_strings = ikev2_handoff_error_strings,
+  .n_next_nodes = 1,
+  .next_nodes = {
+    [0] = "error-drop",
+  },
+};
index 0639809..9631318 100644 (file)
@@ -571,6 +571,12 @@ typedef struct
   /* punt handle for IPsec NATT IPSEC_PUNT_IP4_SPI_UDP_0 reason */
   vlib_punt_hdl_t punt_hdl;
 
+  /** Worker handoff */
+  u32 handoff_thread;
+  u32 handoff_ip4_fq_index;
+  u32 handoff_ip4_natt_fq_index;
+  u32 handoff_ip6_fq_index;
+
 } ikev2_main_t;
 
 extern ikev2_main_t ikev2_main;
index be14df1..341556d 100644 (file)
@@ -675,7 +675,7 @@ class IkePeer(VppTestCase):
         self.assertIsNotNone(self.p.query_vpp_config())
         if self.sa.is_initiator:
             self.sa.generate_dh_data()
-        self.vapi.cli("ikev2 set logging level 4")
+        self.vapi.cli("ikev2 set logging level 5")
         self.vapi.cli("event-lo clear")
 
     def assert_counter(self, count, name, version="ip4"):
@@ -2023,20 +2023,21 @@ class TestApi(VppTestCase):
             self.assertEqual(ap.tun_itf, 0xFFFFFFFF)
 
 
-@tag_fixme_vpp_workers
 class TestResponderBehindNAT(TemplateResponder, Ikev2Params):
     """test responder - responder behind NAT"""
 
     IKE_NODE_SUFFIX = "ip4-natt"
+    vpp_worker_count = 2
 
     def config_tc(self):
         self.config_params({"r_natt": True})
 
 
-@tag_fixme_vpp_workers
 class TestInitiatorNATT(TemplateInitiator, Ikev2Params):
     """test ikev2 initiator - NAT traversal (intitiator behind NAT)"""
 
+    vpp_worker_count = 2
+
     def config_tc(self):
         self.config_params(
             {
@@ -2065,10 +2066,11 @@ class TestInitiatorNATT(TemplateInitiator, Ikev2Params):
         )
 
 
-@tag_fixme_vpp_workers
 class TestInitiatorPsk(TemplateInitiator, Ikev2Params):
     """test ikev2 initiator - pre shared key auth"""
 
+    vpp_worker_count = 2
+
     def config_tc(self):
         self.config_params(
             {
@@ -2096,10 +2098,11 @@ class TestInitiatorPsk(TemplateInitiator, Ikev2Params):
         )
 
 
-@tag_fixme_vpp_workers
 class TestInitiatorRequestWindowSize(TestInitiatorPsk):
     """test initiator - request window size (1)"""
 
+    vpp_worker_count = 2
+
     def rekey_respond(self, req, update_child_sa_data):
         ih = self.get_ike_header(req)
         plain = self.sa.hmac_and_decrypt(ih)
@@ -2145,10 +2148,11 @@ class TestInitiatorRequestWindowSize(TestInitiatorPsk):
         self.verify_ipsec_sas(is_rekey=True)
 
 
-@tag_fixme_vpp_workers
 class TestInitiatorRekey(TestInitiatorPsk):
     """test ikev2 initiator - rekey"""
 
+    vpp_worker_count = 2
+
     def rekey_from_initiator(self):
         ispi = int.from_bytes(self.sa.child_sas[0].ispi, "little")
         self.pg0.enable_capture()
@@ -2190,10 +2194,11 @@ class TestInitiatorRekey(TestInitiatorPsk):
         self.verify_ipsec_sas(is_rekey=True)
 
 
-@tag_fixme_vpp_workers
 class TestInitiatorDelSAFromResponder(TemplateInitiator, Ikev2Params):
     """test ikev2 initiator - delete IKE SA from responder"""
 
+    vpp_worker_count = 2
+
     def config_tc(self):
         self.config_params(
             {
@@ -2223,30 +2228,32 @@ class TestInitiatorDelSAFromResponder(TemplateInitiator, Ikev2Params):
         )
 
 
-@tag_fixme_vpp_workers
 class TestResponderInitBehindNATT(TemplateResponder, Ikev2Params):
     """test ikev2 responder - initiator behind NAT"""
 
     IKE_NODE_SUFFIX = "ip4-natt"
+    vpp_worker_count = 2
 
     def config_tc(self):
         self.config_params({"i_natt": True})
 
 
-@tag_fixme_vpp_workers
 class TestResponderPsk(TemplateResponder, Ikev2Params):
     """test ikev2 responder - pre shared key auth"""
 
+    vpp_worker_count = 2
+
     def config_tc(self):
         self.config_params()
 
 
-@tag_fixme_vpp_workers
 class TestResponderDpd(TestResponderPsk):
     """
     Dead peer detection test
     """
 
+    vpp_worker_count = 2
+
     def config_tc(self):
         self.config_params({"dpd_disabled": False})
 
@@ -2274,11 +2281,11 @@ class TestResponderDpd(TestResponderPsk):
         self.assertEqual(len(ipsec_sas), 0)
 
 
-@tag_fixme_vpp_workers
 class TestResponderRekey(TestResponderPsk):
     """test ikev2 responder - rekey"""
 
     WITH_KEX = False
+    vpp_worker_count = 2
 
     def send_rekey_from_initiator(self):
         if self.WITH_KEX:
@@ -2316,10 +2323,11 @@ class TestResponderRekey(TestResponderPsk):
         self.assertEqual(r[0].sa.stats.n_rekey_req, 1)
 
 
-@tag_fixme_vpp_workers
 class TestResponderRekeyRepeat(TestResponderRekey):
     """test ikev2 responder - rekey repeat"""
 
+    vpp_worker_count = 2
+
     def test_responder(self):
         super(TestResponderRekeyRepeat, self).test_responder()
         # rekey request is not accepted until old IPsec SA is expired
@@ -2342,24 +2350,25 @@ class TestResponderRekeyRepeat(TestResponderRekey):
         self.verify_ipsec_sas(sa_count=3)
 
 
-@tag_fixme_vpp_workers
 class TestResponderRekeyKEX(TestResponderRekey):
     """test ikev2 responder - rekey with key exchange"""
 
     WITH_KEX = True
+    vpp_worker_count = 2
 
 
-@tag_fixme_vpp_workers
 class TestResponderRekeyRepeatKEX(TestResponderRekeyRepeat):
     """test ikev2 responder - rekey repeat with key exchange"""
 
     WITH_KEX = True
+    vpp_worker_count = 2
 
 
-@tag_fixme_vpp_workers
 class TestResponderRekeySA(TestResponderPsk):
     """test ikev2 responder - rekey IKE SA"""
 
+    vpp_worker_count = 2
+
     def send_rekey_from_initiator(self, newsa):
         packet = self.create_sa_rekey_request(
             spi=newsa.ispi,
@@ -2409,8 +2418,7 @@ class TestResponderVrf(TestResponderPsk, Ikev2Params):
 
         globals()["ikev2"] = _ikev2
         super(IkePeer, cls).setUpClass()
-        if (is_distro_debian11 == True) and not hasattr(cls, "vpp"):
-            return
+
         cls.create_pg_interfaces(range(1))
         cls.vapi.cli("ip table add 1")
         cls.vapi.cli("set interface ip table pg0 1")
@@ -2425,7 +2433,7 @@ class TestResponderVrf(TestResponderPsk, Ikev2Params):
         self.config_params({"dpd_disabled": False})
 
     def test_responder(self):
-        self.vapi.ikev2_profile_set_liveness(period=2, max_retries=1)
+        self.vapi.ikev2_profile_set_liveness(period=2, max_retries=3)
         super(TestResponderVrf, self).test_responder()
         self.pg0.enable_capture()
         self.pg_start()
@@ -2436,10 +2444,11 @@ class TestResponderVrf(TestResponderPsk, Ikev2Params):
         self.assertEqual(plain, b"")
 
 
-@tag_fixme_vpp_workers
 class TestResponderRsaSign(TemplateResponder, Ikev2Params):
     """test ikev2 responder - cert based auth"""
 
+    vpp_worker_count = 2
+
     def config_tc(self):
         self.config_params(
             {
@@ -2453,7 +2462,6 @@ class TestResponderRsaSign(TemplateResponder, Ikev2Params):
         )
 
 
-@tag_fixme_vpp_workers
 class Test_IKE_AES_CBC_128_SHA256_128_MODP2048_ESP_AES_CBC_192_SHA_384_192(
     TemplateResponder, Ikev2Params
 ):
@@ -2461,6 +2469,8 @@ class Test_IKE_AES_CBC_128_SHA256_128_MODP2048_ESP_AES_CBC_192_SHA_384_192(
     IKE:AES_CBC_128_SHA256_128,DH=modp2048 ESP:AES_CBC_192_SHA_384_192
     """
 
+    vpp_worker_count = 2
+
     def config_tc(self):
         self.config_params(
             {
@@ -2475,7 +2485,6 @@ class Test_IKE_AES_CBC_128_SHA256_128_MODP2048_ESP_AES_CBC_192_SHA_384_192(
         )
 
 
-@tag_fixme_vpp_workers
 class TestAES_CBC_128_SHA256_128_MODP3072_ESP_AES_GCM_16(
     TemplateResponder, Ikev2Params
 ):
@@ -2483,6 +2492,8 @@ class TestAES_CBC_128_SHA256_128_MODP3072_ESP_AES_GCM_16(
     IKE:AES_CBC_128_SHA256_128,DH=modp3072 ESP:AES_GCM_16
     """
 
+    vpp_worker_count = 2
+
     def config_tc(self):
         self.config_params(
             {
@@ -2495,13 +2506,13 @@ class TestAES_CBC_128_SHA256_128_MODP3072_ESP_AES_GCM_16(
         )
 
 
-@tag_fixme_vpp_workers
 class Test_IKE_AES_GCM_16_256(TemplateResponder, Ikev2Params):
     """
     IKE:AES_GCM_16_256
     """
 
     IKE_NODE_SUFFIX = "ip6"
+    vpp_worker_count = 2
 
     def config_tc(self):
         self.config_params(
@@ -2518,12 +2529,13 @@ class Test_IKE_AES_GCM_16_256(TemplateResponder, Ikev2Params):
         )
 
 
-@tag_fixme_vpp_workers
 class TestInitiatorKeepaliveMsg(TestInitiatorPsk):
     """
     Test for keep alive messages
     """
 
+    vpp_worker_count = 2
+
     def send_empty_req_from_responder(self):
         packet = self.create_empty_request()
         self.pg0.add_stream(packet)