cnat: add flow hash config to cnat translation
[vpp.git] / src / plugins / cnat / cnat_translation.c
index 8b7cf24..1cdb94f 100644 (file)
 #include <vnet/dpo/drop_dpo.h>
 
 #include <cnat/cnat_translation.h>
+#include <cnat/cnat_maglev.h>
 #include <cnat/cnat_session.h>
 #include <cnat/cnat_client.h>
 
 cnat_translation_t *cnat_translation_pool;
 clib_bihash_8_8_t cnat_translation_db;
 addr_resolution_t *tr_resolutions;
-
-typedef void (*cnat_if_addr_add_cb_t) (addr_resolution_t * ar,
-                                      ip_address_t * address, u8 is_del);
 cnat_if_addr_add_cb_t *cnat_if_addr_add_cbs;
 
 static fib_node_type_t cnat_translation_fib_node_type;
@@ -203,108 +201,7 @@ cnat_remove_translation_from_db (index_t cci, cnat_endpoint_t * vip,
   clib_bihash_add_del_8_8 (&cnat_translation_db, &bkey, 0);
 }
 
-typedef struct
-{
-  cnat_ep_trk_t *trk;
-  u32 index;
-  u32 offset;
-  u32 skip;
-} cnat_maglev_entry_t;
-
-static int
-cnat_maglev_entry_compare (void *_a, void *_b)
-{
-  cnat_ep_trk_t *a = ((cnat_maglev_entry_t *) _a)->trk;
-  cnat_ep_trk_t *b = ((cnat_maglev_entry_t *) _b)->trk;
-  int rv = 0;
-  if ((rv =
-        ip_address_cmp (&a->ct_ep[VLIB_TX].ce_ip, &b->ct_ep[VLIB_TX].ce_ip)))
-    return rv;
-  if ((rv = a->ct_ep[VLIB_TX].ce_port - a->ct_ep[VLIB_TX].ce_port))
-    return rv;
-  if ((rv =
-        ip_address_cmp (&a->ct_ep[VLIB_RX].ce_ip, &b->ct_ep[VLIB_RX].ce_ip)))
-    return rv;
-  if ((rv = a->ct_ep[VLIB_RX].ce_port - a->ct_ep[VLIB_RX].ce_port))
-    return rv;
-  return 0;
-}
-
-static void
-cnat_translation_init_maglev (cnat_translation_t *ct)
-{
-  cnat_maglev_entry_t *backends = NULL, *bk;
-  cnat_main_t *cm = &cnat_main;
-  u32 done = 0;
-  cnat_ep_trk_t *trk;
-  int ep_idx = 0;
-
-  vec_foreach (trk, ct->ct_active_paths)
-    {
-      cnat_maglev_entry_t bk;
-      u32 h1, h2;
-
-      if (AF_IP4 == ip_addr_version (&trk->ct_ep[VLIB_TX].ce_ip))
-       {
-         u32 a, b, c;
-         a = ip_addr_v4 (&trk->ct_ep[VLIB_TX].ce_ip).data_u32;
-         b = trk->ct_ep[VLIB_TX].ce_port << 16 | trk->ct_ep[VLIB_RX].ce_port;
-         c = ip_addr_v4 (&trk->ct_ep[VLIB_RX].ce_ip).data_u32;
-         hash_v3_mix32 (a, b, c);
-         hash_v3_finalize32 (a, b, c);
-         h1 = c;
-         h2 = b;
-       }
-      else
-       {
-         u64 a, b, c;
-         a = ip_addr_v6 (&trk->ct_ep[VLIB_TX].ce_ip).as_u64[0] ^
-             ip_addr_v6 (&trk->ct_ep[VLIB_TX].ce_ip).as_u64[1];
-         b = trk->ct_ep[VLIB_TX].ce_port << 16 | trk->ct_ep[VLIB_RX].ce_port;
-         c = ip_addr_v6 (&trk->ct_ep[VLIB_RX].ce_ip).as_u64[0] ^
-             ip_addr_v6 (&trk->ct_ep[VLIB_RX].ce_ip).as_u64[1];
-         hash_mix64 (a, b, c);
-         h1 = c;
-         h2 = b;
-       }
-
-      bk.offset = h1 % cm->maglev_len;
-      bk.skip = h2 % (cm->maglev_len - 1) + 1;
-      bk.index = ep_idx++;
-      bk.trk = trk;
-      vec_add1 (backends, bk);
-    }
-
-  if (0 == ep_idx)
-    return;
-
-  vec_sort_with_function (backends, cnat_maglev_entry_compare);
-
-  /* Don't free if previous vector exists, just zero */
-  vec_validate (ct->lb_maglev, cm->maglev_len);
-  vec_set (ct->lb_maglev, -1);
-
-  while (1)
-    {
-      vec_foreach (bk, backends)
-       {
-         u32 next = 0;
-         u32 c = (bk->offset + next * bk->skip) % cm->maglev_len;
-         while (ct->lb_maglev[c] != (u32) -1)
-           {
-             next++;
-             c = (bk->offset + next * bk->skip) % cm->maglev_len;
-           }
-         ct->lb_maglev[c] = bk->index;
-         done++;
-         if (done == cm->maglev_len)
-           goto finished;
-       }
-    }
 
-finished:
-  vec_free (backends);
-}
 
 static void
 cnat_translation_stack (cnat_translation_t * ct)
@@ -324,8 +221,11 @@ cnat_translation_stack (cnat_translation_t * ct)
     if (trk->ct_flags & CNAT_TRK_ACTIVE)
       vec_add1 (ct->ct_active_paths, *trk);
 
+  flow_hash_config_t fhc = IP_FLOW_HASH_DEFAULT;
+  if (ct->fhc != 0)
+    fhc = ct->fhc;
   lbi = load_balance_create (vec_len (ct->ct_active_paths),
-                            fib_proto_to_dpo (fproto), IP_FLOW_HASH_DEFAULT);
+                            fib_proto_to_dpo (fproto), fhc);
 
   ep_idx = 0;
   vec_foreach (trk, ct->ct_active_paths)
@@ -336,7 +236,7 @@ cnat_translation_stack (cnat_translation_t * ct)
 
   dpo_set (&ct->ct_lb, DPO_LOAD_BALANCE, dproto, lbi);
   dpo_stack (cnat_client_dpo, dproto, &ct->ct_lb, &ct->ct_lb);
-  ct->flags |= CNAT_TRANSLATION_STACKED;
+  ct->flags |= CNAT_TR_FLAG_STACKED;
 }
 
 int
@@ -366,7 +266,7 @@ cnat_translation_delete (u32 id)
 u32
 cnat_translation_update (cnat_endpoint_t *vip, ip_protocol_t proto,
                         cnat_endpoint_tuple_t *paths, u8 flags,
-                        cnat_lb_type_t lb_type)
+                        cnat_lb_type_t lb_type, flow_hash_config_t fhc)
 {
   cnat_endpoint_tuple_t *path;
   const cnat_client_t *cc;
@@ -399,6 +299,7 @@ cnat_translation_update (cnat_endpoint_t *vip, ip_protocol_t proto,
       ct->ct_cci = cci;
       ct->index = ct - cnat_translation_pool;
       ct->lb_type = lb_type;
+      ct->fhc = fhc;
 
       cnat_add_translation_to_db (cci, vip, proto, ct->index);
       cnat_client_translation_added (cci);
@@ -418,7 +319,7 @@ cnat_translation_update (cnat_endpoint_t *vip, ip_protocol_t proto,
   }
 
   vec_reset_length (ct->ct_paths);
-  ct->flags &= ~CNAT_TRANSLATION_STACKED;
+  ct->flags &= ~CNAT_TR_FLAG_STACKED;
 
   u64 path_idx = 0;
   vec_foreach (path, paths)
@@ -487,6 +388,11 @@ format_cnat_translation (u8 * s, va_list * args)
              format_ip_protocol, ct->ct_proto);
   s = format (s, "lb:%U ", format_cnat_lb_type, ct->lb_type);
 
+  if ((ct->fhc == 0) || (ct->fhc == IP_FLOW_HASH_DEFAULT))
+    s = format (s, "fhc:0x%x(default)", IP_FLOW_HASH_DEFAULT);
+  else
+    s = format (s, "fhc:0x%x", ct->fhc);
+
   vec_foreach (ck, ct->ct_paths)
     s = format (s, "\n%U", format_cnat_ep_trk, ck, 2);
 
@@ -616,7 +522,7 @@ cnat_translation_back_walk_notify (fib_node_t * node,
   /* If we have more than FIB_PATH_LIST_POPULAR paths
    * we might get called during path tracking
    * (cnat_tracker_track) */
-  if (!(ct->flags & CNAT_TRANSLATION_STACKED))
+  if (!(ct->flags & CNAT_TR_FLAG_STACKED))
     return (FIB_NODE_BACK_WALK_CONTINUE);
 
   cnat_translation_stack (ct);
@@ -679,8 +585,9 @@ cnat_translation_cli_add_del (vlib_main_t * vm,
        }
     }
 
+  flow_hash_config_t fhc = 0;
   if (INDEX_INVALID == del_index)
-    cnat_translation_update (&vip, proto, paths, flags, lb_type);
+    cnat_translation_update (&vip, proto, paths, flags, lb_type, fhc);
   else
     cnat_translation_delete (del_index);
 
@@ -765,37 +672,11 @@ cnat_if_addr_add_del_backend_cb (addr_resolution_t * ar,
       ep->ce_flags |= CNAT_EP_FLAG_RESOLVED;
     }
 
-  ct->flags &= ~CNAT_TRANSLATION_STACKED;
+  ct->flags &= ~CNAT_TR_FLAG_STACKED;
   cnat_tracker_track (ar->cti, trk);
 
   cnat_translation_stack (ct);
-  ct->flags |= CNAT_TRANSLATION_STACKED;
-}
-
-static void
-cnat_if_addr_add_del_snat_cb (addr_resolution_t * ar, ip_address_t * address,
-                             u8 is_del)
-{
-  cnat_endpoint_t *ep;
-  ep = AF_IP4 == ar->af ? &cnat_main.snat_ip4 : &cnat_main.snat_ip6;
-
-  if (!is_del && ep->ce_flags & CNAT_EP_FLAG_RESOLVED)
-    return;
-
-  if (is_del)
-    {
-      ep->ce_flags &= ~CNAT_EP_FLAG_RESOLVED;
-      /* Are there remaining addresses ? */
-      if (0 == cnat_resolve_addr (ar->sw_if_index, ar->af, address))
-       is_del = 0;
-    }
-
-  if (!is_del)
-    {
-      ip_address_copy (&ep->ce_ip, address);
-      ep->ce_flags |= CNAT_EP_FLAG_RESOLVED;
-    }
-
+  ct->flags |= CNAT_TR_FLAG_STACKED;
 }
 
 static void
@@ -837,6 +718,14 @@ cnat_ip4_if_addr_add_del_callback (struct ip4_main_t *im,
   cnat_if_addr_add_del_callback (sw_if_index, &addr, is_del);
 }
 
+void
+cnat_translation_register_addr_add_cb (cnat_addr_resol_type_t typ,
+                                      cnat_if_addr_add_cb_t fn)
+{
+  vec_validate (cnat_if_addr_add_cbs, CNAT_ADDR_N_RESOLUTIONS);
+  cnat_if_addr_add_cbs[typ] = fn;
+}
+
 static clib_error_t *
 cnat_translation_init (vlib_main_t * vm)
 {
@@ -844,7 +733,7 @@ cnat_translation_init (vlib_main_t * vm)
   ip6_main_t *i6m = &ip6_main;
   cnat_main_t *cm = &cnat_main;
   cnat_translation_fib_node_type =
-    fib_node_register_new_type (&cnat_translation_vft);
+    fib_node_register_new_type ("cnat-translation", &cnat_translation_vft);
 
   clib_bihash_init_8_8 (&cnat_translation_db, "CNat translation DB",
                        cm->translation_hash_buckets,
@@ -858,12 +747,11 @@ cnat_translation_init (vlib_main_t * vm)
   cb6.function = cnat_ip6_if_addr_add_del_callback;
   vec_add1 (i6m->add_del_interface_address_callbacks, cb6);
 
-  vec_validate (cnat_if_addr_add_cbs, CNAT_ADDR_N_RESOLUTIONS);
-  cnat_if_addr_add_cbs[CNAT_RESOLV_ADDR_BACKEND] =
-    cnat_if_addr_add_del_backend_cb;
-  cnat_if_addr_add_cbs[CNAT_RESOLV_ADDR_SNAT] = cnat_if_addr_add_del_snat_cb;
-  cnat_if_addr_add_cbs[CNAT_RESOLV_ADDR_TRANSLATION] =
-    cnat_if_addr_add_del_translation_cb;
+  cnat_translation_register_addr_add_cb (CNAT_RESOLV_ADDR_BACKEND,
+                                        cnat_if_addr_add_del_backend_cb);
+  cnat_translation_register_addr_add_cb (CNAT_RESOLV_ADDR_TRANSLATION,
+                                        cnat_if_addr_add_del_translation_cb);
+
   return (NULL);
 }