2 * Copyright (c) 2020 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
16 #include <vnet/ip/ip.h>
17 #include <cnat/cnat_snat.h>
20 cnat_compute_prefix_lengths_in_search_order (cnat_snat_pfx_table_t *
21 table, ip_address_family_t af)
24 vec_reset_length (table->meta[af].prefix_lengths_in_search_order);
25 /* Note: bitmap reversed so this is in fact a longest prefix match */
27 clib_bitmap_foreach (i, table->meta[af].non_empty_dst_address_length_bitmap,
29 int dst_address_length = 128 - i;
30 vec_add1 (table->meta[af].prefix_lengths_in_search_order, dst_address_length);
36 cnat_add_snat_prefix (ip_prefix_t * pfx)
38 /* All packets destined to this prefix won't be source-NAT-ed */
39 cnat_snat_pfx_table_t *table = &cnat_main.snat_pfx_table;
40 clib_bihash_kv_24_8_t kv;
42 u64 af = ip_prefix_version (pfx);;
44 mask = &table->ip_masks[pfx->len];
47 kv.key[0] = (u64) ip_prefix_v4 (pfx).as_u32 & mask->as_u64[0];
52 kv.key[0] = ip_prefix_v6 (pfx).as_u64[0] & mask->as_u64[0];
53 kv.key[1] = ip_prefix_v6 (pfx).as_u64[1] & mask->as_u64[1];
55 kv.key[2] = ((u64) af << 32) | pfx->len;
56 clib_bihash_add_del_24_8 (&table->ip_hash, &kv, 1 /* is_add */ );
58 table->meta[af].dst_address_length_refcounts[pfx->len]++;
59 table->meta[af].non_empty_dst_address_length_bitmap =
60 clib_bitmap_set (table->meta[af].non_empty_dst_address_length_bitmap,
62 cnat_compute_prefix_lengths_in_search_order (table, af);
67 cnat_del_snat_prefix (ip_prefix_t * pfx)
69 cnat_snat_pfx_table_t *table = &cnat_main.snat_pfx_table;
70 clib_bihash_kv_24_8_t kv, val;
72 u64 af = ip_prefix_version (pfx);;
74 mask = &table->ip_masks[pfx->len];
77 kv.key[0] = (u64) ip_prefix_v4 (pfx).as_u32 & mask->as_u64[0];
82 kv.key[0] = ip_prefix_v6 (pfx).as_u64[0] & mask->as_u64[0];
83 kv.key[1] = ip_prefix_v6 (pfx).as_u64[1] & mask->as_u64[1];
85 kv.key[2] = ((u64) af << 32) | pfx->len;
87 if (clib_bihash_search_24_8 (&table->ip_hash, &kv, &val))
91 clib_bihash_add_del_24_8 (&table->ip_hash, &kv, 0 /* is_add */ );
92 /* refcount accounting */
93 ASSERT (table->meta[af].dst_address_length_refcounts[pfx->len] > 0);
94 if (--table->meta[af].dst_address_length_refcounts[pfx->len] == 0)
96 table->meta[af].non_empty_dst_address_length_bitmap =
97 clib_bitmap_set (table->meta[af].non_empty_dst_address_length_bitmap,
99 cnat_compute_prefix_lengths_in_search_order (table, af);
105 format_cnat_snat_prefix (u8 * s, va_list * args)
107 clib_bihash_kv_24_8_t *kv = va_arg (*args, clib_bihash_kv_24_8_t *);
108 CLIB_UNUSED (int verbose) = va_arg (*args, int);
109 u32 af = kv->key[2] >> 32;
110 u32 len = kv->key[2] & 0xffffffff;
112 s = format (s, "%U/%d", format_ip4_address, &kv->key[0], len);
114 s = format (s, "%U/%d", format_ip6_address, &kv->key[0], len);
118 static clib_error_t *
119 cnat_set_snat (vlib_main_t * vm,
120 unformat_input_t * input, vlib_cli_command_t * cmd)
122 unformat_input_t _line_input, *line_input = &_line_input;
126 /* Get a line of input. */
127 if (!unformat_user (input, unformat_line_input, line_input))
130 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
132 if (unformat (line_input, "%U", unformat_ip_address, &addr))
134 if (ip_addr_version (&addr) == AF_IP4)
135 clib_memcpy (&cnat_main.snat_ip4, &ip_addr_v4 (&addr),
136 sizeof (ip4_address_t));
138 clib_memcpy (&cnat_main.snat_ip6, &ip_addr_v6 (&addr),
139 sizeof (ip6_address_t));
143 e = clib_error_return (0, "unknown input '%U'",
144 format_unformat_error, input);
150 unformat_free (line_input);
156 VLIB_CLI_COMMAND (cnat_set_snat_command, static) =
158 .path = "cnat snat with",
159 .short_help = "cnat snat with [<ip4-address>][<ip6-address>]",
160 .function = cnat_set_snat,
164 static clib_error_t *
165 cnat_snat_exclude (vlib_main_t * vm,
166 unformat_input_t * input, vlib_cli_command_t * cmd)
172 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
174 if (unformat (input, "%U", unformat_ip_prefix, &pfx))
176 else if (unformat (input, "del"))
179 return (clib_error_return (0, "unknown input '%U'",
180 format_unformat_error, input));
184 rv = cnat_add_snat_prefix (&pfx);
186 rv = cnat_del_snat_prefix (&pfx);
190 return (clib_error_return (0, "error %d", rv, input));
197 VLIB_CLI_COMMAND (cnat_snat_exclude_command, static) =
199 .path = "cnat snat exclude",
200 .short_help = "cnat snat exclude [ip]",
201 .function = cnat_snat_exclude,
205 static clib_error_t *
206 cnat_show_snat (vlib_main_t * vm,
207 unformat_input_t * input, vlib_cli_command_t * cmd)
209 cnat_snat_pfx_table_t *table = &cnat_main.snat_pfx_table;
210 vlib_cli_output (vm, "Source NAT\nip4: %U\nip6: %U\n",
211 format_ip4_address, &cnat_main.snat_ip4,
212 format_ip6_address, &cnat_main.snat_ip6);
213 vlib_cli_output (vm, "Prefixes:\n%U\n",
214 format_bihash_24_8, &table->ip_hash, 1);
219 VLIB_CLI_COMMAND (cnat_show_snat_command, static) =
221 .path = "show cnat snat",
222 .short_help = "show cnat snat",
223 .function = cnat_show_snat,
227 static clib_error_t *
228 cnat_snat_init (vlib_main_t * vm)
230 cnat_snat_pfx_table_t *table = &cnat_main.snat_pfx_table;
231 cnat_main_t *cm = &cnat_main;
233 for (i = 0; i < ARRAY_LEN (table->ip_masks); i++)
240 for (j = 0; j < i0; j++)
241 table->ip_masks[i].as_u32[j] = ~0;
244 table->ip_masks[i].as_u32[i0] =
245 clib_host_to_net_u32 (pow2_mask (i1) << (32 - i1));
247 clib_bihash_init_24_8 (&table->ip_hash, "snat prefixes",
248 cm->snat_hash_buckets, cm->snat_hash_memory);
249 clib_bihash_set_kvp_format_fn_24_8 (&table->ip_hash,
250 format_cnat_snat_prefix);
255 VLIB_INIT_FUNCTION (cnat_snat_init);
259 * fd.io coding-style-patch-verification: ON
262 * eval: (c-set-style "gnu")