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_translation.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_translation.c')
-rw-r--r-- | src/plugins/cnat/cnat_translation.c | 432 |
1 files changed, 432 insertions, 0 deletions
diff --git a/src/plugins/cnat/cnat_translation.c b/src/plugins/cnat/cnat_translation.c new file mode 100644 index 00000000000..f680a162ec8 --- /dev/null +++ b/src/plugins/cnat/cnat_translation.c @@ -0,0 +1,432 @@ +/* + * 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/fib/fib_source.h> +#include <vnet/fib/fib_table.h> +#include <vnet/fib/fib_entry_track.h> +#include <vnet/dpo/load_balance.h> +#include <vnet/dpo/drop_dpo.h> + +#include <cnat/cnat_translation.h> +#include <cnat/cnat_session.h> +#include <cnat/cnat_client.h> + +cnat_translation_t *cnat_translation_pool; +clib_bihash_8_8_t cnat_translation_db; + +static fib_node_type_t cnat_translation_fib_node_type; + +vlib_combined_counter_main_t cnat_translation_counters = { + .name = "cnat-translation", + .stat_segment_name = "/net/cnat-translation", +}; + +static void +cnat_tracker_release (cnat_ep_trk_t * trk) +{ + fib_entry_untrack (trk->ct_fei, trk->ct_sibling); +} + +static void +cnat_tracker_track (index_t cti, + const cnat_endpoint_tuple_t * path, cnat_ep_trk_t * trk) +{ + fib_prefix_t pfx; + + ip_address_to_fib_prefix (&path->dst_ep.ce_ip, &pfx); + + clib_memcpy (&trk->ct_ep[VLIB_TX], &path->dst_ep, + sizeof (trk->ct_ep[VLIB_TX])); + clib_memcpy (&trk->ct_ep[VLIB_RX], &path->src_ep, + sizeof (trk->ct_ep[VLIB_RX])); + + trk->ct_fei = fib_entry_track (CNAT_FIB_TABLE, + &pfx, + cnat_translation_fib_node_type, + cti, &trk->ct_sibling); + + fib_entry_contribute_forwarding (trk->ct_fei, + fib_forw_chain_type_from_fib_proto + (pfx.fp_proto), &trk->ct_dpo); +} + +void +cnat_add_translation_to_db (index_t cci, u16 port, ip_protocol_t proto, + index_t cti) +{ + clib_bihash_kv_8_8_t bkey; + u64 key; + + key = (proto << 16) | port; + key = key << 32 | (u32) cci; + + bkey.key = key; + bkey.value = cti; + + clib_bihash_add_del_8_8 (&cnat_translation_db, &bkey, 1); +} + +void +cnat_remove_translation_from_db (index_t cci, u16 port, ip_protocol_t proto) +{ + clib_bihash_kv_8_8_t bkey; + u64 key; + + key = (proto << 16) | port; + key = key << 32 | (u32) cci; + + bkey.key = key; + + clib_bihash_add_del_8_8 (&cnat_translation_db, &bkey, 0); +} + +static void +cnat_translation_stack (cnat_translation_t * ct) +{ + fib_protocol_t fproto; + cnat_ep_trk_t *trk; + dpo_proto_t dproto; + index_t lbi; + + fproto = ip_address_family_to_fib_proto (ct->ct_vip.ce_ip.version); + dproto = fib_proto_to_dpo (fproto); + + lbi = load_balance_create (vec_len (ct->ct_paths), + fib_proto_to_dpo (fproto), IP_FLOW_HASH_DEFAULT); + + vec_foreach (trk, ct->ct_paths) + load_balance_set_bucket (lbi, trk - ct->ct_paths, &trk->ct_dpo); + + dpo_set (&ct->ct_lb, DPO_LOAD_BALANCE, dproto, lbi); + dpo_stack (cnat_client_dpo, dproto, &ct->ct_lb, &ct->ct_lb); +} + +int +cnat_translation_delete (u32 id) +{ + cnat_translation_t *ct; + cnat_ep_trk_t *trk; + + if (pool_is_free_index (cnat_translation_pool, id)) + return (VNET_API_ERROR_NO_SUCH_ENTRY); + + ct = pool_elt_at_index (cnat_translation_pool, id); + + dpo_reset (&ct->ct_lb); + + vec_foreach (trk, ct->ct_paths) cnat_tracker_release (trk); + + cnat_remove_translation_from_db (ct->ct_cci, ct->ct_vip.ce_port, + ct->ct_proto); + cnat_client_translation_deleted (ct->ct_cci); + pool_put (cnat_translation_pool, ct); + + return (0); +} + +u32 +cnat_translation_update (const cnat_endpoint_t * vip, + ip_protocol_t proto, + const cnat_endpoint_tuple_t * paths, u8 flags) +{ + const cnat_endpoint_tuple_t *path; + const cnat_client_t *cc; + cnat_translation_t *ct; + cnat_ep_trk_t *trk; + index_t cci; + + /* do we know of this ep's vip */ + cci = cnat_client_add (&vip->ce_ip, flags); + cc = cnat_client_get (cci); + + ct = cnat_find_translation (cc->parent_cci, vip->ce_port, proto); + + if (NULL == ct) + { + pool_get_zero (cnat_translation_pool, ct); + + clib_memcpy (&ct->ct_vip, vip, sizeof (*vip)); + ct->ct_proto = proto; + ct->ct_cci = cci; + ct->index = ct - cnat_translation_pool; + + cnat_add_translation_to_db (cci, ct->ct_vip.ce_port, ct->ct_proto, + ct->index); + cnat_client_translation_added (cci); + + vlib_validate_combined_counter (&cnat_translation_counters, ct->index); + vlib_zero_combined_counter (&cnat_translation_counters, ct->index); + } + ct->flags = flags; + + vec_foreach (trk, ct->ct_paths) + { + cnat_tracker_release (trk); + } + + vec_reset_length (ct->ct_paths); + + vec_foreach (path, paths) + { + vec_add2 (ct->ct_paths, trk, 1); + + cnat_tracker_track (ct->index, path, trk); + } + + cnat_translation_stack (ct); + + return (ct->index); +} + +void +cnat_translation_walk (cnat_translation_walk_cb_t cb, void *ctx) +{ + u32 api; + + /* *INDENT-OFF* */ + pool_foreach_index(api, cnat_translation_pool, + ({ + if (!cb(api, ctx)) + break; + })); + /* *INDENT-ON* */ +} + +static u8 * +format_cnat_ep_trk (u8 * s, va_list * args) +{ + cnat_ep_trk_t *ck = va_arg (*args, cnat_ep_trk_t *); + u32 indent = va_arg (*args, u32); + + s = format (s, "%U->%U", format_cnat_endpoint, &ck->ct_ep[VLIB_RX], + format_cnat_endpoint, &ck->ct_ep[VLIB_TX]); + s = format (s, "\n%Ufib-entry:%d", format_white_space, indent, ck->ct_fei); + s = format (s, "\n%U%U", + format_white_space, indent, format_dpo_id, &ck->ct_dpo, 6); + + return (s); +} + +u8 * +format_cnat_translation (u8 * s, va_list * args) +{ + cnat_translation_t *ct = va_arg (*args, cnat_translation_t *); + cnat_ep_trk_t *ck; + + s = format (s, "[%d] ", ct->index); + s = format (s, "%U %U", format_cnat_endpoint, &ct->ct_vip, + format_ip_protocol, ct->ct_proto); + + vec_foreach (ck, ct->ct_paths) + s = format (s, "\n%U", format_cnat_ep_trk, ck, 2); + + /* If printing a trace, the LB object might be deleted */ + if (!pool_is_free_index (load_balance_pool, ct->ct_lb.dpoi_index)) + { + s = format (s, "\n via:"); + s = format (s, "\n%U%U", + format_white_space, 2, format_dpo_id, &ct->ct_lb, 2); + } + + return (s); +} + +static clib_error_t * +cnat_translation_show (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + index_t cti; + cnat_translation_t *ct; + + cti = INDEX_INVALID; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "%d", &cti)) + ; + else + return (clib_error_return (0, "unknown input '%U'", + format_unformat_error, input)); + } + + if (INDEX_INVALID == cti) + { + /* *INDENT-OFF* */ + pool_foreach_index(cti, cnat_translation_pool, + ({ + ct = pool_elt_at_index (cnat_translation_pool, cti); + vlib_cli_output(vm, "%U", format_cnat_translation, ct); + })); + /* *INDENT-ON* */ + } + else + { + vlib_cli_output (vm, "Invalid policy ID:%d", cti); + } + + return (NULL); +} + +int +cnat_translation_purge (void) +{ + /* purge all the translations */ + index_t tri, *trp, *trs = NULL; + + /* *INDENT-OFF* */ + pool_foreach_index(tri, cnat_translation_pool, + ({ + vec_add1(trs, tri); + })); + /* *INDENT-ON* */ + + vec_foreach (trp, trs) cnat_translation_delete (*trp); + + ASSERT (0 == pool_elts (cnat_translation_pool)); + + vec_free (trs); + + return (0); +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (cnat_translation_show_cmd_node, static) = { + .path = "show cnat translation", + .function = cnat_translation_show, + .short_help = "show cnat translation <VIP>", + .is_mp_safe = 1, +}; +/* *INDENT-ON* */ + +static fib_node_t * +cnat_translation_get_node (fib_node_index_t index) +{ + cnat_translation_t *ct = cnat_translation_get (index); + return (&(ct->ct_node)); +} + +static cnat_translation_t * +cnat_translation_get_from_node (fib_node_t * node) +{ + return ((cnat_translation_t *) (((char *) node) - + STRUCT_OFFSET_OF (cnat_translation_t, + ct_node))); +} + +static void +cnat_translation_last_lock_gone (fib_node_t * node) +{ + /**/} + +/* + * A back walk has reached this ABF policy + */ +static fib_node_back_walk_rc_t +cnat_translation_back_walk_notify (fib_node_t * node, + fib_node_back_walk_ctx_t * ctx) +{ + /* + * re-stack the fmask on the n-eos of the via + */ + cnat_translation_t *ct = cnat_translation_get_from_node (node); + + cnat_translation_stack (ct); + + return (FIB_NODE_BACK_WALK_CONTINUE); +} + +/* + * The translation's graph node virtual function table + */ +static const fib_node_vft_t cnat_translation_vft = { + .fnv_get = cnat_translation_get_node, + .fnv_last_lock = cnat_translation_last_lock_gone, + .fnv_back_walk = cnat_translation_back_walk_notify, +}; + +static clib_error_t * +cnat_translation_cli_add_del (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + u32 del_index = INDEX_INVALID; + ip_protocol_t proto = IP_PROTOCOL_TCP; + cnat_endpoint_t vip; + u8 flags = CNAT_FLAG_EXCLUSIVE; + cnat_endpoint_tuple_t tmp, *paths = NULL, *path; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "add")) + del_index = INDEX_INVALID; + else if (unformat (input, "del %d", &del_index)) + ; + else if (unformat (input, "proto %U", unformat_ip_protocol, &proto)) + ; + else if (unformat (input, "vip %U", unformat_cnat_ep, &vip)) + flags = CNAT_FLAG_EXCLUSIVE; + else if (unformat (input, "real %U", unformat_cnat_ep, &vip)) + flags = 0; + else if (unformat (input, "to %U", unformat_cnat_ep_tuple, &tmp)) + { + pool_get (paths, path); + clib_memcpy (path, &tmp, sizeof (cnat_endpoint_tuple_t)); + } + else + return (clib_error_return (0, "unknown input '%U'", + format_unformat_error, input)); + } + + if (INDEX_INVALID == del_index) + cnat_translation_update (&vip, proto, paths, flags); + else + cnat_translation_delete (del_index); + + pool_free (paths); + return (NULL); +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (cnat_translation_cli_add_del_command, static) = +{ + .path = "cnat translation", + .short_help = "cnat translation [add|del] proto [TCP|UDP] [vip|real] [ip] [port] [to [ip] [port]->[ip] [port]]", + .function = cnat_translation_cli_add_del, +}; +/* *INDENT-ON* */ + +static clib_error_t * +cnat_translation_init (vlib_main_t * vm) +{ + cnat_main_t *cm = &cnat_main; + cnat_translation_fib_node_type = + fib_node_register_new_type (&cnat_translation_vft); + + clib_bihash_init_8_8 (&cnat_translation_db, "CNat translation DB", + cm->translation_hash_buckets, + cm->translation_hash_memory); + + return (NULL); +} + +VLIB_INIT_FUNCTION (cnat_translation_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ |