summaryrefslogtreecommitdiffstats
path: root/src/plugins/cnat
diff options
context:
space:
mode:
authorNathan Skrzypczak <nathan.skrzypczak@gmail.com>2020-09-11 17:30:06 +0200
committerDave Barach <openvpp@barachs.net>2020-09-25 19:55:39 +0000
commitce25b60de5536e2f79bb72e929e70ccc1a75e0f8 (patch)
treedb5b9b18b321bb1dec2f0742afffd647695d12ce /src/plugins/cnat
parent613b2c3c78fbec12cc87a0095ee5488252449698 (diff)
cnat: Introduce parametric source policy
Type: feature Change-Id: I60ae9dd1c100b587d1902a20596b99a5c8a95df7 Signed-off-by: Nathan Skrzypczak <nathan.skrzypczak@gmail.com>
Diffstat (limited to 'src/plugins/cnat')
-rw-r--r--src/plugins/cnat/CMakeLists.txt1
-rw-r--r--src/plugins/cnat/cnat_inline.h98
-rw-r--r--src/plugins/cnat/cnat_node.h1
-rw-r--r--src/plugins/cnat/cnat_node_snat.c14
-rw-r--r--src/plugins/cnat/cnat_node_vip.c77
-rw-r--r--src/plugins/cnat/cnat_session.c6
-rw-r--r--src/plugins/cnat/cnat_session.h5
-rw-r--r--src/plugins/cnat/cnat_snat.c42
-rw-r--r--src/plugins/cnat/cnat_snat.h44
-rw-r--r--src/plugins/cnat/cnat_src_policy.c159
-rw-r--r--src/plugins/cnat/cnat_src_policy.h78
-rw-r--r--src/plugins/cnat/cnat_types.c12
-rw-r--r--src/plugins/cnat/cnat_types.h152
13 files changed, 445 insertions, 244 deletions
diff --git a/src/plugins/cnat/CMakeLists.txt b/src/plugins/cnat/CMakeLists.txt
index b37b02cfc16..95b59e97d10 100644
--- a/src/plugins/cnat/CMakeLists.txt
+++ b/src/plugins/cnat/CMakeLists.txt
@@ -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
index 00000000000..5a55ecbf3c0
--- /dev/null
+++ b/src/plugins/cnat/cnat_inline.h
@@ -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
diff --git a/src/plugins/cnat/cnat_node.h b/src/plugins/cnat/cnat_node.h
index 2e3b0e0275a..64c3db1e7cc 100644
--- a/src/plugins/cnat/cnat_node.h
+++ b/src/plugins/cnat/cnat_node.h
@@ -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,
diff --git a/src/plugins/cnat/cnat_node_snat.c b/src/plugins/cnat/cnat_node_snat.c
index d6c49cf9174..c0000385ffb 100644
--- a/src/plugins/cnat/cnat_node_snat.c
+++ b/src/plugins/cnat/cnat_node_snat.c
@@ -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
diff --git a/src/plugins/cnat/cnat_node_vip.c b/src/plugins/cnat/cnat_node_vip.c
index d041606786b..224dd1cf06b 100644
--- a/src/plugins/cnat/cnat_node_vip.c
+++ b/src/plugins/cnat/cnat_node_vip.c
@@ -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:
+ */
diff --git a/src/plugins/cnat/cnat_session.c b/src/plugins/cnat/cnat_session.c
index 4259f42f398..a80e367c2cc 100644
--- a/src/plugins/cnat/cnat_session.c
+++ b/src/plugins/cnat/cnat_session.c
@@ -15,13 +15,14 @@
#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);
diff --git a/src/plugins/cnat/cnat_session.h b/src/plugins/cnat/cnat_session.h
index 4699dcc4fcf..83b8cd61389 100644
--- a/src/plugins/cnat/cnat_session.h
+++ b/src/plugins/cnat/cnat_session.h
@@ -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
*
diff --git a/src/plugins/cnat/cnat_snat.c b/src/plugins/cnat/cnat_snat.c
index 86fac126226..21ac8257a23 100644
--- a/src/plugins/cnat/cnat_snat.c
+++ b/src/plugins/cnat/cnat_snat.c
@@ -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)
{
diff --git a/src/plugins/cnat/cnat_snat.h b/src/plugins/cnat/cnat_snat.h
index 97bad8b01d0..326746f8915 100644
--- a/src/plugins/cnat/cnat_snat.h
+++ b/src/plugins/cnat/cnat_snat.h
@@ -18,51 +18,11 @@
#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
index 00000000000..e11e912ab15
--- /dev/null
+++ b/src/plugins/cnat/cnat_src_policy.c
@@ -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
index 00000000000..d76de8bd2cd
--- /dev/null
+++ b/src/plugins/cnat/cnat_src_policy.h
@@ -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
diff --git a/src/plugins/cnat/cnat_types.c b/src/plugins/cnat/cnat_types.c
index 9db953f0174..a66ebf647ea 100644
--- a/src/plugins/cnat/cnat_types.c
+++ b/src/plugins/cnat/cnat_types.c
@@ -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);
diff --git a/src/plugins/cnat/cnat_types.h b/src/plugins/cnat/cnat_types.h
index c9c0b70b8c3..b6b6e012c53 100644
--- a/src/plugins/cnat/cnat_types.h
+++ b/src/plugins/cnat/cnat_types.h
@@ -49,15 +49,6 @@
#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);
@@ -213,134 +193,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
*
* Local Variables: