nat: nat44-ed add session timing out indicator in api
[vpp.git] / src / plugins / nat / det44 / det44_cli.c
1 /*
2  * Copyright (c) 2020 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /**
16  * @file
17  * @brief DET44 CLI
18  */
19 #include <nat/det44/det44.h>
20
21 #define DET44_EXPECTED_ARGUMENT "expected required argument(s)"
22
23 static clib_error_t *
24 det44_map_command_fn (vlib_main_t * vm, unformat_input_t * input,
25                       vlib_cli_command_t * cmd)
26 {
27   unformat_input_t _line_input, *line_input = &_line_input;
28   ip4_address_t in_addr, out_addr;
29   u32 in_plen, out_plen;
30   int is_add = 1, rv;
31   clib_error_t *error = 0;
32
33   if (!unformat_user (input, unformat_line_input, line_input))
34     return clib_error_return (0, DET44_EXPECTED_ARGUMENT);
35
36   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
37     {
38       if (unformat
39           (line_input, "in %U/%u", unformat_ip4_address, &in_addr, &in_plen))
40         ;
41       else
42         if (unformat
43             (line_input, "out %U/%u", unformat_ip4_address, &out_addr,
44              &out_plen))
45         ;
46       else if (unformat (line_input, "del"))
47         is_add = 0;
48       else
49         {
50           error = clib_error_return (0, "unknown input '%U'",
51                                      format_unformat_error, line_input);
52           goto done;
53         }
54     }
55
56   rv = snat_det_add_map (&in_addr, (u8) in_plen, &out_addr, (u8) out_plen,
57                          is_add);
58
59   if (rv)
60     {
61       error = clib_error_return (0, "snat_det_add_map return %d", rv);
62       goto done;
63     }
64
65 done:
66   unformat_free (line_input);
67
68   return error;
69 }
70
71 static clib_error_t *
72 det44_show_mappings_command_fn (vlib_main_t * vm,
73                                 unformat_input_t * input,
74                                 vlib_cli_command_t * cmd)
75 {
76   det44_main_t *dm = &det44_main;
77   snat_det_map_t *mp;
78   vlib_cli_output (vm, "NAT44 deterministic mappings:");
79   pool_foreach (mp, dm->det_maps)
80    {
81     vlib_cli_output (vm, " in %U/%d out %U/%d\n",
82                      format_ip4_address, &mp->in_addr, mp->in_plen,
83                      format_ip4_address, &mp->out_addr, mp->out_plen);
84     vlib_cli_output (vm, "  outside address sharing ratio: %d\n",
85                      mp->sharing_ratio);
86     vlib_cli_output (vm, "  number of ports per inside host: %d\n",
87                      mp->ports_per_host);
88     vlib_cli_output (vm, "  sessions number: %d\n", mp->ses_num);
89   }
90   return 0;
91 }
92
93 static clib_error_t *
94 det44_forward_command_fn (vlib_main_t * vm,
95                           unformat_input_t * input, vlib_cli_command_t * cmd)
96 {
97   unformat_input_t _line_input, *line_input = &_line_input;
98   ip4_address_t in_addr, out_addr;
99   u16 lo_port;
100   snat_det_map_t *mp;
101   clib_error_t *error = 0;
102
103   if (!unformat_user (input, unformat_line_input, line_input))
104     return clib_error_return (0, DET44_EXPECTED_ARGUMENT);
105
106   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
107     {
108       if (unformat (line_input, "%U", unformat_ip4_address, &in_addr))
109         ;
110       else
111         {
112           error = clib_error_return (0, "unknown input '%U'",
113                                      format_unformat_error, line_input);
114           goto done;
115         }
116     }
117
118   mp = snat_det_map_by_user (&in_addr);
119   if (!mp)
120     vlib_cli_output (vm, "no match");
121   else
122     {
123       snat_det_forward (mp, &in_addr, &out_addr, &lo_port);
124       vlib_cli_output (vm, "%U:<%d-%d>", format_ip4_address, &out_addr,
125                        lo_port, lo_port + mp->ports_per_host - 1);
126     }
127
128 done:
129   unformat_free (line_input);
130
131   return error;
132 }
133
134 static clib_error_t *
135 det44_reverse_command_fn (vlib_main_t * vm,
136                           unformat_input_t * input, vlib_cli_command_t * cmd)
137 {
138   unformat_input_t _line_input, *line_input = &_line_input;
139   ip4_address_t in_addr, out_addr;
140   clib_error_t *error = 0;
141   snat_det_map_t *mp;
142   u32 out_port;
143
144   if (!unformat_user (input, unformat_line_input, line_input))
145     return clib_error_return (0, DET44_EXPECTED_ARGUMENT);
146
147   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
148     {
149       if (unformat
150           (line_input, "%U:%d", unformat_ip4_address, &out_addr, &out_port))
151         ;
152       else
153         {
154           error = clib_error_return (0, "unknown input '%U'",
155                                      format_unformat_error, line_input);
156           goto done;
157         }
158     }
159
160   if (out_port < 1024 || out_port > 65535)
161     {
162       error = clib_error_return (0, "wrong port, must be <1024-65535>");
163       goto done;
164     }
165
166   mp = snat_det_map_by_out (&out_addr);
167   if (!mp)
168     vlib_cli_output (vm, "no match");
169   else
170     {
171       snat_det_reverse (mp, &out_addr, (u16) out_port, &in_addr);
172       vlib_cli_output (vm, "%U", format_ip4_address, &in_addr);
173     }
174
175 done:
176   unformat_free (line_input);
177
178   return error;
179 }
180
181 static clib_error_t *
182 det44_show_sessions_command_fn (vlib_main_t * vm,
183                                 unformat_input_t * input,
184                                 vlib_cli_command_t * cmd)
185 {
186   det44_main_t *dm = &det44_main;
187   snat_det_session_t *ses;
188   snat_det_map_t *mp;
189   vlib_cli_output (vm, "NAT44 deterministic sessions:");
190   pool_foreach (mp, dm->det_maps)
191    {
192     int i;
193     vec_foreach_index (i, mp->sessions)
194       {
195         ses = vec_elt_at_index (mp->sessions, i);
196         if (ses->in_port)
197           vlib_cli_output (vm, "  %U", format_det_map_ses, mp, ses, &i);
198       }
199   }
200   return 0;
201 }
202
203 static clib_error_t *
204 det44_close_session_out_fn (vlib_main_t * vm,
205                             unformat_input_t * input,
206                             vlib_cli_command_t * cmd)
207 {
208   unformat_input_t _line_input, *line_input = &_line_input;
209   ip4_address_t out_addr, ext_addr, in_addr;
210   u32 out_port, ext_port;
211   snat_det_map_t *mp;
212   snat_det_session_t *ses;
213   snat_det_out_key_t key;
214   clib_error_t *error = 0;
215
216   if (!unformat_user (input, unformat_line_input, line_input))
217     return clib_error_return (0, DET44_EXPECTED_ARGUMENT);
218
219   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
220     {
221       if (unformat (line_input, "%U:%d %U:%d",
222                     unformat_ip4_address, &out_addr, &out_port,
223                     unformat_ip4_address, &ext_addr, &ext_port))
224         ;
225       else
226         {
227           error = clib_error_return (0, "unknown input '%U'",
228                                      format_unformat_error, line_input);
229           goto done;
230         }
231     }
232
233   unformat_free (line_input);
234
235   mp = snat_det_map_by_out (&out_addr);
236   if (!mp)
237     vlib_cli_output (vm, "no match");
238   else
239     {
240       snat_det_reverse (mp, &ext_addr, (u16) out_port, &in_addr);
241       key.ext_host_addr = out_addr;
242       key.ext_host_port = ntohs ((u16) ext_port);
243       key.out_port = ntohs ((u16) out_port);
244       ses = snat_det_get_ses_by_out (mp, &out_addr, key.as_u64);
245       if (!ses)
246         vlib_cli_output (vm, "no match");
247       else
248         snat_det_ses_close (mp, ses);
249     }
250
251 done:
252   unformat_free (line_input);
253
254   return error;
255 }
256
257 static clib_error_t *
258 det44_close_session_in_fn (vlib_main_t * vm,
259                            unformat_input_t * input, vlib_cli_command_t * cmd)
260 {
261   unformat_input_t _line_input, *line_input = &_line_input;
262   ip4_address_t in_addr, ext_addr;
263   u32 in_port, ext_port;
264   snat_det_map_t *mp;
265   snat_det_session_t *ses;
266   snat_det_out_key_t key;
267   clib_error_t *error = 0;
268
269   if (!unformat_user (input, unformat_line_input, line_input))
270     return clib_error_return (0, DET44_EXPECTED_ARGUMENT);
271
272   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
273     {
274       if (unformat (line_input, "%U:%d %U:%d",
275                     unformat_ip4_address, &in_addr, &in_port,
276                     unformat_ip4_address, &ext_addr, &ext_port))
277         ;
278       else
279         {
280           error = clib_error_return (0, "unknown input '%U'",
281                                      format_unformat_error, line_input);
282           goto done;
283         }
284     }
285
286   unformat_free (line_input);
287
288   mp = snat_det_map_by_user (&in_addr);
289   if (!mp)
290     vlib_cli_output (vm, "no match");
291   else
292     {
293       key.ext_host_addr = ext_addr;
294       key.ext_host_port = ntohs ((u16) ext_port);
295       ses =
296         snat_det_find_ses_by_in (mp, &in_addr, ntohs ((u16) in_port), key);
297       if (!ses)
298         vlib_cli_output (vm, "no match");
299       else
300         snat_det_ses_close (mp, ses);
301     }
302
303 done:
304   unformat_free (line_input);
305
306   return error;
307 }
308
309 static clib_error_t *
310 det44_set_timeouts_command_fn (vlib_main_t * vm,
311                                unformat_input_t * input,
312                                vlib_cli_command_t * cmd)
313 {
314   unformat_input_t _line_input, *line_input = &_line_input;
315   nat_timeouts_t timeouts = { 0 };
316   clib_error_t *error = 0;
317   u8 reset = 0;
318
319   if (!unformat_user (input, unformat_line_input, line_input))
320     return clib_error_return (0, DET44_EXPECTED_ARGUMENT);
321
322   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
323     {
324       if (unformat (line_input, "udp %u", &timeouts.udp));
325       else if (unformat (line_input, "tcp established %u",
326                          &timeouts.tcp.established));
327       else if (unformat (line_input, "tcp transitory %u",
328                          &timeouts.tcp.transitory));
329       else if (unformat (line_input, "icmp %u", &timeouts.icmp));
330       else if (unformat (line_input, "reset"))
331         reset = 1;
332       else
333         {
334           error = clib_error_return (0, "unknown input '%U'",
335                                      format_unformat_error, line_input);
336           goto done;
337         }
338     }
339
340   if (!reset)
341     {
342       if (det44_set_timeouts (&timeouts))
343         {
344           error = clib_error_return (0, "error configuring timeouts");
345         }
346     }
347   else
348     det44_reset_timeouts ();
349 done:
350   unformat_free (line_input);
351   return error;
352 }
353
354 static clib_error_t *
355 det44_show_timeouts_command_fn (vlib_main_t * vm,
356                                 unformat_input_t * input,
357                                 vlib_cli_command_t * cmd)
358 {
359   nat_timeouts_t timeouts;
360   timeouts = det44_get_timeouts ();
361   vlib_cli_output (vm, "udp timeout: %dsec", timeouts.udp);
362   vlib_cli_output (vm, "tcp established timeout: %dsec",
363                    timeouts.tcp.established);
364   vlib_cli_output (vm, "tcp transitory timeout: %dsec",
365                    timeouts.tcp.transitory);
366   vlib_cli_output (vm, "icmp timeout: %dsec", timeouts.icmp);
367   return 0;
368 }
369
370 static clib_error_t *
371 det44_plugin_enable_disable_command_fn (vlib_main_t * vm,
372                                         unformat_input_t * input,
373                                         vlib_cli_command_t * cmd)
374 {
375   unformat_input_t _line_input, *line_input = &_line_input;
376   u8 enable = 0, is_set = 0;
377   clib_error_t *error = 0;
378   det44_config_t c = { 0 };
379
380   if (!unformat_user (input, unformat_line_input, line_input))
381     return clib_error_return (0, DET44_EXPECTED_ARGUMENT);
382
383   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
384     {
385       if (!is_set && unformat (line_input, "enable"))
386         {
387           unformat (line_input, "inside vrf %u", &c.inside_vrf_id);
388           unformat (line_input, "outside vrf %u", &c.outside_vrf_id);
389           enable = 1;
390         }
391       else if (!is_set && unformat (line_input, "disable"));
392       else
393         {
394           error = clib_error_return (0, "unknown input '%U'",
395                                      format_unformat_error, line_input);
396           goto done;
397         }
398       is_set = 1;
399     }
400
401   if (enable)
402     {
403       if (det44_plugin_enable (c))
404         error = clib_error_return (0, "plugin enable failed");
405     }
406   else
407     {
408       if (det44_plugin_disable ())
409         error = clib_error_return (0, "plugin disable failed");
410     }
411 done:
412   unformat_free (line_input);
413   return error;
414 }
415
416 typedef struct
417 {
418   u32 sw_if_index;
419   u8 is_inside;
420 } sw_if_indices_t;
421
422 static clib_error_t *
423 det44_feature_command_fn (vlib_main_t * vm,
424                           unformat_input_t * input, vlib_cli_command_t * cmd)
425 {
426   unformat_input_t _line_input, *line_input = &_line_input;
427   sw_if_indices_t *sw_if_indices = 0, *p, e;
428   vnet_main_t *vnm = vnet_get_main ();
429   clib_error_t *error = 0;
430   u8 is_del = 0;
431
432   if (!unformat_user (input, unformat_line_input, line_input))
433     return clib_error_return (0, DET44_EXPECTED_ARGUMENT);
434
435   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
436     {
437       if (unformat (line_input, "inside %U", unformat_vnet_sw_interface,
438                     vnm, &e.sw_if_index))
439         {
440           e.is_inside = 1;
441           vec_add1 (sw_if_indices, e);
442         }
443       else if (unformat (line_input, "outside %U", unformat_vnet_sw_interface,
444                          vnm, &e.sw_if_index))
445         {
446           e.is_inside = 0;
447           vec_add1 (sw_if_indices, e);
448         }
449       else if (unformat (line_input, "del"))
450         is_del = 1;
451       else
452         {
453           error = clib_error_return (0, "unknown input '%U'",
454                                      format_unformat_error, line_input);
455           goto done;
456         }
457     }
458
459   vec_foreach (p, sw_if_indices)
460     {
461       if (det44_interface_add_del (p->sw_if_index, p->is_inside, is_del))
462         {
463           error = clib_error_return (0, "%s %s %U failed",
464                                      is_del ? "del" : "add",
465                                      p->is_inside ? "inside" : "outside",
466                                      format_vnet_sw_if_index_name,
467                                      vnm, p->sw_if_index);
468           break;
469         }
470     }
471 done:
472   unformat_free (line_input);
473   vec_free (sw_if_indices);
474   return error;
475 }
476
477 static clib_error_t *
478 det44_show_interfaces_command_fn (vlib_main_t * vm, unformat_input_t * input,
479                                   vlib_cli_command_t * cmd)
480 {
481   vnet_main_t *vnm = vnet_get_main ();
482   det44_main_t *dm = &det44_main;
483   det44_interface_t *i;
484   vlib_cli_output (vm, "DET44 interfaces:");
485   pool_foreach (i, dm->interfaces)
486    {
487     vlib_cli_output (vm, " %U %s", format_vnet_sw_if_index_name, vnm,
488                      i->sw_if_index,
489                      (det44_interface_is_inside(i) &&
490                       det44_interface_is_outside(i)) ? "in out" :
491                      (det44_interface_is_inside(i) ? "in" : "out"));
492   }
493   return 0;
494 }
495
496 /*?
497  * @cliexpar
498  * @cliexstart{det44 add}
499  * Create bijective mapping of inside address to outside address and port range
500  * pairs, with the purpose of enabling DET44 to reduce logging in CGN
501  * deployments.
502  * To create mapping between inside network 10.0.0.0/18 and
503  * outside network 1.1.1.0/30 use:
504  * # vpp# det44 add in 10.0.0.0/18 out 1.1.1.0/30
505  * @cliexend
506 ?*/
507 VLIB_CLI_COMMAND (det44_map_command, static) = {
508     .path = "det44 add",
509     .short_help = "det44 add in <addr>/<plen> out <addr>/<plen> [del]",
510     .function = det44_map_command_fn,
511 };
512
513 /*?
514  * @cliexpar
515  * @cliexpstart{show det44 mappings}
516  * Show DET44 mappings
517  * vpp# show det44 mappings
518  * DET44 mappings:
519  *  in 10.0.0.0/24 out 1.1.1.1/32
520  *   outside address sharing ratio: 256
521  *   number of ports per inside host: 252
522  *   sessions number: 0
523  * @cliexend
524 ?*/
525 VLIB_CLI_COMMAND (det44_show_mappings_command, static) = {
526     .path = "show det44 mappings",
527     .short_help = "show det44 mappings",
528     .function = det44_show_mappings_command_fn,
529 };
530
531 /*?
532  * @cliexpar
533  * @cliexstart{det44 forward}
534  * Return outside address and port range from inside address for DET44.
535  * To obtain outside address and port of inside host use:
536  *  vpp# det44 forward 10.0.0.2
537  *  1.1.1.0:<1054-1068>
538  * @cliexend
539 ?*/
540 VLIB_CLI_COMMAND (det44_forward_command, static) = {
541     .path = "det44 forward",
542     .short_help = "det44 forward <addr>",
543     .function = det44_forward_command_fn,
544 };
545
546 /*?
547  * @cliexpar
548  * @cliexstart{det44 reverse}
549  * Return inside address from outside address and port for DET44.
550  * To obtain inside host address from outside address and port use:
551  *  #vpp det44 reverse 1.1.1.1:1276
552  *  10.0.16.16
553  * @cliexend
554 ?*/
555 VLIB_CLI_COMMAND (det44_reverse_command, static) = {
556     .path = "det44 reverse",
557     .short_help = "det44 reverse <addr>:<port>",
558     .function = det44_reverse_command_fn,
559 };
560
561 /*?
562  * @cliexpar
563  * @cliexstart{show det44 sessions}
564  * Show DET44 sessions.
565  * vpp# show det44 sessions
566  * DET44 sessions:
567  *   in 10.0.0.3:3005 out 1.1.1.2:1146 external host 172.16.1.2:3006 state: udp-active expire: 306
568  *   in 10.0.0.3:3000 out 1.1.1.2:1141 external host 172.16.1.2:3001 state: udp-active expire: 306
569  *   in 10.0.0.4:3005 out 1.1.1.2:1177 external host 172.16.1.2:3006 state: udp-active expire: 306
570  * @cliexend
571 ?*/
572 VLIB_CLI_COMMAND (det44_show_sessions_command, static) = {
573   .path = "show det44 sessions",
574   .short_help = "show det44 sessions",
575   .function = det44_show_sessions_command_fn,
576 };
577
578 /*?
579  * @cliexpar
580  * @cliexstart{det44 close session out}
581  * Close session using outside ip address and port
582  * and external ip address and port, use:
583  *  vpp# det44 close session out 1.1.1.1:1276 2.2.2.2:2387
584  * @cliexend
585 ?*/
586 VLIB_CLI_COMMAND (det44_close_sesion_out_command, static) = {
587   .path = "det44 close session out",
588   .short_help = "det44 close session out "
589                 "<out_addr>:<out_port> <ext_addr>:<ext_port>",
590   .function = det44_close_session_out_fn,
591 };
592
593 /*?
594  * @cliexpar
595  * @cliexstart{det44 deterministic close session in}
596  * Close session using inside ip address and port
597  * and external ip address and port, use:
598  *  vpp# det44 close session in 3.3.3.3:3487 2.2.2.2:2387
599  * @cliexend
600 ?*/
601 VLIB_CLI_COMMAND (det44_close_session_in_command, static) = {
602   .path = "det44 close session in",
603   .short_help = "det44 close session in "
604                 "<in_addr>:<in_port> <ext_addr>:<ext_port>",
605   .function = det44_close_session_in_fn,
606 };
607
608 /*?
609  * @cliexpar
610  * @cliexstart{set det44 timeout}
611  * Set values of timeouts for DET44 sessions (in seconds), use:
612  *  vpp# set det44 timeouts udp 120 tcp established 7500 tcp transitory 250 icmp 90
613  * To reset default values use:
614  *  vpp# set det44 timeouts reset
615  * @cliexend
616 ?*/
617 VLIB_CLI_COMMAND (det44_set_timeouts_command, static) =
618 {
619   .path = "set det44 timeouts",
620   .short_help = "set det44 timeouts <[udp <sec>] [tcp established <sec>] "
621                 "[tcp transitory <sec>] [icmp <sec>]|reset>",
622   .function = det44_set_timeouts_command_fn,
623 };
624
625 /*?
626  * @cliexpar
627  * @cliexstart{show det44 timeouts}
628  * Show values of timeouts for DET44 sessions.
629  * vpp# show det44 timeouts
630  * udp timeout: 300sec
631  * tcp-established timeout: 7440sec
632  * tcp-transitory timeout: 240sec
633  * icmp timeout: 60sec
634  * @cliexend
635 ?*/
636 VLIB_CLI_COMMAND (det44_show_timeouts_command, static) =
637 {
638   .path = "show det44 timeouts",
639   .short_help = "show det44 timeouts",
640   .function = det44_show_timeouts_command_fn,
641 };
642
643 /*?
644  * @cliexpar
645  * @cliexstart{det44 plugin}
646  * Enable/disable DET44 plugin.
647  * @cliexend
648 ?*/
649 VLIB_CLI_COMMAND (det44_plugin_enable_disable_command, static) =
650 {
651   .path = "det44 plugin",
652   .short_help = "det44 plugin <enable [inside vrf] [outside vrf]|disable>",
653   .function = det44_plugin_enable_disable_command_fn,
654 };
655
656 /*?
657  * @cliexpar
658  * @cliexstart{set interface det44}
659  * Enable/disable DET44 feature on the interface.
660  * To enable DET44 feature with local network interface use:
661  *  vpp# set interface det44 inside GigabitEthernet0/8/0
662  * To enable DET44 feature with external network interface use:
663  *  vpp# set interface det44 outside GigabitEthernet0/a/0
664  * @cliexend
665 ?*/
666 VLIB_CLI_COMMAND (det44_feature_command, static) =
667 {
668   .path = "set interface det44",
669   .short_help = "set interface det44 inside <intfc> outside <intfc> [del]",
670   .function = det44_feature_command_fn,
671 };
672
673 /*?
674  * @cliexpar
675  * @cliexstart{show det44 interfaces}
676  * Show interfaces with DET44 feature.
677  * vpp# show det44 interfaces
678  * DET44 interfaces:
679  *  GigabitEthernet0/8/0 in
680  *  GigabitEthernet0/a/0 out
681  * @cliexend
682 ?*/
683 VLIB_CLI_COMMAND (det44_show_interfaces_command, static) =
684 {
685   .path = "show det44 interfaces",
686   .short_help = "show det44 interfaces",
687   .function = det44_show_interfaces_command_fn,
688 };
689
690 /*
691  * fd.io coding-style-patch-verification: ON
692  *
693  * Local Variables:
694  * eval: (c-set-style "gnu")
695  * End:
696  */