From ce25b60de5536e2f79bb72e929e70ccc1a75e0f8 Mon Sep 17 00:00:00 2001 From: Nathan Skrzypczak Date: Fri, 11 Sep 2020 17:30:06 +0200 Subject: [PATCH] cnat: Introduce parametric source policy Type: feature Change-Id: I60ae9dd1c100b587d1902a20596b99a5c8a95df7 Signed-off-by: Nathan Skrzypczak --- src/plugins/cnat/CMakeLists.txt | 1 + src/plugins/cnat/cnat_inline.h | 98 +++++++++++++++++++++++ src/plugins/cnat/cnat_node.h | 1 + src/plugins/cnat/cnat_node_snat.c | 14 +++- src/plugins/cnat/cnat_node_vip.c | 77 +++++++++--------- src/plugins/cnat/cnat_session.c | 6 +- src/plugins/cnat/cnat_session.h | 5 ++ src/plugins/cnat/cnat_snat.c | 42 ++++++++++ src/plugins/cnat/cnat_snat.h | 44 +--------- src/plugins/cnat/cnat_src_policy.c | 159 +++++++++++++++++++++++++++++++++++++ src/plugins/cnat/cnat_src_policy.h | 78 ++++++++++++++++++ src/plugins/cnat/cnat_types.c | 12 +-- src/plugins/cnat/cnat_types.h | 152 +---------------------------------- 13 files changed, 445 insertions(+), 244 deletions(-) create mode 100644 src/plugins/cnat/cnat_inline.h create mode 100644 src/plugins/cnat/cnat_src_policy.c create mode 100644 src/plugins/cnat/cnat_src_policy.h 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 + +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 #include #include +#include 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 #include #include +#include +#include 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 #include #include +#include +#include #include #include @@ -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 #include +#include #include #include 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 -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 +#include + +#include +#include + +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 +#include +#include +#include + +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); @@ -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 * -- 2.16.6