diff options
author | Neale Ranns <nranns@cisco.com> | 2020-05-19 07:17:19 +0000 |
---|---|---|
committer | Andrew Yourtchenko <ayourtch@gmail.com> | 2020-08-31 09:23:32 +0000 |
commit | 29f3c7d2ecac2f9d80bb33e91bd5d1f9d434768a (patch) | |
tree | 66d7c69f2c24959ef4f6ef67b7c56dba11d8be29 /src/plugins/cnat/cnat_session.c | |
parent | 133c91c1c06e7c773ba675181901ba0dcf955ae6 (diff) |
cnat: Destination based NAT
Type: feature
Signed-off-by: Neale Ranns <nranns@cisco.com>
Change-Id: I64a99a4fbc674212944247793fd5c1fb701408cb
Diffstat (limited to 'src/plugins/cnat/cnat_session.c')
-rw-r--r-- | src/plugins/cnat/cnat_session.c | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/src/plugins/cnat/cnat_session.c b/src/plugins/cnat/cnat_session.c new file mode 100644 index 00000000000..7f95e1bc501 --- /dev/null +++ b/src/plugins/cnat/cnat_session.c @@ -0,0 +1,269 @@ +/* + * 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 <vnet/ip/ip.h> +#include <cnat/cnat_session.h> + +#include <vppinfra/bihash_template.h> +#include <vppinfra/bihash_template.c> + + +clib_bihash_40_48_t cnat_session_db; + + +typedef struct cnat_session_walk_ctx_t_ +{ + cnat_session_walk_cb_t cb; + void *ctx; +} cnat_session_walk_ctx_t; + +static int +cnat_session_walk_cb (BVT (clib_bihash_kv) * kv, void *arg) +{ + cnat_session_t *session = (cnat_session_t *) kv; + cnat_session_walk_ctx_t *ctx = arg; + + ctx->cb (session, ctx->ctx); + + return (BIHASH_WALK_CONTINUE); +} + +void +cnat_session_walk (cnat_session_walk_cb_t cb, void *ctx) +{ + cnat_session_walk_ctx_t wctx = { + .cb = cb, + .ctx = ctx, + }; + BV (clib_bihash_foreach_key_value_pair) (&cnat_session_db, + cnat_session_walk_cb, &wctx); +} + +typedef struct cnat_session_purge_walk_t_ +{ + clib_bihash_kv_40_48_t *keys; +} cnat_session_purge_walk_ctx_t; + +static int +cnat_session_purge_walk (BVT (clib_bihash_kv) * key, void *arg) +{ + cnat_session_purge_walk_ctx_t *ctx = arg; + + vec_add1 (ctx->keys, *key); + + return (BIHASH_WALK_CONTINUE); +} + +u8 * +format_cnat_session (u8 * s, va_list * args) +{ + cnat_session_t *sess = va_arg (*args, cnat_session_t *); + CLIB_UNUSED (int verbose) = va_arg (*args, int); + f64 ts = 0; + if (!pool_is_free_index (cnat_timestamps, sess->value.cs_ts_index)) + ts = cnat_timestamp_exp (sess->value.cs_ts_index); + + s = + format (s, + "session:[%U;%d -> %U;%d, %U] => %U;%d -> %U;%d lb:%d age:%f", + format_ip46_address, &sess->key.cs_ip[VLIB_RX], IP46_TYPE_ANY, + clib_host_to_net_u16 (sess->key.cs_port[VLIB_RX]), + format_ip46_address, &sess->key.cs_ip[VLIB_TX], IP46_TYPE_ANY, + clib_host_to_net_u16 (sess->key.cs_port[VLIB_TX]), + format_ip_protocol, sess->key.cs_proto, format_ip46_address, + &sess->value.cs_ip[VLIB_RX], IP46_TYPE_ANY, + clib_host_to_net_u16 (sess->value.cs_port[VLIB_RX]), + format_ip46_address, &sess->value.cs_ip[VLIB_TX], IP46_TYPE_ANY, + clib_host_to_net_u16 (sess->value.cs_port[VLIB_TX]), + sess->value.cs_lbi, ts); + + return (s); +} + +static clib_error_t * +cnat_session_show (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + u8 verbose = 0; + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "verbose")) + verbose = 1; + else + return (clib_error_return (0, "unknown input '%U'", + format_unformat_error, input)); + } + + vlib_cli_output (vm, "CNat Sessions: now:%f\n%U\n", + vlib_time_now (vm), + BV (format_bihash), &cnat_session_db, verbose); + + return (NULL); +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (cnat_session_show_cmd_node, static) = { + .path = "show cnat session", + .function = cnat_session_show, + .short_help = "show cnat session", + .is_mp_safe = 1, +}; +/* *INDENT-ON* */ + +void +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]); + 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); + + clib_bihash_add_del_40_48 (&cnat_session_db, bkey, 0 /* is_add */ ); +} + +int +cnat_session_purge (void) +{ + /* flush all the session from the DB */ + cnat_session_purge_walk_ctx_t ctx = { }; + clib_bihash_kv_40_48_t *key; + + BV (clib_bihash_foreach_key_value_pair) (&cnat_session_db, + cnat_session_purge_walk, &ctx); + + vec_foreach (key, ctx.keys) cnat_session_free ((cnat_session_t *) key); + + vec_free (ctx.keys); + + return (0); +} + +u64 +cnat_session_scan (vlib_main_t * vm, f64 start_time, int i) +{ + BVT (clib_bihash) * h = &cnat_session_db; + int j, k; + + /* Don't scan the l2 fib if it hasn't been instantiated yet */ + if (alloc_arena (h) == 0) + return 0.0; + + for (i = 0; i < h->nbuckets; i++) + { + /* allow no more than 100us without a pause */ + if ((vlib_time_now (vm) - start_time) > 10e-5) + return (i); + + if (i < (h->nbuckets - 3)) + { + BVT (clib_bihash_bucket) * b = + BV (clib_bihash_get_bucket) (h, i + 3); + CLIB_PREFETCH (b, CLIB_CACHE_LINE_BYTES, LOAD); + b = BV (clib_bihash_get_bucket) (h, i + 1); + if (!BV (clib_bihash_bucket_is_empty) (b)) + { + BVT (clib_bihash_value) * v = + BV (clib_bihash_get_value) (h, b->offset); + CLIB_PREFETCH (v, CLIB_CACHE_LINE_BYTES, LOAD); + } + } + + BVT (clib_bihash_bucket) * b = BV (clib_bihash_get_bucket) (h, i); + if (BV (clib_bihash_bucket_is_empty) (b)) + continue; + BVT (clib_bihash_value) * v = BV (clib_bihash_get_value) (h, b->offset); + for (j = 0; j < (1 << b->log2_pages); j++) + { + for (k = 0; k < BIHASH_KVP_PER_PAGE; k++) + { + if (v->kvp[k].key[0] == ~0ULL && v->kvp[k].value[0] == ~0ULL) + continue; + + cnat_session_t *session = (cnat_session_t *) & v->kvp[k]; + + if (start_time > + cnat_timestamp_exp (session->value.cs_ts_index)) + { + /* age it */ + cnat_session_free (session); + + /* + * Note: we may have just freed the bucket's backing + * storage, so check right here... + */ + if (BV (clib_bihash_bucket_is_empty) (b)) + goto doublebreak; + } + } + v++; + } + doublebreak: + ; + } + + /* start again */ + return (0); +} + +static clib_error_t * +cnat_session_init (vlib_main_t * vm) +{ + cnat_main_t *cm = &cnat_main; + BV (clib_bihash_init) (&cnat_session_db, + "CNat Session DB", cm->session_hash_buckets, + cm->session_hash_memory); + BV (clib_bihash_set_kvp_format_fn) (&cnat_session_db, format_cnat_session); + + return (NULL); +} + +VLIB_INIT_FUNCTION (cnat_session_init); + +static clib_error_t * +cnat_timestamp_show (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + cnat_timestamp_t *ts; + clib_rwlock_reader_lock (&cnat_main.ts_lock); + /* *INDENT-OFF* */ + pool_foreach (ts, cnat_timestamps, ({ + vlib_cli_output (vm, "[%d] last_seen:%f lifetime:%u ref:%u", + ts - cnat_timestamps, + ts->last_seen, ts->lifetime, ts->refcnt); + })); + /* *INDENT-ON* */ + clib_rwlock_reader_unlock (&cnat_main.ts_lock); + return (NULL); +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (cnat_timestamp_show_cmd, static) = { + .path = "show cnat timestamp", + .function = cnat_timestamp_show, + .short_help = "show cnat timestamp", + .is_mp_safe = 1, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ |