summaryrefslogtreecommitdiffstats
path: root/src/plugins/cnat/cnat_src_policy.c
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/cnat_src_policy.c
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/cnat_src_policy.c')
-rw-r--r--src/plugins/cnat/cnat_src_policy.c159
1 files changed, 159 insertions, 0 deletions
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:
+ */