From 3fd77f7dea1ac91c5b4c9ede69b992a4e2243153 Mon Sep 17 00:00:00 2001 From: Nathan Skrzypczak Date: Thu, 25 Feb 2021 17:39:03 +0100 Subject: [PATCH] cnat: Prepare extended snat policies Type: refactor Change-Id: I9ca3333274d6f32b6aff57f0fb3d2049c066337a Signed-off-by: Nathan Skrzypczak --- src/plugins/cnat/CMakeLists.txt | 2 +- src/plugins/cnat/cnat.api | 27 +- src/plugins/cnat/cnat_api.c | 66 +++-- src/plugins/cnat/cnat_node_feature.c | 24 +- src/plugins/cnat/cnat_node_snat.c | 25 +- src/plugins/cnat/cnat_snat.c | 377 ------------------------ src/plugins/cnat/cnat_snat.h | 53 ---- src/plugins/cnat/cnat_snat_policy.c | 542 +++++++++++++++++++++++++++++++++++ src/plugins/cnat/cnat_snat_policy.h | 95 ++++++ src/plugins/cnat/cnat_translation.c | 48 +--- src/plugins/cnat/cnat_translation.h | 9 + src/plugins/cnat/cnat_types.c | 3 + src/plugins/cnat/cnat_types.h | 31 +- src/plugins/cnat/test/test_cnat.py | 17 +- 14 files changed, 770 insertions(+), 549 deletions(-) delete mode 100644 src/plugins/cnat/cnat_snat.c delete mode 100644 src/plugins/cnat/cnat_snat.h create mode 100644 src/plugins/cnat/cnat_snat_policy.c create mode 100644 src/plugins/cnat/cnat_snat_policy.h diff --git a/src/plugins/cnat/CMakeLists.txt b/src/plugins/cnat/CMakeLists.txt index 6eb5968e703..cfb55661a78 100644 --- a/src/plugins/cnat/CMakeLists.txt +++ b/src/plugins/cnat/CMakeLists.txt @@ -22,7 +22,7 @@ add_vpp_plugin(cnat cnat_session.c cnat_translation.c cnat_types.c - cnat_snat.c + cnat_snat_policy.c cnat_src_policy.c API_FILES diff --git a/src/plugins/cnat/cnat.api b/src/plugins/cnat/cnat.api index a5df8b1e578..df57ea54896 100644 --- a/src/plugins/cnat/cnat.api +++ b/src/plugins/cnat/cnat.api @@ -159,7 +159,7 @@ define cnat_get_snat_addresses_reply vl_api_interface_index_t sw_if_index; }; -autoreply define cnat_add_del_snat_prefix +autoreply define cnat_snat_policy_add_del_exclude_pfx { u32 client_index; u32 context; @@ -167,9 +167,29 @@ autoreply define cnat_add_del_snat_prefix vl_api_prefix_t prefix; }; -enum cnat_snat_policies:u32 +enum cnat_snat_policy_table:u8 { - CNAT_SNAT_POLICY_NONE = 1, + CNAT_POLICY_INCLUDE_V4 = 0, + CNAT_POLICY_INCLUDE_V6 = 1, +}; + +autoreply define cnat_snat_policy_add_del_if +{ + u32 client_index; + u32 context; + vl_api_interface_index_t sw_if_index; + u8 is_add; + vl_api_cnat_snat_policy_table_t table; +}; + +/* A snat policy controls what traffic is srcNATed */ +enum cnat_snat_policies:u8 +{ + /* No filter applied, srcNAT everything */ + CNAT_POLICY_NONE = 0, + /* Filter by interface list : snat_policy_add_del_if + * and prefix list : snat_policy_add_del_if */ + CNAT_POLICY_IF_PFX = 1, }; autoreply define cnat_set_snat_policy @@ -178,6 +198,7 @@ autoreply define cnat_set_snat_policy u32 context; vl_api_cnat_snat_policies_t policy; }; + /* * fd.io coding-style-patch-verification: ON * diff --git a/src/plugins/cnat/cnat_api.c b/src/plugins/cnat/cnat_api.c index 652e5c09d1a..ea4b3aeaaef 100644 --- a/src/plugins/cnat/cnat_api.c +++ b/src/plugins/cnat/cnat_api.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include @@ -292,14 +292,15 @@ vl_api_cnat_get_snat_addresses_t_handler (vl_api_cnat_get_snat_addresses_t * mp) { vl_api_cnat_get_snat_addresses_reply_t *rmp; + cnat_snat_policy_main_t *cpm = &cnat_snat_policy_main; int rv = 0; - REPLY_MACRO2 (VL_API_CNAT_GET_SNAT_ADDRESSES_REPLY, - ({ - ip6_address_encode (&ip_addr_v6(&cnat_main.snat_ip6.ce_ip), rmp->snat_ip6); - ip4_address_encode (&ip_addr_v4(&cnat_main.snat_ip4.ce_ip), rmp->snat_ip4); - rmp->sw_if_index = clib_host_to_net_u32 (cnat_main.snat_ip6.ce_sw_if_index); - })); + REPLY_MACRO2 ( + VL_API_CNAT_GET_SNAT_ADDRESSES_REPLY, ({ + ip6_address_encode (&ip_addr_v6 (&cpm->snat_ip6.ce_ip), rmp->snat_ip6); + ip4_address_encode (&ip_addr_v4 (&cpm->snat_ip4.ce_ip), rmp->snat_ip4); + rmp->sw_if_index = clib_host_to_net_u32 (cpm->snat_ip6.ce_sw_if_index); + })); } static void @@ -321,39 +322,54 @@ vl_api_cnat_set_snat_addresses_t_handler (vl_api_cnat_set_snat_addresses_t } static void - vl_api_cnat_add_del_snat_prefix_t_handler - (vl_api_cnat_add_del_snat_prefix_t * mp) +vl_api_cnat_set_snat_policy_t_handler (vl_api_cnat_set_snat_policy_t *mp) +{ + vl_api_cnat_set_snat_policy_reply_t *rmp; + int rv = 0; + cnat_snat_policy_type_t policy = (cnat_snat_policy_type_t) mp->policy; + + rv = cnat_set_snat_policy (policy); + + REPLY_MACRO (VL_API_CNAT_SET_SNAT_POLICY_REPLY); +} + +static void +vl_api_cnat_snat_policy_add_del_exclude_pfx_t_handler ( + vl_api_cnat_snat_policy_add_del_exclude_pfx_t *mp) { - vl_api_cnat_add_del_snat_prefix_reply_t *rmp; + vl_api_cnat_snat_policy_add_del_exclude_pfx_reply_t *rmp; ip_prefix_t pfx; int rv; ip_prefix_decode2 (&mp->prefix, &pfx); if (mp->is_add) - rv = cnat_add_snat_prefix (&pfx); + rv = cnat_snat_policy_add_pfx (&pfx); else - rv = cnat_del_snat_prefix (&pfx); + rv = cnat_snat_policy_del_pfx (&pfx); - REPLY_MACRO (VL_API_CNAT_ADD_DEL_SNAT_PREFIX_REPLY); + REPLY_MACRO (VL_API_CNAT_SNAT_POLICY_ADD_DEL_EXCLUDE_PFX_REPLY); } static void -vl_api_cnat_set_snat_policy_t_handler (vl_api_cnat_set_snat_policy_t *mp) +vl_api_cnat_snat_policy_add_del_if_t_handler ( + vl_api_cnat_snat_policy_add_del_if_t *mp) { - vl_api_cnat_set_snat_policy_reply_t *rmp; + vl_api_cnat_snat_policy_add_del_if_reply_t *rmp; + u32 sw_if_index = ntohl (mp->sw_if_index); int rv = 0; - vl_api_cnat_snat_policies_t policy = clib_net_to_host_u32 (mp->policy); - switch (policy) - { - case CNAT_SNAT_POLICY_NONE: - cnat_set_snat_policy (NULL); - break; - default: - rv = 1; - } - REPLY_MACRO (VL_API_CNAT_SET_SNAT_POLICY_REPLY); + VALIDATE_SW_IF_INDEX (mp); + + cnat_snat_interface_map_type_t table = + (cnat_snat_interface_map_type_t) mp->table; + + rv = cnat_snat_policy_add_del_if (sw_if_index, mp->is_add, table); + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_CNAT_SNAT_POLICY_ADD_DEL_IF_REPLY); } + #include static clib_error_t * diff --git a/src/plugins/cnat/cnat_node_feature.c b/src/plugins/cnat/cnat_node_feature.c index c99160cb33e..4585fcb5dd6 100644 --- a/src/plugins/cnat/cnat_node_feature.c +++ b/src/plugins/cnat/cnat_node_feature.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include @@ -217,10 +217,8 @@ VLIB_REGISTER_NODE (cnat_input_feature_ip6_node) = { .type = VLIB_NODE_TYPE_INTERNAL, .n_errors = CNAT_N_ERROR, .error_strings = cnat_error_strings, - .n_next_nodes = CNAT_FEATURE_N_NEXT, - .next_nodes = { - [CNAT_FEATURE_NEXT_DROP] = "error-drop", - }, + .n_next_nodes = IP6_LOOKUP_N_NEXT, + .next_nodes = IP6_LOOKUP_NEXT_NODES, }; VNET_FEATURE_INIT (cnat_in_ip6_feature, static) = { @@ -236,8 +234,7 @@ cnat_output_feature_fn (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_buffer_t *b, cnat_node_ctx_t *ctx, int session_not_found, cnat_session_t *session) { - cnat_main_t *cm = &cnat_main; - cnat_snat_policy_main_t *cms = &cnat_snat_policy_main; + cnat_snat_policy_main_t *cpm = &cnat_snat_policy_main; ip4_header_t *ip4 = NULL; ip_protocol_t iproto; ip6_header_t *ip6 = NULL; @@ -275,32 +272,31 @@ cnat_output_feature_fn (vlib_main_t *vm, vlib_node_runtime_t *node, /* session table hit */ cnat_timestamp_update (session->value.cs_ts_index, ctx->now); } - else if (!cms->snat_policy) + else if (!cpm->snat_policy) goto trace; else { - /* TODO: handle errors? */ - cms->snat_policy (vm, b, session, ctx, &do_snat); + do_snat = cpm->snat_policy (b, session); if (do_snat != 1) goto trace; if (AF_IP4 == ctx->af) { - if (ip_address_is_zero (&cm->snat_ip4.ce_ip)) + if (ip_address_is_zero (&cpm->snat_ip4.ce_ip)) goto trace; ip46_address_set_ip4 (&session->value.cs_ip[VLIB_RX], - &ip_addr_v4 (&cm->snat_ip4.ce_ip)); + &ip_addr_v4 (&cpm->snat_ip4.ce_ip)); ip46_address_set_ip4 (&session->value.cs_ip[VLIB_TX], &ip4->dst_address); } else { - if (ip_address_is_zero (&cm->snat_ip6.ce_ip)) + if (ip_address_is_zero (&cpm->snat_ip6.ce_ip)) goto trace; ip46_address_set_ip6 (&session->value.cs_ip[VLIB_RX], - &ip_addr_v6 (&cm->snat_ip6.ce_ip)); + &ip_addr_v6 (&cpm->snat_ip6.ce_ip)); ip46_address_set_ip6 (&session->value.cs_ip[VLIB_TX], &ip6->dst_address); } diff --git a/src/plugins/cnat/cnat_node_snat.c b/src/plugins/cnat/cnat_node_snat.c index 5cc84c42ccd..9212d67ead6 100644 --- a/src/plugins/cnat/cnat_node_snat.c +++ b/src/plugins/cnat/cnat_node_snat.c @@ -15,7 +15,7 @@ #include #include -#include +#include #include #include @@ -36,7 +36,7 @@ cnat_snat_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_buffer_t *b, cnat_node_ctx_t *ctx, int session_not_found, cnat_session_t *session) { - cnat_main_t *cm = &cnat_main; + cnat_snat_policy_main_t *cpm = &cnat_snat_policy_main; ip4_header_t *ip4 = NULL; ip_protocol_t iproto; ip6_header_t *ip6 = NULL; @@ -45,7 +45,7 @@ cnat_snat_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node, u16 next0; u16 sport; u8 trace_flags = 0; - int rv; + int rv, do_snat; if (AF_IP4 == ctx->af) { @@ -80,12 +80,11 @@ cnat_snat_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node, ip46_address_set_ip4 (&ip46_dst_address, &ip4->dst_address); else ip46_address_set_ip6 (&ip46_dst_address, &ip6->dst_address); - rv = cnat_search_snat_prefix (&ip46_dst_address, ctx->af); - if (!rv) - { - /* Prefix table hit, we shouldn't source NAT */ - goto trace; - } + + do_snat = cpm->snat_policy (b, session); + if (!do_snat) + goto trace; + /* New flow, create the sessions if necessary. session will be a snat session, and rsession will be a dnat session Note: packet going through this path are going to the outside, @@ -93,19 +92,19 @@ cnat_snat_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node, a VIP) */ if (AF_IP4 == ctx->af) { - if (!(cm->snat_ip4.ce_flags & CNAT_EP_FLAG_RESOLVED)) + if (!(cpm->snat_ip4.ce_flags & CNAT_EP_FLAG_RESOLVED)) goto trace; ip46_address_set_ip4 (&session->value.cs_ip[VLIB_RX], - &ip_addr_v4 (&cm->snat_ip4.ce_ip)); + &ip_addr_v4 (&cpm->snat_ip4.ce_ip)); ip46_address_set_ip4 (&session->value.cs_ip[VLIB_TX], &ip4->dst_address); } else { - if (!(cm->snat_ip6.ce_flags & CNAT_EP_FLAG_RESOLVED)) + if (!(cpm->snat_ip6.ce_flags & CNAT_EP_FLAG_RESOLVED)) goto trace; ip46_address_set_ip6 (&session->value.cs_ip[VLIB_RX], - &ip_addr_v6 (&cm->snat_ip6.ce_ip)); + &ip_addr_v6 (&cpm->snat_ip6.ce_ip)); ip46_address_set_ip6 (&session->value.cs_ip[VLIB_TX], &ip6->dst_address); } diff --git a/src/plugins/cnat/cnat_snat.c b/src/plugins/cnat/cnat_snat.c deleted file mode 100644 index b063715d585..00000000000 --- a/src/plugins/cnat/cnat_snat.c +++ /dev/null @@ -1,377 +0,0 @@ -/* - * 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 - -cnat_snat_policy_main_t cnat_snat_policy_main; - -void -cnat_set_snat_policy (cnat_snat_policy_t fp) -{ - cnat_snat_policy_main.snat_policy = fp; -} - -static clib_error_t * -cnat_snat_policy_cmd (vlib_main_t *vm, unformat_input_t *input, - vlib_cli_command_t *cmd) -{ - cnat_snat_policy_t fp = NULL; - - while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (input, "none")) - ; - else - return clib_error_return (0, "unknown input '%U'", - format_unformat_error, input); - } - - cnat_set_snat_policy (fp); - return NULL; -} - -VLIB_CLI_COMMAND (cnat_snat_policy_command, static) = { - .path = "cnat set snat policy", - .short_help = "cnat set snat policy {none,k8s}", - .function = cnat_snat_policy_cmd, -}; - -static clib_error_t * -show_cnat_snat_policy_cmd (vlib_main_t *vm, unformat_input_t *input, - vlib_cli_command_t *cmd) -{ - u8 *s = format (NULL, "snat policy: "); - if (cnat_snat_policy_main.snat_policy == NULL) - s = format (s, "none"); - else - s = format (s, "unknown (%x)", cnat_snat_policy_main.snat_policy); - - vlib_cli_output (vm, (char *) s); - return NULL; -} - -VLIB_CLI_COMMAND (show_cnat_snat_policy_command, static) = { - .path = "show cnat snat policy", - .short_help = "show cnat snat policy", - .function = show_cnat_snat_policy_cmd, -}; - -static void -cnat_compute_prefix_lengths_in_search_order (cnat_snat_pfx_table_t * - table, ip_address_family_t af) -{ - int i; - vec_reset_length (table->meta[af].prefix_lengths_in_search_order); - /* Note: bitmap reversed so this is in fact a longest prefix match */ - clib_bitmap_foreach (i, table->meta[af].non_empty_dst_address_length_bitmap) - { - int dst_address_length = 128 - i; - vec_add1 (table->meta[af].prefix_lengths_in_search_order, dst_address_length); - } -} - -int -cnat_add_snat_prefix (ip_prefix_t * pfx) -{ - /* All packets destined to this prefix won't be source-NAT-ed */ - cnat_snat_pfx_table_t *table = &cnat_main.snat_pfx_table; - clib_bihash_kv_24_8_t kv; - ip6_address_t *mask; - u64 af = ip_prefix_version (pfx);; - - mask = &table->ip_masks[pfx->len]; - if (AF_IP4 == af) - { - kv.key[0] = (u64) ip_prefix_v4 (pfx).as_u32 & mask->as_u64[0]; - kv.key[1] = 0; - } - else - { - kv.key[0] = ip_prefix_v6 (pfx).as_u64[0] & mask->as_u64[0]; - kv.key[1] = ip_prefix_v6 (pfx).as_u64[1] & mask->as_u64[1]; - } - kv.key[2] = ((u64) af << 32) | pfx->len; - clib_bihash_add_del_24_8 (&table->ip_hash, &kv, 1 /* is_add */ ); - - table->meta[af].dst_address_length_refcounts[pfx->len]++; - table->meta[af].non_empty_dst_address_length_bitmap = - clib_bitmap_set (table->meta[af].non_empty_dst_address_length_bitmap, - 128 - pfx->len, 1); - cnat_compute_prefix_lengths_in_search_order (table, af); - return 0; -} - -int -cnat_del_snat_prefix (ip_prefix_t * pfx) -{ - cnat_snat_pfx_table_t *table = &cnat_main.snat_pfx_table; - clib_bihash_kv_24_8_t kv, val; - ip6_address_t *mask; - u64 af = ip_prefix_version (pfx);; - - mask = &table->ip_masks[pfx->len]; - if (AF_IP4 == af) - { - kv.key[0] = (u64) ip_prefix_v4 (pfx).as_u32 & mask->as_u64[0]; - kv.key[1] = 0; - } - else - { - kv.key[0] = ip_prefix_v6 (pfx).as_u64[0] & mask->as_u64[0]; - kv.key[1] = ip_prefix_v6 (pfx).as_u64[1] & mask->as_u64[1]; - } - kv.key[2] = ((u64) af << 32) | pfx->len; - - if (clib_bihash_search_24_8 (&table->ip_hash, &kv, &val)) - { - return 1; - } - clib_bihash_add_del_24_8 (&table->ip_hash, &kv, 0 /* is_add */ ); - /* refcount accounting */ - ASSERT (table->meta[af].dst_address_length_refcounts[pfx->len] > 0); - if (--table->meta[af].dst_address_length_refcounts[pfx->len] == 0) - { - table->meta[af].non_empty_dst_address_length_bitmap = - clib_bitmap_set (table->meta[af].non_empty_dst_address_length_bitmap, - 128 - pfx->len, 0); - cnat_compute_prefix_lengths_in_search_order (table, af); - } - 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) -{ - clib_bihash_kv_24_8_t *kv = va_arg (*args, clib_bihash_kv_24_8_t *); - CLIB_UNUSED (int verbose) = va_arg (*args, int); - u32 af = kv->key[2] >> 32; - u32 len = kv->key[2] & 0xffffffff; - if (AF_IP4 == af) - s = format (s, "%U/%d", format_ip4_address, &kv->key[0], len); - else - s = format (s, "%U/%d", format_ip6_address, &kv->key[0], len); - return (s); -} - -void -cnat_set_snat (ip4_address_t * ip4, ip6_address_t * ip6, u32 sw_if_index) -{ - cnat_lazy_init (); - - cnat_translation_unwatch_addr (INDEX_INVALID, CNAT_RESOLV_ADDR_SNAT); - - ip_address_set (&cnat_main.snat_ip4.ce_ip, ip4, AF_IP4); - ip_address_set (&cnat_main.snat_ip6.ce_ip, ip6, AF_IP6); - cnat_main.snat_ip4.ce_sw_if_index = sw_if_index; - cnat_main.snat_ip6.ce_sw_if_index = sw_if_index; - - cnat_resolve_ep (&cnat_main.snat_ip4); - cnat_resolve_ep (&cnat_main.snat_ip6); - cnat_translation_watch_addr (INDEX_INVALID, 0, &cnat_main.snat_ip4, - CNAT_RESOLV_ADDR_SNAT); - cnat_translation_watch_addr (INDEX_INVALID, 0, &cnat_main.snat_ip6, - CNAT_RESOLV_ADDR_SNAT); -} - -static clib_error_t * -cnat_set_snat_cli (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - unformat_input_t _line_input, *line_input = &_line_input; - vnet_main_t *vnm = vnet_get_main (); - ip4_address_t ip4 = { {0} }; - ip6_address_t ip6 = { {0} }; - clib_error_t *e = 0; - u32 sw_if_index = INDEX_INVALID; - - cnat_lazy_init (); - - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat_user (line_input, unformat_ip4_address, &ip4)) - ; - else if (unformat_user (line_input, unformat_ip6_address, &ip6)) - ; - else if (unformat_user (line_input, unformat_vnet_sw_interface, - vnm, &sw_if_index)) - ; - else - { - e = clib_error_return (0, "unknown input '%U'", - format_unformat_error, input); - goto done; - } - } - - cnat_set_snat (&ip4, &ip6, sw_if_index); - -done: - unformat_free (line_input); - - return (e); -} - -VLIB_CLI_COMMAND (cnat_set_snat_command, static) = -{ - .path = "cnat snat with", - .short_help = "cnat snat with [][][sw_if_index]", - .function = cnat_set_snat_cli, -}; - -static clib_error_t * -cnat_snat_exclude (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - ip_prefix_t pfx; - u8 is_add = 1; - int rv; - - while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (input, "%U", unformat_ip_prefix, &pfx)) - ; - else if (unformat (input, "del")) - is_add = 0; - else - return (clib_error_return (0, "unknown input '%U'", - format_unformat_error, input)); - } - - if (is_add) - rv = cnat_add_snat_prefix (&pfx); - else - rv = cnat_del_snat_prefix (&pfx); - - if (rv) - { - return (clib_error_return (0, "error %d", rv, input)); - } - - return (NULL); -} - -VLIB_CLI_COMMAND (cnat_snat_exclude_command, static) = -{ - .path = "cnat snat exclude", - .short_help = "cnat snat exclude [ip]", - .function = cnat_snat_exclude, -}; - -static clib_error_t * -cnat_show_snat (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - cnat_snat_pfx_table_t *table = &cnat_main.snat_pfx_table; - vlib_cli_output (vm, "Source NAT\nip4: %U\nip6: %U\n", - format_cnat_endpoint, &cnat_main.snat_ip4, - format_cnat_endpoint, &cnat_main.snat_ip6); - vlib_cli_output (vm, "Prefixes:\n%U\n", - format_bihash_24_8, &table->ip_hash, 1); - return (NULL); -} - -VLIB_CLI_COMMAND (cnat_show_snat_command, static) = -{ - .path = "show cnat snat", - .short_help = "show cnat snat", - .function = cnat_show_snat, -}; - -static clib_error_t * -cnat_snat_init (vlib_main_t * vm) -{ - cnat_main_t *cm = &cnat_main; - cnat_snat_pfx_table_t *table = &cm->snat_pfx_table; - int i; - for (i = 0; i < ARRAY_LEN (table->ip_masks); i++) - { - u32 j, i0, i1; - - i0 = i / 32; - i1 = i % 32; - - for (j = 0; j < i0; j++) - table->ip_masks[i].as_u32[j] = ~0; - - if (i1) - table->ip_masks[i].as_u32[i0] = - clib_host_to_net_u32 (pow2_mask (i1) << (32 - i1)); - } - clib_bihash_init_24_8 (&table->ip_hash, "snat prefixes", - cm->snat_hash_buckets, cm->snat_hash_memory); - clib_bihash_set_kvp_format_fn_24_8 (&table->ip_hash, - format_cnat_snat_prefix); - - return (NULL); -} - -VLIB_INIT_FUNCTION (cnat_snat_init); - - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/cnat/cnat_snat.h b/src/plugins/cnat/cnat_snat.h deleted file mode 100644 index e34cf03bd92..00000000000 --- a/src/plugins/cnat/cnat_snat.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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_SNAT_H__ -#define __CNAT_SNAT_H__ - -#include -#include - -/* function to use to decide whether to snat connections in the output - feature */ -typedef void (*cnat_snat_policy_t) (vlib_main_t *vm, vlib_buffer_t *b, - cnat_session_t *session, - cnat_node_ctx_t *ctx, u8 *do_snat); - -typedef struct cnat_snat_policy_main_t_ -{ - /* SNAT policy for the output feature node */ - cnat_snat_policy_t snat_policy; - -} cnat_snat_policy_main_t; - -extern cnat_snat_policy_main_t cnat_snat_policy_main; - -extern void cnat_set_snat (ip4_address_t * ip4, ip6_address_t * ip6, - u32 sw_if_index); -extern int cnat_add_snat_prefix (ip_prefix_t * pfx); -extern int cnat_del_snat_prefix (ip_prefix_t * pfx); -extern void cnat_set_snat_policy (cnat_snat_policy_t fp); - -int cnat_search_snat_prefix (ip46_address_t * addr, ip_address_family_t af); - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ - -#endif diff --git a/src/plugins/cnat/cnat_snat_policy.c b/src/plugins/cnat/cnat_snat_policy.c new file mode 100644 index 00000000000..83a79c0544d --- /dev/null +++ b/src/plugins/cnat/cnat_snat_policy.c @@ -0,0 +1,542 @@ +/* + * 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 + +cnat_snat_policy_main_t cnat_snat_policy_main; + +uword +unformat_cnat_snat_interface_map_type (unformat_input_t *input, va_list *args) +{ + u8 *a = va_arg (*args, u8 *); + if (unformat (input, "include-v4")) + *a = CNAT_SNAT_IF_MAP_INCLUDE_V4; + else if (unformat (input, "include-v6")) + *a = CNAT_SNAT_IF_MAP_INCLUDE_V6; + else + return 0; + return 1; +} + +u8 * +format_cnat_snat_interface_map_type (u8 *s, va_list *args) +{ + cnat_snat_interface_map_type_t mtype = va_arg (*args, int); + switch (mtype) + { + case CNAT_SNAT_IF_MAP_INCLUDE_V4: + s = format (s, "Included v4"); + break; + case CNAT_SNAT_IF_MAP_INCLUDE_V6: + s = format (s, "Included v6"); + break; + default: + s = format (s, "(unknown)"); + break; + } + return (s); +} + +u8 * +format_cnat_snat_prefix (u8 *s, va_list *args) +{ + clib_bihash_kv_24_8_t *kv = va_arg (*args, clib_bihash_kv_24_8_t *); + CLIB_UNUSED (int verbose) = va_arg (*args, int); + u32 af = kv->key[2] >> 32; + u32 len = kv->key[2] & 0xffffffff; + if (AF_IP4 == af) + s = format (s, "%U/%d", format_ip4_address, &kv->key[0], len); + else + s = format (s, "%U/%d", format_ip6_address, &kv->key[0], len); + return (s); +} + +static void +cnat_compute_prefix_lengths_in_search_order ( + cnat_snat_exclude_pfx_table_t *table, ip_address_family_t af) +{ + int i; + vec_reset_length (table->meta[af].prefix_lengths_in_search_order); + /* Note: bitmap reversed so this is in fact a longest prefix match */ + clib_bitmap_foreach (i, table->meta[af].non_empty_dst_address_length_bitmap) + { + int dst_address_length = 128 - i; + vec_add1 (table->meta[af].prefix_lengths_in_search_order, + dst_address_length); + } +} + +int +cnat_snat_policy_add_del_if (u32 sw_if_index, u8 is_add, + cnat_snat_interface_map_type_t table) +{ + cnat_snat_policy_main_t *cpm = &cnat_snat_policy_main; + + if (table > ARRAY_LEN (cpm->interface_maps)) + return VNET_API_ERROR_INVALID_VALUE; + + clib_bitmap_t **map = &cpm->interface_maps[table]; + + *map = clib_bitmap_set (*map, sw_if_index, is_add); + return 0; +} + +static clib_error_t * +cnat_snat_policy_add_del_if_command_fn (vlib_main_t *vm, + unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + vnet_main_t *vnm = vnet_get_main (); + int is_add = 1; + u32 sw_if_index = ~0; + u32 table; + int rv; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "del")) + is_add = 0; + else if (unformat (input, "table %U", + unformat_cnat_snat_interface_map_type, &table)) + ; + else if (unformat (input, "%U", unformat_vnet_sw_interface, vnm, + &sw_if_index)) + ; + else + return clib_error_return (0, "unknown input '%U'", + format_unformat_error, input); + } + + if (sw_if_index == ~0) + return clib_error_return (0, "Interface not specified"); + + rv = cnat_snat_policy_add_del_if (sw_if_index, is_add, table); + + if (rv) + return clib_error_return (0, "Error %d", rv); + + return NULL; +} + +VLIB_CLI_COMMAND (cnat_snat_policy_add_del_if_command, static) = { + .path = "set cnat snat-policy if", + .short_help = "set cnat snat-policy if [del]" + "[table [include-v4 include-v6]] [interface]", + .function = cnat_snat_policy_add_del_if_command_fn, +}; + +int +cnat_snat_policy_add_pfx (ip_prefix_t *pfx) +{ + /* All packets destined to this prefix won't be source-NAT-ed */ + cnat_snat_exclude_pfx_table_t *table = &cnat_snat_policy_main.excluded_pfx; + clib_bihash_kv_24_8_t kv; + ip6_address_t *mask; + u64 af = ip_prefix_version (pfx); + ; + + mask = &table->ip_masks[pfx->len]; + if (AF_IP4 == af) + { + kv.key[0] = (u64) ip_prefix_v4 (pfx).as_u32 & mask->as_u64[0]; + kv.key[1] = 0; + } + else + { + kv.key[0] = ip_prefix_v6 (pfx).as_u64[0] & mask->as_u64[0]; + kv.key[1] = ip_prefix_v6 (pfx).as_u64[1] & mask->as_u64[1]; + } + kv.key[2] = ((u64) af << 32) | pfx->len; + clib_bihash_add_del_24_8 (&table->ip_hash, &kv, 1 /* is_add */); + + table->meta[af].dst_address_length_refcounts[pfx->len]++; + table->meta[af].non_empty_dst_address_length_bitmap = clib_bitmap_set ( + table->meta[af].non_empty_dst_address_length_bitmap, 128 - pfx->len, 1); + cnat_compute_prefix_lengths_in_search_order (table, af); + return 0; +} + +int +cnat_snat_policy_del_pfx (ip_prefix_t *pfx) +{ + cnat_snat_exclude_pfx_table_t *table = &cnat_snat_policy_main.excluded_pfx; + clib_bihash_kv_24_8_t kv, val; + ip6_address_t *mask; + u64 af = ip_prefix_version (pfx); + ; + + mask = &table->ip_masks[pfx->len]; + if (AF_IP4 == af) + { + kv.key[0] = (u64) ip_prefix_v4 (pfx).as_u32 & mask->as_u64[0]; + kv.key[1] = 0; + } + else + { + kv.key[0] = ip_prefix_v6 (pfx).as_u64[0] & mask->as_u64[0]; + kv.key[1] = ip_prefix_v6 (pfx).as_u64[1] & mask->as_u64[1]; + } + kv.key[2] = ((u64) af << 32) | pfx->len; + + if (clib_bihash_search_24_8 (&table->ip_hash, &kv, &val)) + { + return 1; + } + clib_bihash_add_del_24_8 (&table->ip_hash, &kv, 0 /* is_add */); + /* refcount accounting */ + ASSERT (table->meta[af].dst_address_length_refcounts[pfx->len] > 0); + if (--table->meta[af].dst_address_length_refcounts[pfx->len] == 0) + { + table->meta[af].non_empty_dst_address_length_bitmap = + clib_bitmap_set (table->meta[af].non_empty_dst_address_length_bitmap, + 128 - pfx->len, 0); + cnat_compute_prefix_lengths_in_search_order (table, af); + } + 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_exclude_pfx_table_t *table = &cnat_snat_policy_main.excluded_pfx; + 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; +} + +static_always_inline int +cnat_snat_policy_interface_enabled (u32 sw_if_index, ip_address_family_t af) +{ + cnat_snat_policy_main_t *cpm = &cnat_snat_policy_main; + return clib_bitmap_get (cpm->interface_maps[af], sw_if_index); +} + +int +cnat_snat_policy_none (vlib_buffer_t *b, cnat_session_t *session) +{ + /* srcNAT everything by default */ + return 1; +} + +int +cnat_snat_policy_if_pfx (vlib_buffer_t *b, cnat_session_t *session) +{ + ip46_address_t *dst_addr = &session->key.cs_ip[VLIB_TX]; + u32 in_if = vnet_buffer (b)->sw_if_index[VLIB_RX]; + ip_address_family_t af = session->key.cs_af; + + /* source nat for outgoing connections */ + if (cnat_snat_policy_interface_enabled (in_if, af)) + if (cnat_search_snat_prefix (dst_addr, af)) + /* Destination is not in the prefixes that don't require snat */ + return 1; + return 0; +} + +void +cnat_set_snat (ip4_address_t *ip4, ip6_address_t *ip6, u32 sw_if_index) +{ + cnat_snat_policy_main_t *cpm = &cnat_snat_policy_main; + + cnat_lazy_init (); + + cnat_translation_unwatch_addr (INDEX_INVALID, CNAT_RESOLV_ADDR_SNAT); + + ip_address_set (&cpm->snat_ip4.ce_ip, ip4, AF_IP4); + ip_address_set (&cpm->snat_ip6.ce_ip, ip6, AF_IP6); + cpm->snat_ip4.ce_sw_if_index = sw_if_index; + cpm->snat_ip6.ce_sw_if_index = sw_if_index; + + cnat_resolve_ep (&cpm->snat_ip4); + cnat_resolve_ep (&cpm->snat_ip6); + cnat_translation_watch_addr (INDEX_INVALID, 0, &cpm->snat_ip4, + CNAT_RESOLV_ADDR_SNAT); + cnat_translation_watch_addr (INDEX_INVALID, 0, &cpm->snat_ip6, + CNAT_RESOLV_ADDR_SNAT); +} + +static clib_error_t * +cnat_set_snat_cli (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + vnet_main_t *vnm = vnet_get_main (); + ip4_address_t ip4 = { { 0 } }; + ip6_address_t ip6 = { { 0 } }; + clib_error_t *e = 0; + u32 sw_if_index = INDEX_INVALID; + + cnat_lazy_init (); + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat_user (line_input, unformat_ip4_address, &ip4)) + ; + else if (unformat_user (line_input, unformat_ip6_address, &ip6)) + ; + else if (unformat_user (line_input, unformat_vnet_sw_interface, vnm, + &sw_if_index)) + ; + else + { + e = clib_error_return (0, "unknown input '%U'", + format_unformat_error, input); + goto done; + } + } + + cnat_set_snat (&ip4, &ip6, sw_if_index); + +done: + unformat_free (line_input); + + return (e); +} + +VLIB_CLI_COMMAND (cnat_set_snat_command, static) = { + .path = "set cnat snat-policy addr", + .short_help = + "set cnat snat-policy addr [][][sw_if_index]", + .function = cnat_set_snat_cli, +}; + +static clib_error_t * +cnat_snat_policy_add_del_pfx_command_fn (vlib_main_t *vm, + unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + ip_prefix_t pfx; + u8 is_add = 1; + int rv; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "%U", unformat_ip_prefix, &pfx)) + ; + else if (unformat (input, "del")) + is_add = 0; + else + return (clib_error_return (0, "unknown input '%U'", + format_unformat_error, input)); + } + + if (is_add) + rv = cnat_snat_policy_add_pfx (&pfx); + else + rv = cnat_snat_policy_del_pfx (&pfx); + + if (rv) + return (clib_error_return (0, "error %d", rv, input)); + + return (NULL); +} + +VLIB_CLI_COMMAND (cnat_snat_policy_add_del_pfx_command, static) = { + .path = "set cnat snat-policy prefix", + .short_help = "set cnat snat-policy prefix [del] [prefix]", + .function = cnat_snat_policy_add_del_pfx_command_fn, +}; + +static clib_error_t * +cnat_show_snat (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + cnat_snat_exclude_pfx_table_t *excluded_pfx = + &cnat_snat_policy_main.excluded_pfx; + cnat_snat_policy_main_t *cpm = &cnat_snat_policy_main; + vnet_main_t *vnm = vnet_get_main (); + u32 sw_if_index; + + vlib_cli_output (vm, "Source NAT\n ip4: %U\n ip6: %U\n\n", + format_cnat_endpoint, &cpm->snat_ip4, format_cnat_endpoint, + &cpm->snat_ip6); + vlib_cli_output (vm, "Excluded prefixes:\n %U\n", format_bihash_24_8, + &excluded_pfx->ip_hash, 1); + + for (int i = 0; i < CNAT_N_SNAT_IF_MAP; i++) + { + vlib_cli_output (vm, "\n%U interfaces:\n", + format_cnat_snat_interface_map_type, i); + clib_bitmap_foreach (sw_if_index, cpm->interface_maps[i]) + vlib_cli_output (vm, " %U\n", format_vnet_sw_if_index_name, vnm, + sw_if_index); + } + + return (NULL); +} + +VLIB_CLI_COMMAND (cnat_show_snat_command, static) = { + .path = "show cnat snat-policy", + .short_help = "show cnat snat-policy", + .function = cnat_show_snat, +}; + +int +cnat_set_snat_policy (cnat_snat_policy_type_t policy) +{ + cnat_snat_policy_main_t *cpm = &cnat_snat_policy_main; + switch (policy) + { + case CNAT_SNAT_POLICY_NONE: + cpm->snat_policy = cnat_snat_policy_none; + break; + case CNAT_SNAT_POLICY_IF_PFX: + cpm->snat_policy = cnat_snat_policy_if_pfx; + break; + default: + return 1; + } + return 0; +} + +static clib_error_t * +cnat_snat_policy_set_cmd_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + cnat_snat_policy_type_t policy = CNAT_SNAT_POLICY_NONE; + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "none")) + ; + else if (unformat (input, "if-pfx")) + policy = CNAT_SNAT_POLICY_IF_PFX; + else + return clib_error_return (0, "unknown input '%U'", + format_unformat_error, input); + } + + cnat_set_snat_policy (policy); + return NULL; +} + +VLIB_CLI_COMMAND (cnat_snat_policy_set_cmd, static) = { + .path = "set cnat snat-policy", + .short_help = "set cnat snat-policy [none][if-pfx]", + .function = cnat_snat_policy_set_cmd_fn, +}; + +static void +cnat_if_addr_add_del_snat_cb (addr_resolution_t *ar, ip_address_t *address, + u8 is_del) +{ + cnat_snat_policy_main_t *cpm = &cnat_snat_policy_main; + cnat_endpoint_t *ep; + + ep = AF_IP4 == ar->af ? &cpm->snat_ip4 : &cpm->snat_ip6; + + if (!is_del && ep->ce_flags & CNAT_EP_FLAG_RESOLVED) + return; + + if (is_del) + { + ep->ce_flags &= ~CNAT_EP_FLAG_RESOLVED; + /* Are there remaining addresses ? */ + if (0 == cnat_resolve_addr (ar->sw_if_index, ar->af, address)) + is_del = 0; + } + + if (!is_del) + { + ip_address_copy (&ep->ce_ip, address); + ep->ce_flags |= CNAT_EP_FLAG_RESOLVED; + } +} + +static clib_error_t * +cnat_snat_init (vlib_main_t *vm) +{ + cnat_snat_policy_main_t *cpm = &cnat_snat_policy_main; + cnat_main_t *cm = &cnat_main; + cnat_snat_exclude_pfx_table_t *excluded_pfx = &cpm->excluded_pfx; + + int i; + for (i = 0; i < ARRAY_LEN (excluded_pfx->ip_masks); i++) + { + u32 j, i0, i1; + + i0 = i / 32; + i1 = i % 32; + + for (j = 0; j < i0; j++) + excluded_pfx->ip_masks[i].as_u32[j] = ~0; + + if (i1) + excluded_pfx->ip_masks[i].as_u32[i0] = + clib_host_to_net_u32 (pow2_mask (i1) << (32 - i1)); + } + clib_bihash_init_24_8 (&excluded_pfx->ip_hash, "snat prefixes", + cm->snat_hash_buckets, cm->snat_hash_memory); + clib_bihash_set_kvp_format_fn_24_8 (&excluded_pfx->ip_hash, + format_cnat_snat_prefix); + + for (int i = 0; i < CNAT_N_SNAT_IF_MAP; i++) + clib_bitmap_validate (cpm->interface_maps[i], cm->snat_if_map_length); + + cnat_translation_register_addr_add_cb (CNAT_RESOLV_ADDR_SNAT, + cnat_if_addr_add_del_snat_cb); + + cpm->snat_policy = cnat_snat_policy_none; + + return (NULL); +} + +VLIB_INIT_FUNCTION (cnat_snat_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/cnat/cnat_snat_policy.h b/src/plugins/cnat/cnat_snat_policy.h new file mode 100644 index 00000000000..ff30d19c884 --- /dev/null +++ b/src/plugins/cnat/cnat_snat_policy.h @@ -0,0 +1,95 @@ +/* + * 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_SNAT_H__ +#define __CNAT_SNAT_H__ + +#include +#include + +/* function to use to decide whether to snat connections in the output + * feature. Returns 1 if we should source NAT */ +typedef int (*cnat_snat_policy_t) (vlib_buffer_t *b, cnat_session_t *session); + +typedef struct cnat_snat_pfx_table_meta_t_ +{ + u32 dst_address_length_refcounts[129]; + u16 *prefix_lengths_in_search_order; + uword *non_empty_dst_address_length_bitmap; +} cnat_snat_pfx_table_meta_t; + +typedef struct cnat_snat_exclude_pfx_table_t_ +{ + /* Stores (ip family, prefix & mask) */ + clib_bihash_24_8_t ip_hash; + /* family dependant cache */ + cnat_snat_pfx_table_meta_t meta[2]; + /* Precomputed ip masks (ip4 & ip6) */ + ip6_address_t ip_masks[129]; +} cnat_snat_exclude_pfx_table_t; + +typedef enum cnat_snat_interface_map_type_t_ +{ + CNAT_SNAT_IF_MAP_INCLUDE_V4 = AF_IP4, + CNAT_SNAT_IF_MAP_INCLUDE_V6 = AF_IP6, + CNAT_N_SNAT_IF_MAP, +} cnat_snat_interface_map_type_t; + +typedef enum cnat_snat_policy_type_t_ +{ + CNAT_SNAT_POLICY_NONE = 0, + CNAT_SNAT_POLICY_IF_PFX = 1, +} cnat_snat_policy_type_t; + +typedef struct cnat_snat_policy_main_t_ +{ + /* Longest prefix Match table for source NATing */ + cnat_snat_exclude_pfx_table_t excluded_pfx; + + /* interface maps including or excluding sw_if_indexes */ + clib_bitmap_t *interface_maps[CNAT_N_SNAT_IF_MAP]; + + /* SNAT policy for the output feature node */ + cnat_snat_policy_t snat_policy; + + /* Ip4 Address to use for source NATing */ + cnat_endpoint_t snat_ip4; + + /* Ip6 Address to use for source NATing */ + cnat_endpoint_t snat_ip6; + +} cnat_snat_policy_main_t; + +extern cnat_snat_policy_main_t cnat_snat_policy_main; + +extern void cnat_set_snat (ip4_address_t *ip4, ip6_address_t *ip6, + u32 sw_if_index); +extern int cnat_snat_policy_add_pfx (ip_prefix_t *pfx); +extern int cnat_snat_policy_del_pfx (ip_prefix_t *pfx); +extern int cnat_set_snat_policy (cnat_snat_policy_type_t policy); +extern int cnat_snat_policy_add_del_if (u32 sw_if_index, u8 is_add, + cnat_snat_interface_map_type_t table); + +int cnat_search_snat_prefix (ip46_address_t *addr, ip_address_family_t af); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ + +#endif diff --git a/src/plugins/cnat/cnat_translation.c b/src/plugins/cnat/cnat_translation.c index bd456c82fef..049809a8684 100644 --- a/src/plugins/cnat/cnat_translation.c +++ b/src/plugins/cnat/cnat_translation.c @@ -26,9 +26,6 @@ cnat_translation_t *cnat_translation_pool; clib_bihash_8_8_t cnat_translation_db; addr_resolution_t *tr_resolutions; - -typedef void (*cnat_if_addr_add_cb_t) (addr_resolution_t * ar, - ip_address_t * address, u8 is_del); cnat_if_addr_add_cb_t *cnat_if_addr_add_cbs; static fib_node_type_t cnat_translation_fib_node_type; @@ -774,32 +771,6 @@ cnat_if_addr_add_del_backend_cb (addr_resolution_t * ar, ct->flags |= CNAT_TRANSLATION_STACKED; } -static void -cnat_if_addr_add_del_snat_cb (addr_resolution_t * ar, ip_address_t * address, - u8 is_del) -{ - cnat_endpoint_t *ep; - ep = AF_IP4 == ar->af ? &cnat_main.snat_ip4 : &cnat_main.snat_ip6; - - if (!is_del && ep->ce_flags & CNAT_EP_FLAG_RESOLVED) - return; - - if (is_del) - { - ep->ce_flags &= ~CNAT_EP_FLAG_RESOLVED; - /* Are there remaining addresses ? */ - if (0 == cnat_resolve_addr (ar->sw_if_index, ar->af, address)) - is_del = 0; - } - - if (!is_del) - { - ip_address_copy (&ep->ce_ip, address); - ep->ce_flags |= CNAT_EP_FLAG_RESOLVED; - } - -} - static void cnat_if_addr_add_del_callback (u32 sw_if_index, ip_address_t * address, u8 is_del) @@ -839,6 +810,14 @@ cnat_ip4_if_addr_add_del_callback (struct ip4_main_t *im, cnat_if_addr_add_del_callback (sw_if_index, &addr, is_del); } +void +cnat_translation_register_addr_add_cb (cnat_addr_resol_type_t typ, + cnat_if_addr_add_cb_t fn) +{ + vec_validate (cnat_if_addr_add_cbs, CNAT_ADDR_N_RESOLUTIONS); + cnat_if_addr_add_cbs[typ] = fn; +} + static clib_error_t * cnat_translation_init (vlib_main_t * vm) { @@ -860,12 +839,11 @@ cnat_translation_init (vlib_main_t * vm) cb6.function = cnat_ip6_if_addr_add_del_callback; vec_add1 (i6m->add_del_interface_address_callbacks, cb6); - vec_validate (cnat_if_addr_add_cbs, CNAT_ADDR_N_RESOLUTIONS); - cnat_if_addr_add_cbs[CNAT_RESOLV_ADDR_BACKEND] = - cnat_if_addr_add_del_backend_cb; - cnat_if_addr_add_cbs[CNAT_RESOLV_ADDR_SNAT] = cnat_if_addr_add_del_snat_cb; - cnat_if_addr_add_cbs[CNAT_RESOLV_ADDR_TRANSLATION] = - cnat_if_addr_add_del_translation_cb; + cnat_translation_register_addr_add_cb (CNAT_RESOLV_ADDR_BACKEND, + cnat_if_addr_add_del_backend_cb); + cnat_translation_register_addr_add_cb (CNAT_RESOLV_ADDR_TRANSLATION, + cnat_if_addr_add_del_translation_cb); + return (NULL); } diff --git a/src/plugins/cnat/cnat_translation.h b/src/plugins/cnat/cnat_translation.h index af0b94867af..3b6e694a845 100644 --- a/src/plugins/cnat/cnat_translation.h +++ b/src/plugins/cnat/cnat_translation.h @@ -231,6 +231,15 @@ extern void cnat_translation_watch_addr (index_t cti, u64 opaque, extern void cnat_translation_unwatch_addr (u32 cti, cnat_addr_resol_type_t type); +/** + * Register a call back for endpoint->address resolution + */ +typedef void (*cnat_if_addr_add_cb_t) (addr_resolution_t *ar, + ip_address_t *address, u8 is_del); + +extern void cnat_translation_register_addr_add_cb (cnat_addr_resol_type_t typ, + cnat_if_addr_add_cb_t fn); + /* * Data plane functions */ diff --git a/src/plugins/cnat/cnat_types.c b/src/plugins/cnat/cnat_types.c index 74c1c24389f..837f40082c3 100644 --- a/src/plugins/cnat/cnat_types.c +++ b/src/plugins/cnat/cnat_types.c @@ -181,6 +181,7 @@ cnat_config (vlib_main_t * vm, unformat_input_t * input) cm->translation_hash_buckets = CNAT_DEFAULT_TRANSLATION_BUCKETS; cm->snat_hash_memory = CNAT_DEFAULT_SNAT_MEMORY; cm->snat_hash_buckets = CNAT_DEFAULT_SNAT_BUCKETS; + cm->snat_if_map_length = CNAT_DEFAULT_SNAT_IF_MAP_LEN; cm->scanner_timeout = CNAT_DEFAULT_SCANNER_TIMEOUT; cm->session_max_age = CNAT_DEFAULT_SESSION_MAX_AGE; cm->tcp_max_age = CNAT_DEFAULT_TCP_MAX_AGE; @@ -204,6 +205,8 @@ cnat_config (vlib_main_t * vm, unformat_input_t * input) ; else if (unformat (input, "snat-db-buckets %u", &cm->snat_hash_buckets)) ; + else if (unformat (input, "snat-if-map-len %u", &cm->snat_if_map_length)) + ; else if (unformat (input, "snat-db-memory %U", unformat_memory_size, &cm->snat_hash_memory)) ; diff --git a/src/plugins/cnat/cnat_types.h b/src/plugins/cnat/cnat_types.h index f0911d22d75..47e34e1f232 100644 --- a/src/plugins/cnat/cnat_types.h +++ b/src/plugins/cnat/cnat_types.h @@ -37,6 +37,7 @@ #define CNAT_DEFAULT_SESSION_BUCKETS 1024 #define CNAT_DEFAULT_TRANSLATION_BUCKETS 1024 #define CNAT_DEFAULT_SNAT_BUCKETS 1024 +#define CNAT_DEFAULT_SNAT_IF_MAP_LEN 4096 #define CNAT_DEFAULT_SESSION_MEMORY (1 << 20) #define CNAT_DEFAULT_TRANSLATION_MEMORY (256 << 10) @@ -81,23 +82,6 @@ typedef struct u16 sequence; } cnat_echo_header_t; -typedef struct -{ - u32 dst_address_length_refcounts[129]; - u16 *prefix_lengths_in_search_order; - uword *non_empty_dst_address_length_bitmap; -} cnat_snat_pfx_table_meta_t; - -typedef struct -{ - /* Stores (ip family, prefix & mask) */ - clib_bihash_24_8_t ip_hash; - /* family dependant cache */ - cnat_snat_pfx_table_meta_t meta[2]; - /* Precomputed ip masks (ip4 & ip6) */ - ip6_address_t ip_masks[129]; -} cnat_snat_pfx_table_t; - typedef struct cnat_main_ { /* Memory size of the session bihash */ @@ -118,6 +102,10 @@ typedef struct cnat_main_ /* Number of buckets of the source NAT prefix bihash */ u32 snat_hash_buckets; + /* Bit map for include / exclude sw_if_index + * so max number of expected interfaces */ + u32 snat_if_map_length; + /* Timeout after which to clear sessions (in seconds) */ u32 session_max_age; @@ -131,15 +119,6 @@ typedef struct cnat_main_ /* Lock for the timestamp pool */ clib_rwlock_t ts_lock; - /* Ip4 Address to use for source NATing */ - cnat_endpoint_t snat_ip4; - - /* Ip6 Address to use for source NATing */ - cnat_endpoint_t snat_ip6; - - /* Longest prefix Match table for source NATing */ - cnat_snat_pfx_table_t snat_pfx_table; - /* Index of the scanner process node */ uword scanner_node_index; diff --git a/src/plugins/cnat/test/test_cnat.py b/src/plugins/cnat/test/test_cnat.py index ce32644a78c..ff4c44033cb 100644 --- a/src/plugins/cnat/test/test_cnat.py +++ b/src/plugins/cnat/test/test_cnat.py @@ -573,6 +573,17 @@ class TestCNatSourceNAT(VppTestCase): feature_name="cnat-snat-ip4", sw_if_index=self.pg0.sw_if_index) + policie_tbls = VppEnum.vl_api_cnat_snat_policy_table_t + self.vapi.cnat_set_snat_policy( + policy=VppEnum.vl_api_cnat_snat_policies_t.CNAT_POLICY_IF_PFX) + for i in self.pg_interfaces: + self.vapi.cnat_snat_policy_add_del_if( + sw_if_index=i.sw_if_index, is_add=1, + table=policie_tbls.CNAT_POLICY_INCLUDE_V6) + self.vapi.cnat_snat_policy_add_del_if( + sw_if_index=i.sw_if_index, is_add=1, + table=policie_tbls.CNAT_POLICY_INCLUDE_V4) + def tearDown(self): self.vapi.cnat_session_purge() for i in self.pg_interfaces: @@ -808,7 +819,8 @@ class TestCNatSourceNAT(VppTestCase): self.assertEqual(rx[IP46].src, remote_addr) # add remote host to exclude list - self.vapi.cnat_add_del_snat_prefix(prefix=exclude_prefix, is_add=1) + self.vapi.cnat_snat_policy_add_del_exclude_pfx( + prefix=exclude_prefix, is_add=1) self.vapi.cnat_session_purge() rxs = self.send_and_expect( @@ -822,7 +834,8 @@ class TestCNatSourceNAT(VppTestCase): self.assertEqual(rx[IP46].src, client_addr) # remove remote host from exclude list - self.vapi.cnat_add_del_snat_prefix(prefix=exclude_prefix, is_add=0) + self.vapi.cnat_snat_policy_add_del_exclude_pfx( + prefix=exclude_prefix, is_add=0) self.vapi.cnat_session_purge() rxs = self.send_and_expect( -- 2.16.6