blob: df00f5e2ece91fd8dd72710d3b5a216aabada091 [file] [log] [blame]
Dave Barach20c02cb2016-06-26 10:42:08 -04001/*
2 * snat.c - simple nat plugin
3 *
4 * Copyright (c) 2016 Cisco and/or its affiliates.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include <vnet/vnet.h>
Dave Barachcab65ec2017-01-11 13:01:14 -050019#include <vnet/ip/ip.h>
20#include <vnet/ip/ip4.h>
Dave Barach20c02cb2016-06-26 10:42:08 -040021#include <vnet/plugin/plugin.h>
Matus Fabian2ba92e32017-08-21 07:05:03 -070022#include <nat/nat.h>
23#include <nat/nat_ipfix_logging.h>
24#include <nat/nat_det.h>
25#include <nat/nat64.h>
Matus Fabian8ebe6252017-11-06 05:04:53 -080026#include <nat/dslite.h>
Matus Fabianefcd1e92017-08-15 06:59:19 -070027#include <nat/nat_reass.h>
Matus Fabiane1ae29a2017-01-27 00:47:58 -080028#include <vnet/fib/fib_table.h>
29#include <vnet/fib/ip4_fib.h>
Dave Barach20c02cb2016-06-26 10:42:08 -040030
Damjan Marion3b46cba2017-01-23 21:13:45 +010031#include <vpp/app/version.h>
Dave Barach20c02cb2016-06-26 10:42:08 -040032
33snat_main_t snat_main;
34
Dave Barach20c02cb2016-06-26 10:42:08 -040035
Dave Barach20c02cb2016-06-26 10:42:08 -040036/* Hook up input features */
Damjan Marion8b3191e2016-11-09 19:54:20 +010037VNET_FEATURE_INIT (ip4_snat_in2out, static) = {
38 .arc_name = "ip4-unicast",
Matus Fabian2ba92e32017-08-21 07:05:03 -070039 .node_name = "nat44-in2out",
40 .runs_before = VNET_FEATURES ("nat44-out2in"),
Dave Barach20c02cb2016-06-26 10:42:08 -040041};
Damjan Marion8b3191e2016-11-09 19:54:20 +010042VNET_FEATURE_INIT (ip4_snat_out2in, static) = {
43 .arc_name = "ip4-unicast",
Matus Fabian2ba92e32017-08-21 07:05:03 -070044 .node_name = "nat44-out2in",
Damjan Marion8b3191e2016-11-09 19:54:20 +010045 .runs_before = VNET_FEATURES ("ip4-lookup"),
Dave Barach20c02cb2016-06-26 10:42:08 -040046};
Matus Fabian36ea2d62017-10-24 04:13:49 -070047VNET_FEATURE_INIT (ip4_nat_classify, static) = {
48 .arc_name = "ip4-unicast",
49 .node_name = "nat44-classify",
50 .runs_before = VNET_FEATURES ("ip4-lookup"),
51};
Matus Fabian066f0342017-02-10 03:48:01 -080052VNET_FEATURE_INIT (ip4_snat_det_in2out, static) = {
53 .arc_name = "ip4-unicast",
Matus Fabian2ba92e32017-08-21 07:05:03 -070054 .node_name = "nat44-det-in2out",
55 .runs_before = VNET_FEATURES ("nat44-det-out2in"),
Matus Fabian066f0342017-02-10 03:48:01 -080056};
57VNET_FEATURE_INIT (ip4_snat_det_out2in, static) = {
58 .arc_name = "ip4-unicast",
Matus Fabian2ba92e32017-08-21 07:05:03 -070059 .node_name = "nat44-det-out2in",
Matus Fabian066f0342017-02-10 03:48:01 -080060 .runs_before = VNET_FEATURES ("ip4-lookup"),
61};
Matus Fabian36ea2d62017-10-24 04:13:49 -070062VNET_FEATURE_INIT (ip4_nat_det_classify, static) = {
63 .arc_name = "ip4-unicast",
64 .node_name = "nat44-det-classify",
65 .runs_before = VNET_FEATURES ("ip4-lookup"),
66};
Matus Fabian475f0552016-10-19 06:17:52 -070067VNET_FEATURE_INIT (ip4_snat_in2out_worker_handoff, static) = {
68 .arc_name = "ip4-unicast",
Matus Fabian2ba92e32017-08-21 07:05:03 -070069 .node_name = "nat44-in2out-worker-handoff",
70 .runs_before = VNET_FEATURES ("nat44-out2in-worker-handoff"),
Matus Fabian475f0552016-10-19 06:17:52 -070071};
72VNET_FEATURE_INIT (ip4_snat_out2in_worker_handoff, static) = {
73 .arc_name = "ip4-unicast",
Matus Fabian2ba92e32017-08-21 07:05:03 -070074 .node_name = "nat44-out2in-worker-handoff",
Matus Fabian475f0552016-10-19 06:17:52 -070075 .runs_before = VNET_FEATURES ("ip4-lookup"),
76};
Matus Fabian36ea2d62017-10-24 04:13:49 -070077VNET_FEATURE_INIT (ip4_nat_handoff_classify, static) = {
78 .arc_name = "ip4-unicast",
79 .node_name = "nat44-handoff-classify",
80 .runs_before = VNET_FEATURES ("ip4-lookup"),
81};
Damjan Marion8b3191e2016-11-09 19:54:20 +010082VNET_FEATURE_INIT (ip4_snat_in2out_fast, static) = {
83 .arc_name = "ip4-unicast",
Matus Fabian2ba92e32017-08-21 07:05:03 -070084 .node_name = "nat44-in2out-fast",
85 .runs_before = VNET_FEATURES ("nat44-out2in-fast"),
Matus Fabiandb649882016-08-26 05:45:27 -070086};
Damjan Marion8b3191e2016-11-09 19:54:20 +010087VNET_FEATURE_INIT (ip4_snat_out2in_fast, static) = {
88 .arc_name = "ip4-unicast",
Matus Fabian2ba92e32017-08-21 07:05:03 -070089 .node_name = "nat44-out2in-fast",
Damjan Marion8b3191e2016-11-09 19:54:20 +010090 .runs_before = VNET_FEATURES ("ip4-lookup"),
Matus Fabiandb649882016-08-26 05:45:27 -070091};
Matus Fabian161c59c2017-07-21 03:46:03 -070092VNET_FEATURE_INIT (ip4_snat_hairpin_dst, static) = {
93 .arc_name = "ip4-unicast",
Matus Fabian2ba92e32017-08-21 07:05:03 -070094 .node_name = "nat44-hairpin-dst",
Matus Fabian161c59c2017-07-21 03:46:03 -070095 .runs_before = VNET_FEATURES ("ip4-lookup"),
96};
Matus Fabiandb649882016-08-26 05:45:27 -070097
Matus Fabian93d84c92017-07-19 08:06:01 -070098/* Hook up output features */
99VNET_FEATURE_INIT (ip4_snat_in2out_output, static) = {
100 .arc_name = "ip4-output",
Matus Fabian2ba92e32017-08-21 07:05:03 -0700101 .node_name = "nat44-in2out-output",
Matus Fabian93d84c92017-07-19 08:06:01 -0700102 .runs_before = VNET_FEATURES ("interface-output"),
103};
Matus Fabian93d84c92017-07-19 08:06:01 -0700104VNET_FEATURE_INIT (ip4_snat_in2out_output_worker_handoff, static) = {
105 .arc_name = "ip4-output",
Matus Fabian2ba92e32017-08-21 07:05:03 -0700106 .node_name = "nat44-in2out-output-worker-handoff",
Matus Fabian93d84c92017-07-19 08:06:01 -0700107 .runs_before = VNET_FEATURES ("interface-output"),
108};
Matus Fabian161c59c2017-07-21 03:46:03 -0700109VNET_FEATURE_INIT (ip4_snat_hairpin_src, static) = {
110 .arc_name = "ip4-output",
Matus Fabian2ba92e32017-08-21 07:05:03 -0700111 .node_name = "nat44-hairpin-src",
Matus Fabian161c59c2017-07-21 03:46:03 -0700112 .runs_before = VNET_FEATURES ("interface-output"),
113};
Matus Fabian93d84c92017-07-19 08:06:01 -0700114
Matus Fabian87da4762017-10-04 08:03:56 -0700115/* Hook up ip4-local features */
116VNET_FEATURE_INIT (ip4_nat_hairpinning, static) =
117{
118 .arc_name = "ip4-local",
119 .node_name = "nat44-hairpinning",
120 .runs_before = VNET_FEATURES("ip4-local-end-of-arc"),
121};
122
Matus Fabian93d84c92017-07-19 08:06:01 -0700123
Damjan Marion3b46cba2017-01-23 21:13:45 +0100124/* *INDENT-OFF* */
125VLIB_PLUGIN_REGISTER () = {
126 .version = VPP_BUILD_VER,
Damjan Marion1bfb0dd2017-03-22 11:08:39 +0100127 .description = "Network Address Translation",
Damjan Marion3b46cba2017-01-23 21:13:45 +0100128};
129/* *INDENT-ON* */
Dave Barach20c02cb2016-06-26 10:42:08 -0400130
Matus Fabian36ea2d62017-10-24 04:13:49 -0700131vlib_node_registration_t nat44_classify_node;
132vlib_node_registration_t nat44_det_classify_node;
133vlib_node_registration_t nat44_handoff_classify_node;
134
135typedef enum {
136 NAT44_CLASSIFY_NEXT_IN2OUT,
137 NAT44_CLASSIFY_NEXT_OUT2IN,
138 NAT44_CLASSIFY_N_NEXT,
139} nat44_classify_next_t;
140
Matus Fabianb932d262017-12-18 05:38:24 -0800141void
142nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index)
143{
144 snat_session_key_t key;
145 clib_bihash_kv_8_8_t kv;
146 nat_ed_ses_key_t ed_key;
147 clib_bihash_kv_16_8_t ed_kv;
148 int i;
149 snat_address_t *a;
150 snat_main_per_thread_data_t *tsm =
151 vec_elt_at_index (sm->per_thread_data, thread_index);
152
153 /* Endpoint dependent session lookup tables */
154 if (is_ed_session (s))
155 {
156 ed_key.l_addr = s->out2in.addr;
157 ed_key.r_addr = s->ext_host_addr;
158 ed_key.fib_index = s->out2in.fib_index;
159 if (snat_is_unk_proto_session (s))
160 {
161 ed_key.proto = s->in2out.port;
162 ed_key.r_port = 0;
163 ed_key.l_port = 0;
164 }
165 else
166 {
167 ed_key.proto = snat_proto_to_ip_proto (s->in2out.protocol);
168 ed_key.l_port = s->out2in.port;
169 ed_key.r_port = s->ext_host_port;
170 }
171 ed_kv.key[0] = ed_key.as_u64[0];
172 ed_kv.key[1] = ed_key.as_u64[1];
173 if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &ed_kv, 0))
174 clib_warning ("out2in_ed key del failed");
175
176 ed_key.l_addr = s->in2out.addr;
177 ed_key.fib_index = s->in2out.fib_index;
178 if (!snat_is_unk_proto_session (s))
179 ed_key.l_port = s->in2out.port;
180 if (is_twice_nat_session (s))
181 {
182 ed_key.r_addr = s->ext_host_nat_addr;
183 ed_key.r_port = s->ext_host_nat_port;
184 }
185 ed_kv.key[0] = ed_key.as_u64[0];
186 ed_kv.key[1] = ed_key.as_u64[1];
187 if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &ed_kv, 0))
188 clib_warning ("in2out_ed key del failed");
189 }
190
191 if (snat_is_unk_proto_session (s))
192 return;
193
194 /* log NAT event */
195 snat_ipfix_logging_nat44_ses_delete(s->in2out.addr.as_u32,
196 s->out2in.addr.as_u32,
197 s->in2out.protocol,
198 s->in2out.port,
199 s->out2in.port,
200 s->in2out.fib_index);
201
202 /* Twice NAT address and port for external host */
203 if (is_twice_nat_session (s))
204 {
205 for (i = 0; i < vec_len (sm->twice_nat_addresses); i++)
206 {
207 key.protocol = s->in2out.protocol;
208 key.port = s->ext_host_nat_port;
209 a = sm->twice_nat_addresses + i;
210 if (a->addr.as_u32 == s->ext_host_nat_addr.as_u32)
211 {
212 snat_free_outside_address_and_port (sm->twice_nat_addresses,
213 thread_index, &key, i);
214 break;
215 }
216 }
217 }
218
219 if (is_ed_session (s))
220 return;
221
222 /* Session lookup tables */
223 kv.key = s->in2out.as_u64;
224 if (clib_bihash_add_del_8_8 (&tsm->in2out, &kv, 0))
225 clib_warning ("in2out key del failed");
226 kv.key = s->out2in.as_u64;
227 if (clib_bihash_add_del_8_8 (&tsm->out2in, &kv, 0))
228 clib_warning ("out2in key del failed");
229
230 if (snat_is_session_static (s))
231 return;
232
233 if (s->outside_address_index != ~0)
234 snat_free_outside_address_and_port (sm->addresses, thread_index,
235 &s->out2in, s->outside_address_index);
236}
237
238snat_user_t *
239nat_user_get_or_create (snat_main_t *sm, ip4_address_t *addr, u32 fib_index,
240 u32 thread_index)
241{
242 snat_user_t *u = 0;
243 snat_user_key_t user_key;
244 clib_bihash_kv_8_8_t kv, value;
245 snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
246 dlist_elt_t * per_user_list_head_elt;
247
248 user_key.addr.as_u32 = addr->as_u32;
249 user_key.fib_index = fib_index;
250 kv.key = user_key.as_u64;
251
252 /* Ever heard of the "user" = src ip4 address before? */
253 if (clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
254 {
255 /* no, make a new one */
256 pool_get (tsm->users, u);
257 memset (u, 0, sizeof (*u));
258 u->addr.as_u32 = addr->as_u32;
259 u->fib_index = fib_index;
260
261 pool_get (tsm->list_pool, per_user_list_head_elt);
262
263 u->sessions_per_user_list_head_index = per_user_list_head_elt -
264 tsm->list_pool;
265
266 clib_dlist_init (tsm->list_pool, u->sessions_per_user_list_head_index);
267
268 kv.value = u - tsm->users;
269
270 /* add user */
271 if (clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 1))
272 clib_warning ("user_hash keay add failed");
273 }
274 else
275 {
276 u = pool_elt_at_index (tsm->users, value.value);
277 }
278
279 return u;
280}
281
282snat_session_t *
283nat_session_alloc_or_recycle (snat_main_t *sm, snat_user_t *u, u32 thread_index)
284{
285 snat_session_t *s;
286 snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
287 u32 oldest_per_user_translation_list_index, session_index;
288 dlist_elt_t * oldest_per_user_translation_list_elt;
289 dlist_elt_t * per_user_translation_list_elt;
290
291 /* Over quota? Recycle the least recently used translation */
292 if ((u->nsessions + u->nstaticsessions) >= sm->max_translations_per_user)
293 {
294 oldest_per_user_translation_list_index =
295 clib_dlist_remove_head (tsm->list_pool,
296 u->sessions_per_user_list_head_index);
297
298 ASSERT (oldest_per_user_translation_list_index != ~0);
299
300 /* Add it back to the end of the LRU list */
301 clib_dlist_addtail (tsm->list_pool,
302 u->sessions_per_user_list_head_index,
303 oldest_per_user_translation_list_index);
304 /* Get the list element */
305 oldest_per_user_translation_list_elt =
306 pool_elt_at_index (tsm->list_pool,
307 oldest_per_user_translation_list_index);
308
309 /* Get the session index from the list element */
310 session_index = oldest_per_user_translation_list_elt->value;
311
312 /* Get the session */
313 s = pool_elt_at_index (tsm->sessions, session_index);
314 nat_free_session_data (sm, s, thread_index);
315 s->outside_address_index = ~0;
316 s->flags = 0;
317 s->total_bytes = 0;
318 s->total_pkts = 0;
319 }
320 else
321 {
322 pool_get (tsm->sessions, s);
323 memset (s, 0, sizeof (*s));
324 s->outside_address_index = ~0;
325
326 /* Create list elts */
327 pool_get (tsm->list_pool, per_user_translation_list_elt);
328 clib_dlist_init (tsm->list_pool,
329 per_user_translation_list_elt - tsm->list_pool);
330
331 per_user_translation_list_elt->value = s - tsm->sessions;
332 s->per_user_index = per_user_translation_list_elt - tsm->list_pool;
333 s->per_user_list_head_index = u->sessions_per_user_list_head_index;
334
335 clib_dlist_addtail (tsm->list_pool,
336 s->per_user_list_head_index,
337 per_user_translation_list_elt - tsm->list_pool);
338 }
339
340 return s;
341}
342
Matus Fabian36ea2d62017-10-24 04:13:49 -0700343static inline uword
344nat44_classify_node_fn_inline (vlib_main_t * vm,
345 vlib_node_runtime_t * node,
346 vlib_frame_t * frame)
347{
348 u32 n_left_from, * from, * to_next;
349 nat44_classify_next_t next_index;
350 snat_main_t *sm = &snat_main;
351
352 from = vlib_frame_vector_args (frame);
353 n_left_from = frame->n_vectors;
354 next_index = node->cached_next_index;
355
356 while (n_left_from > 0)
357 {
358 u32 n_left_to_next;
359
360 vlib_get_next_frame (vm, node, next_index,
361 to_next, n_left_to_next);
362
363 while (n_left_from > 0 && n_left_to_next > 0)
364 {
365 u32 bi0;
366 vlib_buffer_t *b0;
367 u32 next0 = NAT44_CLASSIFY_NEXT_IN2OUT;
368 ip4_header_t *ip0;
369 snat_address_t *ap;
370 snat_session_key_t m_key0;
371 clib_bihash_kv_8_8_t kv0, value0;
372
373 /* speculatively enqueue b0 to the current next frame */
374 bi0 = from[0];
375 to_next[0] = bi0;
376 from += 1;
377 to_next += 1;
378 n_left_from -= 1;
379 n_left_to_next -= 1;
380
381 b0 = vlib_get_buffer (vm, bi0);
382 ip0 = vlib_buffer_get_current (b0);
383
384 vec_foreach (ap, sm->addresses)
385 {
386 if (ip0->dst_address.as_u32 == ap->addr.as_u32)
387 {
388 next0 = NAT44_CLASSIFY_NEXT_OUT2IN;
389 break;
390 }
391 }
392
393 if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
394 {
395 m_key0.addr = ip0->dst_address;
396 m_key0.port = 0;
397 m_key0.protocol = 0;
398 m_key0.fib_index = sm->outside_fib_index;
399 kv0.key = m_key0.as_u64;
400 if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv0, &value0))
401 {
402 next0 = NAT44_CLASSIFY_NEXT_OUT2IN;
403 }
404 }
405 /* verify speculative enqueue, maybe switch current next frame */
406 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
407 to_next, n_left_to_next,
408 bi0, next0);
409 }
410
411 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
412 }
413
414 return frame->n_vectors;
415}
416
417static uword
418nat44_classify_node_fn (vlib_main_t * vm,
419 vlib_node_runtime_t * node,
420 vlib_frame_t * frame)
421{
422 return nat44_classify_node_fn_inline (vm, node, frame);
423};
424
425VLIB_REGISTER_NODE (nat44_classify_node) = {
426 .function = nat44_classify_node_fn,
427 .name = "nat44-classify",
428 .vector_size = sizeof (u32),
429 .type = VLIB_NODE_TYPE_INTERNAL,
430 .n_next_nodes = NAT44_CLASSIFY_N_NEXT,
431 .next_nodes = {
432 [NAT44_CLASSIFY_NEXT_IN2OUT] = "nat44-in2out",
433 [NAT44_CLASSIFY_NEXT_OUT2IN] = "nat44-out2in",
434 },
435};
436
437VLIB_NODE_FUNCTION_MULTIARCH (nat44_classify_node,
438 nat44_classify_node_fn);
439
440static uword
441nat44_det_classify_node_fn (vlib_main_t * vm,
442 vlib_node_runtime_t * node,
443 vlib_frame_t * frame)
444{
445 return nat44_classify_node_fn_inline (vm, node, frame);
446};
447
448VLIB_REGISTER_NODE (nat44_det_classify_node) = {
449 .function = nat44_det_classify_node_fn,
450 .name = "nat44-det-classify",
451 .vector_size = sizeof (u32),
452 .type = VLIB_NODE_TYPE_INTERNAL,
453 .n_next_nodes = NAT44_CLASSIFY_N_NEXT,
454 .next_nodes = {
455 [NAT44_CLASSIFY_NEXT_IN2OUT] = "nat44-det-in2out",
456 [NAT44_CLASSIFY_NEXT_OUT2IN] = "nat44-det-out2in",
457 },
458};
459
460VLIB_NODE_FUNCTION_MULTIARCH (nat44_det_classify_node,
461 nat44_det_classify_node_fn);
462
463static uword
464nat44_handoff_classify_node_fn (vlib_main_t * vm,
465 vlib_node_runtime_t * node,
466 vlib_frame_t * frame)
467{
468 return nat44_classify_node_fn_inline (vm, node, frame);
469};
470
471VLIB_REGISTER_NODE (nat44_handoff_classify_node) = {
472 .function = nat44_handoff_classify_node_fn,
473 .name = "nat44-handoff-classify",
474 .vector_size = sizeof (u32),
475 .type = VLIB_NODE_TYPE_INTERNAL,
476 .n_next_nodes = NAT44_CLASSIFY_N_NEXT,
477 .next_nodes = {
478 [NAT44_CLASSIFY_NEXT_IN2OUT] = "nat44-in2out-worker-handoff",
479 [NAT44_CLASSIFY_NEXT_OUT2IN] = "nat44-out2in-worker-handoff",
480 },
481};
482
483VLIB_NODE_FUNCTION_MULTIARCH (nat44_handoff_classify_node,
484 nat44_handoff_classify_node_fn);
485
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800486/**
487 * @brief Add/del NAT address to FIB.
488 *
489 * Add the external NAT address to the FIB as receive entries. This ensures
490 * that VPP will reply to ARP for this address and we don't need to enable
491 * proxy ARP on the outside interface.
492 *
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800493 * @param addr IPv4 address.
Matus Fabian066f0342017-02-10 03:48:01 -0800494 * @param plen address prefix length
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800495 * @param sw_if_index Interface.
496 * @param is_add If 0 delete, otherwise add.
497 */
Matus Fabian066f0342017-02-10 03:48:01 -0800498void
499snat_add_del_addr_to_fib (ip4_address_t * addr, u8 p_len, u32 sw_if_index,
500 int is_add)
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800501{
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800502 fib_prefix_t prefix = {
Matus Fabian066f0342017-02-10 03:48:01 -0800503 .fp_len = p_len,
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800504 .fp_proto = FIB_PROTOCOL_IP4,
505 .fp_addr = {
506 .ip4.as_u32 = addr->as_u32,
507 },
508 };
509 u32 fib_index = ip4_fib_table_get_index_for_sw_if_index(sw_if_index);
510
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800511 if (is_add)
512 fib_table_entry_update_one_path(fib_index,
513 &prefix,
Matus Fabiandccbee32017-01-31 22:20:30 -0800514 FIB_SOURCE_PLUGIN_HI,
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800515 (FIB_ENTRY_FLAG_CONNECTED |
516 FIB_ENTRY_FLAG_LOCAL |
517 FIB_ENTRY_FLAG_EXCLUSIVE),
Neale Rannsda78f952017-05-24 09:15:43 -0700518 DPO_PROTO_IP4,
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800519 NULL,
520 sw_if_index,
521 ~0,
522 1,
523 NULL,
524 FIB_ROUTE_PATH_FLAG_NONE);
525 else
526 fib_table_entry_delete(fib_index,
527 &prefix,
Matus Fabiandccbee32017-01-31 22:20:30 -0800528 FIB_SOURCE_PLUGIN_HI);
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800529}
530
Matus Fabianb932d262017-12-18 05:38:24 -0800531void snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id,
532 u8 twice_nat)
Dave Barach20c02cb2016-06-26 10:42:08 -0400533{
534 snat_address_t * ap;
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800535 snat_interface_t *i;
Matus Fabian624b8d92017-09-12 04:15:30 -0700536 vlib_thread_main_t *tm = vlib_get_thread_main ();
Dave Barach20c02cb2016-06-26 10:42:08 -0400537
Matus Fabian860dacc2016-10-25 04:19:26 -0700538 /* Check if address already exists */
Matus Fabianb932d262017-12-18 05:38:24 -0800539 vec_foreach (ap, twice_nat ? sm->twice_nat_addresses : sm->addresses)
Matus Fabian860dacc2016-10-25 04:19:26 -0700540 {
541 if (ap->addr.as_u32 == addr->as_u32)
542 return;
543 }
544
Matus Fabianb932d262017-12-18 05:38:24 -0800545 if (twice_nat)
546 vec_add2 (sm->twice_nat_addresses, ap, 1);
547 else
548 vec_add2 (sm->addresses, ap, 1);
549
Dave Barach20c02cb2016-06-26 10:42:08 -0400550 ap->addr = *addr;
Matus Fabianf8d84902017-07-23 23:41:03 -0700551 if (vrf_id != ~0)
552 ap->fib_index =
Neale Ranns15002542017-09-10 04:39:11 -0700553 fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id,
554 FIB_SOURCE_PLUGIN_HI);
Matus Fabianf8d84902017-07-23 23:41:03 -0700555 else
556 ap->fib_index = ~0;
Matus Fabian09d96f42017-02-02 01:43:00 -0800557#define _(N, i, n, s) \
Matus Fabian624b8d92017-09-12 04:15:30 -0700558 clib_bitmap_alloc (ap->busy_##n##_port_bitmap, 65535); \
559 ap->busy_##n##_ports = 0; \
560 vec_validate_init_empty (ap->busy_##n##_ports_per_thread, tm->n_vlib_mains - 1, 0);
Matus Fabian09d96f42017-02-02 01:43:00 -0800561 foreach_snat_protocol
562#undef _
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800563
Matus Fabianb932d262017-12-18 05:38:24 -0800564 if (twice_nat)
565 return;
566
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800567 /* Add external address to FIB */
568 pool_foreach (i, sm->interfaces,
569 ({
Matus Fabian36ea2d62017-10-24 04:13:49 -0700570 if (nat_interface_is_inside(i))
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800571 continue;
572
Matus Fabian066f0342017-02-10 03:48:01 -0800573 snat_add_del_addr_to_fib(addr, 32, i->sw_if_index, 1);
Matus Fabiandccbee32017-01-31 22:20:30 -0800574 break;
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800575 }));
Matus Fabian93d84c92017-07-19 08:06:01 -0700576 pool_foreach (i, sm->output_feature_interfaces,
577 ({
Matus Fabian36ea2d62017-10-24 04:13:49 -0700578 if (nat_interface_is_inside(i))
Matus Fabian93d84c92017-07-19 08:06:01 -0700579 continue;
580
581 snat_add_del_addr_to_fib(addr, 32, i->sw_if_index, 1);
582 break;
583 }));
Dave Barach20c02cb2016-06-26 10:42:08 -0400584}
585
Matus Fabian724b8152016-10-04 03:23:43 -0700586static int is_snat_address_used_in_static_mapping (snat_main_t *sm,
587 ip4_address_t addr)
588{
589 snat_static_mapping_t *m;
590 pool_foreach (m, sm->static_mappings,
591 ({
592 if (m->external_addr.as_u32 == addr.as_u32)
593 return 1;
594 }));
595
596 return 0;
597}
598
Matus Fabiancfe0fc92017-05-10 06:37:47 -0700599void increment_v4_address (ip4_address_t * a)
Dave Barach20c02cb2016-06-26 10:42:08 -0400600{
601 u32 v;
Matus Fabian2ba92e32017-08-21 07:05:03 -0700602
Dave Barach20c02cb2016-06-26 10:42:08 -0400603 v = clib_net_to_host_u32(a->as_u32) + 1;
604 a->as_u32 = clib_host_to_net_u32(v);
605}
606
Matus Fabian2ba92e32017-08-21 07:05:03 -0700607static void
608snat_add_static_mapping_when_resolved (snat_main_t * sm,
609 ip4_address_t l_addr,
610 u16 l_port,
611 u32 sw_if_index,
612 u16 e_port,
Dave Barach8b275372017-01-16 10:54:02 -0500613 u32 vrf_id,
Matus Fabian09d96f42017-02-02 01:43:00 -0800614 snat_protocol_t proto,
Matus Fabian2ba92e32017-08-21 07:05:03 -0700615 int addr_only,
Dave Barach8b275372017-01-16 10:54:02 -0500616 int is_add)
617{
618 snat_static_map_resolve_t *rp;
619
620 vec_add2 (sm->to_resolve, rp, 1);
621 rp->l_addr.as_u32 = l_addr.as_u32;
622 rp->l_port = l_port;
623 rp->sw_if_index = sw_if_index;
624 rp->e_port = e_port;
625 rp->vrf_id = vrf_id;
Matus Fabian09d96f42017-02-02 01:43:00 -0800626 rp->proto = proto;
Dave Barach8b275372017-01-16 10:54:02 -0500627 rp->addr_only = addr_only;
628 rp->is_add = is_add;
629}
630
Matus Fabiandb649882016-08-26 05:45:27 -0700631/**
632 * @brief Add static mapping.
633 *
634 * Create static mapping between local addr+port and external addr+port.
635 *
636 * @param l_addr Local IPv4 address.
637 * @param e_addr External IPv4 address.
638 * @param l_port Local port number.
639 * @param e_port External port number.
640 * @param vrf_id VRF ID.
641 * @param addr_only If 0 address port and pair mapping, otherwise address only.
Matus Fabian36532bd2017-01-23 23:42:28 -0800642 * @param sw_if_index External port instead of specific IP address.
Matus Fabiandb649882016-08-26 05:45:27 -0700643 * @param is_add If 0 delete static mapping, otherwise add.
Matus Fabianb932d262017-12-18 05:38:24 -0800644 * @param twice_nat If 1 translate external host address and port.
Matus Fabiandb649882016-08-26 05:45:27 -0700645 *
646 * @returns
647 */
648int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
649 u16 l_port, u16 e_port, u32 vrf_id, int addr_only,
Matus Fabianb932d262017-12-18 05:38:24 -0800650 u32 sw_if_index, snat_protocol_t proto, int is_add,
651 u8 twice_nat)
Matus Fabiandb649882016-08-26 05:45:27 -0700652{
653 snat_main_t * sm = &snat_main;
654 snat_static_mapping_t *m;
Matus Fabian09d96f42017-02-02 01:43:00 -0800655 snat_session_key_t m_key;
Matus Fabiandb649882016-08-26 05:45:27 -0700656 clib_bihash_kv_8_8_t kv, value;
657 snat_address_t *a = 0;
658 u32 fib_index = ~0;
659 uword * p;
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800660 snat_interface_t *interface;
Matus Fabiandb649882016-08-26 05:45:27 -0700661 int i;
Matus Fabianb932d262017-12-18 05:38:24 -0800662 snat_main_per_thread_data_t *tsm;
Matus Fabiandb649882016-08-26 05:45:27 -0700663
Dave Barach8b275372017-01-16 10:54:02 -0500664 /* If the external address is a specific interface address */
665 if (sw_if_index != ~0)
666 {
667 ip4_address_t * first_int_addr;
668
669 /* Might be already set... */
Matus Fabian2ba92e32017-08-21 07:05:03 -0700670 first_int_addr = ip4_interface_first_address
Dave Barach8b275372017-01-16 10:54:02 -0500671 (sm->ip4_main, sw_if_index, 0 /* just want the address*/);
672
673 /* DHCP resolution required? */
674 if (first_int_addr == 0)
675 {
Matus Fabian2ba92e32017-08-21 07:05:03 -0700676 snat_add_static_mapping_when_resolved
Matus Fabian09d96f42017-02-02 01:43:00 -0800677 (sm, l_addr, l_port, sw_if_index, e_port, vrf_id, proto,
Dave Barach8b275372017-01-16 10:54:02 -0500678 addr_only, is_add);
679 return 0;
680 }
681 else
Dave Barache6e012f2017-12-18 08:11:37 -0500682 {
Dave Barach8b275372017-01-16 10:54:02 -0500683 e_addr.as_u32 = first_int_addr->as_u32;
Dave Barache6e012f2017-12-18 08:11:37 -0500684 /* Identity mapping? */
685 if (l_addr.as_u32 == 0)
686 l_addr.as_u32 = e_addr.as_u32;
687 }
Dave Barach8b275372017-01-16 10:54:02 -0500688 }
689
Matus Fabiandb649882016-08-26 05:45:27 -0700690 m_key.addr = e_addr;
691 m_key.port = addr_only ? 0 : e_port;
Matus Fabian09d96f42017-02-02 01:43:00 -0800692 m_key.protocol = addr_only ? 0 : proto;
Matus Fabian7e46a4d2016-10-06 04:28:29 -0700693 m_key.fib_index = sm->outside_fib_index;
Matus Fabiandb649882016-08-26 05:45:27 -0700694 kv.key = m_key.as_u64;
695 if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value))
696 m = 0;
697 else
698 m = pool_elt_at_index (sm->static_mappings, value.value);
699
700 if (is_add)
701 {
702 if (m)
703 return VNET_API_ERROR_VALUE_EXIST;
704
Matus Fabianb932d262017-12-18 05:38:24 -0800705 if (twice_nat && addr_only)
706 return VNET_API_ERROR_UNSUPPORTED;
707
Matus Fabiandb649882016-08-26 05:45:27 -0700708 /* Convert VRF id to FIB index */
709 if (vrf_id != ~0)
710 {
711 p = hash_get (sm->ip4_main->fib_index_by_table_id, vrf_id);
712 if (!p)
713 return VNET_API_ERROR_NO_SUCH_FIB;
714 fib_index = p[0];
715 }
716 /* If not specified use inside VRF id from SNAT plugin startup config */
717 else
718 {
Matus Fabian31c31aa2017-02-05 22:45:57 -0800719 fib_index = sm->inside_fib_index;
Matus Fabiandb649882016-08-26 05:45:27 -0700720 vrf_id = sm->inside_vrf_id;
721 }
722
Matus Fabiandb649882016-08-26 05:45:27 -0700723 /* Find external address in allocated addresses and reserve port for
724 address and port pair mapping when dynamic translations enabled */
725 if (!addr_only && !(sm->static_mapping_only))
726 {
727 for (i = 0; i < vec_len (sm->addresses); i++)
728 {
729 if (sm->addresses[i].addr.as_u32 == e_addr.as_u32)
730 {
731 a = sm->addresses + i;
732 /* External port must be unused */
Matus Fabian09d96f42017-02-02 01:43:00 -0800733 switch (proto)
734 {
735#define _(N, j, n, s) \
736 case SNAT_PROTOCOL_##N: \
737 if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, e_port)) \
738 return VNET_API_ERROR_INVALID_VALUE; \
739 clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 1); \
740 if (e_port > 1024) \
Matus Fabian624b8d92017-09-12 04:15:30 -0700741 { \
742 a->busy_##n##_ports++; \
Matus Fabianed3c1602017-09-21 05:07:12 -0700743 a->busy_##n##_ports_per_thread[(e_port - 1024) / sm->port_per_thread]++; \
Matus Fabian624b8d92017-09-12 04:15:30 -0700744 } \
Matus Fabian09d96f42017-02-02 01:43:00 -0800745 break;
746 foreach_snat_protocol
747#undef _
748 default:
749 clib_warning("unknown_protocol");
750 return VNET_API_ERROR_INVALID_VALUE_2;
751 }
Matus Fabiandb649882016-08-26 05:45:27 -0700752 break;
753 }
754 }
755 /* External address must be allocated */
756 if (!a)
757 return VNET_API_ERROR_NO_SUCH_ENTRY;
758 }
759
760 pool_get (sm->static_mappings, m);
761 memset (m, 0, sizeof (*m));
762 m->local_addr = l_addr;
763 m->external_addr = e_addr;
764 m->addr_only = addr_only;
765 m->vrf_id = vrf_id;
766 m->fib_index = fib_index;
Matus Fabianb932d262017-12-18 05:38:24 -0800767 m->twice_nat = twice_nat;
Matus Fabiandb649882016-08-26 05:45:27 -0700768 if (!addr_only)
769 {
770 m->local_port = l_port;
771 m->external_port = e_port;
Matus Fabian09d96f42017-02-02 01:43:00 -0800772 m->proto = proto;
Matus Fabiandb649882016-08-26 05:45:27 -0700773 }
774
Matus Fabianb932d262017-12-18 05:38:24 -0800775 if (sm->workers)
776 {
777 ip4_header_t ip = {
778 .src_address = m->local_addr,
779 };
780 m->worker_index = sm->worker_in2out_cb (&ip, m->fib_index);
781 tsm = vec_elt_at_index (sm->per_thread_data, m->worker_index);
782 }
783 else
784 tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
785
Matus Fabiandb649882016-08-26 05:45:27 -0700786 m_key.addr = m->local_addr;
787 m_key.port = m->local_port;
Matus Fabian09d96f42017-02-02 01:43:00 -0800788 m_key.protocol = m->proto;
Matus Fabian7e46a4d2016-10-06 04:28:29 -0700789 m_key.fib_index = m->fib_index;
Matus Fabiandb649882016-08-26 05:45:27 -0700790 kv.key = m_key.as_u64;
791 kv.value = m - sm->static_mappings;
792 clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 1);
Matus Fabianb932d262017-12-18 05:38:24 -0800793 if (twice_nat)
794 {
795 m_key.port = clib_host_to_net_u16 (l_port);
796 kv.key = m_key.as_u64;
797 kv.value = ~0ULL;
798 if (clib_bihash_add_del_8_8(&tsm->in2out, &kv, 1))
799 clib_warning ("in2out key add failed");
800 }
Matus Fabiandb649882016-08-26 05:45:27 -0700801
802 m_key.addr = m->external_addr;
803 m_key.port = m->external_port;
Matus Fabian7e46a4d2016-10-06 04:28:29 -0700804 m_key.fib_index = sm->outside_fib_index;
Matus Fabiandb649882016-08-26 05:45:27 -0700805 kv.key = m_key.as_u64;
806 kv.value = m - sm->static_mappings;
807 clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 1);
Matus Fabianb932d262017-12-18 05:38:24 -0800808 if (twice_nat)
Matus Fabian49331682016-12-01 01:32:03 -0800809 {
Matus Fabianb932d262017-12-18 05:38:24 -0800810 m_key.port = clib_host_to_net_u16 (e_port);
811 kv.key = m_key.as_u64;
812 kv.value = ~0ULL;
813 if (clib_bihash_add_del_8_8(&tsm->out2in, &kv, 1))
814 clib_warning ("out2in key add failed");
Matus Fabian49331682016-12-01 01:32:03 -0800815 }
Matus Fabianb932d262017-12-18 05:38:24 -0800816
Matus Fabiandb649882016-08-26 05:45:27 -0700817 }
818 else
819 {
820 if (!m)
821 return VNET_API_ERROR_NO_SUCH_ENTRY;
822
823 /* Free external address port */
824 if (!addr_only && !(sm->static_mapping_only))
825 {
826 for (i = 0; i < vec_len (sm->addresses); i++)
827 {
828 if (sm->addresses[i].addr.as_u32 == e_addr.as_u32)
829 {
830 a = sm->addresses + i;
Matus Fabian09d96f42017-02-02 01:43:00 -0800831 switch (proto)
832 {
833#define _(N, j, n, s) \
834 case SNAT_PROTOCOL_##N: \
835 clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 0); \
836 if (e_port > 1024) \
Matus Fabian624b8d92017-09-12 04:15:30 -0700837 { \
838 a->busy_##n##_ports--; \
Matus Fabianed3c1602017-09-21 05:07:12 -0700839 a->busy_##n##_ports_per_thread[(e_port - 1024) / sm->port_per_thread]--; \
Matus Fabian624b8d92017-09-12 04:15:30 -0700840 } \
Matus Fabian09d96f42017-02-02 01:43:00 -0800841 break;
842 foreach_snat_protocol
843#undef _
844 default:
845 clib_warning("unknown_protocol");
846 return VNET_API_ERROR_INVALID_VALUE_2;
847 }
Matus Fabiandb649882016-08-26 05:45:27 -0700848 break;
849 }
850 }
851 }
852
Matus Fabianb932d262017-12-18 05:38:24 -0800853 if (sm->num_workers > 1)
854 tsm = vec_elt_at_index (sm->per_thread_data, m->worker_index);
855 else
856 tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
857
Matus Fabiandb649882016-08-26 05:45:27 -0700858 m_key.addr = m->local_addr;
859 m_key.port = m->local_port;
Matus Fabian09d96f42017-02-02 01:43:00 -0800860 m_key.protocol = m->proto;
Matus Fabian7e46a4d2016-10-06 04:28:29 -0700861 m_key.fib_index = m->fib_index;
Matus Fabiandb649882016-08-26 05:45:27 -0700862 kv.key = m_key.as_u64;
863 clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 0);
Matus Fabianb932d262017-12-18 05:38:24 -0800864 if (twice_nat)
865 {
866 m_key.port = clib_host_to_net_u16 (m->local_port);
867 kv.key = m_key.as_u64;
868 kv.value = ~0ULL;
869 if (clib_bihash_add_del_8_8(&tsm->in2out, &kv, 0))
870 clib_warning ("in2out key del failed");
871 }
Matus Fabiandb649882016-08-26 05:45:27 -0700872
873 m_key.addr = m->external_addr;
874 m_key.port = m->external_port;
Matus Fabian7e46a4d2016-10-06 04:28:29 -0700875 m_key.fib_index = sm->outside_fib_index;
Matus Fabiandb649882016-08-26 05:45:27 -0700876 kv.key = m_key.as_u64;
877 clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 0);
Matus Fabianb932d262017-12-18 05:38:24 -0800878 if (twice_nat)
879 {
880 m_key.port = clib_host_to_net_u16 (m->external_port);
881 kv.key = m_key.as_u64;
882 kv.value = ~0ULL;
883 if (clib_bihash_add_del_8_8(&tsm->out2in, &kv, 0))
884 clib_warning ("in2out key del failed");
885 }
Matus Fabiandb649882016-08-26 05:45:27 -0700886
887 /* Delete session(s) for static mapping if exist */
888 if (!(sm->static_mapping_only) ||
889 (sm->static_mapping_only && sm->static_mapping_connection_tracking))
890 {
891 snat_user_key_t u_key;
892 snat_user_t *u;
893 dlist_elt_t * head, * elt;
Matus Fabianb932d262017-12-18 05:38:24 -0800894 u32 elt_index, head_index;
Matus Fabiandb649882016-08-26 05:45:27 -0700895 u32 ses_index;
Matus Fabian475f0552016-10-19 06:17:52 -0700896 u64 user_index;
Matus Fabiandb649882016-08-26 05:45:27 -0700897 snat_session_t * s;
898
899 u_key.addr = m->local_addr;
900 u_key.fib_index = m->fib_index;
901 kv.key = u_key.as_u64;
Matus Fabian092b3cd2017-09-19 05:42:38 -0700902 if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
Matus Fabiandb649882016-08-26 05:45:27 -0700903 {
Matus Fabian475f0552016-10-19 06:17:52 -0700904 user_index = value.value;
Matus Fabian475f0552016-10-19 06:17:52 -0700905 u = pool_elt_at_index (tsm->users, user_index);
Matus Fabiandb649882016-08-26 05:45:27 -0700906 if (u->nstaticsessions)
907 {
908 head_index = u->sessions_per_user_list_head_index;
Matus Fabian475f0552016-10-19 06:17:52 -0700909 head = pool_elt_at_index (tsm->list_pool, head_index);
Matus Fabiandb649882016-08-26 05:45:27 -0700910 elt_index = head->next;
Matus Fabian475f0552016-10-19 06:17:52 -0700911 elt = pool_elt_at_index (tsm->list_pool, elt_index);
Matus Fabiandb649882016-08-26 05:45:27 -0700912 ses_index = elt->value;
913 while (ses_index != ~0)
914 {
Matus Fabian475f0552016-10-19 06:17:52 -0700915 s = pool_elt_at_index (tsm->sessions, ses_index);
Matus Fabianb932d262017-12-18 05:38:24 -0800916 elt = pool_elt_at_index (tsm->list_pool, elt->next);
Matus Fabian475f0552016-10-19 06:17:52 -0700917 ses_index = elt->value;
Matus Fabiandb649882016-08-26 05:45:27 -0700918
919 if (!addr_only)
920 {
921 if ((s->out2in.addr.as_u32 != e_addr.as_u32) &&
922 (clib_net_to_host_u16 (s->out2in.port) != e_port))
923 continue;
924 }
Matus Fabian475f0552016-10-19 06:17:52 -0700925
Matus Fabianb932d262017-12-18 05:38:24 -0800926 nat_free_session_data (sm, s, tsm - sm->per_thread_data);
927 clib_dlist_remove (tsm->list_pool, s->per_user_index);
928 pool_put_index (tsm->list_pool, s->per_user_index);
Matus Fabian475f0552016-10-19 06:17:52 -0700929 pool_put (tsm->sessions, s);
Matus Fabian475f0552016-10-19 06:17:52 -0700930 u->nstaticsessions--;
Matus Fabiandb649882016-08-26 05:45:27 -0700931
932 if (!addr_only)
933 break;
Matus Fabiandb649882016-08-26 05:45:27 -0700934 }
935 if (addr_only)
936 {
Matus Fabian475f0552016-10-19 06:17:52 -0700937 pool_put (tsm->users, u);
Matus Fabian092b3cd2017-09-19 05:42:38 -0700938 clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 0);
Matus Fabiandb649882016-08-26 05:45:27 -0700939 }
Matus Fabiandb649882016-08-26 05:45:27 -0700940 }
941 }
942 }
943
944 /* Delete static mapping from pool */
945 pool_put (sm->static_mappings, m);
946 }
947
Matus Fabianab7a8052017-11-28 04:29:41 -0800948 if (!addr_only || (l_addr.as_u32 == e_addr.as_u32))
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800949 return 0;
950
951 /* Add/delete external address to FIB */
952 pool_foreach (interface, sm->interfaces,
953 ({
Matus Fabian36ea2d62017-10-24 04:13:49 -0700954 if (nat_interface_is_inside(interface))
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800955 continue;
956
Matus Fabian066f0342017-02-10 03:48:01 -0800957 snat_add_del_addr_to_fib(&e_addr, 32, interface->sw_if_index, is_add);
Matus Fabiandccbee32017-01-31 22:20:30 -0800958 break;
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800959 }));
Matus Fabian93d84c92017-07-19 08:06:01 -0700960 pool_foreach (interface, sm->output_feature_interfaces,
961 ({
Matus Fabian36ea2d62017-10-24 04:13:49 -0700962 if (nat_interface_is_inside(interface))
Matus Fabian93d84c92017-07-19 08:06:01 -0700963 continue;
964
965 snat_add_del_addr_to_fib(&e_addr, 32, interface->sw_if_index, is_add);
966 break;
967 }));
Matus Fabiane1ae29a2017-01-27 00:47:58 -0800968
Matus Fabiandb649882016-08-26 05:45:27 -0700969 return 0;
970}
971
Matus Fabian704018c2017-09-04 02:17:18 -0700972int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
973 snat_protocol_t proto, u32 vrf_id,
Matus Fabianb932d262017-12-18 05:38:24 -0800974 nat44_lb_addr_port_t *locals, u8 is_add,
975 u8 twice_nat)
Matus Fabian704018c2017-09-04 02:17:18 -0700976{
977 snat_main_t * sm = &snat_main;
978 snat_static_mapping_t *m;
979 snat_session_key_t m_key;
980 clib_bihash_kv_8_8_t kv, value;
981 u32 fib_index;
982 snat_address_t *a = 0;
983 int i;
984 nat44_lb_addr_port_t *local;
Matus Fabianb932d262017-12-18 05:38:24 -0800985 u32 worker_index = 0, elt_index, head_index, ses_index;
Matus Fabian092b3cd2017-09-19 05:42:38 -0700986 snat_main_per_thread_data_t *tsm;
Matus Fabianb932d262017-12-18 05:38:24 -0800987 snat_user_key_t u_key;
988 snat_user_t *u;
989 snat_session_t * s;
990 dlist_elt_t * head, * elt;
Matus Fabian704018c2017-09-04 02:17:18 -0700991
992 m_key.addr = e_addr;
993 m_key.port = e_port;
994 m_key.protocol = proto;
995 m_key.fib_index = sm->outside_fib_index;
996 kv.key = m_key.as_u64;
997 if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value))
998 m = 0;
999 else
1000 m = pool_elt_at_index (sm->static_mappings, value.value);
1001
1002 if (is_add)
1003 {
1004 if (m)
1005 return VNET_API_ERROR_VALUE_EXIST;
1006
1007 if (vec_len (locals) < 2)
1008 return VNET_API_ERROR_INVALID_VALUE;
1009
1010 fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
Neale Ranns15002542017-09-10 04:39:11 -07001011 vrf_id,
1012 FIB_SOURCE_PLUGIN_HI);
Matus Fabian704018c2017-09-04 02:17:18 -07001013
1014 /* Find external address in allocated addresses and reserve port for
1015 address and port pair mapping when dynamic translations enabled */
1016 if (!sm->static_mapping_only)
1017 {
1018 for (i = 0; i < vec_len (sm->addresses); i++)
1019 {
1020 if (sm->addresses[i].addr.as_u32 == e_addr.as_u32)
1021 {
1022 a = sm->addresses + i;
1023 /* External port must be unused */
1024 switch (proto)
1025 {
1026#define _(N, j, n, s) \
1027 case SNAT_PROTOCOL_##N: \
1028 if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, e_port)) \
1029 return VNET_API_ERROR_INVALID_VALUE; \
1030 clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 1); \
1031 if (e_port > 1024) \
Matus Fabian624b8d92017-09-12 04:15:30 -07001032 { \
1033 a->busy_##n##_ports++; \
Matus Fabianed3c1602017-09-21 05:07:12 -07001034 a->busy_##n##_ports_per_thread[(e_port - 1024) / sm->port_per_thread]++; \
Matus Fabian624b8d92017-09-12 04:15:30 -07001035 } \
Matus Fabian704018c2017-09-04 02:17:18 -07001036 break;
1037 foreach_snat_protocol
1038#undef _
1039 default:
1040 clib_warning("unknown_protocol");
1041 return VNET_API_ERROR_INVALID_VALUE_2;
1042 }
1043 break;
1044 }
1045 }
1046 /* External address must be allocated */
1047 if (!a)
1048 return VNET_API_ERROR_NO_SUCH_ENTRY;
1049 }
1050
1051 pool_get (sm->static_mappings, m);
1052 memset (m, 0, sizeof (*m));
1053 m->external_addr = e_addr;
1054 m->addr_only = 0;
1055 m->vrf_id = vrf_id;
1056 m->fib_index = fib_index;
1057 m->external_port = e_port;
1058 m->proto = proto;
Matus Fabianb932d262017-12-18 05:38:24 -08001059 m->twice_nat = twice_nat;
Matus Fabian704018c2017-09-04 02:17:18 -07001060
1061 m_key.addr = m->external_addr;
1062 m_key.port = m->external_port;
1063 m_key.protocol = m->proto;
1064 m_key.fib_index = sm->outside_fib_index;
1065 kv.key = m_key.as_u64;
1066 kv.value = m - sm->static_mappings;
1067 if (clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 1))
1068 {
1069 clib_warning ("static_mapping_by_external key add failed");
1070 return VNET_API_ERROR_UNSPECIFIED;
1071 }
Matus Fabian704018c2017-09-04 02:17:18 -07001072
1073 /* Assign worker */
1074 if (sm->workers)
1075 {
Matus Fabian7865b5c2017-09-26 01:23:01 -07001076 worker_index = sm->first_worker_index +
1077 sm->workers[sm->next_worker++ % vec_len (sm->workers)];
Matus Fabian092b3cd2017-09-19 05:42:38 -07001078 tsm = vec_elt_at_index (sm->per_thread_data, worker_index);
Matus Fabianed3c1602017-09-21 05:07:12 -07001079 m->worker_index = worker_index;
Matus Fabian092b3cd2017-09-19 05:42:38 -07001080 }
1081 else
1082 tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
1083
1084 m_key.port = clib_host_to_net_u16 (m->external_port);
1085 kv.key = m_key.as_u64;
1086 kv.value = ~0ULL;
1087 if (clib_bihash_add_del_8_8(&tsm->out2in, &kv, 1))
1088 {
Matus Fabianb932d262017-12-18 05:38:24 -08001089 clib_warning ("out2in key add failed");
Matus Fabian092b3cd2017-09-19 05:42:38 -07001090 return VNET_API_ERROR_UNSPECIFIED;
Matus Fabian704018c2017-09-04 02:17:18 -07001091 }
1092
Matus Fabian092b3cd2017-09-19 05:42:38 -07001093 m_key.fib_index = m->fib_index;
Matus Fabian704018c2017-09-04 02:17:18 -07001094 for (i = 0; i < vec_len (locals); i++)
1095 {
1096 m_key.addr = locals[i].addr;
1097 m_key.port = locals[i].port;
1098 kv.key = m_key.as_u64;
1099 kv.value = m - sm->static_mappings;
1100 clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 1);
flyingeagle23e8efd7d2017-09-11 16:02:40 +08001101 locals[i].prefix = (i == 0) ? locals[i].probability :\
1102 (locals[i - 1].prefix + locals[i].probability);
Matus Fabian704018c2017-09-04 02:17:18 -07001103 vec_add1 (m->locals, locals[i]);
Matus Fabian7865b5c2017-09-26 01:23:01 -07001104
Matus Fabian704018c2017-09-04 02:17:18 -07001105 m_key.port = clib_host_to_net_u16 (locals[i].port);
1106 kv.key = m_key.as_u64;
1107 kv.value = ~0ULL;
Matus Fabian092b3cd2017-09-19 05:42:38 -07001108 if (clib_bihash_add_del_8_8(&tsm->in2out, &kv, 1))
Matus Fabian704018c2017-09-04 02:17:18 -07001109 {
1110 clib_warning ("in2out key add failed");
1111 return VNET_API_ERROR_UNSPECIFIED;
1112 }
Matus Fabian704018c2017-09-04 02:17:18 -07001113 }
1114 }
1115 else
1116 {
1117 if (!m)
1118 return VNET_API_ERROR_NO_SUCH_ENTRY;
1119
Neale Ranns15002542017-09-10 04:39:11 -07001120 fib_table_unlock (m->fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_PLUGIN_HI);
Matus Fabian704018c2017-09-04 02:17:18 -07001121
1122 /* Free external address port */
1123 if (!sm->static_mapping_only)
1124 {
1125 for (i = 0; i < vec_len (sm->addresses); i++)
1126 {
1127 if (sm->addresses[i].addr.as_u32 == e_addr.as_u32)
1128 {
1129 a = sm->addresses + i;
1130 switch (proto)
1131 {
1132#define _(N, j, n, s) \
1133 case SNAT_PROTOCOL_##N: \
1134 clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 0); \
1135 if (e_port > 1024) \
Matus Fabian624b8d92017-09-12 04:15:30 -07001136 { \
1137 a->busy_##n##_ports--; \
Matus Fabianed3c1602017-09-21 05:07:12 -07001138 a->busy_##n##_ports_per_thread[(e_port - 1024) / sm->port_per_thread]--; \
Matus Fabian624b8d92017-09-12 04:15:30 -07001139 } \
Matus Fabian704018c2017-09-04 02:17:18 -07001140 break;
1141 foreach_snat_protocol
1142#undef _
1143 default:
1144 clib_warning("unknown_protocol");
1145 return VNET_API_ERROR_INVALID_VALUE_2;
1146 }
1147 break;
1148 }
1149 }
1150 }
1151
Matus Fabianed3c1602017-09-21 05:07:12 -07001152 tsm = vec_elt_at_index (sm->per_thread_data, m->worker_index);
Matus Fabian704018c2017-09-04 02:17:18 -07001153 m_key.addr = m->external_addr;
1154 m_key.port = m->external_port;
1155 m_key.protocol = m->proto;
1156 m_key.fib_index = sm->outside_fib_index;
1157 kv.key = m_key.as_u64;
1158 if (clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 0))
1159 {
1160 clib_warning ("static_mapping_by_external key del failed");
1161 return VNET_API_ERROR_UNSPECIFIED;
1162 }
Matus Fabian7865b5c2017-09-26 01:23:01 -07001163
Matus Fabian704018c2017-09-04 02:17:18 -07001164 m_key.port = clib_host_to_net_u16 (m->external_port);
1165 kv.key = m_key.as_u64;
Matus Fabian092b3cd2017-09-19 05:42:38 -07001166 if (clib_bihash_add_del_8_8(&tsm->out2in, &kv, 0))
Matus Fabian704018c2017-09-04 02:17:18 -07001167 {
1168 clib_warning ("outi2in key del failed");
1169 return VNET_API_ERROR_UNSPECIFIED;
1170 }
1171
1172 vec_foreach (local, m->locals)
1173 {
1174 m_key.addr = local->addr;
1175 m_key.port = local->port;
1176 m_key.fib_index = m->fib_index;
1177 kv.key = m_key.as_u64;
1178 if (clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 0))
1179 {
1180 clib_warning ("static_mapping_by_local key del failed");
1181 return VNET_API_ERROR_UNSPECIFIED;
1182 }
Matus Fabian7865b5c2017-09-26 01:23:01 -07001183
Matus Fabian704018c2017-09-04 02:17:18 -07001184 m_key.port = clib_host_to_net_u16 (local->port);
1185 kv.key = m_key.as_u64;
Matus Fabian092b3cd2017-09-19 05:42:38 -07001186 if (clib_bihash_add_del_8_8(&tsm->in2out, &kv, 0))
Matus Fabian704018c2017-09-04 02:17:18 -07001187 {
1188 clib_warning ("in2out key del failed");
1189 return VNET_API_ERROR_UNSPECIFIED;
1190 }
Matus Fabianb932d262017-12-18 05:38:24 -08001191 /* Delete sessions */
1192 u_key.addr = local->addr;
1193 u_key.fib_index = m->fib_index;
1194 kv.key = u_key.as_u64;
1195 if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
1196 {
1197 u = pool_elt_at_index (tsm->users, value.value);
1198 if (u->nstaticsessions)
1199 {
1200 head_index = u->sessions_per_user_list_head_index;
1201 head = pool_elt_at_index (tsm->list_pool, head_index);
1202 elt_index = head->next;
1203 elt = pool_elt_at_index (tsm->list_pool, elt_index);
1204 ses_index = elt->value;
1205 while (ses_index != ~0)
1206 {
1207 s = pool_elt_at_index (tsm->sessions, ses_index);
1208 elt = pool_elt_at_index (tsm->list_pool, elt->next);
1209 ses_index = elt->value;
1210
1211 if ((s->in2out.addr.as_u32 != local->addr.as_u32) &&
1212 (clib_net_to_host_u16 (s->in2out.port) != local->port))
1213 continue;
1214
1215 nat_free_session_data (sm, s, tsm - sm->per_thread_data);
1216 clib_dlist_remove (tsm->list_pool, s->per_user_index);
1217 pool_put_index (tsm->list_pool, s->per_user_index);
1218 pool_put (tsm->sessions, s);
1219 u->nstaticsessions--;
1220 }
1221 }
1222 }
Matus Fabian704018c2017-09-04 02:17:18 -07001223 }
flyingeagle2301ffc0c2017-09-13 19:03:56 +08001224 vec_free(m->locals);
Matus Fabian704018c2017-09-04 02:17:18 -07001225
1226 pool_put (sm->static_mappings, m);
1227 }
1228
1229 return 0;
1230}
1231
Matus Fabianb932d262017-12-18 05:38:24 -08001232int
1233snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm,
1234 u8 twice_nat)
Matus Fabian36532bd2017-01-23 23:42:28 -08001235{
1236 snat_address_t *a = 0;
1237 snat_session_t *ses;
1238 u32 *ses_to_be_removed = 0, *ses_index;
1239 clib_bihash_kv_8_8_t kv, value;
1240 snat_user_key_t user_key;
1241 snat_user_t *u;
1242 snat_main_per_thread_data_t *tsm;
1243 snat_static_mapping_t *m;
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001244 snat_interface_t *interface;
Matus Fabian36532bd2017-01-23 23:42:28 -08001245 int i;
Matus Fabianb932d262017-12-18 05:38:24 -08001246 snat_address_t *addresses = twice_nat ? sm->twice_nat_addresses : sm->addresses;
Matus Fabian36532bd2017-01-23 23:42:28 -08001247
1248 /* Find SNAT address */
Matus Fabianb932d262017-12-18 05:38:24 -08001249 for (i=0; i < vec_len (addresses); i++)
Matus Fabian36532bd2017-01-23 23:42:28 -08001250 {
Matus Fabianb932d262017-12-18 05:38:24 -08001251 if (addresses[i].addr.as_u32 == addr.as_u32)
Matus Fabian36532bd2017-01-23 23:42:28 -08001252 {
Matus Fabianb932d262017-12-18 05:38:24 -08001253 a = addresses + i;
Matus Fabian36532bd2017-01-23 23:42:28 -08001254 break;
1255 }
1256 }
1257 if (!a)
1258 return VNET_API_ERROR_NO_SUCH_ENTRY;
1259
1260 if (delete_sm)
1261 {
1262 pool_foreach (m, sm->static_mappings,
1263 ({
1264 if (m->external_addr.as_u32 == addr.as_u32)
1265 (void) snat_add_static_mapping (m->local_addr, m->external_addr,
1266 m->local_port, m->external_port,
Matus Fabian09d96f42017-02-02 01:43:00 -08001267 m->vrf_id, m->addr_only, ~0,
Matus Fabianb932d262017-12-18 05:38:24 -08001268 m->proto, 0, m->twice_nat);
Matus Fabian36532bd2017-01-23 23:42:28 -08001269 }));
1270 }
1271 else
1272 {
1273 /* Check if address is used in some static mapping */
1274 if (is_snat_address_used_in_static_mapping(sm, addr))
1275 {
1276 clib_warning ("address used in static mapping");
1277 return VNET_API_ERROR_UNSPECIFIED;
1278 }
1279 }
1280
Matus Fabianf8d84902017-07-23 23:41:03 -07001281 if (a->fib_index != ~0)
Neale Ranns15002542017-09-10 04:39:11 -07001282 fib_table_unlock(a->fib_index, FIB_PROTOCOL_IP4,
1283 FIB_SOURCE_PLUGIN_HI);
Matus Fabianf8d84902017-07-23 23:41:03 -07001284
Matus Fabian36532bd2017-01-23 23:42:28 -08001285 /* Delete sessions using address */
Matus Fabian09d96f42017-02-02 01:43:00 -08001286 if (a->busy_tcp_ports || a->busy_udp_ports || a->busy_icmp_ports)
Matus Fabian36532bd2017-01-23 23:42:28 -08001287 {
1288 vec_foreach (tsm, sm->per_thread_data)
1289 {
1290 pool_foreach (ses, tsm->sessions, ({
1291 if (ses->out2in.addr.as_u32 == addr.as_u32)
1292 {
Matus Fabianb932d262017-12-18 05:38:24 -08001293 ses->outside_address_index = ~0;
1294 nat_free_session_data (sm, ses, tsm - sm->per_thread_data);
Matus Fabian36532bd2017-01-23 23:42:28 -08001295 clib_dlist_remove (tsm->list_pool, ses->per_user_index);
Matus Fabianb932d262017-12-18 05:38:24 -08001296 pool_put_index (tsm->list_pool, ses->per_user_index);
1297 vec_add1 (ses_to_be_removed, ses - tsm->sessions);
Matus Fabian36532bd2017-01-23 23:42:28 -08001298 user_key.addr = ses->in2out.addr;
1299 user_key.fib_index = ses->in2out.fib_index;
1300 kv.key = user_key.as_u64;
Matus Fabian092b3cd2017-09-19 05:42:38 -07001301 if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
Matus Fabian36532bd2017-01-23 23:42:28 -08001302 {
1303 u = pool_elt_at_index (tsm->users, value.value);
1304 u->nsessions--;
1305 }
1306 }
1307 }));
1308
1309 vec_foreach (ses_index, ses_to_be_removed)
1310 pool_put_index (tsm->sessions, ses_index[0]);
1311
1312 vec_free (ses_to_be_removed);
1313 }
1314 }
1315
Matus Fabianb932d262017-12-18 05:38:24 -08001316 if (twice_nat)
1317 {
1318 vec_del1 (sm->twice_nat_addresses, i);
1319 return 0;
1320 }
1321 else
1322 vec_del1 (sm->addresses, i);
Matus Fabian36532bd2017-01-23 23:42:28 -08001323
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001324 /* Delete external address from FIB */
1325 pool_foreach (interface, sm->interfaces,
1326 ({
Matus Fabian36ea2d62017-10-24 04:13:49 -07001327 if (nat_interface_is_inside(interface))
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001328 continue;
1329
Matus Fabian066f0342017-02-10 03:48:01 -08001330 snat_add_del_addr_to_fib(&addr, 32, interface->sw_if_index, 0);
Matus Fabiandccbee32017-01-31 22:20:30 -08001331 break;
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001332 }));
Matus Fabian93d84c92017-07-19 08:06:01 -07001333 pool_foreach (interface, sm->output_feature_interfaces,
1334 ({
Matus Fabian36ea2d62017-10-24 04:13:49 -07001335 if (nat_interface_is_inside(interface))
Matus Fabian93d84c92017-07-19 08:06:01 -07001336 continue;
1337
1338 snat_add_del_addr_to_fib(&addr, 32, interface->sw_if_index, 0);
1339 break;
1340 }));
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001341
Matus Fabian36532bd2017-01-23 23:42:28 -08001342 return 0;
1343}
1344
Matus Fabiancfe0fc92017-05-10 06:37:47 -07001345int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del)
Matus Fabian588144a2016-10-24 03:30:00 -07001346{
1347 snat_main_t *sm = &snat_main;
1348 snat_interface_t *i;
Matus Fabian36ea2d62017-10-24 04:13:49 -07001349 const char * feature_name, *del_feature_name;
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001350 snat_address_t * ap;
1351 snat_static_mapping_t * m;
Matus Fabian066f0342017-02-10 03:48:01 -08001352 snat_det_map_t * dm;
Matus Fabian588144a2016-10-24 03:30:00 -07001353
1354 if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking))
Matus Fabian2ba92e32017-08-21 07:05:03 -07001355 feature_name = is_inside ? "nat44-in2out-fast" : "nat44-out2in-fast";
Matus Fabian588144a2016-10-24 03:30:00 -07001356 else
Matus Fabian475f0552016-10-19 06:17:52 -07001357 {
Matus Fabian066f0342017-02-10 03:48:01 -08001358 if (sm->num_workers > 1 && !sm->deterministic)
Matus Fabian2ba92e32017-08-21 07:05:03 -07001359 feature_name = is_inside ? "nat44-in2out-worker-handoff" : "nat44-out2in-worker-handoff";
Matus Fabian066f0342017-02-10 03:48:01 -08001360 else if (sm->deterministic)
Matus Fabian2ba92e32017-08-21 07:05:03 -07001361 feature_name = is_inside ? "nat44-det-in2out" : "nat44-det-out2in";
Matus Fabian475f0552016-10-19 06:17:52 -07001362 else
Matus Fabian2ba92e32017-08-21 07:05:03 -07001363 feature_name = is_inside ? "nat44-in2out" : "nat44-out2in";
Matus Fabian475f0552016-10-19 06:17:52 -07001364 }
Matus Fabian588144a2016-10-24 03:30:00 -07001365
Matus Fabian066f0342017-02-10 03:48:01 -08001366 if (sm->fq_in2out_index == ~0 && !sm->deterministic && sm->num_workers > 1)
1367 sm->fq_in2out_index = vlib_frame_queue_main_init (sm->in2out_node_index, 0);
Matus Fabian475f0552016-10-19 06:17:52 -07001368
Matus Fabian066f0342017-02-10 03:48:01 -08001369 if (sm->fq_out2in_index == ~0 && !sm->deterministic && sm->num_workers > 1)
1370 sm->fq_out2in_index = vlib_frame_queue_main_init (sm->out2in_node_index, 0);
Matus Fabian475f0552016-10-19 06:17:52 -07001371
Matus Fabian588144a2016-10-24 03:30:00 -07001372 pool_foreach (i, sm->interfaces,
1373 ({
1374 if (i->sw_if_index == sw_if_index)
1375 {
1376 if (is_del)
Matus Fabian36ea2d62017-10-24 04:13:49 -07001377 {
1378 if (nat_interface_is_inside(i) && nat_interface_is_outside(i))
1379 {
1380 if (is_inside)
1381 i->flags &= ~NAT_INTERFACE_FLAG_IS_INSIDE;
1382 else
1383 i->flags &= ~NAT_INTERFACE_FLAG_IS_OUTSIDE;
1384
1385 if (sm->num_workers > 1 && !sm->deterministic)
1386 del_feature_name = "nat44-handoff-classify";
1387 else if (sm->deterministic)
1388 del_feature_name = "nat44-det-classify";
1389 else
1390 del_feature_name = "nat44-classify";
1391
1392 vnet_feature_enable_disable ("ip4-unicast", del_feature_name,
1393 sw_if_index, 0, 0, 0);
1394 vnet_feature_enable_disable ("ip4-unicast", feature_name,
1395 sw_if_index, 1, 0, 0);
1396 }
1397 else
1398 {
1399 vnet_feature_enable_disable ("ip4-unicast", feature_name,
1400 sw_if_index, 0, 0, 0);
1401 pool_put (sm->interfaces, i);
1402 }
1403 }
Matus Fabian588144a2016-10-24 03:30:00 -07001404 else
Matus Fabian36ea2d62017-10-24 04:13:49 -07001405 {
1406 if ((nat_interface_is_inside(i) && is_inside) ||
1407 (nat_interface_is_outside(i) && !is_inside))
1408 return 0;
1409
1410 if (sm->num_workers > 1 && !sm->deterministic)
1411 {
1412 del_feature_name = !is_inside ? "nat44-in2out-worker-handoff" :
1413 "nat44-out2in-worker-handoff";
1414 feature_name = "nat44-handoff-classify";
1415 }
1416 else if (sm->deterministic)
1417 {
1418 del_feature_name = !is_inside ? "nat44-det-in2out" :
1419 "nat44-det-out2in";
1420 feature_name = "nat44-det-classify";
1421 }
1422 else
1423 {
1424 del_feature_name = !is_inside ? "nat44-in2out" : "nat44-out2in";
1425 feature_name = "nat44-classify";
1426 }
1427
1428 vnet_feature_enable_disable ("ip4-unicast", del_feature_name,
1429 sw_if_index, 0, 0, 0);
1430 vnet_feature_enable_disable ("ip4-unicast", feature_name,
1431 sw_if_index, 1, 0, 0);
1432 goto set_flags;
1433 }
Matus Fabian588144a2016-10-24 03:30:00 -07001434
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001435 goto fib;
Matus Fabian588144a2016-10-24 03:30:00 -07001436 }
1437 }));
1438
1439 if (is_del)
1440 return VNET_API_ERROR_NO_SUCH_ENTRY;
1441
1442 pool_get (sm->interfaces, i);
1443 i->sw_if_index = sw_if_index;
Matus Fabian36ea2d62017-10-24 04:13:49 -07001444 i->flags = 0;
1445 vnet_feature_enable_disable ("ip4-unicast", feature_name, sw_if_index, 1, 0, 0);
1446
1447set_flags:
1448 if (is_inside)
1449 i->flags |= NAT_INTERFACE_FLAG_IS_INSIDE;
1450 else
1451 i->flags |= NAT_INTERFACE_FLAG_IS_OUTSIDE;
Matus Fabian588144a2016-10-24 03:30:00 -07001452
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001453 /* Add/delete external addresses to FIB */
1454fib:
1455 if (is_inside)
Matus Fabian87da4762017-10-04 08:03:56 -07001456 {
1457 vnet_feature_enable_disable ("ip4-local", "nat44-hairpinning",
1458 sw_if_index, !is_del, 0, 0);
1459 return 0;
1460 }
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001461
1462 vec_foreach (ap, sm->addresses)
Matus Fabian066f0342017-02-10 03:48:01 -08001463 snat_add_del_addr_to_fib(&ap->addr, 32, sw_if_index, !is_del);
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001464
1465 pool_foreach (m, sm->static_mappings,
1466 ({
Matus Fabianab7a8052017-11-28 04:29:41 -08001467 if (!(m->addr_only) || (m->local_addr.as_u32 == m->external_addr.as_u32))
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001468 continue;
1469
Matus Fabian066f0342017-02-10 03:48:01 -08001470 snat_add_del_addr_to_fib(&m->external_addr, 32, sw_if_index, !is_del);
1471 }));
1472
1473 pool_foreach (dm, sm->det_maps,
1474 ({
1475 snat_add_del_addr_to_fib(&dm->out_addr, dm->out_plen, sw_if_index, !is_del);
Matus Fabiane1ae29a2017-01-27 00:47:58 -08001476 }));
1477
Matus Fabian588144a2016-10-24 03:30:00 -07001478 return 0;
1479}
1480
Matus Fabian93d84c92017-07-19 08:06:01 -07001481int snat_interface_add_del_output_feature (u32 sw_if_index,
1482 u8 is_inside,
1483 int is_del)
1484{
1485 snat_main_t *sm = &snat_main;
1486 snat_interface_t *i;
1487 snat_address_t * ap;
1488 snat_static_mapping_t * m;
1489
1490 if (sm->deterministic ||
1491 (sm->static_mapping_only && !(sm->static_mapping_connection_tracking)))
1492 return VNET_API_ERROR_UNSUPPORTED;
1493
1494 if (is_inside)
Matus Fabian161c59c2017-07-21 03:46:03 -07001495 {
Matus Fabian2ba92e32017-08-21 07:05:03 -07001496 vnet_feature_enable_disable ("ip4-unicast", "nat44-hairpin-dst",
Matus Fabian161c59c2017-07-21 03:46:03 -07001497 sw_if_index, !is_del, 0, 0);
Matus Fabian2ba92e32017-08-21 07:05:03 -07001498 vnet_feature_enable_disable ("ip4-output", "nat44-hairpin-src",
Matus Fabian161c59c2017-07-21 03:46:03 -07001499 sw_if_index, !is_del, 0, 0);
1500 goto fq;
1501 }
Matus Fabian93d84c92017-07-19 08:06:01 -07001502
1503 if (sm->num_workers > 1)
1504 {
Matus Fabian2ba92e32017-08-21 07:05:03 -07001505 vnet_feature_enable_disable ("ip4-unicast", "nat44-out2in-worker-handoff",
Matus Fabian93d84c92017-07-19 08:06:01 -07001506 sw_if_index, !is_del, 0, 0);
1507 vnet_feature_enable_disable ("ip4-output",
Matus Fabian2ba92e32017-08-21 07:05:03 -07001508 "nat44-in2out-output-worker-handoff",
Matus Fabian93d84c92017-07-19 08:06:01 -07001509 sw_if_index, !is_del, 0, 0);
1510 }
1511 else
1512 {
Matus Fabian2ba92e32017-08-21 07:05:03 -07001513 vnet_feature_enable_disable ("ip4-unicast", "nat44-out2in", sw_if_index,
Matus Fabian93d84c92017-07-19 08:06:01 -07001514 !is_del, 0, 0);
Matus Fabian2ba92e32017-08-21 07:05:03 -07001515 vnet_feature_enable_disable ("ip4-output", "nat44-in2out-output",
Matus Fabian93d84c92017-07-19 08:06:01 -07001516 sw_if_index, !is_del, 0, 0);
1517 }
1518
Matus Fabian161c59c2017-07-21 03:46:03 -07001519fq:
Matus Fabian93d84c92017-07-19 08:06:01 -07001520 if (sm->fq_in2out_output_index == ~0 && sm->num_workers > 1)
1521 sm->fq_in2out_output_index =
1522 vlib_frame_queue_main_init (sm->in2out_output_node_index, 0);
1523
1524 if (sm->fq_out2in_index == ~0 && sm->num_workers > 1)
1525 sm->fq_out2in_index = vlib_frame_queue_main_init (sm->out2in_node_index, 0);
1526
Matus Fabian93d84c92017-07-19 08:06:01 -07001527 pool_foreach (i, sm->output_feature_interfaces,
1528 ({
1529 if (i->sw_if_index == sw_if_index)
1530 {
1531 if (is_del)
1532 pool_put (sm->output_feature_interfaces, i);
1533 else
1534 return VNET_API_ERROR_VALUE_EXIST;
1535
1536 goto fib;
1537 }
1538 }));
1539
1540 if (is_del)
1541 return VNET_API_ERROR_NO_SUCH_ENTRY;
1542
1543 pool_get (sm->output_feature_interfaces, i);
1544 i->sw_if_index = sw_if_index;
Matus Fabian36ea2d62017-10-24 04:13:49 -07001545 i->flags = 0;
1546 if (is_inside)
1547 i->flags |= NAT_INTERFACE_FLAG_IS_INSIDE;
1548 else
1549 i->flags |= NAT_INTERFACE_FLAG_IS_OUTSIDE;
Matus Fabian93d84c92017-07-19 08:06:01 -07001550
1551 /* Add/delete external addresses to FIB */
1552fib:
1553 if (is_inside)
1554 return 0;
1555
1556 vec_foreach (ap, sm->addresses)
1557 snat_add_del_addr_to_fib(&ap->addr, 32, sw_if_index, !is_del);
1558
1559 pool_foreach (m, sm->static_mappings,
1560 ({
1561 if (!(m->addr_only))
1562 continue;
1563
1564 snat_add_del_addr_to_fib(&m->external_addr, 32, sw_if_index, !is_del);
1565 }));
1566
1567 return 0;
1568}
1569
Matus Fabiancfe0fc92017-05-10 06:37:47 -07001570int snat_set_workers (uword * bitmap)
Matus Fabian475f0552016-10-19 06:17:52 -07001571{
1572 snat_main_t *sm = &snat_main;
Matus Fabian7801ca22017-08-03 00:58:05 -07001573 int i, j = 0;
Matus Fabian475f0552016-10-19 06:17:52 -07001574
1575 if (sm->num_workers < 2)
1576 return VNET_API_ERROR_FEATURE_DISABLED;
1577
1578 if (clib_bitmap_last_set (bitmap) >= sm->num_workers)
1579 return VNET_API_ERROR_INVALID_WORKER;
1580
1581 vec_free (sm->workers);
1582 clib_bitmap_foreach (i, bitmap,
1583 ({
1584 vec_add1(sm->workers, i);
Matus Fabian7801ca22017-08-03 00:58:05 -07001585 sm->per_thread_data[i].snat_thread_index = j;
1586 j++;
Matus Fabian475f0552016-10-19 06:17:52 -07001587 }));
1588
Matus Fabian7801ca22017-08-03 00:58:05 -07001589 sm->port_per_thread = (0xffff - 1024) / _vec_len (sm->workers);
1590 sm->num_snat_thread = _vec_len (sm->workers);
1591
Matus Fabian475f0552016-10-19 06:17:52 -07001592 return 0;
1593}
1594
Dave Barach8b275372017-01-16 10:54:02 -05001595
Dave Barachcab65ec2017-01-11 13:01:14 -05001596static void
1597snat_ip4_add_del_interface_address_cb (ip4_main_t * im,
1598 uword opaque,
1599 u32 sw_if_index,
1600 ip4_address_t * address,
1601 u32 address_length,
1602 u32 if_address_index,
1603 u32 is_delete);
1604
Matus Fabian27697102017-11-09 01:43:47 -08001605static int
1606nat_alloc_addr_and_port_default (snat_address_t * addresses,
1607 u32 fib_index,
1608 u32 thread_index,
1609 snat_session_key_t * k,
1610 u32 * address_indexp,
Matus Fabian27697102017-11-09 01:43:47 -08001611 u16 port_per_thread,
1612 u32 snat_thread_index);
1613
Dave Barach20c02cb2016-06-26 10:42:08 -04001614static clib_error_t * snat_init (vlib_main_t * vm)
1615{
1616 snat_main_t * sm = &snat_main;
Matus Fabian08ce4322017-06-19 05:28:27 -07001617 clib_error_t * error = 0;
Dave Barach20c02cb2016-06-26 10:42:08 -04001618 ip4_main_t * im = &ip4_main;
1619 ip_lookup_main_t * lm = &im->lookup_main;
Matus Fabian475f0552016-10-19 06:17:52 -07001620 uword *p;
1621 vlib_thread_registration_t *tr;
1622 vlib_thread_main_t *tm = vlib_get_thread_main ();
1623 uword *bitmap = 0;
1624 u32 i;
Dave Barachcab65ec2017-01-11 13:01:14 -05001625 ip4_add_del_interface_address_callback_t cb4;
Dave Barach20c02cb2016-06-26 10:42:08 -04001626
Dave Barach20c02cb2016-06-26 10:42:08 -04001627 sm->vlib_main = vm;
1628 sm->vnet_main = vnet_get_main();
1629 sm->ip4_main = im;
1630 sm->ip4_lookup_main = lm;
1631 sm->api_main = &api_main;
Matus Fabian475f0552016-10-19 06:17:52 -07001632 sm->first_worker_index = 0;
1633 sm->next_worker = 0;
1634 sm->num_workers = 0;
Matus Fabian7801ca22017-08-03 00:58:05 -07001635 sm->num_snat_thread = 1;
Matus Fabian475f0552016-10-19 06:17:52 -07001636 sm->workers = 0;
Matus Fabian7801ca22017-08-03 00:58:05 -07001637 sm->port_per_thread = 0xffff - 1024;
Matus Fabian475f0552016-10-19 06:17:52 -07001638 sm->fq_in2out_index = ~0;
1639 sm->fq_out2in_index = ~0;
Matus Fabian6a0946f2017-04-12 03:36:13 -07001640 sm->udp_timeout = SNAT_UDP_TIMEOUT;
1641 sm->tcp_established_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT;
1642 sm->tcp_transitory_timeout = SNAT_TCP_TRANSITORY_TIMEOUT;
1643 sm->icmp_timeout = SNAT_ICMP_TIMEOUT;
Matus Fabian27697102017-11-09 01:43:47 -08001644 sm->alloc_addr_and_port = nat_alloc_addr_and_port_default;
Juraj Sloboda7b929792017-11-23 13:20:48 +01001645 sm->forwarding_enabled = 0;
Matus Fabian475f0552016-10-19 06:17:52 -07001646
1647 p = hash_get_mem (tm->thread_registrations_by_name, "workers");
1648 if (p)
1649 {
1650 tr = (vlib_thread_registration_t *) p[0];
1651 if (tr)
1652 {
1653 sm->num_workers = tr->count;
1654 sm->first_worker_index = tr->first_index;
1655 }
1656 }
1657
Matus Fabian7801ca22017-08-03 00:58:05 -07001658 vec_validate (sm->per_thread_data, tm->n_vlib_mains - 1);
1659
Matus Fabian475f0552016-10-19 06:17:52 -07001660 /* Use all available workers by default */
1661 if (sm->num_workers > 1)
1662 {
1663 for (i=0; i < sm->num_workers; i++)
1664 bitmap = clib_bitmap_set (bitmap, i, 1);
1665 snat_set_workers(bitmap);
1666 clib_bitmap_free (bitmap);
1667 }
Matus Fabian7801ca22017-08-03 00:58:05 -07001668 else
1669 {
1670 sm->per_thread_data[0].snat_thread_index = 0;
1671 }
Dave Barach20c02cb2016-06-26 10:42:08 -04001672
Matus Fabiancfe0fc92017-05-10 06:37:47 -07001673 error = snat_api_init(vm, sm);
Matus Fabian08ce4322017-06-19 05:28:27 -07001674 if (error)
1675 return error;
Dave Barach20c02cb2016-06-26 10:42:08 -04001676
Dave Barachcab65ec2017-01-11 13:01:14 -05001677 /* Set up the interface address add/del callback */
1678 cb4.function = snat_ip4_add_del_interface_address_cb;
1679 cb4.function_opaque = 0;
1680
1681 vec_add1 (im->add_del_interface_address_callbacks, cb4);
1682
Matus Fabianeea28d72017-01-13 04:15:54 -08001683 /* Init IPFIX logging */
1684 snat_ipfix_logging_init(vm);
1685
Matus Fabianefcd1e92017-08-15 06:59:19 -07001686 /* Init NAT64 */
Matus Fabian08ce4322017-06-19 05:28:27 -07001687 error = nat64_init(vm);
Matus Fabianefcd1e92017-08-15 06:59:19 -07001688 if (error)
1689 return error;
Matus Fabian06596c52017-06-06 04:53:28 -07001690
Matus Fabian8ebe6252017-11-06 05:04:53 -08001691 dslite_init(vm);
1692
Matus Fabianefcd1e92017-08-15 06:59:19 -07001693 /* Init virtual fragmenentation reassembly */
1694 return nat_reass_init(vm);
Dave Barach20c02cb2016-06-26 10:42:08 -04001695}
1696
1697VLIB_INIT_FUNCTION (snat_init);
1698
Matus Fabian8ebe6252017-11-06 05:04:53 -08001699void snat_free_outside_address_and_port (snat_address_t * addresses,
Matus Fabian624b8d92017-09-12 04:15:30 -07001700 u32 thread_index,
Matus Fabian2ba92e32017-08-21 07:05:03 -07001701 snat_session_key_t * k,
Dave Barach20c02cb2016-06-26 10:42:08 -04001702 u32 address_index)
1703{
1704 snat_address_t *a;
1705 u16 port_host_byte_order = clib_net_to_host_u16 (k->port);
Matus Fabian2ba92e32017-08-21 07:05:03 -07001706
Matus Fabian8ebe6252017-11-06 05:04:53 -08001707 ASSERT (address_index < vec_len (addresses));
Dave Barach20c02cb2016-06-26 10:42:08 -04001708
Matus Fabian8ebe6252017-11-06 05:04:53 -08001709 a = addresses + address_index;
Dave Barach20c02cb2016-06-26 10:42:08 -04001710
Matus Fabian09d96f42017-02-02 01:43:00 -08001711 switch (k->protocol)
1712 {
1713#define _(N, i, n, s) \
1714 case SNAT_PROTOCOL_##N: \
1715 ASSERT (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \
1716 port_host_byte_order) == 1); \
1717 clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, \
1718 port_host_byte_order, 0); \
1719 a->busy_##n##_ports--; \
Matus Fabian624b8d92017-09-12 04:15:30 -07001720 a->busy_##n##_ports_per_thread[thread_index]--; \
Matus Fabian09d96f42017-02-02 01:43:00 -08001721 break;
1722 foreach_snat_protocol
1723#undef _
1724 default:
1725 clib_warning("unknown_protocol");
1726 return;
1727 }
Matus Fabian2ba92e32017-08-21 07:05:03 -07001728}
Dave Barach20c02cb2016-06-26 10:42:08 -04001729
Matus Fabiandb649882016-08-26 05:45:27 -07001730/**
Matus Fabian2ba92e32017-08-21 07:05:03 -07001731 * @brief Match NAT44 static mapping.
Matus Fabiandb649882016-08-26 05:45:27 -07001732 *
Matus Fabian2ba92e32017-08-21 07:05:03 -07001733 * @param sm NAT main.
Matus Fabiandb649882016-08-26 05:45:27 -07001734 * @param match Address and port to match.
1735 * @param mapping External or local address and port of the matched mapping.
1736 * @param by_external If 0 match by local address otherwise match by external
1737 * address.
Juraj Slobodad3677682017-04-14 03:24:45 +02001738 * @param is_addr_only If matched mapping is address only
Matus Fabianb932d262017-12-18 05:38:24 -08001739 * @param twice_nat If matched mapping is twice NAT.
Matus Fabiandb649882016-08-26 05:45:27 -07001740 *
1741 * @returns 0 if match found otherwise 1.
1742 */
1743int snat_static_mapping_match (snat_main_t * sm,
1744 snat_session_key_t match,
1745 snat_session_key_t * mapping,
Juraj Slobodad3677682017-04-14 03:24:45 +02001746 u8 by_external,
Matus Fabianb932d262017-12-18 05:38:24 -08001747 u8 *is_addr_only,
1748 u8 *twice_nat)
Matus Fabiandb649882016-08-26 05:45:27 -07001749{
1750 clib_bihash_kv_8_8_t kv, value;
1751 snat_static_mapping_t *m;
Matus Fabian09d96f42017-02-02 01:43:00 -08001752 snat_session_key_t m_key;
Matus Fabiandb649882016-08-26 05:45:27 -07001753 clib_bihash_8_8_t *mapping_hash = &sm->static_mapping_by_local;
Matus Fabian704018c2017-09-04 02:17:18 -07001754 u32 rand, lo = 0, hi, mid;
Matus Fabiandb649882016-08-26 05:45:27 -07001755
1756 if (by_external)
1757 mapping_hash = &sm->static_mapping_by_external;
1758
1759 m_key.addr = match.addr;
1760 m_key.port = clib_net_to_host_u16 (match.port);
Matus Fabian09d96f42017-02-02 01:43:00 -08001761 m_key.protocol = match.protocol;
Matus Fabian7e46a4d2016-10-06 04:28:29 -07001762 m_key.fib_index = match.fib_index;
Matus Fabiandb649882016-08-26 05:45:27 -07001763
1764 kv.key = m_key.as_u64;
1765
1766 if (clib_bihash_search_8_8 (mapping_hash, &kv, &value))
1767 {
1768 /* Try address only mapping */
1769 m_key.port = 0;
Matus Fabian09d96f42017-02-02 01:43:00 -08001770 m_key.protocol = 0;
Matus Fabiandb649882016-08-26 05:45:27 -07001771 kv.key = m_key.as_u64;
1772 if (clib_bihash_search_8_8 (mapping_hash, &kv, &value))
1773 return 1;
1774 }
1775
1776 m = pool_elt_at_index (sm->static_mappings, value.value);
1777
1778 if (by_external)
1779 {
Matus Fabian704018c2017-09-04 02:17:18 -07001780 if (vec_len (m->locals))
1781 {
1782 hi = vec_len (m->locals) - 1;
1783 rand = 1 + (random_u32 (&sm->random_seed) % m->locals[hi].prefix);
1784 while (lo < hi)
1785 {
flyingeagle236a58f4a2017-09-12 15:10:46 +08001786 mid = ((hi - lo) >> 1) + lo;
Matus Fabian704018c2017-09-04 02:17:18 -07001787 (rand > m->locals[mid].prefix) ? (lo = mid + 1) : (hi = mid);
1788 }
1789 if (!(m->locals[lo].prefix >= rand))
1790 return 1;
1791 mapping->addr = m->locals[lo].addr;
1792 mapping->port = clib_host_to_net_u16 (m->locals[lo].port);
1793 }
1794 else
1795 {
1796 mapping->addr = m->local_addr;
1797 /* Address only mapping doesn't change port */
1798 mapping->port = m->addr_only ? match.port
1799 : clib_host_to_net_u16 (m->local_port);
1800 }
Matus Fabiandb649882016-08-26 05:45:27 -07001801 mapping->fib_index = m->fib_index;
Matus Fabian704018c2017-09-04 02:17:18 -07001802 mapping->protocol = m->proto;
Matus Fabiandb649882016-08-26 05:45:27 -07001803 }
1804 else
1805 {
1806 mapping->addr = m->external_addr;
1807 /* Address only mapping doesn't change port */
1808 mapping->port = m->addr_only ? match.port
1809 : clib_host_to_net_u16 (m->external_port);
1810 mapping->fib_index = sm->outside_fib_index;
1811 }
1812
Juraj Slobodad3677682017-04-14 03:24:45 +02001813 if (PREDICT_FALSE(is_addr_only != 0))
1814 *is_addr_only = m->addr_only;
1815
Matus Fabianb932d262017-12-18 05:38:24 -08001816 if (PREDICT_FALSE(twice_nat != 0))
1817 *twice_nat = m->twice_nat;
1818
Matus Fabiandb649882016-08-26 05:45:27 -07001819 return 0;
1820}
1821
Matus Fabian7801ca22017-08-03 00:58:05 -07001822static_always_inline u16
Matus Fabian8ebe6252017-11-06 05:04:53 -08001823snat_random_port (u16 min, u16 max)
Matus Fabian7801ca22017-08-03 00:58:05 -07001824{
Matus Fabian8ebe6252017-11-06 05:04:53 -08001825 snat_main_t *sm = &snat_main;
Matus Fabian7801ca22017-08-03 00:58:05 -07001826 return min + random_u32 (&sm->random_seed) /
1827 (random_u32_max() / (max - min + 1) + 1);
1828}
1829
Matus Fabian27697102017-11-09 01:43:47 -08001830int
1831snat_alloc_outside_address_and_port (snat_address_t * addresses,
1832 u32 fib_index,
1833 u32 thread_index,
1834 snat_session_key_t * k,
1835 u32 * address_indexp,
Matus Fabian27697102017-11-09 01:43:47 -08001836 u16 port_per_thread,
1837 u32 snat_thread_index)
1838{
1839 snat_main_t *sm = &snat_main;
1840
1841 return sm->alloc_addr_and_port(addresses, fib_index, thread_index, k,
Matus Fabian51e759f2017-12-07 23:22:51 -08001842 address_indexp, port_per_thread,
Matus Fabian27697102017-11-09 01:43:47 -08001843 snat_thread_index);
1844}
1845
1846static int
1847nat_alloc_addr_and_port_default (snat_address_t * addresses,
1848 u32 fib_index,
1849 u32 thread_index,
1850 snat_session_key_t * k,
1851 u32 * address_indexp,
Matus Fabian27697102017-11-09 01:43:47 -08001852 u16 port_per_thread,
1853 u32 snat_thread_index)
Dave Barach20c02cb2016-06-26 10:42:08 -04001854{
Matus Fabian51e759f2017-12-07 23:22:51 -08001855 int i, gi = 0;
1856 snat_address_t *a, *ga = 0;
Dave Barach20c02cb2016-06-26 10:42:08 -04001857 u32 portnum;
1858
Matus Fabian8ebe6252017-11-06 05:04:53 -08001859 for (i = 0; i < vec_len (addresses); i++)
Dave Barach20c02cb2016-06-26 10:42:08 -04001860 {
Matus Fabian8ebe6252017-11-06 05:04:53 -08001861 a = addresses + i;
Matus Fabian09d96f42017-02-02 01:43:00 -08001862 switch (k->protocol)
Dave Barach20c02cb2016-06-26 10:42:08 -04001863 {
Matus Fabian09d96f42017-02-02 01:43:00 -08001864#define _(N, j, n, s) \
1865 case SNAT_PROTOCOL_##N: \
Matus Fabian8ebe6252017-11-06 05:04:53 -08001866 if (a->busy_##n##_ports_per_thread[thread_index] < port_per_thread) \
Matus Fabian09d96f42017-02-02 01:43:00 -08001867 { \
Matus Fabian51e759f2017-12-07 23:22:51 -08001868 if (a->fib_index == fib_index) \
Matus Fabian09d96f42017-02-02 01:43:00 -08001869 { \
Matus Fabian51e759f2017-12-07 23:22:51 -08001870 while (1) \
1871 { \
1872 portnum = (port_per_thread * \
1873 snat_thread_index) + \
1874 snat_random_port(1, port_per_thread) + 1024; \
1875 if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \
1876 continue; \
1877 clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \
1878 a->busy_##n##_ports_per_thread[thread_index]++; \
1879 a->busy_##n##_ports++; \
1880 k->addr = a->addr; \
1881 k->port = clib_host_to_net_u16(portnum); \
1882 *address_indexp = i; \
1883 return 0; \
1884 } \
1885 } \
1886 else if (a->fib_index == ~0) \
1887 { \
1888 ga = a; \
1889 gi = i; \
Matus Fabian09d96f42017-02-02 01:43:00 -08001890 } \
1891 } \
1892 break;
1893 foreach_snat_protocol
1894#undef _
1895 default:
1896 clib_warning("unknown protocol");
1897 return 1;
Dave Barach20c02cb2016-06-26 10:42:08 -04001898 }
Matus Fabian09d96f42017-02-02 01:43:00 -08001899
Dave Barach20c02cb2016-06-26 10:42:08 -04001900 }
Matus Fabian51e759f2017-12-07 23:22:51 -08001901
1902 if (ga)
1903 {
1904 a = ga;
1905 switch (k->protocol)
1906 {
1907#define _(N, j, n, s) \
1908 case SNAT_PROTOCOL_##N: \
1909 while (1) \
1910 { \
1911 portnum = (port_per_thread * \
1912 snat_thread_index) + \
1913 snat_random_port(1, port_per_thread) + 1024; \
1914 if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \
1915 continue; \
1916 clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \
1917 a->busy_##n##_ports_per_thread[thread_index]++; \
1918 a->busy_##n##_ports++; \
1919 k->addr = a->addr; \
1920 k->port = clib_host_to_net_u16(portnum); \
1921 *address_indexp = gi; \
1922 return 0; \
1923 }
1924 break;
1925 foreach_snat_protocol
1926#undef _
1927 default:
1928 clib_warning ("unknown protocol");
1929 return 1;
1930 }
1931 }
1932
Dave Barach20c02cb2016-06-26 10:42:08 -04001933 /* Totally out of translations to use... */
Matus Fabianeea28d72017-01-13 04:15:54 -08001934 snat_ipfix_logging_addresses_exhausted(0);
Dave Barach20c02cb2016-06-26 10:42:08 -04001935 return 1;
1936}
1937
Matus Fabian27697102017-11-09 01:43:47 -08001938static int
1939nat_alloc_addr_and_port_mape (snat_address_t * addresses,
1940 u32 fib_index,
1941 u32 thread_index,
1942 snat_session_key_t * k,
1943 u32 * address_indexp,
Matus Fabian27697102017-11-09 01:43:47 -08001944 u16 port_per_thread,
1945 u32 snat_thread_index)
1946{
1947 snat_main_t *sm = &snat_main;
1948 snat_address_t *a = addresses;
1949 u16 m, ports, portnum, A, j;
1950 m = 16 - (sm->psid_offset + sm->psid_length);
1951 ports = (1 << (16 - sm->psid_length)) - (1 << m);
1952
1953 if (!vec_len (addresses))
1954 goto exhausted;
1955
1956 switch (k->protocol)
1957 {
1958#define _(N, i, n, s) \
1959 case SNAT_PROTOCOL_##N: \
1960 if (a->busy_##n##_ports < ports) \
1961 { \
1962 while (1) \
1963 { \
1964 A = snat_random_port(1, pow2_mask(sm->psid_offset)); \
1965 j = snat_random_port(0, pow2_mask(m)); \
1966 portnum = A | (sm->psid << sm->psid_offset) | (j << (16 - m)); \
1967 if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \
1968 continue; \
1969 clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \
1970 a->busy_##n##_ports++; \
1971 k->addr = a->addr; \
1972 k->port = clib_host_to_net_u16 (portnum); \
1973 *address_indexp = i; \
1974 return 0; \
1975 } \
1976 } \
1977 break;
1978 foreach_snat_protocol
1979#undef _
1980 default:
1981 clib_warning("unknown protocol");
1982 return 1;
1983 }
1984
1985exhausted:
1986 /* Totally out of translations to use... */
1987 snat_ipfix_logging_addresses_exhausted(0);
1988 return 1;
1989}
Dave Barach20c02cb2016-06-26 10:42:08 -04001990
1991static clib_error_t *
1992add_address_command_fn (vlib_main_t * vm,
1993 unformat_input_t * input,
1994 vlib_cli_command_t * cmd)
1995{
Matus Fabiandb649882016-08-26 05:45:27 -07001996 unformat_input_t _line_input, *line_input = &_line_input;
Dave Barach20c02cb2016-06-26 10:42:08 -04001997 snat_main_t * sm = &snat_main;
1998 ip4_address_t start_addr, end_addr, this_addr;
1999 u32 start_host_order, end_host_order;
Juraj Slobodaeab38d92017-03-06 19:55:21 -08002000 u32 vrf_id = ~0;
Dave Barach20c02cb2016-06-26 10:42:08 -04002001 int i, count;
Matus Fabian724b8152016-10-04 03:23:43 -07002002 int is_add = 1;
2003 int rv = 0;
Billy McFalla9a20e72017-02-15 11:39:12 -05002004 clib_error_t *error = 0;
Matus Fabianb932d262017-12-18 05:38:24 -08002005 u8 twice_nat = 0;
Dave Barach20c02cb2016-06-26 10:42:08 -04002006
Matus Fabiandb649882016-08-26 05:45:27 -07002007 /* Get a line of input. */
2008 if (!unformat_user (input, unformat_line_input, line_input))
2009 return 0;
2010
Matus Fabian724b8152016-10-04 03:23:43 -07002011 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
2012 {
2013 if (unformat (line_input, "%U - %U",
2014 unformat_ip4_address, &start_addr,
2015 unformat_ip4_address, &end_addr))
2016 ;
Juraj Slobodaeab38d92017-03-06 19:55:21 -08002017 else if (unformat (line_input, "tenant-vrf %u", &vrf_id))
2018 ;
Matus Fabian724b8152016-10-04 03:23:43 -07002019 else if (unformat (line_input, "%U", unformat_ip4_address, &start_addr))
2020 end_addr = start_addr;
Matus Fabianb932d262017-12-18 05:38:24 -08002021 else if (unformat (line_input, "twice-nat"))
2022 twice_nat = 1;
Matus Fabian724b8152016-10-04 03:23:43 -07002023 else if (unformat (line_input, "del"))
2024 is_add = 0;
2025 else
Billy McFalla9a20e72017-02-15 11:39:12 -05002026 {
2027 error = clib_error_return (0, "unknown input '%U'",
2028 format_unformat_error, line_input);
2029 goto done;
2030 }
Matus Fabian724b8152016-10-04 03:23:43 -07002031 }
Matus Fabiandb649882016-08-26 05:45:27 -07002032
2033 if (sm->static_mapping_only)
Billy McFalla9a20e72017-02-15 11:39:12 -05002034 {
2035 error = clib_error_return (0, "static mapping only mode");
2036 goto done;
2037 }
Dave Barach20c02cb2016-06-26 10:42:08 -04002038
2039 start_host_order = clib_host_to_net_u32 (start_addr.as_u32);
2040 end_host_order = clib_host_to_net_u32 (end_addr.as_u32);
Matus Fabian2ba92e32017-08-21 07:05:03 -07002041
Dave Barach20c02cb2016-06-26 10:42:08 -04002042 if (end_host_order < start_host_order)
Billy McFalla9a20e72017-02-15 11:39:12 -05002043 {
2044 error = clib_error_return (0, "end address less than start address");
2045 goto done;
2046 }
Dave Barach20c02cb2016-06-26 10:42:08 -04002047
2048 count = (end_host_order - start_host_order) + 1;
2049
2050 if (count > 1024)
2051 clib_warning ("%U - %U, %d addresses...",
2052 format_ip4_address, &start_addr,
2053 format_ip4_address, &end_addr,
2054 count);
Matus Fabian2ba92e32017-08-21 07:05:03 -07002055
Dave Barach20c02cb2016-06-26 10:42:08 -04002056 this_addr = start_addr;
2057
2058 for (i = 0; i < count; i++)
2059 {
Matus Fabian724b8152016-10-04 03:23:43 -07002060 if (is_add)
Matus Fabianb932d262017-12-18 05:38:24 -08002061 snat_add_address (sm, &this_addr, vrf_id, twice_nat);
Matus Fabian724b8152016-10-04 03:23:43 -07002062 else
Matus Fabianb932d262017-12-18 05:38:24 -08002063 rv = snat_del_address (sm, this_addr, 0, twice_nat);
Matus Fabian724b8152016-10-04 03:23:43 -07002064
2065 switch (rv)
2066 {
2067 case VNET_API_ERROR_NO_SUCH_ENTRY:
Billy McFalla9a20e72017-02-15 11:39:12 -05002068 error = clib_error_return (0, "S-NAT address not exist.");
2069 goto done;
Matus Fabian724b8152016-10-04 03:23:43 -07002070 case VNET_API_ERROR_UNSPECIFIED:
Billy McFalla9a20e72017-02-15 11:39:12 -05002071 error = clib_error_return (0, "S-NAT address used in static mapping.");
2072 goto done;
Matus Fabian724b8152016-10-04 03:23:43 -07002073 default:
2074 break;
2075 }
2076
Dave Barach20c02cb2016-06-26 10:42:08 -04002077 increment_v4_address (&this_addr);
2078 }
2079
Billy McFalla9a20e72017-02-15 11:39:12 -05002080done:
2081 unformat_free (line_input);
2082
2083 return error;
Dave Barach20c02cb2016-06-26 10:42:08 -04002084}
2085
2086VLIB_CLI_COMMAND (add_address_command, static) = {
Matus Fabian2ba92e32017-08-21 07:05:03 -07002087 .path = "nat44 add address",
Hongjun Ni2bd3f8a2017-08-29 20:39:42 +08002088 .short_help = "nat44 add address <ip4-range-start> [- <ip4-range-end>] "
Matus Fabianb932d262017-12-18 05:38:24 -08002089 "[tenant-vrf <vrf-id>] [twice-nat] [del]",
Dave Barach20c02cb2016-06-26 10:42:08 -04002090 .function = add_address_command_fn,
2091};
2092
2093static clib_error_t *
2094snat_feature_command_fn (vlib_main_t * vm,
2095 unformat_input_t * input,
2096 vlib_cli_command_t * cmd)
2097{
Matus Fabiandb649882016-08-26 05:45:27 -07002098 unformat_input_t _line_input, *line_input = &_line_input;
Dave Barach20c02cb2016-06-26 10:42:08 -04002099 vnet_main_t * vnm = vnet_get_main();
Dave Barach20c02cb2016-06-26 10:42:08 -04002100 clib_error_t * error = 0;
Matus Fabian588144a2016-10-24 03:30:00 -07002101 u32 sw_if_index;
Dave Barach20c02cb2016-06-26 10:42:08 -04002102 u32 * inside_sw_if_indices = 0;
2103 u32 * outside_sw_if_indices = 0;
Matus Fabian93d84c92017-07-19 08:06:01 -07002104 u8 is_output_feature = 0;
Dave Barach20c02cb2016-06-26 10:42:08 -04002105 int is_del = 0;
2106 int i;
2107
2108 sw_if_index = ~0;
2109
Matus Fabiandb649882016-08-26 05:45:27 -07002110 /* Get a line of input. */
2111 if (!unformat_user (input, unformat_line_input, line_input))
2112 return 0;
2113
2114 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
Dave Barach20c02cb2016-06-26 10:42:08 -04002115 {
Matus Fabiandb649882016-08-26 05:45:27 -07002116 if (unformat (line_input, "in %U", unformat_vnet_sw_interface,
Dave Barach20c02cb2016-06-26 10:42:08 -04002117 vnm, &sw_if_index))
2118 vec_add1 (inside_sw_if_indices, sw_if_index);
Matus Fabiandb649882016-08-26 05:45:27 -07002119 else if (unformat (line_input, "out %U", unformat_vnet_sw_interface,
Dave Barach20c02cb2016-06-26 10:42:08 -04002120 vnm, &sw_if_index))
2121 vec_add1 (outside_sw_if_indices, sw_if_index);
Matus Fabian93d84c92017-07-19 08:06:01 -07002122 else if (unformat (line_input, "output-feature"))
2123 is_output_feature = 1;
Matus Fabiandb649882016-08-26 05:45:27 -07002124 else if (unformat (line_input, "del"))
Dave Barach20c02cb2016-06-26 10:42:08 -04002125 is_del = 1;
2126 else
Billy McFalla9a20e72017-02-15 11:39:12 -05002127 {
2128 error = clib_error_return (0, "unknown input '%U'",
2129 format_unformat_error, line_input);
2130 goto done;
2131 }
Dave Barach20c02cb2016-06-26 10:42:08 -04002132 }
2133
2134 if (vec_len (inside_sw_if_indices))
2135 {
Dave Barach20c02cb2016-06-26 10:42:08 -04002136 for (i = 0; i < vec_len(inside_sw_if_indices); i++)
2137 {
2138 sw_if_index = inside_sw_if_indices[i];
Matus Fabian93d84c92017-07-19 08:06:01 -07002139 if (is_output_feature)
2140 {
2141 if (snat_interface_add_del_output_feature (sw_if_index, 1, is_del))
2142 {
2143 error = clib_error_return (0, "%s %U failed",
2144 is_del ? "del" : "add",
2145 format_vnet_sw_interface_name, vnm,
2146 vnet_get_sw_interface (vnm,
2147 sw_if_index));
2148 goto done;
2149 }
2150 }
2151 else
2152 {
2153 if (snat_interface_add_del (sw_if_index, 1, is_del))
2154 {
2155 error = clib_error_return (0, "%s %U failed",
2156 is_del ? "del" : "add",
2157 format_vnet_sw_interface_name, vnm,
2158 vnet_get_sw_interface (vnm,
2159 sw_if_index));
2160 goto done;
2161 }
2162 }
Dave Barach20c02cb2016-06-26 10:42:08 -04002163 }
2164 }
2165
2166 if (vec_len (outside_sw_if_indices))
2167 {
Dave Barach20c02cb2016-06-26 10:42:08 -04002168 for (i = 0; i < vec_len(outside_sw_if_indices); i++)
2169 {
2170 sw_if_index = outside_sw_if_indices[i];
Matus Fabian93d84c92017-07-19 08:06:01 -07002171 if (is_output_feature)
2172 {
2173 if (snat_interface_add_del_output_feature (sw_if_index, 0, is_del))
2174 {
2175 error = clib_error_return (0, "%s %U failed",
2176 is_del ? "del" : "add",
2177 format_vnet_sw_interface_name, vnm,
2178 vnet_get_sw_interface (vnm,
2179 sw_if_index));
2180 goto done;
2181 }
2182 }
2183 else
2184 {
2185 if (snat_interface_add_del (sw_if_index, 0, is_del))
2186 {
2187 error = clib_error_return (0, "%s %U failed",
2188 is_del ? "del" : "add",
2189 format_vnet_sw_interface_name, vnm,
2190 vnet_get_sw_interface (vnm,
2191 sw_if_index));
2192 goto done;
2193 }
2194 }
Dave Barach20c02cb2016-06-26 10:42:08 -04002195 }
2196 }
2197
Billy McFalla9a20e72017-02-15 11:39:12 -05002198done:
2199 unformat_free (line_input);
Dave Barach20c02cb2016-06-26 10:42:08 -04002200 vec_free (inside_sw_if_indices);
2201 vec_free (outside_sw_if_indices);
2202
2203 return error;
2204}
2205
2206VLIB_CLI_COMMAND (set_interface_snat_command, static) = {
Matus Fabian2ba92e32017-08-21 07:05:03 -07002207 .path = "set interface nat44",
Dave Barach20c02cb2016-06-26 10:42:08 -04002208 .function = snat_feature_command_fn,
Matus Fabian2ba92e32017-08-21 07:05:03 -07002209 .short_help = "set interface nat44 in <intfc> out <intfc> [output-feature] "
Matus Fabian93d84c92017-07-19 08:06:01 -07002210 "[del]",
Dave Barach20c02cb2016-06-26 10:42:08 -04002211};
2212
Matus Fabian09d96f42017-02-02 01:43:00 -08002213uword
2214unformat_snat_protocol (unformat_input_t * input, va_list * args)
2215{
2216 u32 *r = va_arg (*args, u32 *);
2217
2218 if (0);
2219#define _(N, i, n, s) else if (unformat (input, s)) *r = SNAT_PROTOCOL_##N;
2220 foreach_snat_protocol
2221#undef _
2222 else
2223 return 0;
2224 return 1;
2225}
2226
2227u8 *
2228format_snat_protocol (u8 * s, va_list * args)
2229{
2230 u32 i = va_arg (*args, u32);
2231 u8 *t = 0;
2232
2233 switch (i)
2234 {
2235#define _(N, j, n, str) case SNAT_PROTOCOL_##N: t = (u8 *) str; break;
2236 foreach_snat_protocol
2237#undef _
2238 default:
2239 s = format (s, "unknown");
Matus Fabianf8cd5812017-07-11 03:55:02 -07002240 return s;
Matus Fabian09d96f42017-02-02 01:43:00 -08002241 }
2242 s = format (s, "%s", t);
2243 return s;
2244}
2245
Dave Barach20c02cb2016-06-26 10:42:08 -04002246static clib_error_t *
Matus Fabiandb649882016-08-26 05:45:27 -07002247add_static_mapping_command_fn (vlib_main_t * vm,
2248 unformat_input_t * input,
2249 vlib_cli_command_t * cmd)
2250{
2251 unformat_input_t _line_input, *line_input = &_line_input;
2252 clib_error_t * error = 0;
2253 ip4_address_t l_addr, e_addr;
2254 u32 l_port = 0, e_port = 0, vrf_id = ~0;
2255 int is_add = 1;
2256 int addr_only = 1;
Dave Barach8b275372017-01-16 10:54:02 -05002257 u32 sw_if_index = ~0;
2258 vnet_main_t * vnm = vnet_get_main();
Matus Fabiandb649882016-08-26 05:45:27 -07002259 int rv;
Matus Fabianab7a8052017-11-28 04:29:41 -08002260 snat_protocol_t proto = ~0;
Matus Fabianb449f482017-02-05 22:14:41 -08002261 u8 proto_set = 0;
Matus Fabianb932d262017-12-18 05:38:24 -08002262 u8 twice_nat = 0;
Matus Fabiandb649882016-08-26 05:45:27 -07002263
2264 /* Get a line of input. */
2265 if (!unformat_user (input, unformat_line_input, line_input))
2266 return 0;
2267
2268 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
2269 {
2270 if (unformat (line_input, "local %U %u", unformat_ip4_address, &l_addr,
2271 &l_port))
2272 addr_only = 0;
2273 else if (unformat (line_input, "local %U", unformat_ip4_address, &l_addr))
2274 ;
2275 else if (unformat (line_input, "external %U %u", unformat_ip4_address,
2276 &e_addr, &e_port))
2277 addr_only = 0;
2278 else if (unformat (line_input, "external %U", unformat_ip4_address,
2279 &e_addr))
2280 ;
Dave Barach8b275372017-01-16 10:54:02 -05002281 else if (unformat (line_input, "external %U %u",
2282 unformat_vnet_sw_interface, vnm, &sw_if_index,
2283 &e_port))
2284 addr_only = 0;
2285
2286 else if (unformat (line_input, "external %U",
2287 unformat_vnet_sw_interface, vnm, &sw_if_index))
2288 ;
Matus Fabiandb649882016-08-26 05:45:27 -07002289 else if (unformat (line_input, "vrf %u", &vrf_id))
2290 ;
Matus Fabian09d96f42017-02-02 01:43:00 -08002291 else if (unformat (line_input, "%U", unformat_snat_protocol, &proto))
Matus Fabianb449f482017-02-05 22:14:41 -08002292 proto_set = 1;
Matus Fabianb932d262017-12-18 05:38:24 -08002293 else if (unformat (line_input, "twice-nat"))
2294 twice_nat = 1;
Matus Fabiandb649882016-08-26 05:45:27 -07002295 else if (unformat (line_input, "del"))
2296 is_add = 0;
2297 else
Billy McFalla9a20e72017-02-15 11:39:12 -05002298 {
2299 error = clib_error_return (0, "unknown input: '%U'",
2300 format_unformat_error, line_input);
2301 goto done;
2302 }
Matus Fabiandb649882016-08-26 05:45:27 -07002303 }
Matus Fabiandb649882016-08-26 05:45:27 -07002304
Matus Fabianb932d262017-12-18 05:38:24 -08002305 if (twice_nat && addr_only)
2306 {
2307 error = clib_error_return (0, "twice NAT only for 1:1 NAPT");
2308 goto done;
2309 }
2310
Matus Fabianb449f482017-02-05 22:14:41 -08002311 if (!addr_only && !proto_set)
Billy McFalla9a20e72017-02-15 11:39:12 -05002312 {
2313 error = clib_error_return (0, "missing protocol");
2314 goto done;
2315 }
Matus Fabianb449f482017-02-05 22:14:41 -08002316
Matus Fabiandb649882016-08-26 05:45:27 -07002317 rv = snat_add_static_mapping(l_addr, e_addr, (u16) l_port, (u16) e_port,
Matus Fabianb932d262017-12-18 05:38:24 -08002318 vrf_id, addr_only, sw_if_index, proto, is_add,
2319 twice_nat);
Matus Fabiandb649882016-08-26 05:45:27 -07002320
2321 switch (rv)
2322 {
2323 case VNET_API_ERROR_INVALID_VALUE:
Billy McFalla9a20e72017-02-15 11:39:12 -05002324 error = clib_error_return (0, "External port already in use.");
2325 goto done;
Matus Fabiandb649882016-08-26 05:45:27 -07002326 case VNET_API_ERROR_NO_SUCH_ENTRY:
2327 if (is_add)
Billy McFalla9a20e72017-02-15 11:39:12 -05002328 error = clib_error_return (0, "External addres must be allocated.");
Matus Fabiandb649882016-08-26 05:45:27 -07002329 else
Billy McFalla9a20e72017-02-15 11:39:12 -05002330 error = clib_error_return (0, "Mapping not exist.");
2331 goto done;
Matus Fabiandb649882016-08-26 05:45:27 -07002332 case VNET_API_ERROR_NO_SUCH_FIB:
Billy McFalla9a20e72017-02-15 11:39:12 -05002333 error = clib_error_return (0, "No such VRF id.");
2334 goto done;
Matus Fabiandb649882016-08-26 05:45:27 -07002335 case VNET_API_ERROR_VALUE_EXIST:
Billy McFalla9a20e72017-02-15 11:39:12 -05002336 error = clib_error_return (0, "Mapping already exist.");
2337 goto done;
Matus Fabiandb649882016-08-26 05:45:27 -07002338 default:
2339 break;
2340 }
2341
Billy McFalla9a20e72017-02-15 11:39:12 -05002342done:
2343 unformat_free (line_input);
2344
Matus Fabiandb649882016-08-26 05:45:27 -07002345 return error;
2346}
2347
2348/*?
2349 * @cliexpar
2350 * @cliexstart{snat add static mapping}
2351 * Static mapping allows hosts on the external network to initiate connection
2352 * to to the local network host.
2353 * To create static mapping between local host address 10.0.0.3 port 6303 and
Matus Fabianb449f482017-02-05 22:14:41 -08002354 * external address 4.4.4.4 port 3606 for TCP protocol use:
Hongjun Ni2bd3f8a2017-08-29 20:39:42 +08002355 * vpp# nat44 add static mapping tcp local 10.0.0.3 6303 external 4.4.4.4 3606
Matus Fabian2ba92e32017-08-21 07:05:03 -07002356 * If not runnig "static mapping only" NAT plugin mode use before:
2357 * vpp# nat44 add address 4.4.4.4
Matus Fabiandb649882016-08-26 05:45:27 -07002358 * To create static mapping between local and external address use:
Matus Fabian2ba92e32017-08-21 07:05:03 -07002359 * vpp# nat44 add static mapping local 10.0.0.3 external 4.4.4.4
Matus Fabiandb649882016-08-26 05:45:27 -07002360 * @cliexend
2361?*/
2362VLIB_CLI_COMMAND (add_static_mapping_command, static) = {
Matus Fabian2ba92e32017-08-21 07:05:03 -07002363 .path = "nat44 add static mapping",
Matus Fabiandb649882016-08-26 05:45:27 -07002364 .function = add_static_mapping_command_fn,
2365 .short_help =
Matus Fabianb932d262017-12-18 05:38:24 -08002366 "nat44 add static mapping tcp|udp|icmp local <addr> [<port>] "
2367 "external <addr> [<port>] [vrf <table-id>] [twice-nat] [del]",
Matus Fabiandb649882016-08-26 05:45:27 -07002368};
2369
2370static clib_error_t *
Matus Fabianab7a8052017-11-28 04:29:41 -08002371add_identity_mapping_command_fn (vlib_main_t * vm,
2372 unformat_input_t * input,
2373 vlib_cli_command_t * cmd)
2374{
2375 unformat_input_t _line_input, *line_input = &_line_input;
2376 clib_error_t * error = 0;
2377 ip4_address_t addr;
2378 u32 port = 0, vrf_id = ~0;
2379 int is_add = 1;
2380 int addr_only = 1;
2381 u32 sw_if_index = ~0;
2382 vnet_main_t * vnm = vnet_get_main();
2383 int rv;
2384 snat_protocol_t proto;
2385
2386 addr.as_u32 = 0;
2387
2388 /* Get a line of input. */
2389 if (!unformat_user (input, unformat_line_input, line_input))
2390 return 0;
2391
2392 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
2393 {
2394 if (unformat (line_input, "%U", unformat_ip4_address, &addr))
2395 ;
2396 else if (unformat (line_input, "external %U",
2397 unformat_vnet_sw_interface, vnm, &sw_if_index))
2398 ;
2399 else if (unformat (line_input, "vrf %u", &vrf_id))
2400 ;
2401 else if (unformat (line_input, "%U %u", unformat_snat_protocol, &proto,
2402 &port))
2403 addr_only = 0;
2404 else if (unformat (line_input, "del"))
2405 is_add = 0;
2406 else
2407 {
2408 error = clib_error_return (0, "unknown input: '%U'",
2409 format_unformat_error, line_input);
2410 goto done;
2411 }
2412 }
2413
2414 rv = snat_add_static_mapping(addr, addr, (u16) port, (u16) port,
Matus Fabianb932d262017-12-18 05:38:24 -08002415 vrf_id, addr_only, sw_if_index, proto, is_add,
2416 0);
Matus Fabianab7a8052017-11-28 04:29:41 -08002417
2418 switch (rv)
2419 {
2420 case VNET_API_ERROR_INVALID_VALUE:
2421 error = clib_error_return (0, "External port already in use.");
2422 goto done;
2423 case VNET_API_ERROR_NO_SUCH_ENTRY:
2424 if (is_add)
2425 error = clib_error_return (0, "External addres must be allocated.");
2426 else
2427 error = clib_error_return (0, "Mapping not exist.");
2428 goto done;
2429 case VNET_API_ERROR_NO_SUCH_FIB:
2430 error = clib_error_return (0, "No such VRF id.");
2431 goto done;
2432 case VNET_API_ERROR_VALUE_EXIST:
2433 error = clib_error_return (0, "Mapping already exist.");
2434 goto done;
2435 default:
2436 break;
2437 }
2438
2439done:
2440 unformat_free (line_input);
2441
2442 return error;
2443}
2444
2445/*?
2446 * @cliexpar
2447 * @cliexstart{snat add identity mapping}
2448 * Identity mapping translate an IP address to itself.
2449 * To create identity mapping for address 10.0.0.3 port 6303 for TCP protocol
2450 * use:
2451 * vpp# nat44 add identity mapping 10.0.0.3 tcp 6303
2452 * To create identity mapping for address 10.0.0.3 use:
2453 * vpp# nat44 add identity mapping 10.0.0.3
2454 * To create identity mapping for DHCP addressed interface use:
2455 * vpp# nat44 add identity mapping GigabitEthernet0/a/0 tcp 3606
2456 * @cliexend
2457?*/
2458VLIB_CLI_COMMAND (add_identity_mapping_command, static) = {
2459 .path = "nat44 add identity mapping",
2460 .function = add_identity_mapping_command_fn,
2461 .short_help = "nat44 add identity mapping <interface>|<ip4-addr> "
2462 "[<protocol> <port>] [vrf <table-id>] [del]",
2463};
2464
2465static clib_error_t *
Matus Fabian704018c2017-09-04 02:17:18 -07002466add_lb_static_mapping_command_fn (vlib_main_t * vm,
2467 unformat_input_t * input,
2468 vlib_cli_command_t * cmd)
2469{
2470 unformat_input_t _line_input, *line_input = &_line_input;
2471 clib_error_t * error = 0;
2472 ip4_address_t l_addr, e_addr;
2473 u32 l_port = 0, e_port = 0, vrf_id = 0, probability = 0;
2474 int is_add = 1;
2475 int rv;
2476 snat_protocol_t proto;
2477 u8 proto_set = 0;
2478 nat44_lb_addr_port_t *locals = 0, local;
Matus Fabianb932d262017-12-18 05:38:24 -08002479 u8 twice_nat = 0;
Matus Fabian704018c2017-09-04 02:17:18 -07002480
2481 /* Get a line of input. */
2482 if (!unformat_user (input, unformat_line_input, line_input))
2483 return 0;
2484
2485 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
2486 {
2487 if (unformat (line_input, "local %U:%u probability %u",
2488 unformat_ip4_address, &l_addr, &l_port, &probability))
2489 {
2490 memset (&local, 0, sizeof (local));
2491 local.addr = l_addr;
2492 local.port = (u16) l_port;
2493 local.probability = (u8) probability;
2494 vec_add1 (locals, local);
2495 }
2496 else if (unformat (line_input, "external %U:%u", unformat_ip4_address,
2497 &e_addr, &e_port))
2498 ;
2499 else if (unformat (line_input, "vrf %u", &vrf_id))
2500 ;
2501 else if (unformat (line_input, "protocol %U", unformat_snat_protocol,
2502 &proto))
2503 proto_set = 1;
Matus Fabianb932d262017-12-18 05:38:24 -08002504 else if (unformat (line_input, "twice-nat"))
2505 twice_nat = 1;
Matus Fabian704018c2017-09-04 02:17:18 -07002506 else if (unformat (line_input, "del"))
2507 is_add = 0;
2508 else
2509 {
2510 error = clib_error_return (0, "unknown input: '%U'",
2511 format_unformat_error, line_input);
2512 goto done;
2513 }
2514 }
2515
2516 if (vec_len (locals) < 2)
2517 {
2518 error = clib_error_return (0, "at least two local must be set");
2519 goto done;
2520 }
2521
2522 if (!proto_set)
2523 {
2524 error = clib_error_return (0, "missing protocol");
2525 goto done;
2526 }
2527
2528 rv = nat44_add_del_lb_static_mapping (e_addr, (u16) e_port, proto, vrf_id,
Matus Fabianb932d262017-12-18 05:38:24 -08002529 locals, is_add, twice_nat);
Matus Fabian704018c2017-09-04 02:17:18 -07002530
2531 switch (rv)
2532 {
2533 case VNET_API_ERROR_INVALID_VALUE:
2534 error = clib_error_return (0, "External port already in use.");
2535 goto done;
2536 case VNET_API_ERROR_NO_SUCH_ENTRY:
2537 if (is_add)
2538 error = clib_error_return (0, "External addres must be allocated.");
2539 else
2540 error = clib_error_return (0, "Mapping not exist.");
2541 goto done;
2542 case VNET_API_ERROR_VALUE_EXIST:
2543 error = clib_error_return (0, "Mapping already exist.");
2544 goto done;
2545 default:
2546 break;
2547 }
2548
2549done:
2550 unformat_free (line_input);
2551 vec_free (locals);
2552
2553 return error;
2554}
2555
2556VLIB_CLI_COMMAND (add_lb_static_mapping_command, static) = {
2557 .path = "nat44 add load-balancing static mapping",
2558 .function = add_lb_static_mapping_command_fn,
2559 .short_help =
Matus Fabianb932d262017-12-18 05:38:24 -08002560 "nat44 add load-balancing static mapping protocol tcp|udp "
2561 "external <addr>:<port> local <addr>:<port> probability <n> [twice-nat] "
2562 "[vrf <table-id>] [del]",
Matus Fabian704018c2017-09-04 02:17:18 -07002563};
2564
2565static clib_error_t *
Matus Fabian475f0552016-10-19 06:17:52 -07002566set_workers_command_fn (vlib_main_t * vm,
2567 unformat_input_t * input,
2568 vlib_cli_command_t * cmd)
2569{
2570 unformat_input_t _line_input, *line_input = &_line_input;
2571 uword *bitmap = 0;
2572 int rv = 0;
Billy McFalla9a20e72017-02-15 11:39:12 -05002573 clib_error_t *error = 0;
Matus Fabian475f0552016-10-19 06:17:52 -07002574
2575 /* Get a line of input. */
2576 if (!unformat_user (input, unformat_line_input, line_input))
2577 return 0;
2578
2579 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
2580 {
2581 if (unformat (line_input, "%U", unformat_bitmap_list, &bitmap))
2582 ;
2583 else
Billy McFalla9a20e72017-02-15 11:39:12 -05002584 {
2585 error = clib_error_return (0, "unknown input '%U'",
2586 format_unformat_error, line_input);
2587 goto done;
2588 }
Matus Fabian475f0552016-10-19 06:17:52 -07002589 }
Matus Fabian475f0552016-10-19 06:17:52 -07002590
2591 if (bitmap == 0)
Billy McFalla9a20e72017-02-15 11:39:12 -05002592 {
2593 error = clib_error_return (0, "List of workers must be specified.");
2594 goto done;
2595 }
Matus Fabian475f0552016-10-19 06:17:52 -07002596
2597 rv = snat_set_workers(bitmap);
2598
2599 clib_bitmap_free (bitmap);
2600
2601 switch (rv)
2602 {
2603 case VNET_API_ERROR_INVALID_WORKER:
Billy McFalla9a20e72017-02-15 11:39:12 -05002604 error = clib_error_return (0, "Invalid worker(s).");
2605 goto done;
Matus Fabian475f0552016-10-19 06:17:52 -07002606 case VNET_API_ERROR_FEATURE_DISABLED:
Billy McFalla9a20e72017-02-15 11:39:12 -05002607 error = clib_error_return (0,
Matus Fabian475f0552016-10-19 06:17:52 -07002608 "Supported only if 2 or more workes available.");
Billy McFalla9a20e72017-02-15 11:39:12 -05002609 goto done;
Matus Fabian475f0552016-10-19 06:17:52 -07002610 default:
2611 break;
2612 }
2613
Billy McFalla9a20e72017-02-15 11:39:12 -05002614done:
2615 unformat_free (line_input);
2616
2617 return error;
Matus Fabian475f0552016-10-19 06:17:52 -07002618}
2619
2620/*?
2621 * @cliexpar
2622 * @cliexstart{set snat workers}
Matus Fabian2ba92e32017-08-21 07:05:03 -07002623 * Set NAT workers if 2 or more workers available, use:
Matus Fabian475f0552016-10-19 06:17:52 -07002624 * vpp# set snat workers 0-2,5
2625 * @cliexend
2626?*/
2627VLIB_CLI_COMMAND (set_workers_command, static) = {
Matus Fabian2ba92e32017-08-21 07:05:03 -07002628 .path = "set nat workers",
Matus Fabian475f0552016-10-19 06:17:52 -07002629 .function = set_workers_command_fn,
2630 .short_help =
Matus Fabian2ba92e32017-08-21 07:05:03 -07002631 "set nat workers <workers-list>",
Matus Fabian475f0552016-10-19 06:17:52 -07002632};
2633
2634static clib_error_t *
Matus Fabianeea28d72017-01-13 04:15:54 -08002635snat_ipfix_logging_enable_disable_command_fn (vlib_main_t * vm,
2636 unformat_input_t * input,
2637 vlib_cli_command_t * cmd)
2638{
2639 unformat_input_t _line_input, *line_input = &_line_input;
2640 u32 domain_id = 0;
2641 u32 src_port = 0;
2642 u8 enable = 1;
2643 int rv = 0;
Billy McFalla9a20e72017-02-15 11:39:12 -05002644 clib_error_t *error = 0;
Matus Fabianeea28d72017-01-13 04:15:54 -08002645
2646 /* Get a line of input. */
2647 if (!unformat_user (input, unformat_line_input, line_input))
2648 return 0;
2649
2650 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
2651 {
2652 if (unformat (line_input, "domain %d", &domain_id))
2653 ;
2654 else if (unformat (line_input, "src-port %d", &src_port))
2655 ;
2656 else if (unformat (line_input, "disable"))
2657 enable = 0;
2658 else
Billy McFalla9a20e72017-02-15 11:39:12 -05002659 {
2660 error = clib_error_return (0, "unknown input '%U'",
2661 format_unformat_error, line_input);
2662 goto done;
2663 }
Matus Fabianeea28d72017-01-13 04:15:54 -08002664 }
Matus Fabianeea28d72017-01-13 04:15:54 -08002665
2666 rv = snat_ipfix_logging_enable_disable (enable, domain_id, (u16) src_port);
2667
2668 if (rv)
Billy McFalla9a20e72017-02-15 11:39:12 -05002669 {
2670 error = clib_error_return (0, "ipfix logging enable failed");
2671 goto done;
2672 }
Matus Fabianeea28d72017-01-13 04:15:54 -08002673
Billy McFalla9a20e72017-02-15 11:39:12 -05002674done:
2675 unformat_free (line_input);
2676
2677 return error;
Matus Fabianeea28d72017-01-13 04:15:54 -08002678}
2679
2680/*?
2681 * @cliexpar
2682 * @cliexstart{snat ipfix logging}
Matus Fabian2ba92e32017-08-21 07:05:03 -07002683 * To enable NAT IPFIX logging use:
2684 * vpp# nat ipfix logging
Matus Fabianeea28d72017-01-13 04:15:54 -08002685 * To set IPFIX exporter use:
2686 * vpp# set ipfix exporter collector 10.10.10.3 src 10.10.10.1
2687 * @cliexend
2688?*/
2689VLIB_CLI_COMMAND (snat_ipfix_logging_enable_disable_command, static) = {
Matus Fabian2ba92e32017-08-21 07:05:03 -07002690 .path = "nat ipfix logging",
Matus Fabianeea28d72017-01-13 04:15:54 -08002691 .function = snat_ipfix_logging_enable_disable_command_fn,
Matus Fabian2ba92e32017-08-21 07:05:03 -07002692 .short_help = "nat ipfix logging [domain <domain-id>] [src-port <port>] [disable]",
Matus Fabianeea28d72017-01-13 04:15:54 -08002693};
2694
Matus Fabian066f0342017-02-10 03:48:01 -08002695static u32
2696snat_get_worker_in2out_cb (ip4_header_t * ip0, u32 rx_fib_index0)
2697{
2698 snat_main_t *sm = &snat_main;
Matus Fabian066f0342017-02-10 03:48:01 -08002699 u32 next_worker_index = 0;
Matus Fabian7865b5c2017-09-26 01:23:01 -07002700 u32 hash;
Matus Fabian066f0342017-02-10 03:48:01 -08002701
Matus Fabian7865b5c2017-09-26 01:23:01 -07002702 next_worker_index = sm->first_worker_index;
2703 hash = ip0->src_address.as_u32 + (ip0->src_address.as_u32 >> 8) +
2704 (ip0->src_address.as_u32 >> 16) + (ip0->src_address.as_u32 >>24);
Matus Fabian066f0342017-02-10 03:48:01 -08002705
Matus Fabian7865b5c2017-09-26 01:23:01 -07002706 if (PREDICT_TRUE (is_pow2 (_vec_len (sm->workers))))
2707 next_worker_index += sm->workers[hash & (_vec_len (sm->workers) - 1)];
Matus Fabian066f0342017-02-10 03:48:01 -08002708 else
Matus Fabian7865b5c2017-09-26 01:23:01 -07002709 next_worker_index += sm->workers[hash % _vec_len (sm->workers)];
Matus Fabian066f0342017-02-10 03:48:01 -08002710
2711 return next_worker_index;
2712}
2713
2714static u32
2715snat_get_worker_out2in_cb (ip4_header_t * ip0, u32 rx_fib_index0)
2716{
2717 snat_main_t *sm = &snat_main;
Matus Fabianed3c1602017-09-21 05:07:12 -07002718 udp_header_t *udp;
2719 u16 port;
2720 snat_session_key_t m_key;
2721 clib_bihash_kv_8_8_t kv, value;
2722 snat_static_mapping_t *m;
2723 nat_ed_ses_key_t key;
2724 clib_bihash_kv_16_8_t s_kv, s_value;
2725 snat_main_per_thread_data_t *tsm;
2726 snat_session_t *s;
2727 int i;
2728 u32 proto;
Matus Fabian066f0342017-02-10 03:48:01 -08002729
Matus Fabianed3c1602017-09-21 05:07:12 -07002730 /* first try static mappings without port */
2731 if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
Matus Fabian066f0342017-02-10 03:48:01 -08002732 {
Matus Fabianed3c1602017-09-21 05:07:12 -07002733 m_key.addr = ip0->dst_address;
2734 m_key.port = 0;
2735 m_key.protocol = 0;
2736 m_key.fib_index = rx_fib_index0;
2737 kv.key = m_key.as_u64;
2738 if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value))
2739 {
2740 m = pool_elt_at_index (sm->static_mappings, value.value);
2741 return m->worker_index;
2742 }
Matus Fabian066f0342017-02-10 03:48:01 -08002743 }
2744
Matus Fabianed3c1602017-09-21 05:07:12 -07002745 proto = ip_proto_to_snat_proto (ip0->protocol);
2746 udp = ip4_next_header (ip0);
2747 port = udp->dst_port;
Matus Fabian066f0342017-02-10 03:48:01 -08002748
Matus Fabian51e759f2017-12-07 23:22:51 -08002749 if (PREDICT_FALSE (ip4_is_fragment (ip0)))
2750 {
2751 if (PREDICT_FALSE (nat_reass_is_drop_frag (0)))
2752 return vlib_get_thread_index ();
2753
2754 if (PREDICT_TRUE (!ip4_is_first_fragment (ip0)))
2755 {
2756 nat_reass_ip4_t *reass;
2757
2758 reass = nat_ip4_reass_find (ip0->src_address, ip0->dst_address,
2759 ip0->fragment_id, ip0->protocol);
2760
2761 if (reass && (reass->thread_index != (u32) ~ 0))
2762 return reass->thread_index;
2763 else
2764 return vlib_get_thread_index ();
2765 }
2766 }
2767
Matus Fabianed3c1602017-09-21 05:07:12 -07002768 /* unknown protocol */
2769 if (PREDICT_FALSE (proto == ~0))
Matus Fabian066f0342017-02-10 03:48:01 -08002770 {
Matus Fabianed3c1602017-09-21 05:07:12 -07002771 key.l_addr = ip0->dst_address;
2772 key.r_addr = ip0->src_address;
2773 key.fib_index = rx_fib_index0;
2774 key.proto = ip0->protocol;
Matus Fabianb932d262017-12-18 05:38:24 -08002775 key.r_port = 0;
Matus Fabianed3c1602017-09-21 05:07:12 -07002776 key.l_port = 0;
2777 s_kv.key[0] = key.as_u64[0];
2778 s_kv.key[1] = key.as_u64[1];
Matus Fabian066f0342017-02-10 03:48:01 -08002779
Matus Fabianed3c1602017-09-21 05:07:12 -07002780 if (!clib_bihash_search_16_8 (&sm->out2in_ed, &s_kv, &s_value))
Matus Fabian066f0342017-02-10 03:48:01 -08002781 {
Matus Fabianed3c1602017-09-21 05:07:12 -07002782 for (i = 0; i < _vec_len (sm->per_thread_data); i++)
Matus Fabian066f0342017-02-10 03:48:01 -08002783 {
Matus Fabianed3c1602017-09-21 05:07:12 -07002784 tsm = vec_elt_at_index (sm->per_thread_data, i);
2785 if (!pool_is_free_index(tsm->sessions, s_value.value))
2786 {
2787 s = pool_elt_at_index (tsm->sessions, s_value.value);
2788 if (s->out2in.addr.as_u32 == ip0->dst_address.as_u32 &&
2789 s->out2in.port == ip0->protocol &&
2790 snat_is_unk_proto_session (s))
2791 return i;
2792 }
Matus Fabian066f0342017-02-10 03:48:01 -08002793 }
Matus Fabianed3c1602017-09-21 05:07:12 -07002794 }
2795
2796 /* if no session use current thread */
2797 return vlib_get_thread_index ();
2798 }
2799
2800 if (PREDICT_FALSE (ip0->protocol == IP_PROTOCOL_ICMP))
2801 {
2802 icmp46_header_t * icmp = (icmp46_header_t *) udp;
2803 icmp_echo_header_t *echo = (icmp_echo_header_t *)(icmp + 1);
2804 if (!icmp_is_error_message (icmp))
2805 port = echo->identifier;
Matus Fabian066f0342017-02-10 03:48:01 -08002806 else
2807 {
Matus Fabianed3c1602017-09-21 05:07:12 -07002808 ip4_header_t *inner_ip = (ip4_header_t *)(echo + 1);
2809 proto = ip_proto_to_snat_proto (inner_ip->protocol);
2810 void *l4_header = ip4_next_header (inner_ip);
2811 switch (proto)
2812 {
2813 case SNAT_PROTOCOL_ICMP:
2814 icmp = (icmp46_header_t*)l4_header;
2815 echo = (icmp_echo_header_t *)(icmp + 1);
2816 port = echo->identifier;
2817 break;
2818 case SNAT_PROTOCOL_UDP:
2819 case SNAT_PROTOCOL_TCP:
2820 port = ((tcp_udp_header_t*)l4_header)->src_port;
2821 break;
2822 default:
2823 return vlib_get_thread_index ();
2824 }
Matus Fabian066f0342017-02-10 03:48:01 -08002825 }
Matus Fabian066f0342017-02-10 03:48:01 -08002826 }
Matus Fabian066f0342017-02-10 03:48:01 -08002827
Matus Fabianed3c1602017-09-21 05:07:12 -07002828 /* try static mappings with port */
2829 if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
2830 {
2831 m_key.addr = ip0->dst_address;
2832 m_key.port = clib_net_to_host_u16 (port);
2833 m_key.protocol = proto;
2834 m_key.fib_index = rx_fib_index0;
2835 kv.key = m_key.as_u64;
2836 if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value))
2837 {
2838 m = pool_elt_at_index (sm->static_mappings, value.value);
2839 return m->worker_index;
2840 }
2841 }
2842
2843 /* worker by outside port */
2844 return (u32) ((clib_net_to_host_u16 (port) - 1024) / sm->port_per_thread);
Matus Fabian066f0342017-02-10 03:48:01 -08002845}
2846
Matus Fabianeea28d72017-01-13 04:15:54 -08002847static clib_error_t *
Dave Barach20c02cb2016-06-26 10:42:08 -04002848snat_config (vlib_main_t * vm, unformat_input_t * input)
2849{
2850 snat_main_t * sm = &snat_main;
2851 u32 translation_buckets = 1024;
2852 u32 translation_memory_size = 128<<20;
2853 u32 user_buckets = 128;
2854 u32 user_memory_size = 64<<20;
2855 u32 max_translations_per_user = 100;
2856 u32 outside_vrf_id = 0;
Matus Fabiandb649882016-08-26 05:45:27 -07002857 u32 inside_vrf_id = 0;
2858 u32 static_mapping_buckets = 1024;
2859 u32 static_mapping_memory_size = 64<<20;
Matus Fabian51e759f2017-12-07 23:22:51 -08002860 u32 nat64_bib_buckets = 1024;
2861 u32 nat64_bib_memory_size = 128 << 20;
2862 u32 nat64_st_buckets = 2048;
2863 u32 nat64_st_memory_size = 256 << 20;
Matus Fabiandb649882016-08-26 05:45:27 -07002864 u8 static_mapping_only = 0;
2865 u8 static_mapping_connection_tracking = 0;
Matus Fabian092b3cd2017-09-19 05:42:38 -07002866 snat_main_per_thread_data_t *tsm;
Dave Barach20c02cb2016-06-26 10:42:08 -04002867
Matus Fabian066f0342017-02-10 03:48:01 -08002868 sm->deterministic = 0;
2869
Dave Barach20c02cb2016-06-26 10:42:08 -04002870 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2871 {
2872 if (unformat (input, "translation hash buckets %d", &translation_buckets))
2873 ;
2874 else if (unformat (input, "translation hash memory %d",
2875 &translation_memory_size));
2876 else if (unformat (input, "user hash buckets %d", &user_buckets))
2877 ;
2878 else if (unformat (input, "user hash memory %d",
2879 &user_memory_size))
2880 ;
2881 else if (unformat (input, "max translations per user %d",
2882 &max_translations_per_user))
2883 ;
2884 else if (unformat (input, "outside VRF id %d",
2885 &outside_vrf_id))
2886 ;
Matus Fabiandb649882016-08-26 05:45:27 -07002887 else if (unformat (input, "inside VRF id %d",
2888 &inside_vrf_id))
2889 ;
2890 else if (unformat (input, "static mapping only"))
2891 {
2892 static_mapping_only = 1;
2893 if (unformat (input, "connection tracking"))
2894 static_mapping_connection_tracking = 1;
2895 }
Matus Fabian066f0342017-02-10 03:48:01 -08002896 else if (unformat (input, "deterministic"))
2897 sm->deterministic = 1;
Matus Fabian51e759f2017-12-07 23:22:51 -08002898 else if (unformat (input, "nat64 bib hash buckets %d",
2899 &nat64_bib_buckets))
2900 ;
2901 else if (unformat (input, "nat64 bib hash memory %d",
2902 &nat64_bib_memory_size))
2903 ;
2904 else if (unformat (input, "nat64 st hash buckets %d", &nat64_st_buckets))
2905 ;
2906 else if (unformat (input, "nat64 st hash memory %d",
2907 &nat64_st_memory_size))
2908 ;
Matus Fabian066f0342017-02-10 03:48:01 -08002909 else
Matus Fabiandb649882016-08-26 05:45:27 -07002910 return clib_error_return (0, "unknown input '%U'",
Dave Barach20c02cb2016-06-26 10:42:08 -04002911 format_unformat_error, input);
2912 }
2913
2914 /* for show commands, etc. */
2915 sm->translation_buckets = translation_buckets;
2916 sm->translation_memory_size = translation_memory_size;
Matus Fabian41fef502017-09-22 02:43:05 -07002917 /* do not exceed load factor 10 */
2918 sm->max_translations = 10 * translation_buckets;
Dave Barach20c02cb2016-06-26 10:42:08 -04002919 sm->user_buckets = user_buckets;
2920 sm->user_memory_size = user_memory_size;
2921 sm->max_translations_per_user = max_translations_per_user;
2922 sm->outside_vrf_id = outside_vrf_id;
Matus Fabian31c31aa2017-02-05 22:45:57 -08002923 sm->outside_fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
Neale Ranns15002542017-09-10 04:39:11 -07002924 outside_vrf_id,
2925 FIB_SOURCE_PLUGIN_HI);
Matus Fabiandb649882016-08-26 05:45:27 -07002926 sm->inside_vrf_id = inside_vrf_id;
Matus Fabian31c31aa2017-02-05 22:45:57 -08002927 sm->inside_fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
Neale Ranns15002542017-09-10 04:39:11 -07002928 inside_vrf_id,
2929 FIB_SOURCE_PLUGIN_HI);
Matus Fabiandb649882016-08-26 05:45:27 -07002930 sm->static_mapping_only = static_mapping_only;
2931 sm->static_mapping_connection_tracking = static_mapping_connection_tracking;
Dave Barach20c02cb2016-06-26 10:42:08 -04002932
Matus Fabian51e759f2017-12-07 23:22:51 -08002933 nat64_set_hash(nat64_bib_buckets, nat64_bib_memory_size, nat64_st_buckets,
2934 nat64_st_memory_size);
2935
Matus Fabian066f0342017-02-10 03:48:01 -08002936 if (sm->deterministic)
Matus Fabiandb649882016-08-26 05:45:27 -07002937 {
Matus Fabian066f0342017-02-10 03:48:01 -08002938 sm->in2out_node_index = snat_det_in2out_node.index;
Matus Fabian93d84c92017-07-19 08:06:01 -07002939 sm->in2out_output_node_index = ~0;
Matus Fabian066f0342017-02-10 03:48:01 -08002940 sm->out2in_node_index = snat_det_out2in_node.index;
Juraj Sloboda7a1bde02017-04-03 08:43:58 +02002941 sm->icmp_match_in2out_cb = icmp_match_in2out_det;
2942 sm->icmp_match_out2in_cb = icmp_match_out2in_det;
Matus Fabiandb649882016-08-26 05:45:27 -07002943 }
Matus Fabian066f0342017-02-10 03:48:01 -08002944 else
2945 {
2946 sm->worker_in2out_cb = snat_get_worker_in2out_cb;
2947 sm->worker_out2in_cb = snat_get_worker_out2in_cb;
2948 sm->in2out_node_index = snat_in2out_node.index;
Matus Fabian93d84c92017-07-19 08:06:01 -07002949 sm->in2out_output_node_index = snat_in2out_output_node.index;
Matus Fabian066f0342017-02-10 03:48:01 -08002950 sm->out2in_node_index = snat_out2in_node.index;
2951 if (!static_mapping_only ||
2952 (static_mapping_only && static_mapping_connection_tracking))
2953 {
Juraj Sloboda557a71c2017-02-22 05:16:06 -08002954 sm->icmp_match_in2out_cb = icmp_match_in2out_slow;
2955 sm->icmp_match_out2in_cb = icmp_match_out2in_slow;
2956
Matus Fabian092b3cd2017-09-19 05:42:38 -07002957 vec_foreach (tsm, sm->per_thread_data)
2958 {
2959 clib_bihash_init_8_8 (&tsm->in2out, "in2out", translation_buckets,
2960 translation_memory_size);
2961
2962 clib_bihash_init_8_8 (&tsm->out2in, "out2in", translation_buckets,
2963 translation_memory_size);
2964
2965 clib_bihash_init_8_8 (&tsm->user_hash, "users", user_buckets,
2966 user_memory_size);
2967 }
2968
Matus Fabian704018c2017-09-04 02:17:18 -07002969 clib_bihash_init_16_8 (&sm->in2out_ed, "in2out-ed",
Matus Fabian7968e6c2017-07-06 05:37:49 -07002970 translation_buckets, translation_memory_size);
2971
Matus Fabian704018c2017-09-04 02:17:18 -07002972 clib_bihash_init_16_8 (&sm->out2in_ed, "out2in-ed",
Matus Fabian7968e6c2017-07-06 05:37:49 -07002973 translation_buckets, translation_memory_size);
Matus Fabian066f0342017-02-10 03:48:01 -08002974 }
Juraj Sloboda557a71c2017-02-22 05:16:06 -08002975 else
2976 {
2977 sm->icmp_match_in2out_cb = icmp_match_in2out_fast;
2978 sm->icmp_match_out2in_cb = icmp_match_out2in_fast;
2979 }
Matus Fabian066f0342017-02-10 03:48:01 -08002980 clib_bihash_init_8_8 (&sm->static_mapping_by_local,
2981 "static_mapping_by_local", static_mapping_buckets,
2982 static_mapping_memory_size);
2983
2984 clib_bihash_init_8_8 (&sm->static_mapping_by_external,
2985 "static_mapping_by_external", static_mapping_buckets,
2986 static_mapping_memory_size);
2987 }
2988
Dave Barach20c02cb2016-06-26 10:42:08 -04002989 return 0;
2990}
2991
Matus Fabian2ba92e32017-08-21 07:05:03 -07002992VLIB_CONFIG_FUNCTION (snat_config, "nat");
Dave Barach20c02cb2016-06-26 10:42:08 -04002993
Matus Fabian066f0342017-02-10 03:48:01 -08002994u8 * format_snat_session_state (u8 * s, va_list * args)
2995{
2996 u32 i = va_arg (*args, u32);
2997 u8 *t = 0;
2998
2999 switch (i)
3000 {
3001#define _(v, N, str) case SNAT_SESSION_##N: t = (u8 *) str; break;
3002 foreach_snat_session_state
3003#undef _
3004 default:
3005 t = format (t, "unknown");
3006 }
3007 s = format (s, "%s", t);
3008 return s;
3009}
3010
Dave Barach20c02cb2016-06-26 10:42:08 -04003011u8 * format_snat_key (u8 * s, va_list * args)
3012{
3013 snat_session_key_t * key = va_arg (*args, snat_session_key_t *);
Dave Barach20c02cb2016-06-26 10:42:08 -04003014
Matus Fabian704018c2017-09-04 02:17:18 -07003015 s = format (s, "%U proto %U port %d fib %d",
3016 format_ip4_address, &key->addr,
3017 format_snat_protocol, key->protocol,
Matus Fabiandb649882016-08-26 05:45:27 -07003018 clib_net_to_host_u16 (key->port), key->fib_index);
Dave Barach20c02cb2016-06-26 10:42:08 -04003019 return s;
3020}
3021
3022u8 * format_snat_session (u8 * s, va_list * args)
3023{
3024 snat_main_t * sm __attribute__((unused)) = va_arg (*args, snat_main_t *);
3025 snat_session_t * sess = va_arg (*args, snat_session_t *);
3026
Matus Fabian7968e6c2017-07-06 05:37:49 -07003027 if (snat_is_unk_proto_session (sess))
3028 {
3029 s = format (s, " i2o %U proto %u fib %u\n",
Matus Fabianb932d262017-12-18 05:38:24 -08003030 format_ip4_address, &sess->in2out.addr,
3031 clib_net_to_host_u16 (sess->in2out.port),
Matus Fabian7968e6c2017-07-06 05:37:49 -07003032 sess->in2out.fib_index);
3033 s = format (s, " o2i %U proto %u fib %u\n",
Matus Fabianb932d262017-12-18 05:38:24 -08003034 format_ip4_address, &sess->out2in.addr,
3035 clib_net_to_host_u16 (sess->out2in.port),
Matus Fabian7968e6c2017-07-06 05:37:49 -07003036 sess->out2in.fib_index);
3037 }
3038 else
3039 {
3040 s = format (s, " i2o %U\n", format_snat_key, &sess->in2out);
3041 s = format (s, " o2i %U\n", format_snat_key, &sess->out2in);
3042 }
Matus Fabianb932d262017-12-18 05:38:24 -08003043 if (is_twice_nat_session (sess))
3044 {
3045 s = format (s, " external host o2i %U:%d i2o %U:%d\n",
3046 format_ip4_address, &sess->ext_host_addr,
3047 clib_net_to_host_u16 (sess->ext_host_port),
3048 format_ip4_address, &sess->ext_host_nat_addr,
3049 clib_net_to_host_u16 (sess->ext_host_nat_port));
3050 }
3051 else
3052 {
3053 if (sess->ext_host_addr.as_u32)
3054 s = format (s, " external host %U\n",
3055 format_ip4_address, &sess->ext_host_addr);
3056 }
Dave Barach20c02cb2016-06-26 10:42:08 -04003057 s = format (s, " last heard %.2f\n", sess->last_heard);
3058 s = format (s, " total pkts %d, total bytes %lld\n",
3059 sess->total_pkts, sess->total_bytes);
Matus Fabiandb649882016-08-26 05:45:27 -07003060 if (snat_is_session_static (sess))
3061 s = format (s, " static translation\n");
3062 else
3063 s = format (s, " dynamic translation\n");
Matus Fabian704018c2017-09-04 02:17:18 -07003064 if (sess->flags & SNAT_SESSION_FLAG_LOAD_BALANCING)
3065 s = format (s, " load-balancing\n");
Matus Fabianb932d262017-12-18 05:38:24 -08003066 if (is_twice_nat_session (sess))
3067 s = format (s, " twice-nat\n");
Dave Barach20c02cb2016-06-26 10:42:08 -04003068
3069 return s;
3070}
3071
3072u8 * format_snat_user (u8 * s, va_list * args)
3073{
Matus Fabian475f0552016-10-19 06:17:52 -07003074 snat_main_per_thread_data_t * sm = va_arg (*args, snat_main_per_thread_data_t *);
Dave Barach20c02cb2016-06-26 10:42:08 -04003075 snat_user_t * u = va_arg (*args, snat_user_t *);
3076 int verbose = va_arg (*args, int);
3077 dlist_elt_t * head, * elt;
3078 u32 elt_index, head_index;
3079 u32 session_index;
3080 snat_session_t * sess;
3081
Matus Fabiandb649882016-08-26 05:45:27 -07003082 s = format (s, "%U: %d dynamic translations, %d static translations\n",
3083 format_ip4_address, &u->addr, u->nsessions, u->nstaticsessions);
Dave Barach20c02cb2016-06-26 10:42:08 -04003084
3085 if (verbose == 0)
3086 return s;
3087
Matus Fabiandb649882016-08-26 05:45:27 -07003088 if (u->nsessions || u->nstaticsessions)
Dave Barach20c02cb2016-06-26 10:42:08 -04003089 {
Matus Fabiandb649882016-08-26 05:45:27 -07003090 head_index = u->sessions_per_user_list_head_index;
3091 head = pool_elt_at_index (sm->list_pool, head_index);
Dave Barach20c02cb2016-06-26 10:42:08 -04003092
Matus Fabiandb649882016-08-26 05:45:27 -07003093 elt_index = head->next;
Dave Barach20c02cb2016-06-26 10:42:08 -04003094 elt = pool_elt_at_index (sm->list_pool, elt_index);
3095 session_index = elt->value;
Matus Fabiandb649882016-08-26 05:45:27 -07003096
3097 while (session_index != ~0)
3098 {
3099 sess = pool_elt_at_index (sm->sessions, session_index);
3100
3101 s = format (s, " %U\n", format_snat_session, sm, sess);
3102
3103 elt_index = elt->next;
3104 elt = pool_elt_at_index (sm->list_pool, elt_index);
3105 session_index = elt->value;
3106 }
Dave Barach20c02cb2016-06-26 10:42:08 -04003107 }
3108
3109 return s;
3110}
3111
Matus Fabiandb649882016-08-26 05:45:27 -07003112u8 * format_snat_static_mapping (u8 * s, va_list * args)
3113{
3114 snat_static_mapping_t *m = va_arg (*args, snat_static_mapping_t *);
Matus Fabian704018c2017-09-04 02:17:18 -07003115 nat44_lb_addr_port_t *local;
Matus Fabiandb649882016-08-26 05:45:27 -07003116
3117 if (m->addr_only)
Matus Fabianb932d262017-12-18 05:38:24 -08003118 s = format (s, "local %U external %U vrf %d %s",
Matus Fabiandb649882016-08-26 05:45:27 -07003119 format_ip4_address, &m->local_addr,
3120 format_ip4_address, &m->external_addr,
Matus Fabianb932d262017-12-18 05:38:24 -08003121 m->vrf_id, m->twice_nat ? "twice-nat" : "");
Matus Fabiandb649882016-08-26 05:45:27 -07003122 else
Matus Fabian704018c2017-09-04 02:17:18 -07003123 {
3124 if (vec_len (m->locals))
3125 {
Matus Fabianb932d262017-12-18 05:38:24 -08003126 s = format (s, "%U vrf %d external %U:%d %s",
Matus Fabian704018c2017-09-04 02:17:18 -07003127 format_snat_protocol, m->proto,
3128 m->vrf_id,
Matus Fabianb932d262017-12-18 05:38:24 -08003129 format_ip4_address, &m->external_addr, m->external_port,
3130 m->twice_nat ? "twice-nat" : "");
Matus Fabian704018c2017-09-04 02:17:18 -07003131 vec_foreach (local, m->locals)
3132 s = format (s, "\n local %U:%d probability %d\%",
3133 format_ip4_address, &local->addr, local->port,
3134 local->probability);
3135 }
3136 else
Matus Fabianb932d262017-12-18 05:38:24 -08003137 s = format (s, "%U local %U:%d external %U:%d vrf %d %s",
Matus Fabian704018c2017-09-04 02:17:18 -07003138 format_snat_protocol, m->proto,
3139 format_ip4_address, &m->local_addr, m->local_port,
3140 format_ip4_address, &m->external_addr, m->external_port,
Matus Fabianb932d262017-12-18 05:38:24 -08003141 m->vrf_id, m->twice_nat ? "twice-nat" : "");
Matus Fabian704018c2017-09-04 02:17:18 -07003142 }
Matus Fabiandb649882016-08-26 05:45:27 -07003143 return s;
3144}
3145
Matus Fabiane22e5462017-02-14 23:33:43 -08003146u8 * format_snat_static_map_to_resolve (u8 * s, va_list * args)
3147{
3148 snat_static_map_resolve_t *m = va_arg (*args, snat_static_map_resolve_t *);
3149 vnet_main_t *vnm = vnet_get_main();
3150
3151 if (m->addr_only)
3152 s = format (s, "local %U external %U vrf %d",
3153 format_ip4_address, &m->l_addr,
3154 format_vnet_sw_interface_name, vnm,
3155 vnet_get_sw_interface (vnm, m->sw_if_index),
3156 m->vrf_id);
3157 else
3158 s = format (s, "%U local %U:%d external %U:%d vrf %d",
3159 format_snat_protocol, m->proto,
3160 format_ip4_address, &m->l_addr, m->l_port,
3161 format_vnet_sw_interface_name, vnm,
3162 vnet_get_sw_interface (vnm, m->sw_if_index), m->e_port,
3163 m->vrf_id);
3164
3165 return s;
3166}
3167
Matus Fabian066f0342017-02-10 03:48:01 -08003168u8 * format_det_map_ses (u8 * s, va_list * args)
3169{
3170 snat_det_map_t * det_map = va_arg (*args, snat_det_map_t *);
3171 ip4_address_t in_addr, out_addr;
3172 u32 in_offset, out_offset;
3173 snat_det_session_t * ses = va_arg (*args, snat_det_session_t *);
3174 u32 * i = va_arg (*args, u32 *);
3175
3176 u32 user_index = *i / SNAT_DET_SES_PER_USER;
3177 in_addr.as_u32 = clib_host_to_net_u32 (
3178 clib_net_to_host_u32(det_map->in_addr.as_u32) + user_index);
3179 in_offset = clib_net_to_host_u32(in_addr.as_u32) -
3180 clib_net_to_host_u32(det_map->in_addr.as_u32);
3181 out_offset = in_offset / det_map->sharing_ratio;
3182 out_addr.as_u32 = clib_host_to_net_u32(
3183 clib_net_to_host_u32(det_map->out_addr.as_u32) + out_offset);
3184 s = format (s, "in %U:%d out %U:%d external host %U:%d state: %U expire: %d\n",
3185 format_ip4_address, &in_addr,
3186 clib_net_to_host_u16 (ses->in_port),
3187 format_ip4_address, &out_addr,
3188 clib_net_to_host_u16 (ses->out.out_port),
3189 format_ip4_address, &ses->out.ext_host_addr,
3190 clib_net_to_host_u16 (ses->out.ext_host_port),
3191 format_snat_session_state, ses->state,
3192 ses->expire);
3193
3194 return s;
3195}
3196
Dave Barach20c02cb2016-06-26 10:42:08 -04003197static clib_error_t *
3198show_snat_command_fn (vlib_main_t * vm,
3199 unformat_input_t * input,
3200 vlib_cli_command_t * cmd)
3201{
3202 int verbose = 0;
3203 snat_main_t * sm = &snat_main;
3204 snat_user_t * u;
Matus Fabiandb649882016-08-26 05:45:27 -07003205 snat_static_mapping_t *m;
Matus Fabian588144a2016-10-24 03:30:00 -07003206 snat_interface_t *i;
Matus Fabian07ea7612016-12-15 05:30:37 -08003207 snat_address_t * ap;
Matus Fabian588144a2016-10-24 03:30:00 -07003208 vnet_main_t *vnm = vnet_get_main();
Matus Fabian475f0552016-10-19 06:17:52 -07003209 snat_main_per_thread_data_t *tsm;
Matus Fabian8bf68e82017-01-12 04:24:35 -08003210 u32 users_num = 0, sessions_num = 0, *worker, *sw_if_index;
Matus Fabian475f0552016-10-19 06:17:52 -07003211 uword j = 0;
Matus Fabiane22e5462017-02-14 23:33:43 -08003212 snat_static_map_resolve_t *rp;
Matus Fabian066f0342017-02-10 03:48:01 -08003213 snat_det_map_t * dm;
3214 snat_det_session_t * ses;
Dave Barach20c02cb2016-06-26 10:42:08 -04003215
3216 if (unformat (input, "detail"))
3217 verbose = 1;
3218 else if (unformat (input, "verbose"))
3219 verbose = 2;
3220
Matus Fabiandb649882016-08-26 05:45:27 -07003221 if (sm->static_mapping_only)
Dave Barach20c02cb2016-06-26 10:42:08 -04003222 {
Matus Fabiandb649882016-08-26 05:45:27 -07003223 if (sm->static_mapping_connection_tracking)
Matus Fabian2ba92e32017-08-21 07:05:03 -07003224 vlib_cli_output (vm, "NAT plugin mode: static mapping only connection "
Matus Fabiandb649882016-08-26 05:45:27 -07003225 "tracking");
3226 else
Matus Fabian2ba92e32017-08-21 07:05:03 -07003227 vlib_cli_output (vm, "NAT plugin mode: static mapping only");
Matus Fabiandb649882016-08-26 05:45:27 -07003228 }
Matus Fabian066f0342017-02-10 03:48:01 -08003229 else if (sm->deterministic)
3230 {
Matus Fabian2ba92e32017-08-21 07:05:03 -07003231 vlib_cli_output (vm, "NAT plugin mode: deterministic mapping");
Matus Fabian066f0342017-02-10 03:48:01 -08003232 }
Matus Fabiandb649882016-08-26 05:45:27 -07003233 else
3234 {
Matus Fabian2ba92e32017-08-21 07:05:03 -07003235 vlib_cli_output (vm, "NAT plugin mode: dynamic translations enabled");
Matus Fabiandb649882016-08-26 05:45:27 -07003236 }
Dave Barach20c02cb2016-06-26 10:42:08 -04003237
Matus Fabian588144a2016-10-24 03:30:00 -07003238 if (verbose > 0)
3239 {
3240 pool_foreach (i, sm->interfaces,
3241 ({
3242 vlib_cli_output (vm, "%U %s", format_vnet_sw_interface_name, vnm,
3243 vnet_get_sw_interface (vnm, i->sw_if_index),
Matus Fabian36ea2d62017-10-24 04:13:49 -07003244 (nat_interface_is_inside(i) &&
3245 nat_interface_is_outside(i)) ? "in out" :
3246 (nat_interface_is_inside(i) ? "in" : "out"));
Matus Fabian588144a2016-10-24 03:30:00 -07003247 }));
Matus Fabian07ea7612016-12-15 05:30:37 -08003248
Matus Fabian93d84c92017-07-19 08:06:01 -07003249 pool_foreach (i, sm->output_feature_interfaces,
3250 ({
3251 vlib_cli_output (vm, "%U output-feature %s",
3252 format_vnet_sw_interface_name, vnm,
3253 vnet_get_sw_interface (vnm, i->sw_if_index),
Matus Fabian36ea2d62017-10-24 04:13:49 -07003254 (nat_interface_is_inside(i) &&
3255 nat_interface_is_outside(i)) ? "in out" :
3256 (nat_interface_is_inside(i) ? "in" : "out"));
Matus Fabian93d84c92017-07-19 08:06:01 -07003257 }));
3258
Matus Fabian8bf68e82017-01-12 04:24:35 -08003259 if (vec_len (sm->auto_add_sw_if_indices))
3260 {
Matus Fabian2ba92e32017-08-21 07:05:03 -07003261 vlib_cli_output (vm, "NAT44 pool addresses interfaces:");
Matus Fabian8bf68e82017-01-12 04:24:35 -08003262 vec_foreach (sw_if_index, sm->auto_add_sw_if_indices)
3263 {
3264 vlib_cli_output (vm, "%U", format_vnet_sw_interface_name, vnm,
3265 vnet_get_sw_interface (vnm, *sw_if_index));
3266 }
3267 }
3268
Matus Fabianb932d262017-12-18 05:38:24 -08003269 if (vec_len (sm->auto_add_sw_if_indices_twice_nat))
3270 {
3271 vlib_cli_output (vm, "NAT44 twice-nat pool addresses interfaces:");
3272 vec_foreach (sw_if_index, sm->auto_add_sw_if_indices_twice_nat)
3273 {
3274 vlib_cli_output (vm, "%U", format_vnet_sw_interface_name, vnm,
3275 vnet_get_sw_interface (vnm, *sw_if_index));
3276 }
3277 }
3278
3279 vlib_cli_output (vm, "NAT44 pool addresses:");
Matus Fabian07ea7612016-12-15 05:30:37 -08003280 vec_foreach (ap, sm->addresses)
3281 {
Matus Fabian07ea7612016-12-15 05:30:37 -08003282 vlib_cli_output (vm, "%U", format_ip4_address, &ap->addr);
Juraj Slobodaeab38d92017-03-06 19:55:21 -08003283 if (ap->fib_index != ~0)
3284 vlib_cli_output (vm, " tenant VRF: %u",
3285 ip4_fib_get(ap->fib_index)->table_id);
3286 else
3287 vlib_cli_output (vm, " tenant VRF independent");
Matus Fabian09d96f42017-02-02 01:43:00 -08003288#define _(N, i, n, s) \
3289 vlib_cli_output (vm, " %d busy %s ports", ap->busy_##n##_ports, s);
3290 foreach_snat_protocol
3291#undef _
Matus Fabian07ea7612016-12-15 05:30:37 -08003292 }
Matus Fabianb932d262017-12-18 05:38:24 -08003293
3294 vlib_cli_output (vm, "NAT44 twice-nat pool addresses:");
3295 vec_foreach (ap, sm->twice_nat_addresses)
3296 {
3297 vlib_cli_output (vm, "%U", format_ip4_address, &ap->addr);
3298 if (ap->fib_index != ~0)
3299 vlib_cli_output (vm, " tenant VRF: %u",
3300 ip4_fib_get(ap->fib_index)->table_id);
3301 else
3302 vlib_cli_output (vm, " tenant VRF independent");
3303#define _(N, i, n, s) \
3304 vlib_cli_output (vm, " %d busy %s ports", ap->busy_##n##_ports, s);
3305 foreach_snat_protocol
3306#undef _
3307 }
Matus Fabian588144a2016-10-24 03:30:00 -07003308 }
3309
Matus Fabian475f0552016-10-19 06:17:52 -07003310 if (sm->num_workers > 1)
3311 {
3312 vlib_cli_output (vm, "%d workers", vec_len (sm->workers));
3313 if (verbose > 0)
3314 {
3315 vec_foreach (worker, sm->workers)
3316 {
3317 vlib_worker_thread_t *w =
3318 vlib_worker_threads + *worker + sm->first_worker_index;
Matus Fabianeea28d72017-01-13 04:15:54 -08003319 vlib_cli_output (vm, " %s", w->name);
Matus Fabian475f0552016-10-19 06:17:52 -07003320 }
3321 }
3322 }
3323
Matus Fabian066f0342017-02-10 03:48:01 -08003324 if (sm->deterministic)
Matus Fabiandb649882016-08-26 05:45:27 -07003325 {
Matus Fabian6a0946f2017-04-12 03:36:13 -07003326 vlib_cli_output (vm, "udp timeout: %dsec", sm->udp_timeout);
3327 vlib_cli_output (vm, "tcp-established timeout: %dsec",
3328 sm->tcp_established_timeout);
3329 vlib_cli_output (vm, "tcp-transitory timeout: %dsec",
3330 sm->tcp_transitory_timeout);
3331 vlib_cli_output (vm, "icmp timeout: %dsec", sm->icmp_timeout);
Matus Fabian066f0342017-02-10 03:48:01 -08003332 vlib_cli_output (vm, "%d deterministic mappings",
3333 pool_elts (sm->det_maps));
Matus Fabiandb649882016-08-26 05:45:27 -07003334 if (verbose > 0)
3335 {
Matus Fabian066f0342017-02-10 03:48:01 -08003336 pool_foreach (dm, sm->det_maps,
Matus Fabiandb649882016-08-26 05:45:27 -07003337 ({
Matus Fabian066f0342017-02-10 03:48:01 -08003338 vlib_cli_output (vm, "in %U/%d out %U/%d\n",
3339 format_ip4_address, &dm->in_addr, dm->in_plen,
3340 format_ip4_address, &dm->out_addr, dm->out_plen);
3341 vlib_cli_output (vm, " outside address sharing ratio: %d\n",
3342 dm->sharing_ratio);
3343 vlib_cli_output (vm, " number of ports per inside host: %d\n",
3344 dm->ports_per_host);
3345 vlib_cli_output (vm, " sessions number: %d\n", dm->ses_num);
3346 if (verbose > 1)
3347 {
3348 vec_foreach_index (j, dm->sessions)
3349 {
3350 ses = vec_elt_at_index (dm->sessions, j);
3351 if (ses->in_port)
3352 vlib_cli_output (vm, " %U", format_det_map_ses, dm, ses,
3353 &j);
3354 }
3355 }
Matus Fabiandb649882016-08-26 05:45:27 -07003356 }));
3357 }
3358 }
3359 else
3360 {
Matus Fabian066f0342017-02-10 03:48:01 -08003361 if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking))
Matus Fabian475f0552016-10-19 06:17:52 -07003362 {
Matus Fabian066f0342017-02-10 03:48:01 -08003363 vlib_cli_output (vm, "%d static mappings",
3364 pool_elts (sm->static_mappings));
Matus Fabian475f0552016-10-19 06:17:52 -07003365
Matus Fabian066f0342017-02-10 03:48:01 -08003366 if (verbose > 0)
Matus Fabian475f0552016-10-19 06:17:52 -07003367 {
Matus Fabiandb649882016-08-26 05:45:27 -07003368 pool_foreach (m, sm->static_mappings,
3369 ({
3370 vlib_cli_output (vm, "%U", format_snat_static_mapping, m);
3371 }));
Matus Fabian066f0342017-02-10 03:48:01 -08003372 }
3373 }
3374 else
3375 {
3376 vec_foreach (tsm, sm->per_thread_data)
3377 {
3378 users_num += pool_elts (tsm->users);
3379 sessions_num += pool_elts (tsm->sessions);
3380 }
3381
3382 vlib_cli_output (vm, "%d users, %d outside addresses, %d active sessions,"
Matus Fabianb932d262017-12-18 05:38:24 -08003383 " %d static mappings, %d twice-nat addresses",
Matus Fabian066f0342017-02-10 03:48:01 -08003384 users_num,
3385 vec_len (sm->addresses),
3386 sessions_num,
Matus Fabianb932d262017-12-18 05:38:24 -08003387 pool_elts (sm->static_mappings),
3388 vec_len (sm->twice_nat_addresses));
Matus Fabian066f0342017-02-10 03:48:01 -08003389
3390 if (verbose > 0)
3391 {
Matus Fabian704018c2017-09-04 02:17:18 -07003392 vlib_cli_output (vm, "%U", format_bihash_16_8, &sm->in2out_ed,
3393 verbose - 1);
3394 vlib_cli_output (vm, "%U", format_bihash_16_8, &sm->out2in_ed,
3395 verbose - 1);
Matus Fabian066f0342017-02-10 03:48:01 -08003396 vec_foreach_index (j, sm->per_thread_data)
Matus Fabiane22e5462017-02-14 23:33:43 -08003397 {
Matus Fabian066f0342017-02-10 03:48:01 -08003398 tsm = vec_elt_at_index (sm->per_thread_data, j);
3399
3400 if (pool_elts (tsm->users) == 0)
3401 continue;
3402
3403 vlib_worker_thread_t *w = vlib_worker_threads + j;
3404 vlib_cli_output (vm, "Thread %d (%s at lcore %u):", j, w->name,
3405 w->lcore_id);
Matus Fabian092b3cd2017-09-19 05:42:38 -07003406 vlib_cli_output (vm, " %U", format_bihash_8_8, &tsm->in2out,
3407 verbose - 1);
3408 vlib_cli_output (vm, " %U", format_bihash_8_8, &tsm->out2in,
3409 verbose - 1);
Matus Fabian066f0342017-02-10 03:48:01 -08003410 vlib_cli_output (vm, " %d list pool elements",
3411 pool_elts (tsm->list_pool));
3412
3413 pool_foreach (u, tsm->users,
3414 ({
3415 vlib_cli_output (vm, " %U", format_snat_user, tsm, u,
3416 verbose - 1);
3417 }));
3418 }
3419
3420 if (pool_elts (sm->static_mappings))
3421 {
3422 vlib_cli_output (vm, "static mappings:");
3423 pool_foreach (m, sm->static_mappings,
3424 ({
3425 vlib_cli_output (vm, "%U", format_snat_static_mapping, m);
3426 }));
3427 for (j = 0; j < vec_len (sm->to_resolve); j++)
3428 {
3429 rp = sm->to_resolve + j;
3430 vlib_cli_output (vm, "%U",
3431 format_snat_static_map_to_resolve, rp);
3432 }
Matus Fabiane22e5462017-02-14 23:33:43 -08003433 }
Matus Fabiandb649882016-08-26 05:45:27 -07003434 }
3435 }
Dave Barach20c02cb2016-06-26 10:42:08 -04003436 }
Matus Fabianefcd1e92017-08-15 06:59:19 -07003437
Dave Barach20c02cb2016-06-26 10:42:08 -04003438 return 0;
3439}
3440
3441VLIB_CLI_COMMAND (show_snat_command, static) = {
Matus Fabian2ba92e32017-08-21 07:05:03 -07003442 .path = "show nat44",
3443 .short_help = "show nat44",
Dave Barach20c02cb2016-06-26 10:42:08 -04003444 .function = show_snat_command_fn,
3445};
Dave Barachcab65ec2017-01-11 13:01:14 -05003446
3447
3448static void
3449snat_ip4_add_del_interface_address_cb (ip4_main_t * im,
3450 uword opaque,
3451 u32 sw_if_index,
3452 ip4_address_t * address,
3453 u32 address_length,
3454 u32 if_address_index,
3455 u32 is_delete)
3456{
3457 snat_main_t *sm = &snat_main;
Dave Barach8b275372017-01-16 10:54:02 -05003458 snat_static_map_resolve_t *rp;
3459 u32 *indices_to_delete = 0;
Matus Fabianab7a8052017-11-28 04:29:41 -08003460 ip4_address_t l_addr;
Dave Barachcab65ec2017-01-11 13:01:14 -05003461 int i, j;
Dave Barach8b275372017-01-16 10:54:02 -05003462 int rv;
Matus Fabianb932d262017-12-18 05:38:24 -08003463 u8 twice_nat = 0;
3464 snat_address_t *addresses = sm->addresses;
Dave Barachcab65ec2017-01-11 13:01:14 -05003465
3466 for (i = 0; i < vec_len(sm->auto_add_sw_if_indices); i++)
3467 {
3468 if (sw_if_index == sm->auto_add_sw_if_indices[i])
Matus Fabianb932d262017-12-18 05:38:24 -08003469 goto match;
3470 }
Dave Barachcab65ec2017-01-11 13:01:14 -05003471
Matus Fabianb932d262017-12-18 05:38:24 -08003472 for (i = 0; i < vec_len(sm->auto_add_sw_if_indices_twice_nat); i++)
3473 {
3474 twice_nat = 1;
3475 addresses = sm->twice_nat_addresses;
3476 if (sw_if_index == sm->auto_add_sw_if_indices_twice_nat[i])
3477 goto match;
3478 }
3479
3480 return;
3481
3482match:
3483 if (!is_delete)
3484 {
3485 /* Don't trip over lease renewal, static config */
3486 for (j = 0; j < vec_len(addresses); j++)
3487 if (addresses[j].addr.as_u32 == address->as_u32)
3488 return;
3489
3490 snat_add_address (sm, address, ~0, twice_nat);
3491 /* Scan static map resolution vector */
3492 for (j = 0; j < vec_len (sm->to_resolve); j++)
3493 {
3494 rp = sm->to_resolve + j;
3495 /* On this interface? */
3496 if (rp->sw_if_index == sw_if_index)
Dave Barachcab65ec2017-01-11 13:01:14 -05003497 {
Matus Fabianb932d262017-12-18 05:38:24 -08003498 /* Indetity mapping? */
3499 if (rp->l_addr.as_u32 == 0)
3500 l_addr.as_u32 = address[0].as_u32;
3501 else
3502 l_addr.as_u32 = rp->l_addr.as_u32;
3503 /* Add the static mapping */
3504 rv = snat_add_static_mapping (l_addr,
3505 address[0],
3506 rp->l_port,
3507 rp->e_port,
3508 rp->vrf_id,
3509 rp->addr_only,
3510 ~0 /* sw_if_index */,
3511 rp->proto,
3512 rp->is_add,
3513 0);
3514 if (rv)
3515 clib_warning ("snat_add_static_mapping returned %d",
3516 rv);
3517 vec_add1 (indices_to_delete, j);
Dave Barachcab65ec2017-01-11 13:01:14 -05003518 }
3519 }
Matus Fabianb932d262017-12-18 05:38:24 -08003520 /* If we resolved any of the outstanding static mappings */
3521 if (vec_len(indices_to_delete))
3522 {
3523 /* Delete them */
3524 for (j = vec_len(indices_to_delete)-1; j >= 0; j--)
3525 vec_delete(sm->to_resolve, 1, j);
3526 vec_free(indices_to_delete);
3527 }
3528 return;
3529 }
3530 else
3531 {
3532 (void) snat_del_address(sm, address[0], 1, twice_nat);
3533 return;
Dave Barachcab65ec2017-01-11 13:01:14 -05003534 }
3535}
3536
3537
Matus Fabianb932d262017-12-18 05:38:24 -08003538int snat_add_interface_address (snat_main_t *sm, u32 sw_if_index, int is_del,
3539 u8 twice_nat)
Dave Barachcab65ec2017-01-11 13:01:14 -05003540{
3541 ip4_main_t * ip4_main = sm->ip4_main;
3542 ip4_address_t * first_int_addr;
Matus Fabian36532bd2017-01-23 23:42:28 -08003543 snat_static_map_resolve_t *rp;
3544 u32 *indices_to_delete = 0;
3545 int i, j;
Matus Fabianb932d262017-12-18 05:38:24 -08003546 u32 *auto_add_sw_if_indices =
3547 twice_nat ? sm->auto_add_sw_if_indices_twice_nat : sm->auto_add_sw_if_indices;
Dave Barachcab65ec2017-01-11 13:01:14 -05003548
3549 first_int_addr = ip4_interface_first_address (ip4_main, sw_if_index,
3550 0 /* just want the address*/);
3551
Matus Fabianb932d262017-12-18 05:38:24 -08003552 for (i = 0; i < vec_len(auto_add_sw_if_indices); i++)
Dave Barachcab65ec2017-01-11 13:01:14 -05003553 {
Matus Fabianb932d262017-12-18 05:38:24 -08003554 if (auto_add_sw_if_indices[i] == sw_if_index)
Matus Fabian8bf68e82017-01-12 04:24:35 -08003555 {
3556 if (is_del)
3557 {
3558 /* if have address remove it */
3559 if (first_int_addr)
Matus Fabianb932d262017-12-18 05:38:24 -08003560 (void) snat_del_address (sm, first_int_addr[0], 1, twice_nat);
Matus Fabian36532bd2017-01-23 23:42:28 -08003561 else
3562 {
3563 for (j = 0; j < vec_len (sm->to_resolve); j++)
3564 {
3565 rp = sm->to_resolve + j;
3566 if (rp->sw_if_index == sw_if_index)
3567 vec_add1 (indices_to_delete, j);
3568 }
3569 if (vec_len(indices_to_delete))
3570 {
3571 for (j = vec_len(indices_to_delete)-1; j >= 0; j--)
3572 vec_del1(sm->to_resolve, j);
3573 vec_free(indices_to_delete);
3574 }
3575 }
Matus Fabianb932d262017-12-18 05:38:24 -08003576 if (twice_nat)
3577 vec_del1(sm->auto_add_sw_if_indices_twice_nat, i);
3578 else
3579 vec_del1(sm->auto_add_sw_if_indices, i);
Matus Fabian8bf68e82017-01-12 04:24:35 -08003580 }
3581 else
3582 return VNET_API_ERROR_VALUE_EXIST;
3583
3584 return 0;
3585 }
Dave Barachcab65ec2017-01-11 13:01:14 -05003586 }
Matus Fabian2ba92e32017-08-21 07:05:03 -07003587
Matus Fabian8bf68e82017-01-12 04:24:35 -08003588 if (is_del)
3589 return VNET_API_ERROR_NO_SUCH_ENTRY;
3590
Dave Barachcab65ec2017-01-11 13:01:14 -05003591 /* add to the auto-address list */
Matus Fabianb932d262017-12-18 05:38:24 -08003592 if (twice_nat)
3593 vec_add1(sm->auto_add_sw_if_indices_twice_nat, sw_if_index);
3594 else
3595 vec_add1(sm->auto_add_sw_if_indices, sw_if_index);
Dave Barachcab65ec2017-01-11 13:01:14 -05003596
3597 /* If the address is already bound - or static - add it now */
3598 if (first_int_addr)
Matus Fabianb932d262017-12-18 05:38:24 -08003599 snat_add_address (sm, first_int_addr, ~0, twice_nat);
Dave Barachcab65ec2017-01-11 13:01:14 -05003600
3601 return 0;
3602}
3603
3604static clib_error_t *
3605snat_add_interface_address_command_fn (vlib_main_t * vm,
3606 unformat_input_t * input,
3607 vlib_cli_command_t * cmd)
3608{
3609 snat_main_t *sm = &snat_main;
3610 unformat_input_t _line_input, *line_input = &_line_input;
3611 u32 sw_if_index;
3612 int rv;
Matus Fabian8bf68e82017-01-12 04:24:35 -08003613 int is_del = 0;
Billy McFalla9a20e72017-02-15 11:39:12 -05003614 clib_error_t *error = 0;
Matus Fabianb932d262017-12-18 05:38:24 -08003615 u8 twice_nat = 0;
Dave Barachcab65ec2017-01-11 13:01:14 -05003616
3617 /* Get a line of input. */
3618 if (!unformat_user (input, unformat_line_input, line_input))
3619 return 0;
3620
3621 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
3622 {
3623 if (unformat (line_input, "%U", unformat_vnet_sw_interface,
3624 sm->vnet_main, &sw_if_index))
3625 ;
Matus Fabianb932d262017-12-18 05:38:24 -08003626 else if (unformat (line_input, "twice-nat"))
3627 twice_nat = 1;
Matus Fabian8bf68e82017-01-12 04:24:35 -08003628 else if (unformat (line_input, "del"))
3629 is_del = 1;
Dave Barachcab65ec2017-01-11 13:01:14 -05003630 else
Billy McFalla9a20e72017-02-15 11:39:12 -05003631 {
3632 error = clib_error_return (0, "unknown input '%U'",
3633 format_unformat_error, line_input);
3634 goto done;
3635 }
Dave Barachcab65ec2017-01-11 13:01:14 -05003636 }
3637
Matus Fabianb932d262017-12-18 05:38:24 -08003638 rv = snat_add_interface_address (sm, sw_if_index, is_del, twice_nat);
Dave Barachcab65ec2017-01-11 13:01:14 -05003639
3640 switch (rv)
3641 {
3642 case 0:
3643 break;
3644
3645 default:
Billy McFalla9a20e72017-02-15 11:39:12 -05003646 error = clib_error_return (0, "snat_add_interface_address returned %d",
3647 rv);
3648 goto done;
Dave Barachcab65ec2017-01-11 13:01:14 -05003649 }
Billy McFalla9a20e72017-02-15 11:39:12 -05003650
3651done:
3652 unformat_free (line_input);
3653
3654 return error;
Dave Barachcab65ec2017-01-11 13:01:14 -05003655}
3656
3657VLIB_CLI_COMMAND (snat_add_interface_address_command, static) = {
Matus Fabian2ba92e32017-08-21 07:05:03 -07003658 .path = "nat44 add interface address",
Matus Fabianb932d262017-12-18 05:38:24 -08003659 .short_help = "nat44 add interface address <interface> [twice-nat] [del]",
Dave Barachcab65ec2017-01-11 13:01:14 -05003660 .function = snat_add_interface_address_command_fn,
3661};
Matus Fabian066f0342017-02-10 03:48:01 -08003662
Matus Fabian5ba86f72017-10-26 03:37:38 -07003663int
3664nat44_del_session (snat_main_t *sm, ip4_address_t *addr, u16 port,
3665 snat_protocol_t proto, u32 vrf_id, int is_in)
3666{
3667 snat_main_per_thread_data_t *tsm;
3668 clib_bihash_kv_8_8_t kv, value;
3669 ip4_header_t ip;
3670 u32 fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id);
3671 snat_session_key_t key;
3672 snat_session_t *s;
3673 clib_bihash_8_8_t *t;
3674 snat_user_key_t u_key;
3675 snat_user_t *u;
3676
3677 ip.dst_address.as_u32 = ip.src_address.as_u32 = addr->as_u32;
3678 if (sm->num_workers)
3679 tsm =
3680 vec_elt_at_index (sm->per_thread_data,
3681 sm->worker_in2out_cb (&ip, fib_index));
3682 else
3683 tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
3684
3685 key.addr.as_u32 = addr->as_u32;
3686 key.port = clib_host_to_net_u16 (port);
3687 key.protocol = proto;
3688 key.fib_index = fib_index;
3689 kv.key = key.as_u64;
3690 t = is_in ? &tsm->in2out : &tsm->out2in;
3691 if (!clib_bihash_search_8_8 (t, &kv, &value))
3692 {
3693 s = pool_elt_at_index (tsm->sessions, value.value);
3694 kv.key = s->in2out.as_u64;
3695 clib_bihash_add_del_8_8 (&tsm->in2out, &kv, 0);
3696 kv.key = s->out2in.as_u64;
3697 clib_bihash_add_del_8_8 (&tsm->out2in, &kv, 0);
3698 u_key.addr = s->in2out.addr;
3699 u_key.fib_index = s->in2out.fib_index;
3700 kv.key = u_key.as_u64;
3701 if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
3702 {
3703 u = pool_elt_at_index (tsm->users, value.value);
3704 u->nsessions--;
3705 }
3706 clib_dlist_remove (tsm->list_pool, s->per_user_index);
3707 pool_put (tsm->sessions, s);
3708 return 0;
3709 }
3710
3711 return VNET_API_ERROR_NO_SUCH_ENTRY;
3712}
3713
3714static clib_error_t *
3715nat44_del_session_command_fn (vlib_main_t * vm,
3716 unformat_input_t * input,
3717 vlib_cli_command_t * cmd)
3718{
3719 snat_main_t *sm = &snat_main;
3720 unformat_input_t _line_input, *line_input = &_line_input;
3721 int is_in = 0;
3722 clib_error_t *error = 0;
3723 ip4_address_t addr;
3724 u32 port = 0, vrf_id = sm->outside_vrf_id;
3725 snat_protocol_t proto;
3726 int rv;
3727
3728 /* Get a line of input. */
3729 if (!unformat_user (input, unformat_line_input, line_input))
3730 return 0;
3731
3732 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
3733 {
3734 if (unformat (line_input, "%U:%u %U", unformat_ip4_address, &addr, &port,
3735 unformat_snat_protocol, &proto))
3736 ;
3737 else if (unformat (line_input, "in"))
3738 {
3739 is_in = 1;
3740 vrf_id = sm->inside_vrf_id;
3741 }
3742 else if (unformat (line_input, "vrf %u", &vrf_id))
3743 ;
3744 else
3745 {
3746 error = clib_error_return (0, "unknown input '%U'",
3747 format_unformat_error, line_input);
3748 goto done;
3749 }
3750 }
3751
3752 rv = nat44_del_session(sm, &addr, port, proto, vrf_id, is_in);
3753
3754 switch (rv)
3755 {
3756 case 0:
3757 break;
3758
3759 default:
3760 error = clib_error_return (0, "nat44_del_session returned %d", rv);
3761 goto done;
3762 }
3763
3764done:
3765 unformat_free (line_input);
3766
3767 return error;
3768}
3769
3770VLIB_CLI_COMMAND (nat44_del_session_command, static) = {
3771 .path = "nat44 del session",
3772 .short_help = "nat44 del session in|out <addr>:<port> tcp|udp|icmp [vrf <id>]",
3773 .function = nat44_del_session_command_fn,
3774};
3775
Matus Fabian066f0342017-02-10 03:48:01 -08003776static clib_error_t *
Matus Fabian27697102017-11-09 01:43:47 -08003777nat44_set_alloc_addr_and_port_alg_command_fn (vlib_main_t * vm,
3778 unformat_input_t * input,
3779 vlib_cli_command_t * cmd)
3780{
3781 snat_main_t *sm = &snat_main;
3782 unformat_input_t _line_input, *line_input = &_line_input;
3783 clib_error_t *error = 0;
3784 u32 psid, psid_offset, psid_length;
3785
3786 /* Get a line of input. */
3787 if (!unformat_user (input, unformat_line_input, line_input))
3788 return 0;
3789
3790 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
3791 {
3792 if (unformat (line_input, "default"))
3793 sm->alloc_addr_and_port = nat_alloc_addr_and_port_default;
3794 else if (unformat (line_input, "map-e psid %d psid-offset %d psid-len %d",
3795 &psid, &psid_offset, &psid_length))
3796 {
3797 sm->alloc_addr_and_port = nat_alloc_addr_and_port_mape;
3798 sm->psid = (u16) psid;
3799 sm->psid_offset = (u16) psid_offset;
3800 sm->psid_length = (u16) psid_length;
3801 }
3802 else
3803 {
3804 error = clib_error_return (0, "unknown input '%U'",
3805 format_unformat_error, line_input);
3806 goto done;
3807 }
3808 }
3809
3810done:
3811 unformat_free (line_input);
3812
3813 return error;
3814};
3815
3816VLIB_CLI_COMMAND (nat44_set_alloc_addr_and_port_alg_command, static) = {
Matus Fabian51e759f2017-12-07 23:22:51 -08003817 .path = "nat addr-port-assignment-alg",
3818 .short_help = "nat addr-port-assignment-alg <alg-name> [<alg-params>]",
Matus Fabian27697102017-11-09 01:43:47 -08003819 .function = nat44_set_alloc_addr_and_port_alg_command_fn,
3820};
3821
3822static clib_error_t *
Matus Fabian066f0342017-02-10 03:48:01 -08003823snat_det_map_command_fn (vlib_main_t * vm,
3824 unformat_input_t * input,
3825 vlib_cli_command_t * cmd)
3826{
3827 snat_main_t *sm = &snat_main;
3828 unformat_input_t _line_input, *line_input = &_line_input;
3829 ip4_address_t in_addr, out_addr;
3830 u32 in_plen, out_plen;
3831 int is_add = 1, rv;
3832 clib_error_t *error = 0;
3833
3834 /* Get a line of input. */
3835 if (!unformat_user (input, unformat_line_input, line_input))
3836 return 0;
3837
3838 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
3839 {
3840 if (unformat (line_input, "in %U/%u", unformat_ip4_address, &in_addr, &in_plen))
3841 ;
3842 else if (unformat (line_input, "out %U/%u", unformat_ip4_address, &out_addr, &out_plen))
3843 ;
3844 else if (unformat (line_input, "del"))
3845 is_add = 0;
3846 else
3847 {
3848 error = clib_error_return (0, "unknown input '%U'",
3849 format_unformat_error, line_input);
3850 goto done;
3851 }
3852 }
3853
Matus Fabian066f0342017-02-10 03:48:01 -08003854 rv = snat_det_add_map(sm, &in_addr, (u8) in_plen, &out_addr, (u8)out_plen,
3855 is_add);
3856
3857 if (rv)
3858 {
3859 error = clib_error_return (0, "snat_det_add_map return %d", rv);
3860 goto done;
3861 }
3862
3863done:
3864 unformat_free (line_input);
3865
3866 return error;
3867}
3868
3869/*?
3870 * @cliexpar
3871 * @cliexstart{snat deterministic add}
3872 * Create bijective mapping of inside address to outside address and port range
3873 * pairs, with the purpose of enabling deterministic NAT to reduce logging in
3874 * CGN deployments.
3875 * To create deterministic mapping between inside network 10.0.0.0/18 and
3876 * outside network 1.1.1.0/30 use:
Matus Fabian2ba92e32017-08-21 07:05:03 -07003877 * # vpp# nat44 deterministic add in 10.0.0.0/18 out 1.1.1.0/30
Matus Fabian066f0342017-02-10 03:48:01 -08003878 * @cliexend
3879?*/
3880VLIB_CLI_COMMAND (snat_det_map_command, static) = {
Matus Fabian2ba92e32017-08-21 07:05:03 -07003881 .path = "nat44 deterministic add",
3882 .short_help = "nat44 deterministic add in <addr>/<plen> out <addr>/<plen> [del]",
Matus Fabian066f0342017-02-10 03:48:01 -08003883 .function = snat_det_map_command_fn,
3884};
3885
3886static clib_error_t *
3887snat_det_forward_command_fn (vlib_main_t * vm,
3888 unformat_input_t * input,
3889 vlib_cli_command_t * cmd)
3890{
3891 snat_main_t *sm = &snat_main;
3892 unformat_input_t _line_input, *line_input = &_line_input;
3893 ip4_address_t in_addr, out_addr;
3894 u16 lo_port;
3895 snat_det_map_t * dm;
3896 clib_error_t *error = 0;
3897
3898 /* Get a line of input. */
3899 if (!unformat_user (input, unformat_line_input, line_input))
3900 return 0;
3901
3902 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
3903 {
3904 if (unformat (line_input, "%U", unformat_ip4_address, &in_addr))
3905 ;
3906 else
3907 {
3908 error = clib_error_return (0, "unknown input '%U'",
3909 format_unformat_error, line_input);
3910 goto done;
3911 }
3912 }
3913
Matus Fabian066f0342017-02-10 03:48:01 -08003914 dm = snat_det_map_by_user(sm, &in_addr);
3915 if (!dm)
3916 vlib_cli_output (vm, "no match");
3917 else
3918 {
3919 snat_det_forward (dm, &in_addr, &out_addr, &lo_port);
3920 vlib_cli_output (vm, "%U:<%d-%d>", format_ip4_address, &out_addr,
3921 lo_port, lo_port + dm->ports_per_host - 1);
3922 }
3923
3924done:
3925 unformat_free (line_input);
3926
3927 return error;
3928}
3929
3930/*?
3931 * @cliexpar
3932 * @cliexstart{snat deterministic forward}
3933 * Return outside address and port range from inside address for deterministic
3934 * NAT.
3935 * To obtain outside address and port of inside host use:
Matus Fabian2ba92e32017-08-21 07:05:03 -07003936 * vpp# nat44 deterministic forward 10.0.0.2
Matus Fabian066f0342017-02-10 03:48:01 -08003937 * 1.1.1.0:<1054-1068>
3938 * @cliexend
3939?*/
3940VLIB_CLI_COMMAND (snat_det_forward_command, static) = {
Matus Fabian2ba92e32017-08-21 07:05:03 -07003941 .path = "nat44 deterministic forward",
3942 .short_help = "nat44 deterministic forward <addr>",
Matus Fabian066f0342017-02-10 03:48:01 -08003943 .function = snat_det_forward_command_fn,
3944};
3945
3946static clib_error_t *
3947snat_det_reverse_command_fn (vlib_main_t * vm,
3948 unformat_input_t * input,
3949 vlib_cli_command_t * cmd)
3950{
3951 snat_main_t *sm = &snat_main;
3952 unformat_input_t _line_input, *line_input = &_line_input;
3953 ip4_address_t in_addr, out_addr;
3954 u32 out_port;
3955 snat_det_map_t * dm;
3956 clib_error_t *error = 0;
3957
3958 /* Get a line of input. */
3959 if (!unformat_user (input, unformat_line_input, line_input))
3960 return 0;
3961
3962 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
3963 {
3964 if (unformat (line_input, "%U:%d", unformat_ip4_address, &out_addr, &out_port))
3965 ;
3966 else
3967 {
3968 error = clib_error_return (0, "unknown input '%U'",
3969 format_unformat_error, line_input);
3970 }
3971 }
3972
Matus Fabian066f0342017-02-10 03:48:01 -08003973 if (out_port < 1024 || out_port > 65535)
3974 {
3975 error = clib_error_return (0, "wrong port, must be <1024-65535>");
3976 goto done;
3977 }
3978
3979 dm = snat_det_map_by_out(sm, &out_addr);
3980 if (!dm)
3981 vlib_cli_output (vm, "no match");
3982 else
3983 {
3984 snat_det_reverse (dm, &out_addr, (u16) out_port, &in_addr);
3985 vlib_cli_output (vm, "%U", format_ip4_address, &in_addr);
3986 }
3987
3988done:
3989 unformat_free (line_input);
3990
3991 return error;
3992}
3993
3994/*?
3995 * @cliexpar
3996 * @cliexstart{snat deterministic reverse}
3997 * Return inside address from outside address and port for deterministic NAT.
3998 * To obtain inside host address from outside address and port use:
Matus Fabian2ba92e32017-08-21 07:05:03 -07003999 * #vpp nat44 deterministic reverse 1.1.1.1:1276
Matus Fabian066f0342017-02-10 03:48:01 -08004000 * 10.0.16.16
4001 * @cliexend
4002?*/
4003VLIB_CLI_COMMAND (snat_det_reverse_command, static) = {
Matus Fabian2ba92e32017-08-21 07:05:03 -07004004 .path = "nat44 deterministic reverse",
4005 .short_help = "nat44 deterministic reverse <addr>:<port>",
Matus Fabian066f0342017-02-10 03:48:01 -08004006 .function = snat_det_reverse_command_fn,
4007};
Matus Fabian6a0946f2017-04-12 03:36:13 -07004008
4009static clib_error_t *
4010set_timeout_command_fn (vlib_main_t * vm,
4011 unformat_input_t * input,
4012 vlib_cli_command_t * cmd)
4013{
4014 snat_main_t *sm = &snat_main;
4015 unformat_input_t _line_input, *line_input = &_line_input;
4016 clib_error_t *error = 0;
4017
4018 /* Get a line of input. */
4019 if (!unformat_user (input, unformat_line_input, line_input))
4020 return 0;
4021
4022 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
4023 {
4024 if (unformat (line_input, "udp %u", &sm->udp_timeout))
4025 ;
4026 else if (unformat (line_input, "tcp-established %u",
4027 &sm->tcp_established_timeout))
4028 ;
4029 else if (unformat (line_input, "tcp-transitory %u",
4030 &sm->tcp_transitory_timeout))
4031 ;
4032 else if (unformat (line_input, "icmp %u", &sm->icmp_timeout))
4033 ;
4034 else if (unformat (line_input, "reset"))
4035 {
4036 sm->udp_timeout = SNAT_UDP_TIMEOUT;
4037 sm->tcp_established_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT;
4038 sm->tcp_transitory_timeout = SNAT_TCP_TRANSITORY_TIMEOUT;
4039 sm->icmp_timeout = SNAT_ICMP_TIMEOUT;
4040 }
4041 else
4042 {
4043 error = clib_error_return (0, "unknown input '%U'",
4044 format_unformat_error, line_input);
4045 goto done;
4046 }
4047 }
4048
Matus Fabian6a0946f2017-04-12 03:36:13 -07004049done:
4050 unformat_free (line_input);
4051
4052 return error;
4053}
4054
4055/*?
4056 * @cliexpar
4057 * @cliexstart{set snat deterministic timeout}
4058 * Set values of timeouts for deterministic NAT (in seconds), use:
Matus Fabian2ba92e32017-08-21 07:05:03 -07004059 * vpp# set nat44 deterministic timeout udp 120 tcp-established 7500
Matus Fabian6a0946f2017-04-12 03:36:13 -07004060 * tcp-transitory 250 icmp 90
4061 * To reset default values use:
Matus Fabian2ba92e32017-08-21 07:05:03 -07004062 * vpp# set nat44 deterministic timeout reset
Matus Fabian6a0946f2017-04-12 03:36:13 -07004063 * @cliexend
4064?*/
4065VLIB_CLI_COMMAND (set_timeout_command, static) = {
Matus Fabian2ba92e32017-08-21 07:05:03 -07004066 .path = "set nat44 deterministic timeout",
Matus Fabian6a0946f2017-04-12 03:36:13 -07004067 .function = set_timeout_command_fn,
4068 .short_help =
Matus Fabian2ba92e32017-08-21 07:05:03 -07004069 "set nat44 deterministic timeout [udp <sec> | tcp-established <sec> "
Matus Fabian6a0946f2017-04-12 03:36:13 -07004070 "tcp-transitory <sec> | icmp <sec> | reset]",
4071};
Martin Gálik6bc8c642017-04-19 01:12:27 -07004072
4073static clib_error_t *
4074snat_det_close_session_out_fn (vlib_main_t *vm,
4075 unformat_input_t * input,
4076 vlib_cli_command_t * cmd)
4077{
4078 snat_main_t *sm = &snat_main;
4079 unformat_input_t _line_input, *line_input = &_line_input;
4080 ip4_address_t out_addr, ext_addr, in_addr;
Aequitasfc4510b2017-09-23 12:58:49 +08004081 u32 out_port, ext_port;
Martin Gálik6bc8c642017-04-19 01:12:27 -07004082 snat_det_map_t * dm;
4083 snat_det_session_t * ses;
4084 snat_det_out_key_t key;
4085 clib_error_t *error = 0;
4086
4087 /* Get a line of input. */
4088 if (!unformat_user (input, unformat_line_input, line_input))
4089 return 0;
4090
4091 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
4092 {
4093 if (unformat (line_input, "%U:%d %U:%d",
4094 unformat_ip4_address, &out_addr, &out_port,
4095 unformat_ip4_address, &ext_addr, &ext_port))
4096 ;
4097 else
4098 {
4099 error = clib_error_return (0, "unknown input '%U'",
4100 format_unformat_error, line_input);
4101 goto done;
4102 }
4103 }
4104
4105 unformat_free (line_input);
4106
4107 dm = snat_det_map_by_out(sm, &out_addr);
4108 if (!dm)
4109 vlib_cli_output (vm, "no match");
4110 else
4111 {
Aequitasfc4510b2017-09-23 12:58:49 +08004112 snat_det_reverse(dm, &ext_addr, (u16)out_port, &in_addr);
Martin Gálik6bc8c642017-04-19 01:12:27 -07004113 key.ext_host_addr = out_addr;
Aequitasfc4510b2017-09-23 12:58:49 +08004114 key.ext_host_port = ntohs((u16)ext_port);
4115 key.out_port = ntohs((u16)out_port);
Martin Gálik6bc8c642017-04-19 01:12:27 -07004116 ses = snat_det_get_ses_by_out(dm, &out_addr, key.as_u64);
4117 if (!ses)
4118 vlib_cli_output (vm, "no match");
4119 else
4120 snat_det_ses_close(dm, ses);
4121 }
4122
4123done:
4124 unformat_free (line_input);
4125
4126 return error;
4127}
4128
4129/*?
4130 * @cliexpar
4131 * @cliexstart{snat deterministic close session out}
4132 * Close session using outside ip address and port
4133 * and external ip address and port, use:
Matus Fabian2ba92e32017-08-21 07:05:03 -07004134 * vpp# nat44 deterministic close session out 1.1.1.1:1276 2.2.2.2:2387
Martin Gálik6bc8c642017-04-19 01:12:27 -07004135 * @cliexend
4136?*/
4137VLIB_CLI_COMMAND (snat_det_close_sesion_out_command, static) = {
Matus Fabian2ba92e32017-08-21 07:05:03 -07004138 .path = "nat44 deterministic close session out",
4139 .short_help = "nat44 deterministic close session out "
Martin Gálik6bc8c642017-04-19 01:12:27 -07004140 "<out_addr>:<out_port> <ext_addr>:<ext_port>",
4141 .function = snat_det_close_session_out_fn,
4142};
4143
4144static clib_error_t *
4145snat_det_close_session_in_fn (vlib_main_t *vm,
4146 unformat_input_t * input,
4147 vlib_cli_command_t * cmd)
4148{
4149 snat_main_t *sm = &snat_main;
4150 unformat_input_t _line_input, *line_input = &_line_input;
4151 ip4_address_t in_addr, ext_addr;
Aequitasfc4510b2017-09-23 12:58:49 +08004152 u32 in_port, ext_port;
Martin Gálik6bc8c642017-04-19 01:12:27 -07004153 snat_det_map_t * dm;
4154 snat_det_session_t * ses;
4155 snat_det_out_key_t key;
4156 clib_error_t *error = 0;
4157
4158 /* Get a line of input. */
4159 if (!unformat_user (input, unformat_line_input, line_input))
4160 return 0;
4161
4162 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
4163 {
4164 if (unformat (line_input, "%U:%d %U:%d",
4165 unformat_ip4_address, &in_addr, &in_port,
4166 unformat_ip4_address, &ext_addr, &ext_port))
4167 ;
4168 else
4169 {
4170 error = clib_error_return (0, "unknown input '%U'",
4171 format_unformat_error, line_input);
4172 goto done;
4173 }
4174 }
4175
4176 unformat_free (line_input);
4177
4178 dm = snat_det_map_by_user (sm, &in_addr);
4179 if (!dm)
4180 vlib_cli_output (vm, "no match");
4181 else
4182 {
4183 key.ext_host_addr = ext_addr;
Aequitasfc4510b2017-09-23 12:58:49 +08004184 key.ext_host_port = ntohs ((u16)ext_port);
4185 ses = snat_det_find_ses_by_in (dm, &in_addr, ntohs((u16)in_port), key);
Martin Gálik6bc8c642017-04-19 01:12:27 -07004186 if (!ses)
4187 vlib_cli_output (vm, "no match");
4188 else
4189 snat_det_ses_close(dm, ses);
4190 }
4191
4192done:
4193 unformat_free(line_input);
4194
4195 return error;
4196}
4197
4198/*?
4199 * @cliexpar
4200 * @cliexstart{snat deterministic close_session_in}
4201 * Close session using inside ip address and port
4202 * and external ip address and port, use:
Matus Fabian2ba92e32017-08-21 07:05:03 -07004203 * vpp# nat44 deterministic close session in 3.3.3.3:3487 2.2.2.2:2387
Martin Gálik6bc8c642017-04-19 01:12:27 -07004204 * @cliexend
4205?*/
4206VLIB_CLI_COMMAND (snat_det_close_session_in_command, static) = {
Matus Fabian2ba92e32017-08-21 07:05:03 -07004207 .path = "nat44 deterministic close session in",
4208 .short_help = "nat44 deterministic close session in "
Martin Gálik6bc8c642017-04-19 01:12:27 -07004209 "<in_addr>:<in_port> <ext_addr>:<ext_port>",
4210 .function = snat_det_close_session_in_fn,
4211};
Juraj Sloboda7b929792017-11-23 13:20:48 +01004212
4213static clib_error_t *
4214snat_forwarding_set_command_fn (vlib_main_t *vm,
4215 unformat_input_t * input,
4216 vlib_cli_command_t * cmd)
4217{
4218 snat_main_t *sm = &snat_main;
4219 unformat_input_t _line_input, *line_input = &_line_input;
4220 u8 forwarding_enable;
4221 u8 forwarding_enable_set = 0;
4222 clib_error_t *error = 0;
4223
4224 /* Get a line of input. */
4225 if (!unformat_user (input, unformat_line_input, line_input))
4226 return clib_error_return (0, "'enable' or 'disable' expected");
4227
4228 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
4229 {
4230 if (!forwarding_enable_set && unformat (line_input, "enable"))
4231 {
4232 forwarding_enable = 1;
4233 forwarding_enable_set = 1;
4234 }
4235 else if (!forwarding_enable_set && unformat (line_input, "disable"))
4236 {
4237 forwarding_enable = 0;
4238 forwarding_enable_set = 1;
4239 }
4240 else
4241 {
4242 error = clib_error_return (0, "unknown input '%U'",
4243 format_unformat_error, line_input);
4244 goto done;
4245 }
4246 }
4247
4248 if (!forwarding_enable_set)
4249 {
4250 error = clib_error_return (0, "'enable' or 'disable' expected");
4251 goto done;
4252 }
4253
4254 sm->forwarding_enabled = forwarding_enable;
4255
4256done:
4257 unformat_free(line_input);
4258
4259 return error;
4260}
4261
4262/*?
4263 * @cliexpar
4264 * @cliexstart{nat44 forwarding}
4265 * Enable or disable forwarding
4266 * Forward packets which don't match existing translation
4267 * or static mapping instead of dropping them.
4268 * To enable forwarding, use:
4269 * vpp# nat44 forwarding enable
4270 * To disable forwarding, use:
4271 * vpp# nat44 forwarding disable
4272 * @cliexend
4273?*/
4274VLIB_CLI_COMMAND (snat_forwarding_set_command, static) = {
4275 .path = "nat44 forwarding",
4276 .short_help = "nat44 forwarding enable|disable",
4277 .function = snat_forwarding_set_command_fn,
4278};