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 cnat_search_snat_prefix (ip46_address_t * addr, ip_address_family_t af)
107 /* Returns 0 if addr matches any of the listed prefixes */
108 cnat_snat_pfx_table_t *table = &cnat_main.snat_pfx_table;
109 clib_bihash_kv_24_8_t kv, val;
111 n_p = vec_len (table->meta[af].prefix_lengths_in_search_order);
114 kv.key[0] = addr->ip4.as_u32;
119 kv.key[0] = addr->as_u64[0];
120 kv.key[1] = addr->as_u64[1];
124 * start search from a mask length same length or shorter.
125 * we don't want matches longer than the mask passed
130 int dst_address_length =
131 table->meta[af].prefix_lengths_in_search_order[i];
132 ip6_address_t *mask = &table->ip_masks[dst_address_length];
134 ASSERT (dst_address_length >= 0 && dst_address_length <= 128);
135 /* As lengths are decreasing, masks are increasingly specific. */
136 kv.key[0] &= mask->as_u64[0];
137 kv.key[1] &= mask->as_u64[1];
138 kv.key[2] = ((u64) af << 32) | dst_address_length;
139 rv = clib_bihash_search_inline_2_24_8 (&table->ip_hash, &kv, &val);
147 format_cnat_snat_prefix (u8 * s, va_list * args)
149 clib_bihash_kv_24_8_t *kv = va_arg (*args, clib_bihash_kv_24_8_t *);
150 CLIB_UNUSED (int verbose) = va_arg (*args, int);
151 u32 af = kv->key[2] >> 32;
152 u32 len = kv->key[2] & 0xffffffff;
154 s = format (s, "%U/%d", format_ip4_address, &kv->key[0], len);
156 s = format (s, "%U/%d", format_ip6_address, &kv->key[0], len);
160 static clib_error_t *
161 cnat_set_snat (vlib_main_t * vm,
162 unformat_input_t * input, vlib_cli_command_t * cmd)
164 unformat_input_t _line_input, *line_input = &_line_input;
170 /* Get a line of input. */
171 if (!unformat_user (input, unformat_line_input, line_input))
174 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
176 if (unformat (line_input, "%U", unformat_ip_address, &addr))
178 if (ip_addr_version (&addr) == AF_IP4)
179 clib_memcpy (&cnat_main.snat_ip4, &ip_addr_v4 (&addr),
180 sizeof (ip4_address_t));
182 clib_memcpy (&cnat_main.snat_ip6, &ip_addr_v6 (&addr),
183 sizeof (ip6_address_t));
187 e = clib_error_return (0, "unknown input '%U'",
188 format_unformat_error, input);
194 unformat_free (line_input);
200 VLIB_CLI_COMMAND (cnat_set_snat_command, static) =
202 .path = "cnat snat with",
203 .short_help = "cnat snat with [<ip4-address>][<ip6-address>]",
204 .function = cnat_set_snat,
208 static clib_error_t *
209 cnat_snat_exclude (vlib_main_t * vm,
210 unformat_input_t * input, vlib_cli_command_t * cmd)
216 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
218 if (unformat (input, "%U", unformat_ip_prefix, &pfx))
220 else if (unformat (input, "del"))
223 return (clib_error_return (0, "unknown input '%U'",
224 format_unformat_error, input));
228 rv = cnat_add_snat_prefix (&pfx);
230 rv = cnat_del_snat_prefix (&pfx);
234 return (clib_error_return (0, "error %d", rv, input));
241 VLIB_CLI_COMMAND (cnat_snat_exclude_command, static) =
243 .path = "cnat snat exclude",
244 .short_help = "cnat snat exclude [ip]",
245 .function = cnat_snat_exclude,
249 static clib_error_t *
250 cnat_show_snat (vlib_main_t * vm,
251 unformat_input_t * input, vlib_cli_command_t * cmd)
253 cnat_snat_pfx_table_t *table = &cnat_main.snat_pfx_table;
254 vlib_cli_output (vm, "Source NAT\nip4: %U\nip6: %U\n",
255 format_ip4_address, &cnat_main.snat_ip4,
256 format_ip6_address, &cnat_main.snat_ip6);
257 vlib_cli_output (vm, "Prefixes:\n%U\n",
258 format_bihash_24_8, &table->ip_hash, 1);
263 VLIB_CLI_COMMAND (cnat_show_snat_command, static) =
265 .path = "show cnat snat",
266 .short_help = "show cnat snat",
267 .function = cnat_show_snat,
271 static clib_error_t *
272 cnat_snat_init (vlib_main_t * vm)
274 cnat_snat_pfx_table_t *table = &cnat_main.snat_pfx_table;
275 cnat_main_t *cm = &cnat_main;
277 for (i = 0; i < ARRAY_LEN (table->ip_masks); i++)
284 for (j = 0; j < i0; j++)
285 table->ip_masks[i].as_u32[j] = ~0;
288 table->ip_masks[i].as_u32[i0] =
289 clib_host_to_net_u32 (pow2_mask (i1) << (32 - i1));
291 clib_bihash_init_24_8 (&table->ip_hash, "snat prefixes",
292 cm->snat_hash_buckets, cm->snat_hash_memory);
293 clib_bihash_set_kvp_format_fn_24_8 (&table->ip_hash,
294 format_cnat_snat_prefix);
299 VLIB_INIT_FUNCTION (cnat_snat_init);
303 * fd.io coding-style-patch-verification: ON
306 * eval: (c-set-style "gnu")