cnat: Introduce parametric source policy 92/28792/3
authorNathan Skrzypczak <nathan.skrzypczak@gmail.com>
Fri, 11 Sep 2020 15:30:06 +0000 (17:30 +0200)
committerDave Barach <openvpp@barachs.net>
Fri, 25 Sep 2020 19:55:39 +0000 (19:55 +0000)
Type: feature

Change-Id: I60ae9dd1c100b587d1902a20596b99a5c8a95df7
Signed-off-by: Nathan Skrzypczak <nathan.skrzypczak@gmail.com>
13 files changed:
src/plugins/cnat/CMakeLists.txt
src/plugins/cnat/cnat_inline.h [new file with mode: 0644]
src/plugins/cnat/cnat_node.h
src/plugins/cnat/cnat_node_snat.c
src/plugins/cnat/cnat_node_vip.c
src/plugins/cnat/cnat_session.c
src/plugins/cnat/cnat_session.h
src/plugins/cnat/cnat_snat.c
src/plugins/cnat/cnat_snat.h
src/plugins/cnat/cnat_src_policy.c [new file with mode: 0644]
src/plugins/cnat/cnat_src_policy.h [new file with mode: 0644]
src/plugins/cnat/cnat_types.c
src/plugins/cnat/cnat_types.h

index b37b02c..95b59e9 100644 (file)
@@ -22,6 +22,7 @@ add_vpp_plugin(cnat
   cnat_translation.c
   cnat_types.c
   cnat_snat.c
+  cnat_src_policy.c
 
   API_FILES
   cnat.api
diff --git a/src/plugins/cnat/cnat_inline.h b/src/plugins/cnat/cnat_inline.h
new file mode 100644 (file)
index 0000000..5a55ecb
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2020 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.
+ */
+
+
+#ifndef __CNAT_INLINE_H__
+#define __CNAT_INLINE_H__
+
+#include <cnat/cnat_types.h>
+
+always_inline u32
+cnat_timestamp_new (f64 t)
+{
+  u32 index;
+  cnat_timestamp_t *ts;
+  clib_rwlock_writer_lock (&cnat_main.ts_lock);
+  pool_get (cnat_timestamps, ts);
+  ts->last_seen = t;
+  ts->lifetime = cnat_main.session_max_age;
+  ts->refcnt = CNAT_TIMESTAMP_INIT_REFCNT;
+  index = ts - cnat_timestamps;
+  clib_rwlock_writer_unlock (&cnat_main.ts_lock);
+  return index;
+}
+
+always_inline void
+cnat_timestamp_inc_refcnt (u32 index)
+{
+  clib_rwlock_reader_lock (&cnat_main.ts_lock);
+  cnat_timestamp_t *ts = pool_elt_at_index (cnat_timestamps, index);
+  ts->refcnt++;
+  clib_rwlock_reader_unlock (&cnat_main.ts_lock);
+}
+
+always_inline void
+cnat_timestamp_update (u32 index, f64 t)
+{
+  clib_rwlock_reader_lock (&cnat_main.ts_lock);
+  cnat_timestamp_t *ts = pool_elt_at_index (cnat_timestamps, index);
+  ts->last_seen = t;
+  clib_rwlock_reader_unlock (&cnat_main.ts_lock);
+}
+
+always_inline void
+cnat_timestamp_set_lifetime (u32 index, u16 lifetime)
+{
+  clib_rwlock_reader_lock (&cnat_main.ts_lock);
+  cnat_timestamp_t *ts = pool_elt_at_index (cnat_timestamps, index);
+  ts->lifetime = lifetime;
+  clib_rwlock_reader_unlock (&cnat_main.ts_lock);
+}
+
+always_inline f64
+cnat_timestamp_exp (u32 index)
+{
+  f64 t;
+  if (INDEX_INVALID == index)
+    return -1;
+  clib_rwlock_reader_lock (&cnat_main.ts_lock);
+  cnat_timestamp_t *ts = pool_elt_at_index (cnat_timestamps, index);
+  t = ts->last_seen + (f64) ts->lifetime;
+  clib_rwlock_reader_unlock (&cnat_main.ts_lock);
+  return t;
+}
+
+always_inline void
+cnat_timestamp_free (u32 index)
+{
+  if (INDEX_INVALID == index)
+    return;
+  clib_rwlock_writer_lock (&cnat_main.ts_lock);
+  cnat_timestamp_t *ts = pool_elt_at_index (cnat_timestamps, index);
+  ts->refcnt--;
+  if (0 == ts->refcnt)
+    pool_put (cnat_timestamps, ts);
+  clib_rwlock_writer_unlock (&cnat_main.ts_lock);
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
+
+#endif
index 2e3b0e0..64c3db1 100644 (file)
@@ -19,6 +19,7 @@
 #include <vlibmemory/api.h>
 #include <cnat/cnat_session.h>
 #include <cnat/cnat_client.h>
+#include <cnat/cnat_inline.h>
 
 typedef uword (*cnat_node_sub_t) (vlib_main_t * vm,
                                  vlib_node_runtime_t * node,
index d6c49cf..c000038 100644 (file)
@@ -16,6 +16,8 @@
 #include <vlibmemory/api.h>
 #include <cnat/cnat_node.h>
 #include <cnat/cnat_snat.h>
+#include <cnat/cnat_inline.h>
+#include <cnat/cnat_src_policy.h>
 
 typedef enum cnat_snat_next_
 {
@@ -227,16 +229,20 @@ VLIB_REGISTER_NODE (cnat_snat_ip6_node) =
     [CNAT_SNAT_NEXT_DROP] = "ip6-drop",
   }
 };
-/* *INDENT-ON* */
-
 
 VNET_FEATURE_INIT (cnat_snat_ip4_node, static) =
 {
-.arc_name = "ip4-unicast",.node_name = "ip4-cnat-snat",};
+  .arc_name = "ip4-unicast",
+  .node_name = "ip4-cnat-snat",
+};
 
 VNET_FEATURE_INIT (cnat_snat_ip6_node, static) =
 {
-.arc_name = "ip6-unicast",.node_name = "ip6-cnat-snat",};
+  .arc_name = "ip6-unicast",
+  .node_name = "ip6-cnat-snat",
+};
+
+/* *INDENT-ON* */
 
 /*
  * fd.io coding-style-patch-verification: ON
index d041606..224dd1c 100644 (file)
@@ -16,6 +16,8 @@
 #include <vlibmemory/api.h>
 #include <cnat/cnat_node.h>
 #include <cnat/cnat_translation.h>
+#include <cnat/cnat_inline.h>
+#include <cnat/cnat_src_policy.h>
 
 #include <vnet/dpo/load_balance.h>
 #include <vnet/dpo/load_balance_map.h>
@@ -44,19 +46,16 @@ format_cnat_translation_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 *);
-  cnat_translation_trace_t *t =
-    va_arg (*args, cnat_translation_trace_t *);
+  cnat_translation_trace_t *t = va_arg (*args, cnat_translation_trace_t *);
 
   if (t->found_session)
     s = format (s, "found: %U", format_cnat_session, &t->session, 1);
   else if (t->created_session)
     s = format (s, "created: %U\n  tr: %U",
                format_cnat_session, &t->session, 1,
-               format_cnat_translation,
-               &t->tr, 0);
+               format_cnat_translation, &t->tr, 0);
   else if (t->has_tr)
-    s = format (s, "tr pass: %U", format_cnat_translation,
-       &t->tr, 0);
+    s = format (s, "tr pass: %U", format_cnat_translation, &t->tr, 0);
   else
     s = format (s, "not found");
   return s;
@@ -65,9 +64,9 @@ format_cnat_translation_trace (u8 * s, va_list * args)
 /* CNat sub for NAT behind a fib entry (VIP or interposed real IP) */
 always_inline uword
 cnat_vip_inline (vlib_main_t * vm,
-                  vlib_node_runtime_t * node,
-                  vlib_buffer_t * b,
-                  cnat_node_ctx_t * ctx, int rv, cnat_session_t * session)
+                vlib_node_runtime_t * node,
+                vlib_buffer_t * b,
+                cnat_node_ctx_t * ctx, int rv, cnat_session_t * session)
 {
   vlib_combined_counter_main_t *cntm = &cnat_translation_counters;
   const cnat_translation_t *ct = NULL;
@@ -79,6 +78,7 @@ cnat_vip_inline (vlib_main_t * vm,
   u16 next0;
   index_t cti;
   int created_session = 0;
+  cnat_src_policy_main_t *cspm = &cnat_src_policy_main;
   if (AF_IP4 == ctx->af)
     {
       ip4 = vlib_buffer_get_current (b);
@@ -104,8 +104,7 @@ cnat_vip_inline (vlib_main_t * vm,
     }
 
   ct = cnat_find_translation (cc->parent_cci,
-                               clib_host_to_net_u16 (udp0->dst_port),
-                               iproto);
+                             clib_host_to_net_u16 (udp0->dst_port), iproto);
 
   if (!rv)
     {
@@ -191,28 +190,19 @@ cnat_vip_inline (vlib_main_t * vm,
        clib_host_to_net_u16 (trk0->ct_ep[VLIB_RX].ce_port);
 
       session->value.flags = 0;
-      if (!session->value.cs_port[VLIB_RX])
-       {
-         u16 sport;
-         sport = udp0->src_port;
-         /* Allocate a port only if asked and if we actually sNATed */
-         if ((ct->flags & CNAT_TRANSLATION_FLAG_ALLOCATE_PORT)
-              && (rsession_flags & CNAT_SESSION_FLAG_HAS_SNAT)) {
-           sport = 0; /* force allocation */
-           session->value.flags |= CNAT_SESSION_FLAG_ALLOC_PORT;
-           rv = cnat_allocate_port (&sport, iproto);
-           if (rv)
-             {
-               vlib_node_increment_counter (vm, cnat_vip_ip4_node.index,
-                                           CNAT_ERROR_EXHAUSTED_PORTS, 1);
-               next0 = CNAT_TRANSLATION_NEXT_DROP;
-               goto trace;
-             }
-           }
+      session->value.cs_lbi = dpo0->dpoi_index;
 
-         session->value.cs_port[VLIB_RX] = sport;
+      rv = cspm->vip_policy (vm, b, session, &rsession_flags, ct, ctx);
+      if (CNAT_SOURCE_ERROR_USE_DEFAULT == rv)
+       rv = cspm->default_policy (vm, b, session, &rsession_flags, ct, ctx);
+      if (rv)
+       {
+         if (CNAT_SOURCE_ERROR_EXHAUSTED_PORTS == rv)
+           vlib_node_increment_counter (vm, cnat_vip_ip4_node.index,
+                                        CNAT_ERROR_EXHAUSTED_PORTS, 1);
+         next0 = CNAT_TRANSLATION_NEXT_DROP;
+         goto trace;
        }
-      session->value.cs_lbi = dpo0->dpoi_index;
 
       /* refcnt session in current client */
       cnat_client_cnt_session (cc);
@@ -232,7 +222,7 @@ cnat_vip_inline (vlib_main_t * vm,
     {
       cti = ct - cnat_translation_pool;
       vlib_increment_combined_counter (cntm, ctx->thread_index, cti, 1,
-                                 vlib_buffer_length_in_chain (vm, b));
+                                      vlib_buffer_length_in_chain (vm, b));
     }
 
 trace:
@@ -254,25 +244,25 @@ trace:
 }
 
 VLIB_NODE_FN (cnat_vip_ip4_node) (vlib_main_t * vm,
-                                   vlib_node_runtime_t * node,
-                                   vlib_frame_t * frame)
+                                 vlib_node_runtime_t * node,
+                                 vlib_frame_t * frame)
 {
   if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
     return cnat_node_inline (vm, node, frame, cnat_vip_inline, AF_IP4,
-                              1 /* do_trace */ );
+                            1 /* do_trace */ );
   return cnat_node_inline (vm, node, frame, cnat_vip_inline, AF_IP4,
-                            0 /* do_trace */ );
+                          0 /* do_trace */ );
 }
 
 VLIB_NODE_FN (cnat_vip_ip6_node) (vlib_main_t * vm,
-                                   vlib_node_runtime_t * node,
-                                   vlib_frame_t * frame)
+                                 vlib_node_runtime_t * node,
+                                 vlib_frame_t * frame)
 {
   if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
     return cnat_node_inline (vm, node, frame, cnat_vip_inline, AF_IP6,
-                              1 /* do_trace */ );
+                            1 /* do_trace */ );
   return cnat_node_inline (vm, node, frame, cnat_vip_inline, AF_IP6,
-                            0 /* do_trace */ );
+                          0 /* do_trace */ );
 }
 
 /* *INDENT-OFF* */
@@ -306,3 +296,10 @@ VLIB_REGISTER_NODE (cnat_vip_ip6_node) =
 };
 /* *INDENT-ON* */
 
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
index 4259f42..a80e367 100644 (file)
 
 #include <vnet/ip/ip.h>
 #include <cnat/cnat_session.h>
+#include <cnat/cnat_inline.h>
 
 #include <vppinfra/bihash_template.h>
 #include <vppinfra/bihash_template.c>
 
 
 clib_bihash_40_48_t cnat_session_db;
-
+void (*cnat_free_port_cb) (u16 port, ip_protocol_t iproto);
 
 typedef struct cnat_session_walk_ctx_t_
 {
@@ -128,7 +129,8 @@ cnat_session_free (cnat_session_t * session)
   clib_bihash_kv_40_48_t *bkey = (clib_bihash_kv_40_48_t *) session;
   /* age it */
   if (session->value.flags & CNAT_SESSION_FLAG_ALLOC_PORT)
-    cnat_free_port (session->value.cs_port[VLIB_RX], session->key.cs_proto);
+    cnat_free_port_cb (session->value.cs_port[VLIB_RX],
+                      session->key.cs_proto);
   if (!(session->value.flags & CNAT_SESSION_FLAG_NO_CLIENT))
     cnat_client_free_by_ip (&session->key.cs_ip[VLIB_TX], session->key.cs_af);
   cnat_timestamp_free (session->value.cs_ts_index);
index 4699dcc..83b8cd6 100644 (file)
@@ -151,6 +151,11 @@ extern int cnat_session_purge (void);
  */
 extern void cnat_session_free (cnat_session_t * session);
 
+/**
+ * Port cleanup callback
+ */
+extern void (*cnat_free_port_cb) (u16 port, ip_protocol_t iproto);
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
index 86fac12..21ac825 100644 (file)
@@ -101,6 +101,48 @@ cnat_del_snat_prefix (ip_prefix_t * pfx)
   return 0;
 }
 
+int
+cnat_search_snat_prefix (ip46_address_t * addr, ip_address_family_t af)
+{
+  /* Returns 0 if addr matches any of the listed prefixes */
+  cnat_snat_pfx_table_t *table = &cnat_main.snat_pfx_table;
+  clib_bihash_kv_24_8_t kv, val;
+  int i, n_p, rv;
+  n_p = vec_len (table->meta[af].prefix_lengths_in_search_order);
+  if (AF_IP4 == af)
+    {
+      kv.key[0] = addr->ip4.as_u32;
+      kv.key[1] = 0;
+    }
+  else
+    {
+      kv.key[0] = addr->as_u64[0];
+      kv.key[1] = addr->as_u64[1];
+    }
+
+  /*
+   * start search from a mask length same length or shorter.
+   * we don't want matches longer than the mask passed
+   */
+  i = 0;
+  for (; i < n_p; i++)
+    {
+      int dst_address_length =
+       table->meta[af].prefix_lengths_in_search_order[i];
+      ip6_address_t *mask = &table->ip_masks[dst_address_length];
+
+      ASSERT (dst_address_length >= 0 && dst_address_length <= 128);
+      /* As lengths are decreasing, masks are increasingly specific. */
+      kv.key[0] &= mask->as_u64[0];
+      kv.key[1] &= mask->as_u64[1];
+      kv.key[2] = ((u64) af << 32) | dst_address_length;
+      rv = clib_bihash_search_inline_2_24_8 (&table->ip_hash, &kv, &val);
+      if (rv == 0)
+       return 0;
+    }
+  return -1;
+}
+
 u8 *
 format_cnat_snat_prefix (u8 * s, va_list * args)
 {
index 97bad8b..326746f 100644 (file)
 
 #include <cnat/cnat_types.h>
 
-always_inline int
-cnat_search_snat_prefix (ip46_address_t * addr, ip_address_family_t af)
-{
-  /* Returns 0 if addr matches any of the listed prefixes */
-  cnat_snat_pfx_table_t *table = &cnat_main.snat_pfx_table;
-  clib_bihash_kv_24_8_t kv, val;
-  int i, n_p, rv;
-  n_p = vec_len (table->meta[af].prefix_lengths_in_search_order);
-  if (AF_IP4 == af)
-    {
-      kv.key[0] = addr->ip4.as_u32;
-      kv.key[1] = 0;
-    }
-  else
-    {
-      kv.key[0] = addr->as_u64[0];
-      kv.key[1] = addr->as_u64[1];
-    }
-
-  /*
-   * start search from a mask length same length or shorter.
-   * we don't want matches longer than the mask passed
-   */
-  i = 0;
-  for (; i < n_p; i++)
-    {
-      int dst_address_length =
-       table->meta[af].prefix_lengths_in_search_order[i];
-      ip6_address_t *mask = &table->ip_masks[dst_address_length];
-
-      ASSERT (dst_address_length >= 0 && dst_address_length <= 128);
-      /* As lengths are decreasing, masks are increasingly specific. */
-      kv.key[0] &= mask->as_u64[0];
-      kv.key[1] &= mask->as_u64[1];
-      kv.key[2] = ((u64) af << 32) | dst_address_length;
-      rv = clib_bihash_search_inline_2_24_8 (&table->ip_hash, &kv, &val);
-      if (rv == 0)
-       return 0;
-    }
-  return -1;
-}
-
 extern int cnat_add_snat_prefix (ip_prefix_t * pfx);
 extern int cnat_del_snat_prefix (ip_prefix_t * pfx);
 
+int cnat_search_snat_prefix (ip46_address_t * addr, ip_address_family_t af);
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
diff --git a/src/plugins/cnat/cnat_src_policy.c b/src/plugins/cnat/cnat_src_policy.c
new file mode 100644 (file)
index 0000000..e11e912
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2020 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 <cnat/cnat_src_policy.h>
+#include <cnat/cnat_inline.h>
+
+#include <cnat/cnat_session.h>
+#include <cnat/cnat_translation.h>
+
+cnat_src_policy_main_t cnat_src_policy_main;
+
+void
+cnat_register_vip_src_policy (cnat_vip_source_policy_t fp)
+{
+  cnat_src_policy_main.vip_policy = fp;
+}
+
+cnat_source_policy_errors_t
+cnat_vip_default_source_policy (vlib_main_t * vm,
+                               vlib_buffer_t * b,
+                               cnat_session_t * session,
+                               u32 * rsession_flags,
+                               const cnat_translation_t * ct,
+                               cnat_node_ctx_t * ctx)
+{
+  ip_protocol_t iproto;
+  udp_header_t *udp0;
+  ip4_header_t *ip4;
+  ip6_header_t *ip6;
+
+  if (AF_IP4 == ctx->af)
+    {
+      ip4 = vlib_buffer_get_current (b);
+      iproto = ip4->protocol;
+      udp0 = (udp_header_t *) (ip4 + 1);
+    }
+  else
+    {
+      ip6 = vlib_buffer_get_current (b);
+      iproto = ip6->protocol;
+      udp0 = (udp_header_t *) (ip6 + 1);
+    }
+
+  int rv = 0;
+  if (!session->value.cs_port[VLIB_RX])
+    {
+      u16 sport;
+      sport = udp0->src_port;
+      /* Allocate a port only if asked and if we actually sNATed */
+      if ((ct->flags & CNAT_TRANSLATION_FLAG_ALLOCATE_PORT)
+         && (*rsession_flags & CNAT_SESSION_FLAG_HAS_SNAT))
+       {
+         sport = 0;            /* force allocation */
+         session->value.flags |= CNAT_SESSION_FLAG_ALLOC_PORT;
+         rv = cnat_allocate_port (&sport, iproto);
+         if (rv)
+           return CNAT_SOURCE_ERROR_EXHAUSTED_PORTS;
+       }
+
+      session->value.cs_port[VLIB_RX] = sport;
+    }
+  return 0;
+}
+
+always_inline cnat_src_port_allocator_t *
+cnat_get_src_port_allocator (ip_protocol_t iproto)
+{
+  cnat_src_policy_main_t *cspm = &cnat_src_policy_main;
+  switch (iproto)
+    {
+    case IP_PROTOCOL_TCP:
+      return &cspm->src_ports[CNAT_SPORT_PROTO_TCP];
+    case IP_PROTOCOL_UDP:
+      return &cspm->src_ports[CNAT_SPORT_PROTO_UDP];
+    case IP_PROTOCOL_ICMP:
+      return &cspm->src_ports[CNAT_SPORT_PROTO_ICMP];
+    case IP_PROTOCOL_ICMP6:
+      return &cspm->src_ports[CNAT_SPORT_PROTO_ICMP6];
+    default:
+      return 0;
+    }
+}
+
+void
+cnat_free_port (u16 port, ip_protocol_t iproto)
+{
+  cnat_src_port_allocator_t *ca;
+  ca = cnat_get_src_port_allocator (iproto);
+  if (!ca)
+    return;
+  clib_spinlock_lock (&ca->lock);
+  clib_bitmap_set_no_check (ca->bmap, port, 0);
+  clib_spinlock_unlock (&ca->lock);
+}
+
+int
+cnat_allocate_port (u16 * port, ip_protocol_t iproto)
+{
+  *port = clib_net_to_host_u16 (*port);
+  if (*port == 0)
+    *port = MIN_SRC_PORT;
+  cnat_src_port_allocator_t *ca;
+  ca = cnat_get_src_port_allocator (iproto);
+  if (!ca)
+    return -1;
+  clib_spinlock_lock (&ca->lock);
+  if (clib_bitmap_get_no_check (ca->bmap, *port))
+    {
+      *port = clib_bitmap_next_clear (ca->bmap, *port);
+      if (PREDICT_FALSE (*port >= UINT16_MAX))
+       *port = clib_bitmap_next_clear (ca->bmap, MIN_SRC_PORT);
+      if (PREDICT_FALSE (*port >= UINT16_MAX))
+       return -1;
+    }
+  clib_bitmap_set_no_check (ca->bmap, *port, 1);
+  *port = clib_host_to_net_u16 (*port);
+  clib_spinlock_unlock (&ca->lock);
+  return 0;
+}
+
+static clib_error_t *
+cnat_src_policy_init (vlib_main_t * vm)
+{
+  cnat_src_policy_main_t *cspm = &cnat_src_policy_main;
+  cspm->vip_policy = cnat_vip_default_source_policy;
+  cspm->default_policy = cnat_vip_default_source_policy;
+
+  vec_validate (cspm->src_ports, CNAT_N_SPORT_PROTO);
+  for (int i = 0; i < CNAT_N_SPORT_PROTO; i++)
+    {
+      clib_spinlock_init (&cspm->src_ports[i].lock);
+      clib_bitmap_validate (cspm->src_ports[i].bmap, UINT16_MAX);
+    }
+  /* Inject cleanup callback */
+  cnat_free_port_cb = cnat_free_port;
+  return (NULL);
+}
+
+VLIB_INIT_FUNCTION (cnat_src_policy_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/cnat/cnat_src_policy.h b/src/plugins/cnat/cnat_src_policy.h
new file mode 100644 (file)
index 0000000..d76de8b
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2020 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.
+ */
+
+#ifndef __CNAT_SRC_POLICY_H__
+#define __CNAT_SRC_POLICY_H__
+
+// #include <vnet/udp/udp.h>
+#include <cnat/cnat_types.h>
+#include <cnat/cnat_session.h>
+#include <cnat/cnat_translation.h>
+
+typedef enum
+{
+  CNAT_SPORT_PROTO_TCP,
+  CNAT_SPORT_PROTO_UDP,
+  CNAT_SPORT_PROTO_ICMP,
+  CNAT_SPORT_PROTO_ICMP6,
+  CNAT_N_SPORT_PROTO
+} cnat_sport_proto_t;
+
+typedef enum cnat_source_policy_errors_
+{
+  CNAT_SOURCE_ERROR_EXHAUSTED_PORTS,
+  CNAT_SOURCE_ERROR_USE_DEFAULT,
+  CNAT_SOURCE_N_ERRORS,
+} cnat_source_policy_errors_t;
+
+typedef struct cnat_src_port_allocator_
+{
+  /* Source ports bitmap for snat */
+  clib_bitmap_t *bmap;
+
+  /* Lock for src_ports access */
+  clib_spinlock_t lock;
+} cnat_src_port_allocator_t;
+
+/* function to use to compute source (IP, port) for a new session to a vip */
+typedef cnat_source_policy_errors_t (*cnat_vip_source_policy_t)
+  (vlib_main_t * vm, vlib_buffer_t * b, cnat_session_t * session,
+   u32 * rsession_flags, const cnat_translation_t * ct,
+   cnat_node_ctx_t * ctx);
+
+typedef struct cnat_src_policy_main_
+{
+  cnat_vip_source_policy_t vip_policy;
+  cnat_vip_source_policy_t default_policy;
+
+  /* Per proto source ports allocator for snat */
+  cnat_src_port_allocator_t *src_ports;
+} cnat_src_policy_main_t;
+
+extern cnat_src_policy_main_t cnat_src_policy_main;
+
+void cnat_register_vip_src_policy (cnat_vip_source_policy_t fp);
+int cnat_allocate_port (u16 * port, ip_protocol_t iproto);
+void cnat_free_port (u16 port, ip_protocol_t iproto);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
+
+#endif
index 9db953f..a66ebf6 100644 (file)
@@ -82,12 +82,6 @@ cnat_types_init (vlib_main_t * vm)
 
 
   clib_rwlock_init (&cnat_main.ts_lock);
-  vec_validate (cnat_main.src_ports, CNAT_N_SPORT_PROTO);
-  for (int i = 0; i < CNAT_N_SPORT_PROTO; i++)
-    {
-      clib_spinlock_init (&cnat_main.src_ports[i].lock);
-      clib_bitmap_validate (cnat_main.src_ports[i].bmap, UINT16_MAX);
-    }
   throttle_init (&cnat_throttle, n_vlib_mains, 1e-3);
 
   return (NULL);
@@ -165,6 +159,12 @@ cnat_config (vlib_main_t * vm, unformat_input_t * input)
   return 0;
 }
 
+cnat_main_t *
+cnat_get_main ()
+{
+  return &cnat_main;
+}
+
 VLIB_EARLY_CONFIG_FUNCTION (cnat_config, "cnat");
 VLIB_INIT_FUNCTION (cnat_types_init);
 
index c9c0b70..b6b6e01 100644 (file)
 
 #define MIN_SRC_PORT ((u16) 0xC000)
 
-typedef enum
-{
-  CNAT_SPORT_PROTO_TCP,
-  CNAT_SPORT_PROTO_UDP,
-  CNAT_SPORT_PROTO_ICMP,
-  CNAT_SPORT_PROTO_ICMP6,
-  CNAT_N_SPORT_PROTO
-} cnat_sport_proto_t;
-
 typedef struct cnat_endpoint_t_
 {
   ip_address_t ce_ip;
@@ -93,15 +84,6 @@ typedef struct
   ip6_address_t ip_masks[129];
 } cnat_snat_pfx_table_t;
 
-typedef struct cnat_src_port_allocator_
-{
-  /* Source ports bitmap for snat */
-  clib_bitmap_t *bmap;
-
-  /* Lock for src_ports access */
-  clib_spinlock_t lock;
-} cnat_src_port_allocator_t;
-
 typedef struct cnat_main_
 {
   /* Memory size of the session bihash */
@@ -135,9 +117,6 @@ typedef struct cnat_main_
   /* Lock for the timestamp pool */
   clib_rwlock_t ts_lock;
 
-  /* Per proto source ports allocator for snat */
-  cnat_src_port_allocator_t *src_ports;
-
   /* Ip4 Address to use for source NATing */
   ip4_address_t snat_ip4;
 
@@ -167,7 +146,7 @@ typedef struct cnat_timestamp_t_
   u16 refcnt;
 } cnat_timestamp_t;
 
-typedef struct cnat_node_ctx_t_
+typedef struct cnat_node_ctx_
 {
   f64 now;
   u64 seed;
@@ -176,6 +155,7 @@ typedef struct cnat_node_ctx_t_
   u8 do_trace;
 } cnat_node_ctx_t;
 
+cnat_main_t *cnat_get_main ();
 extern u8 *format_cnat_endpoint (u8 * s, va_list * args);
 extern uword unformat_cnat_ep_tuple (unformat_input_t * input,
                                     va_list * args);
@@ -212,134 +192,6 @@ extern void cnat_lazy_init ();
  */
 extern void cnat_enable_disable_scanner (cnat_scanner_cmd_t event_type);
 
-/*
-  Dataplane functions
-*/
-
-always_inline u32
-cnat_timestamp_new (f64 t)
-{
-  u32 index;
-  cnat_timestamp_t *ts;
-  clib_rwlock_writer_lock (&cnat_main.ts_lock);
-  pool_get (cnat_timestamps, ts);
-  ts->last_seen = t;
-  ts->lifetime = cnat_main.session_max_age;
-  ts->refcnt = CNAT_TIMESTAMP_INIT_REFCNT;
-  index = ts - cnat_timestamps;
-  clib_rwlock_writer_unlock (&cnat_main.ts_lock);
-  return index;
-}
-
-always_inline void
-cnat_timestamp_inc_refcnt (u32 index)
-{
-  clib_rwlock_reader_lock (&cnat_main.ts_lock);
-  cnat_timestamp_t *ts = pool_elt_at_index (cnat_timestamps, index);
-  ts->refcnt++;
-  clib_rwlock_reader_unlock (&cnat_main.ts_lock);
-}
-
-always_inline void
-cnat_timestamp_update (u32 index, f64 t)
-{
-  clib_rwlock_reader_lock (&cnat_main.ts_lock);
-  cnat_timestamp_t *ts = pool_elt_at_index (cnat_timestamps, index);
-  ts->last_seen = t;
-  clib_rwlock_reader_unlock (&cnat_main.ts_lock);
-}
-
-always_inline void
-cnat_timestamp_set_lifetime (u32 index, u16 lifetime)
-{
-  clib_rwlock_reader_lock (&cnat_main.ts_lock);
-  cnat_timestamp_t *ts = pool_elt_at_index (cnat_timestamps, index);
-  ts->lifetime = lifetime;
-  clib_rwlock_reader_unlock (&cnat_main.ts_lock);
-}
-
-always_inline f64
-cnat_timestamp_exp (u32 index)
-{
-  f64 t;
-  if (INDEX_INVALID == index)
-    return -1;
-  clib_rwlock_reader_lock (&cnat_main.ts_lock);
-  cnat_timestamp_t *ts = pool_elt_at_index (cnat_timestamps, index);
-  t = ts->last_seen + (f64) ts->lifetime;
-  clib_rwlock_reader_unlock (&cnat_main.ts_lock);
-  return t;
-}
-
-always_inline void
-cnat_timestamp_free (u32 index)
-{
-  if (INDEX_INVALID == index)
-    return;
-  clib_rwlock_writer_lock (&cnat_main.ts_lock);
-  cnat_timestamp_t *ts = pool_elt_at_index (cnat_timestamps, index);
-  ts->refcnt--;
-  if (0 == ts->refcnt)
-    pool_put (cnat_timestamps, ts);
-  clib_rwlock_writer_unlock (&cnat_main.ts_lock);
-}
-
-always_inline cnat_src_port_allocator_t *
-cnat_get_src_port_allocator (ip_protocol_t iproto)
-{
-  cnat_main_t *cm = &cnat_main;
-  switch (iproto)
-    {
-    case IP_PROTOCOL_TCP:
-      return &cm->src_ports[CNAT_SPORT_PROTO_TCP];
-    case IP_PROTOCOL_UDP:
-      return &cm->src_ports[CNAT_SPORT_PROTO_UDP];
-    case IP_PROTOCOL_ICMP:
-      return &cm->src_ports[CNAT_SPORT_PROTO_ICMP];
-    case IP_PROTOCOL_ICMP6:
-      return &cm->src_ports[CNAT_SPORT_PROTO_ICMP6];
-    default:
-      return 0;
-    }
-}
-
-always_inline void
-cnat_free_port (u16 port, ip_protocol_t iproto)
-{
-  cnat_src_port_allocator_t *ca;
-  ca = cnat_get_src_port_allocator (iproto);
-  if (!ca)
-    return;
-  clib_spinlock_lock (&ca->lock);
-  clib_bitmap_set_no_check (ca->bmap, port, 0);
-  clib_spinlock_unlock (&ca->lock);
-}
-
-always_inline int
-cnat_allocate_port (u16 * port, ip_protocol_t iproto)
-{
-  *port = clib_net_to_host_u16 (*port);
-  if (*port == 0)
-    *port = MIN_SRC_PORT;
-  cnat_src_port_allocator_t *ca;
-  ca = cnat_get_src_port_allocator (iproto);
-  if (!ca)
-    return -1;
-  clib_spinlock_lock (&ca->lock);
-  if (clib_bitmap_get_no_check (ca->bmap, *port))
-    {
-      *port = clib_bitmap_next_clear (ca->bmap, *port);
-      if (PREDICT_FALSE (*port >= UINT16_MAX))
-       *port = clib_bitmap_next_clear (ca->bmap, MIN_SRC_PORT);
-      if (PREDICT_FALSE (*port >= UINT16_MAX))
-       return -1;
-    }
-  clib_bitmap_set_no_check (ca->bmap, *port, 1);
-  *port = clib_host_to_net_u16 (*port);
-  clib_spinlock_unlock (&ca->lock);
-  return 0;
-}
-
 /*
  * fd.io coding-style-patch-verification: ON
  *