lb: Fix src_ip_sticky evaluation bug in per-port-vip case. 62/39162/2
authorNobuhiro MIKI <nmiki@yahoo-corp.jp>
Wed, 28 Jun 2023 06:15:58 +0000 (15:15 +0900)
committerMohammed HAWARI <momohawari@gmail.com>
Thu, 13 Jul 2023 08:10:26 +0000 (08:10 +0000)
Before this fix, the src_ip_sticky flag was passed as an argument to
the lb_node_get_hash function, which computes a hash value for a packet.
However, in per-port-vip case, the value of src_ip_sticky flag may be
different for each port number. As a result, the value is the same for
all port numbers, even though it is a per-port-vip case.

This commit fixes the src_ip_sticky evaluation by delaying it until the
packet is received, so that the correct value is obtained. Also, the
unit test case has been enhanced for this bug fix.

The steps to reproduce this bug are described below:
https://lists.fd.io/g/vpp-dev/message/23248

Type: fix
Fixes: 613e6dc0bf92 ("lb: add source ip based sticky load balancing")
Change-Id: I483492b214a1768e7a21fd86edd5151b3c46528b
Signed-off-by: Nobuhiro MIKI <nmiki@yahoo-corp.jp>
src/plugins/lb/lb.c
src/plugins/lb/lb.h
src/plugins/lb/node.c
test/test_lb.py

index c6f5a0a..c0a443b 100644 (file)
@@ -93,78 +93,6 @@ const static char* const * const lb_dpo_nat6_port_nodes[DPO_PROTO_NUM] =
         [DPO_PROTO_IP6]  = lb_dpo_nat6_ip6_port,
     };
 
-const static char *const lb_dpo_gre4_ip4_sticky[] = { "lb4-gre4-sticky",
-                                                     NULL };
-const static char *const lb_dpo_gre4_ip6_sticky[] = { "lb6-gre4-sticky",
-                                                     NULL };
-const static char *const *const lb_dpo_gre4_sticky_nodes[DPO_PROTO_NUM] = {
-  [DPO_PROTO_IP4] = lb_dpo_gre4_ip4_sticky,
-  [DPO_PROTO_IP6] = lb_dpo_gre4_ip6_sticky,
-};
-
-const static char *const lb_dpo_gre6_ip4_sticky[] = { "lb4-gre6-sticky",
-                                                     NULL };
-const static char *const lb_dpo_gre6_ip6_sticky[] = { "lb6-gre6-sticky",
-                                                     NULL };
-const static char *const *const lb_dpo_gre6_sticky_nodes[DPO_PROTO_NUM] = {
-  [DPO_PROTO_IP4] = lb_dpo_gre6_ip4_sticky,
-  [DPO_PROTO_IP6] = lb_dpo_gre6_ip6_sticky,
-};
-
-const static char *const lb_dpo_gre4_ip4_port_sticky[] = {
-  "lb4-gre4-port-sticky", NULL
-};
-const static char *const lb_dpo_gre4_ip6_port_sticky[] = {
-  "lb6-gre4-port-sticky", NULL
-};
-const static char *const
-  *const lb_dpo_gre4_port_sticky_nodes[DPO_PROTO_NUM] = {
-    [DPO_PROTO_IP4] = lb_dpo_gre4_ip4_port_sticky,
-    [DPO_PROTO_IP6] = lb_dpo_gre4_ip6_port_sticky,
-  };
-
-const static char *const lb_dpo_gre6_ip4_port_sticky[] = {
-  "lb4-gre6-port-sticky", NULL
-};
-const static char *const lb_dpo_gre6_ip6_port_sticky[] = {
-  "lb6-gre6-port-sticky", NULL
-};
-const static char *const
-  *const lb_dpo_gre6_port_sticky_nodes[DPO_PROTO_NUM] = {
-    [DPO_PROTO_IP4] = lb_dpo_gre6_ip4_port_sticky,
-    [DPO_PROTO_IP6] = lb_dpo_gre6_ip6_port_sticky,
-  };
-
-const static char *const lb_dpo_l3dsr_ip4_sticky[] = { "lb4-l3dsr-sticky",
-                                                      NULL };
-const static char *const *const lb_dpo_l3dsr_sticky_nodes[DPO_PROTO_NUM] = {
-  [DPO_PROTO_IP4] = lb_dpo_l3dsr_ip4_sticky,
-};
-
-const static char *const lb_dpo_l3dsr_ip4_port_sticky[] = {
-  "lb4-l3dsr-port-sticky", NULL
-};
-const static char *const
-  *const lb_dpo_l3dsr_port_sticky_nodes[DPO_PROTO_NUM] = {
-    [DPO_PROTO_IP4] = lb_dpo_l3dsr_ip4_port_sticky,
-  };
-
-const static char *const lb_dpo_nat4_ip4_port_sticky[] = {
-  "lb4-nat4-port-sticky", NULL
-};
-const static char *const
-  *const lb_dpo_nat4_port_sticky_nodes[DPO_PROTO_NUM] = {
-    [DPO_PROTO_IP4] = lb_dpo_nat4_ip4_port_sticky,
-  };
-
-const static char *const lb_dpo_nat6_ip6_port_sticky[] = {
-  "lb6-nat6-port-sticky", NULL
-};
-const static char *const
-  *const lb_dpo_nat6_port_sticky_nodes[DPO_PROTO_NUM] = {
-    [DPO_PROTO_IP6] = lb_dpo_nat6_ip6_port_sticky,
-  };
-
 u32 lb_hash_time_now(vlib_main_t * vm)
 {
   return (u32) (vlib_time_now(vm) + 10000);
@@ -1024,22 +952,6 @@ static void lb_vip_add_adjacency(lb_main_t *lbm, lb_vip_t *vip,
     dpo_type = lbm->dpo_nat4_port_type;
   else if (lb_vip_is_nat6_port(vip))
     dpo_type = lbm->dpo_nat6_port_type;
-  else if (lb_vip_is_gre4_sticky (vip))
-    dpo_type = lbm->dpo_gre4_sticky_type;
-  else if (lb_vip_is_gre6_sticky (vip))
-    dpo_type = lbm->dpo_gre6_sticky_type;
-  else if (lb_vip_is_gre4_port_sticky (vip))
-    dpo_type = lbm->dpo_gre4_port_sticky_type;
-  else if (lb_vip_is_gre6_port_sticky (vip))
-    dpo_type = lbm->dpo_gre6_port_sticky_type;
-  else if (lb_vip_is_l3dsr_sticky (vip))
-    dpo_type = lbm->dpo_l3dsr_sticky_type;
-  else if (lb_vip_is_l3dsr_port_sticky (vip))
-    dpo_type = lbm->dpo_l3dsr_port_sticky_type;
-  else if (lb_vip_is_nat4_port_sticky (vip))
-    dpo_type = lbm->dpo_nat4_port_sticky_type;
-  else if (lb_vip_is_nat6_port_sticky (vip))
-    dpo_type = lbm->dpo_nat6_port_sticky_type;
 
   dpo_set(&dpo, dpo_type, proto, *vip_prefix_index);
   fib_table_entry_special_dpo_add(0,
@@ -1406,22 +1318,6 @@ lb_as_stack (lb_as_t *as)
     dpo_type = lbm->dpo_nat4_port_type;
   else if (lb_vip_is_nat6_port(vip))
     dpo_type = lbm->dpo_nat6_port_type;
-  else if (lb_vip_is_gre4_sticky (vip))
-    dpo_type = lbm->dpo_gre4_sticky_type;
-  else if (lb_vip_is_gre6_sticky (vip))
-    dpo_type = lbm->dpo_gre6_sticky_type;
-  else if (lb_vip_is_gre4_port_sticky (vip))
-    dpo_type = lbm->dpo_gre4_port_sticky_type;
-  else if (lb_vip_is_gre6_port_sticky (vip))
-    dpo_type = lbm->dpo_gre6_port_sticky_type;
-  else if (lb_vip_is_l3dsr_sticky (vip))
-    dpo_type = lbm->dpo_l3dsr_sticky_type;
-  else if (lb_vip_is_l3dsr_port_sticky (vip))
-    dpo_type = lbm->dpo_l3dsr_port_sticky_type;
-  else if (lb_vip_is_nat4_port_sticky (vip))
-    dpo_type = lbm->dpo_nat4_port_sticky_type;
-  else if (lb_vip_is_nat6_port_sticky (vip))
-    dpo_type = lbm->dpo_nat6_port_sticky_type;
 
   dpo_stack(dpo_type,
             lb_vip_is_ip4(vip->type)?DPO_PROTO_IP4:DPO_PROTO_IP6,
@@ -1523,22 +1419,6 @@ lb_init (vlib_main_t * vm)
                                                   lb_dpo_nat4_port_nodes);
   lbm->dpo_nat6_port_type = dpo_register_new_type(&lb_vft,
                                                   lb_dpo_nat6_port_nodes);
-  lbm->dpo_gre4_sticky_type =
-    dpo_register_new_type (&lb_vft, lb_dpo_gre4_sticky_nodes);
-  lbm->dpo_gre6_sticky_type =
-    dpo_register_new_type (&lb_vft, lb_dpo_gre6_sticky_nodes);
-  lbm->dpo_gre4_port_sticky_type =
-    dpo_register_new_type (&lb_vft, lb_dpo_gre4_port_sticky_nodes);
-  lbm->dpo_gre6_port_sticky_type =
-    dpo_register_new_type (&lb_vft, lb_dpo_gre6_port_sticky_nodes);
-  lbm->dpo_l3dsr_sticky_type =
-    dpo_register_new_type (&lb_vft, lb_dpo_l3dsr_sticky_nodes);
-  lbm->dpo_l3dsr_port_sticky_type =
-    dpo_register_new_type (&lb_vft, lb_dpo_l3dsr_port_sticky_nodes);
-  lbm->dpo_nat4_port_sticky_type =
-    dpo_register_new_type (&lb_vft, lb_dpo_nat4_port_sticky_nodes);
-  lbm->dpo_nat6_port_sticky_type =
-    dpo_register_new_type (&lb_vft, lb_dpo_nat6_port_sticky_nodes);
   lbm->fib_node_type = fib_node_register_new_type ("lb", &lb_fib_node_vft);
 
   //Init AS reference counters
index fa1cfaa..46da409 100644 (file)
@@ -353,94 +353,41 @@ typedef struct {
 /* clang-format off */
 #define lb_vip_is_gre4(vip) (((vip)->type == LB_VIP_TYPE_IP6_GRE4 \
                             || (vip)->type == LB_VIP_TYPE_IP4_GRE4) \
-                            && ((vip)->port == 0) \
-                            && !lb_vip_is_src_ip_sticky (vip))
+                            && ((vip)->port == 0))
 
 #define lb_vip_is_gre6(vip) (((vip)->type == LB_VIP_TYPE_IP6_GRE6 \
                             || (vip)->type == LB_VIP_TYPE_IP4_GRE6) \
-                            && ((vip)->port == 0) \
-                            && !lb_vip_is_src_ip_sticky (vip))
+                            && ((vip)->port == 0))
 
 #define lb_vip_is_gre4_port(vip) (((vip)->type == LB_VIP_TYPE_IP6_GRE4 \
                                  || (vip)->type == LB_VIP_TYPE_IP4_GRE4) \
-                                 && ((vip)->port != 0) \
-                                 && !lb_vip_is_src_ip_sticky (vip))
+                                 && ((vip)->port != 0))
 
 #define lb_vip_is_gre6_port(vip) (((vip)->type == LB_VIP_TYPE_IP6_GRE6 \
                                  || (vip)->type == LB_VIP_TYPE_IP4_GRE6) \
-                                 && ((vip)->port != 0) \
-                                 && !lb_vip_is_src_ip_sticky (vip))
+                                 && ((vip)->port != 0))
 /* clang-format on */
 
-#define lb_vip_is_gre4_sticky(vip)                                            \
-  (((vip)->type == LB_VIP_TYPE_IP6_GRE4 ||                                    \
-    (vip)->type == LB_VIP_TYPE_IP4_GRE4) &&                                   \
-   ((vip)->port == 0) && lb_vip_is_src_ip_sticky (vip))
-
-#define lb_vip_is_gre6_sticky(vip)                                            \
-  (((vip)->type == LB_VIP_TYPE_IP6_GRE6 ||                                    \
-    (vip)->type == LB_VIP_TYPE_IP4_GRE6) &&                                   \
-   ((vip)->port == 0) && lb_vip_is_src_ip_sticky (vip))
-
-#define lb_vip_is_gre4_port_sticky(vip)                                       \
-  (((vip)->type == LB_VIP_TYPE_IP6_GRE4 ||                                    \
-    (vip)->type == LB_VIP_TYPE_IP4_GRE4) &&                                   \
-   ((vip)->port != 0) && lb_vip_is_src_ip_sticky (vip))
-
-#define lb_vip_is_gre6_port_sticky(vip)                                       \
-  (((vip)->type == LB_VIP_TYPE_IP6_GRE6 ||                                    \
-    (vip)->type == LB_VIP_TYPE_IP4_GRE6) &&                                   \
-   ((vip)->port != 0) && lb_vip_is_src_ip_sticky (vip))
-
 always_inline bool
 lb_vip_is_l3dsr(const lb_vip_t *vip)
 {
-  return (vip->type == LB_VIP_TYPE_IP4_L3DSR && vip->port == 0 &&
-         !lb_vip_is_src_ip_sticky (vip));
+  return (vip->type == LB_VIP_TYPE_IP4_L3DSR && vip->port == 0);
 }
 
 always_inline bool
 lb_vip_is_l3dsr_port(const lb_vip_t *vip)
 {
-  return (vip->type == LB_VIP_TYPE_IP4_L3DSR && vip->port != 0 &&
-         !lb_vip_is_src_ip_sticky (vip));
+  return (vip->type == LB_VIP_TYPE_IP4_L3DSR && vip->port != 0);
 }
 always_inline bool
 lb_vip_is_nat4_port(const lb_vip_t *vip)
 {
-  return (vip->type == LB_VIP_TYPE_IP4_NAT4 && vip->port != 0 &&
-         !lb_vip_is_src_ip_sticky (vip));
+  return (vip->type == LB_VIP_TYPE_IP4_NAT4 && vip->port != 0);
 }
 always_inline bool
 lb_vip_is_nat6_port(const lb_vip_t *vip)
 {
-  return (vip->type == LB_VIP_TYPE_IP6_NAT6 && vip->port != 0 &&
-         !lb_vip_is_src_ip_sticky (vip));
-}
-
-always_inline bool
-lb_vip_is_l3dsr_sticky (const lb_vip_t *vip)
-{
-  return (vip->type == LB_VIP_TYPE_IP4_L3DSR && vip->port == 0 &&
-         lb_vip_is_src_ip_sticky (vip));
-}
-always_inline bool
-lb_vip_is_l3dsr_port_sticky (const lb_vip_t *vip)
-{
-  return (vip->type == LB_VIP_TYPE_IP4_L3DSR && vip->port != 0 &&
-         lb_vip_is_src_ip_sticky (vip));
-}
-always_inline bool
-lb_vip_is_nat4_port_sticky (const lb_vip_t *vip)
-{
-  return (vip->type == LB_VIP_TYPE_IP4_NAT4 && vip->port != 0 &&
-         lb_vip_is_src_ip_sticky (vip));
-}
-always_inline bool
-lb_vip_is_nat6_port_sticky (const lb_vip_t *vip)
-{
-  return (vip->type == LB_VIP_TYPE_IP6_NAT6 && vip->port != 0 &&
-         lb_vip_is_src_ip_sticky (vip));
+  return (vip->type == LB_VIP_TYPE_IP6_NAT6 && vip->port != 0);
 }
 
 format_function_t format_lb_vip;
@@ -600,14 +547,6 @@ typedef struct {
   dpo_type_t dpo_l3dsr_port_type;
   dpo_type_t dpo_nat4_port_type;
   dpo_type_t dpo_nat6_port_type;
-  dpo_type_t dpo_gre4_sticky_type;
-  dpo_type_t dpo_gre6_sticky_type;
-  dpo_type_t dpo_gre4_port_sticky_type;
-  dpo_type_t dpo_gre6_port_sticky_type;
-  dpo_type_t dpo_l3dsr_sticky_type;
-  dpo_type_t dpo_l3dsr_port_sticky_type;
-  dpo_type_t dpo_nat4_port_sticky_type;
-  dpo_type_t dpo_nat6_port_sticky_type;
   /**
    * Node type for registering to fib changes.
    */
index f823ea9..7f196c9 100644 (file)
@@ -175,25 +175,21 @@ lb_node_get_other_ports6 (ip6_header_t *ip60)
 
 static_always_inline void
 lb_node_get_hash (lb_main_t *lbm, vlib_buffer_t *p, u8 is_input_v4, u32 *hash,
-                 u32 *vip_idx, u8 per_port_vip, u8 src_ip_sticky)
+                 u32 *vip_idx, u8 per_port_vip)
 {
   vip_port_key_t key;
   clib_bihash_kv_8_8_t kv, value;
+  ip4_header_t *ip40;
+  ip6_header_t *ip60;
+  lb_vip_t *vip0;
+  u64 ports;
 
   /* For vip case, retrieve vip index for ip lookup */
   *vip_idx = vnet_buffer (p)->ip.adj_index[VLIB_TX];
 
-  if (per_port_vip)
-    {
-      /* For per-port-vip case, ip lookup stores placeholder index */
-      key.vip_prefix_index = *vip_idx;
-    }
-
+  /* Extract the L4 port number from the packet */
   if (is_input_v4)
     {
-      ip4_header_t *ip40;
-      u64 ports;
-
       ip40 = vlib_buffer_get_current (p);
       if (PREDICT_TRUE(
           ip40->protocol == IP_PROTOCOL_TCP
@@ -202,28 +198,10 @@ lb_node_get_hash (lb_main_t *lbm, vlib_buffer_t *p, u8 is_input_v4, u32 *hash,
             | ((u64) ((udp_header_t *) (ip40 + 1))->dst_port);
       else
         ports = lb_node_get_other_ports4 (ip40);
-
-      if (src_ip_sticky)
-       {
-         *hash = lb_hash_hash (*((u64 *) &ip40->address_pair), 0, 0, 0, 0);
-       }
-      else
-       {
-         *hash =
-           lb_hash_hash (*((u64 *) &ip40->address_pair), ports, 0, 0, 0);
-       }
-
-      if (per_port_vip)
-        {
-          key.protocol = ip40->protocol;
-          key.port = (u16)(ports & 0xFFFF);
-        }
     }
   else
     {
-      ip6_header_t *ip60;
       ip60 = vlib_buffer_get_current (p);
-      u64 ports;
 
       if (PREDICT_TRUE(
           ip60->protocol == IP_PROTOCOL_TCP
@@ -232,8 +210,52 @@ lb_node_get_hash (lb_main_t *lbm, vlib_buffer_t *p, u8 is_input_v4, u32 *hash,
             | ((u64) ((udp_header_t *) (ip60 + 1))->dst_port);
       else
         ports = lb_node_get_other_ports6 (ip60);
+    }
+
+  if (per_port_vip)
+    {
+      /* For per-port-vip case, ip lookup stores placeholder index */
+      key.vip_prefix_index = *vip_idx;
+      key.port = (u16) (ports & 0xFFFF);
+      if (is_input_v4)
+       {
+         key.protocol = ip40->protocol;
+       }
+      else
+       {
+         key.protocol = ip60->protocol;
+       }
+
+      /* For per-port-vip case, retrieve vip index for vip_port_filter table */
+      kv.key = key.as_u64;
+      if (clib_bihash_search_8_8 (&lbm->vip_index_per_port, &kv, &value) < 0)
+       {
+         /* Set default vip */
+         *vip_idx = 0;
+       }
+      else
+       {
+         *vip_idx = value.value;
+       }
+    }
+
+  vip0 = pool_elt_at_index (lbm->vips, *vip_idx);
 
-      if (src_ip_sticky)
+  if (is_input_v4)
+    {
+      if (lb_vip_is_src_ip_sticky (vip0))
+       {
+         *hash = lb_hash_hash (*((u64 *) &ip40->address_pair), 0, 0, 0, 0);
+       }
+      else
+       {
+         *hash =
+           lb_hash_hash (*((u64 *) &ip40->address_pair), ports, 0, 0, 0);
+       }
+    }
+  else
+    {
+      if (lb_vip_is_src_ip_sticky (vip0))
        {
          *hash = lb_hash_hash (
            ip60->src_address.as_u64[0], ip60->src_address.as_u64[1],
@@ -245,25 +267,6 @@ lb_node_get_hash (lb_main_t *lbm, vlib_buffer_t *p, u8 is_input_v4, u32 *hash,
            ip60->src_address.as_u64[0], ip60->src_address.as_u64[1],
            ip60->dst_address.as_u64[0], ip60->dst_address.as_u64[1], ports);
        }
-
-      if (per_port_vip)
-        {
-          key.protocol = ip60->protocol;
-          key.port = (u16)(ports & 0xFFFF);
-        }
-    }
-
-  /* For per-port-vip case, retrieve vip index for vip_port_filter table */
-  if (per_port_vip)
-    {
-      kv.key = key.as_u64;
-      if (clib_bihash_search_8_8(&lbm->vip_index_per_port, &kv, &value) < 0)
-        {
-          /* return default vip */
-          *vip_idx = 0;
-          return;
-        }
-      *vip_idx = value.value;
     }
 }
 
@@ -274,8 +277,7 @@ lb_node_fn (vlib_main_t * vm,
             vlib_frame_t * frame,
             u8 is_input_v4, //Compile-time parameter stating that is input is v4 (or v6)
             lb_encap_type_t encap_type, //Compile-time parameter is GRE4/GRE6/L3DSR/NAT4/NAT6
-            u8 per_port_vip, //Compile-time parameter stating that is per_port_vip or not
-            u8 src_ip_sticky) //Compile-time parameter stating that is source ip based sticky or not
+            u8 per_port_vip) //Compile-time parameter stating that is per_port_vip or not
 {
   lb_main_t *lbm = &lb_main;
   u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
@@ -293,7 +295,7 @@ lb_node_fn (vlib_main_t * vm,
     {
       vlib_buffer_t *p0 = vlib_get_buffer (vm, from[0]);
       lb_node_get_hash (lbm, p0, is_input_v4, &nexthash0,
-                        &next_vip_idx0, per_port_vip, src_ip_sticky);
+                        &next_vip_idx0, per_port_vip);
     }
 
   while (n_left_from > 0)
@@ -318,7 +320,7 @@ lb_node_fn (vlib_main_t * vm,
               //Compute next hash and prefetch bucket
               lb_node_get_hash (lbm, p1, is_input_v4,
                                 &nexthash0, &next_vip_idx0,
-                                per_port_vip, src_ip_sticky);
+                                per_port_vip);
               lb_hash_prefetch_bucket (sticky_ht, nexthash0);
               //Prefetch for encap, next
               CLIB_PREFETCH(vlib_buffer_get_current (p1) - 64, 64, STORE);
@@ -966,168 +968,84 @@ static uword
 lb6_gre6_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
                   vlib_frame_t * frame)
 {
-  return lb_node_fn (vm, node, frame, 0, LB_ENCAP_TYPE_GRE6, 0, 0);
+  return lb_node_fn (vm, node, frame, 0, LB_ENCAP_TYPE_GRE6, 0);
 }
 
 static uword
 lb6_gre4_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
                   vlib_frame_t * frame)
 {
-  return lb_node_fn (vm, node, frame, 0, LB_ENCAP_TYPE_GRE4, 0, 0);
+  return lb_node_fn (vm, node, frame, 0, LB_ENCAP_TYPE_GRE4, 0);
 }
 
 static uword
 lb4_gre6_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
                   vlib_frame_t * frame)
 {
-  return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_GRE6, 0, 0);
+  return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_GRE6, 0);
 }
 
 static uword
 lb4_gre4_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
                   vlib_frame_t * frame)
 {
-  return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_GRE4, 0, 0);
+  return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_GRE4, 0);
 }
 
 static uword
 lb6_gre6_port_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
                        vlib_frame_t * frame)
 {
-  return lb_node_fn (vm, node, frame, 0, LB_ENCAP_TYPE_GRE6, 1, 0);
+  return lb_node_fn (vm, node, frame, 0, LB_ENCAP_TYPE_GRE6, 1);
 }
 
 static uword
 lb6_gre4_port_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
                        vlib_frame_t * frame)
 {
-  return lb_node_fn (vm, node, frame, 0, LB_ENCAP_TYPE_GRE4, 1, 0);
+  return lb_node_fn (vm, node, frame, 0, LB_ENCAP_TYPE_GRE4, 1);
 }
 
 static uword
 lb4_gre6_port_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
                        vlib_frame_t * frame)
 {
-  return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_GRE6, 1, 0);
+  return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_GRE6, 1);
 }
 
 static uword
 lb4_gre4_port_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
                        vlib_frame_t * frame)
 {
-  return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_GRE4, 1, 0);
+  return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_GRE4, 1);
 }
 
 static uword
 lb4_l3dsr_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
                         vlib_frame_t * frame)
 {
-  return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_L3DSR, 0, 0);
+  return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_L3DSR, 0);
 }
 
 static uword
 lb4_l3dsr_port_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
                         vlib_frame_t * frame)
 {
-  return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_L3DSR, 1, 0);
+  return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_L3DSR, 1);
 }
 
 static uword
 lb6_nat6_port_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
                        vlib_frame_t * frame)
 {
-  return lb_node_fn (vm, node, frame, 0, LB_ENCAP_TYPE_NAT6, 1, 0);
+  return lb_node_fn (vm, node, frame, 0, LB_ENCAP_TYPE_NAT6, 1);
 }
 
 static uword
 lb4_nat4_port_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
                        vlib_frame_t * frame)
 {
-  return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_NAT4, 1, 0);
-}
-
-static uword
-lb6_gre6_sticky_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node,
-                        vlib_frame_t *frame)
-{
-  return lb_node_fn (vm, node, frame, 0, LB_ENCAP_TYPE_GRE6, 0, 1);
-}
-
-static uword
-lb6_gre4_sticky_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node,
-                        vlib_frame_t *frame)
-{
-  return lb_node_fn (vm, node, frame, 0, LB_ENCAP_TYPE_GRE4, 0, 1);
-}
-
-static uword
-lb4_gre6_sticky_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node,
-                        vlib_frame_t *frame)
-{
-  return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_GRE6, 0, 1);
-}
-
-static uword
-lb4_gre4_sticky_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node,
-                        vlib_frame_t *frame)
-{
-  return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_GRE4, 0, 1);
-}
-
-static uword
-lb6_gre6_port_sticky_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node,
-                             vlib_frame_t *frame)
-{
-  return lb_node_fn (vm, node, frame, 0, LB_ENCAP_TYPE_GRE6, 1, 1);
-}
-
-static uword
-lb6_gre4_port_sticky_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node,
-                             vlib_frame_t *frame)
-{
-  return lb_node_fn (vm, node, frame, 0, LB_ENCAP_TYPE_GRE4, 1, 1);
-}
-
-static uword
-lb4_gre6_port_sticky_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node,
-                             vlib_frame_t *frame)
-{
-  return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_GRE6, 1, 1);
-}
-
-static uword
-lb4_gre4_port_sticky_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node,
-                             vlib_frame_t *frame)
-{
-  return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_GRE4, 1, 1);
-}
-
-static uword
-lb4_l3dsr_sticky_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node,
-                         vlib_frame_t *frame)
-{
-  return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_L3DSR, 0, 1);
-}
-
-static uword
-lb4_l3dsr_port_sticky_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node,
-                              vlib_frame_t *frame)
-{
-  return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_L3DSR, 1, 1);
-}
-
-static uword
-lb6_nat6_port_sticky_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node,
-                             vlib_frame_t *frame)
-{
-  return lb_node_fn (vm, node, frame, 0, LB_ENCAP_TYPE_NAT6, 1, 1);
-}
-
-static uword
-lb4_nat4_port_sticky_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node,
-                             vlib_frame_t *frame)
-{
-  return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_NAT4, 1, 1);
+  return lb_node_fn (vm, node, frame, 1, LB_ENCAP_TYPE_NAT4, 1);
 }
 
 static uword
@@ -1300,138 +1218,6 @@ VLIB_REGISTER_NODE (lb4_nat4_port_node) =
         { [LB_NEXT_DROP] = "error-drop" },
   };
 
-VLIB_REGISTER_NODE (lb6_gre6_sticky_node) = {
-  .function = lb6_gre6_sticky_node_fn,
-  .name = "lb6-gre6-sticky",
-  .vector_size = sizeof (u32),
-  .format_trace = format_lb_trace,
-  .n_errors = LB_N_ERROR,
-  .error_strings = lb_error_strings,
-  .n_next_nodes = LB_N_NEXT,
-  .next_nodes = { [LB_NEXT_DROP] = "error-drop" },
-};
-
-VLIB_REGISTER_NODE (lb6_gre4_sticky_node) = {
-  .function = lb6_gre4_sticky_node_fn,
-  .name = "lb6-gre4-sticky",
-  .vector_size = sizeof (u32),
-  .format_trace = format_lb_trace,
-  .n_errors = LB_N_ERROR,
-  .error_strings = lb_error_strings,
-  .n_next_nodes = LB_N_NEXT,
-  .next_nodes = { [LB_NEXT_DROP] = "error-drop" },
-};
-
-VLIB_REGISTER_NODE (lb4_gre6_sticky_node) = {
-  .function = lb4_gre6_sticky_node_fn,
-  .name = "lb4-gre6-sticky",
-  .vector_size = sizeof (u32),
-  .format_trace = format_lb_trace,
-  .n_errors = LB_N_ERROR,
-  .error_strings = lb_error_strings,
-  .n_next_nodes = LB_N_NEXT,
-  .next_nodes = { [LB_NEXT_DROP] = "error-drop" },
-};
-
-VLIB_REGISTER_NODE (lb4_gre4_sticky_node) = {
-  .function = lb4_gre4_sticky_node_fn,
-  .name = "lb4-gre4-sticky",
-  .vector_size = sizeof (u32),
-  .format_trace = format_lb_trace,
-  .n_errors = LB_N_ERROR,
-  .error_strings = lb_error_strings,
-  .n_next_nodes = LB_N_NEXT,
-  .next_nodes = { [LB_NEXT_DROP] = "error-drop" },
-};
-
-VLIB_REGISTER_NODE (lb6_gre6_port_sticky_node) = {
-  .function = lb6_gre6_port_sticky_node_fn,
-  .name = "lb6-gre6-port-sticky",
-  .vector_size = sizeof (u32),
-  .format_trace = format_lb_trace,
-  .n_errors = LB_N_ERROR,
-  .error_strings = lb_error_strings,
-  .n_next_nodes = LB_N_NEXT,
-  .next_nodes = { [LB_NEXT_DROP] = "error-drop" },
-};
-
-VLIB_REGISTER_NODE (lb6_gre4_port_sticky_node) = {
-  .function = lb6_gre4_port_sticky_node_fn,
-  .name = "lb6-gre4-port-sticky",
-  .vector_size = sizeof (u32),
-  .format_trace = format_lb_trace,
-  .n_errors = LB_N_ERROR,
-  .error_strings = lb_error_strings,
-  .n_next_nodes = LB_N_NEXT,
-  .next_nodes = { [LB_NEXT_DROP] = "error-drop" },
-};
-
-VLIB_REGISTER_NODE (lb4_gre6_port_sticky_node) = {
-  .function = lb4_gre6_port_sticky_node_fn,
-  .name = "lb4-gre6-port-sticky",
-  .vector_size = sizeof (u32),
-  .format_trace = format_lb_trace,
-  .n_errors = LB_N_ERROR,
-  .error_strings = lb_error_strings,
-  .n_next_nodes = LB_N_NEXT,
-  .next_nodes = { [LB_NEXT_DROP] = "error-drop" },
-};
-
-VLIB_REGISTER_NODE (lb4_gre4_port_sticky_node) = {
-  .function = lb4_gre4_port_sticky_node_fn,
-  .name = "lb4-gre4-port-sticky",
-  .vector_size = sizeof (u32),
-  .format_trace = format_lb_trace,
-  .n_errors = LB_N_ERROR,
-  .error_strings = lb_error_strings,
-  .n_next_nodes = LB_N_NEXT,
-  .next_nodes = { [LB_NEXT_DROP] = "error-drop" },
-};
-
-VLIB_REGISTER_NODE (lb4_l3dsr_port_sticky_node) = {
-  .function = lb4_l3dsr_port_sticky_node_fn,
-  .name = "lb4-l3dsr-port-sticky",
-  .vector_size = sizeof (u32),
-  .format_trace = format_lb_trace,
-  .n_errors = LB_N_ERROR,
-  .error_strings = lb_error_strings,
-  .n_next_nodes = LB_N_NEXT,
-  .next_nodes = { [LB_NEXT_DROP] = "error-drop" },
-};
-
-VLIB_REGISTER_NODE (lb4_l3dsr_sticky_node) = {
-  .function = lb4_l3dsr_sticky_node_fn,
-  .name = "lb4-l3dsr-sticky",
-  .vector_size = sizeof (u32),
-  .format_trace = format_lb_trace,
-  .n_errors = LB_N_ERROR,
-  .error_strings = lb_error_strings,
-  .n_next_nodes = LB_N_NEXT,
-  .next_nodes = { [LB_NEXT_DROP] = "error-drop" },
-};
-
-VLIB_REGISTER_NODE (lb6_nat6_port_sticky_node) = {
-  .function = lb6_nat6_port_sticky_node_fn,
-  .name = "lb6-nat6-port-sticky",
-  .vector_size = sizeof (u32),
-  .format_trace = format_lb_trace,
-  .n_errors = LB_N_ERROR,
-  .error_strings = lb_error_strings,
-  .n_next_nodes = LB_N_NEXT,
-  .next_nodes = { [LB_NEXT_DROP] = "error-drop" },
-};
-
-VLIB_REGISTER_NODE (lb4_nat4_port_sticky_node) = {
-  .function = lb4_nat4_port_sticky_node_fn,
-  .name = "lb4-nat4-port-sticky",
-  .vector_size = sizeof (u32),
-  .format_trace = format_lb_trace,
-  .n_errors = LB_N_ERROR,
-  .error_strings = lb_error_strings,
-  .n_next_nodes = LB_N_NEXT,
-  .next_nodes = { [LB_NEXT_DROP] = "error-drop" },
-};
-
 static uword
 lb4_nodeport_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
                       vlib_frame_t * frame)
index 6e8d82d..e44684e 100644 (file)
@@ -403,6 +403,29 @@ class TestLB(VppTestCase):
             self.vapi.cli("lb vip 90.0.0.0/8 encap l3dsr dscp 7 del")
             self.vapi.cli("test lb flowtable flush")
 
+    def test_lb_ip4_l3dsr_src_ip_sticky(self):
+        """Load Balancer IP4 L3DSR on vip with src_ip_sticky case"""
+        try:
+            self.vapi.cli("lb vip 90.0.0.0/8 encap l3dsr dscp 7 src_ip_sticky")
+            for asid in self.ass:
+                self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u" % (asid))
+
+            # Generate duplicated packets
+            pkts = self.generatePackets(self.pg0, isv4=True)
+            pkts = pkts[: len(pkts) // 2]
+            pkts = pkts + pkts
+
+            self.pg0.add_stream(pkts)
+            self.pg_enable_capture(self.pg_interfaces)
+            self.pg_start()
+            self.checkCapture(encap="l3dsr", isv4=True, src_ip_sticky=True)
+
+        finally:
+            for asid in self.ass:
+                self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u del" % (asid))
+            self.vapi.cli("lb vip 90.0.0.0/8 encap l3dsr dscp 7 src_ip_sticky del")
+            self.vapi.cli("test lb flowtable flush")
+
     def test_lb_ip4_l3dsr_port(self):
         """Load Balancer IP4 L3DSR on per-port-vip case"""
         try:
@@ -432,6 +455,12 @@ class TestLB(VppTestCase):
     def test_lb_ip4_l3dsr_port_src_ip_sticky(self):
         """Load Balancer IP4 L3DSR on per-port-vip with src_ip_sticky case"""
         try:
+            # This VIP at port 1000 does not receive packets, but is defined
+            # as a dummy to verify that the src_ip_sticky flag can be set
+            # independently for each port.
+            self.vapi.cli(
+                "lb vip 90.0.0.0/8 protocol udp port 10000 encap l3dsr dscp 7"
+            )
             self.vapi.cli(
                 "lb vip 90.0.0.0/8 protocol udp port 20000 encap l3dsr dscp 7 src_ip_sticky"
             )
@@ -458,6 +487,9 @@ class TestLB(VppTestCase):
             self.vapi.cli(
                 "lb vip 90.0.0.0/8 protocol udp port 20000 encap l3dsr dscp 7 src_ip_sticky del"
             )
+            self.vapi.cli(
+                "lb vip 90.0.0.0/8 protocol udp port 10000 encap l3dsr dscp 7 del"
+            )
             self.vapi.cli("test lb flowtable flush")
 
     def test_lb_ip4_nat4_port(self):