ipsec: fix instance, and cli del for new ipsec interface
[vpp.git] / src / vnet / ipsec / ipsec_cli.c
1 /*
2  * decap.c : IPSec tunnel support
3  *
4  * Copyright (c) 2015 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>
19 #include <vnet/api_errno.h>
20 #include <vnet/ip/ip.h>
21 #include <vnet/interface.h>
22 #include <vnet/fib/fib.h>
23 #include <vnet/ipip/ipip.h>
24
25 #include <vnet/ipsec/ipsec.h>
26 #include <vnet/ipsec/ipsec_tun.h>
27
28 static clib_error_t *
29 set_interface_spd_command_fn (vlib_main_t * vm,
30                               unformat_input_t * input,
31                               vlib_cli_command_t * cmd)
32 {
33   unformat_input_t _line_input, *line_input = &_line_input;
34   ipsec_main_t *im = &ipsec_main;
35   u32 sw_if_index = (u32) ~ 0;
36   u32 spd_id;
37   int is_add = 1;
38   clib_error_t *error = NULL;
39
40   if (!unformat_user (input, unformat_line_input, line_input))
41     return 0;
42
43   if (unformat
44       (line_input, "%U %u", unformat_vnet_sw_interface, im->vnet_main,
45        &sw_if_index, &spd_id))
46     ;
47   else if (unformat (line_input, "del"))
48     is_add = 0;
49   else
50     {
51       error = clib_error_return (0, "parse error: '%U'",
52                                  format_unformat_error, line_input);
53       goto done;
54     }
55
56   ipsec_set_interface_spd (vm, sw_if_index, spd_id, is_add);
57
58 done:
59   unformat_free (line_input);
60
61   return error;
62 }
63
64 /* *INDENT-OFF* */
65 VLIB_CLI_COMMAND (set_interface_spd_command, static) = {
66     .path = "set interface ipsec spd",
67     .short_help =
68     "set interface ipsec spd <int> <id>",
69     .function = set_interface_spd_command_fn,
70 };
71 /* *INDENT-ON* */
72
73 static clib_error_t *
74 ipsec_sa_add_del_command_fn (vlib_main_t * vm,
75                              unformat_input_t * input,
76                              vlib_cli_command_t * cmd)
77 {
78   unformat_input_t _line_input, *line_input = &_line_input;
79   ip46_address_t tun_src = { }, tun_dst =
80   {
81   };
82   ipsec_crypto_alg_t crypto_alg;
83   ipsec_integ_alg_t integ_alg;
84   ipsec_protocol_t proto;
85   ipsec_sa_flags_t flags;
86   clib_error_t *error;
87   ipsec_key_t ck = { 0 };
88   ipsec_key_t ik = { 0 };
89   u32 id, spi, salt, sai;
90   u16 udp_src, udp_dst;
91   int is_add, rv;
92   u32 m_args = 0;
93
94   salt = 0;
95   error = NULL;
96   is_add = 0;
97   flags = IPSEC_SA_FLAG_NONE;
98   proto = IPSEC_PROTOCOL_ESP;
99   integ_alg = IPSEC_INTEG_ALG_NONE;
100   crypto_alg = IPSEC_CRYPTO_ALG_NONE;
101   udp_src = udp_dst = IPSEC_UDP_PORT_NONE;
102
103   if (!unformat_user (input, unformat_line_input, line_input))
104     return 0;
105
106   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
107     {
108       if (unformat (line_input, "add %u", &id))
109         {
110           is_add = 1;
111           m_args |= 1 << 0;
112         }
113       else if (unformat (line_input, "del %u", &id))
114         {
115           is_add = 0;
116           m_args |= 1 << 0;
117         }
118       else if (unformat (line_input, "spi %u", &spi))
119         m_args |= 1 << 1;
120       else if (unformat (line_input, "salt 0x%x", &salt))
121         ;
122       else if (unformat (line_input, "esp"))
123         proto = IPSEC_PROTOCOL_ESP;
124       else if (unformat (line_input, "ah"))
125         proto = IPSEC_PROTOCOL_AH;
126       else if (unformat (line_input, "crypto-key %U",
127                          unformat_ipsec_key, &ck))
128         ;
129       else if (unformat (line_input, "crypto-alg %U",
130                          unformat_ipsec_crypto_alg, &crypto_alg))
131         ;
132       else if (unformat (line_input, "integ-key %U", unformat_ipsec_key, &ik))
133         ;
134       else if (unformat (line_input, "integ-alg %U",
135                          unformat_ipsec_integ_alg, &integ_alg))
136         ;
137       else if (unformat (line_input, "tunnel-src %U",
138                          unformat_ip46_address, &tun_src, IP46_TYPE_ANY))
139         {
140           flags |= IPSEC_SA_FLAG_IS_TUNNEL;
141           if (!ip46_address_is_ip4 (&tun_src))
142             flags |= IPSEC_SA_FLAG_IS_TUNNEL_V6;
143         }
144       else if (unformat (line_input, "tunnel-dst %U",
145                          unformat_ip46_address, &tun_dst, IP46_TYPE_ANY))
146         ;
147       else if (unformat (line_input, "inbound"))
148         flags |= IPSEC_SA_FLAG_IS_INBOUND;
149       else if (unformat (line_input, "use-anti-replay"))
150         flags |= IPSEC_SA_FLAG_USE_ANTI_REPLAY;
151       else if (unformat (line_input, "use-esn"))
152         flags |= IPSEC_SA_FLAG_USE_ESN;
153       else if (unformat (line_input, "udp-encap"))
154         flags |= IPSEC_SA_FLAG_UDP_ENCAP;
155       else
156         {
157           error = clib_error_return (0, "parse error: '%U'",
158                                      format_unformat_error, line_input);
159           goto done;
160         }
161     }
162   if ((flags & IPSEC_SA_FLAG_IS_INBOUND)
163       && !(flags & IPSEC_SA_FLAG_IS_TUNNEL))
164     {
165       error = clib_error_return (0, "inbound specified on non-tunnel SA");
166       goto done;
167     }
168
169   if (!(m_args & 1))
170     {
171       error = clib_error_return (0, "missing id");
172       goto done;
173     }
174
175   if (is_add)
176     {
177       if (!(m_args & 2))
178         {
179           error = clib_error_return (0, "missing spi");
180           goto done;
181         }
182       rv = ipsec_sa_add_and_lock (id, spi, proto, crypto_alg,
183                                   &ck, integ_alg, &ik, flags,
184                                   0, clib_host_to_net_u32 (salt),
185                                   &tun_src, &tun_dst, &sai, udp_src, udp_dst);
186     }
187   else
188     {
189       rv = ipsec_sa_unlock_id (id);
190     }
191
192   if (rv)
193     error = clib_error_return (0, "failed");
194
195 done:
196   unformat_free (line_input);
197
198   return error;
199 }
200
201 /* *INDENT-OFF* */
202 VLIB_CLI_COMMAND (ipsec_sa_add_del_command, static) = {
203     .path = "ipsec sa",
204     .short_help =
205     "ipsec sa [add|del]",
206     .function = ipsec_sa_add_del_command_fn,
207 };
208 /* *INDENT-ON* */
209
210 static clib_error_t *
211 ipsec_spd_add_del_command_fn (vlib_main_t * vm,
212                               unformat_input_t * input,
213                               vlib_cli_command_t * cmd)
214 {
215   unformat_input_t _line_input, *line_input = &_line_input;
216   u32 spd_id = ~0;
217   int is_add = ~0;
218   clib_error_t *error = NULL;
219
220   if (!unformat_user (input, unformat_line_input, line_input))
221     return 0;
222
223   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
224     {
225       if (unformat (line_input, "add"))
226         is_add = 1;
227       else if (unformat (line_input, "del"))
228         is_add = 0;
229       else if (unformat (line_input, "%u", &spd_id))
230         ;
231       else
232         {
233           error = clib_error_return (0, "parse error: '%U'",
234                                      format_unformat_error, line_input);
235           goto done;
236         }
237     }
238
239   if (spd_id == ~0)
240     {
241       error = clib_error_return (0, "please specify SPD ID");
242       goto done;
243     }
244
245   ipsec_add_del_spd (vm, spd_id, is_add);
246
247 done:
248   unformat_free (line_input);
249
250   return error;
251 }
252
253 /* *INDENT-OFF* */
254 VLIB_CLI_COMMAND (ipsec_spd_add_del_command, static) = {
255     .path = "ipsec spd",
256     .short_help =
257     "ipsec spd [add|del] <id>",
258     .function = ipsec_spd_add_del_command_fn,
259 };
260 /* *INDENT-ON* */
261
262
263 static clib_error_t *
264 ipsec_policy_add_del_command_fn (vlib_main_t * vm,
265                                  unformat_input_t * input,
266                                  vlib_cli_command_t * cmd)
267 {
268   unformat_input_t _line_input, *line_input = &_line_input;
269   ipsec_policy_t p;
270   int rv, is_add = 0;
271   u32 tmp, tmp2, stat_index, local_range_set, remote_range_set;
272   clib_error_t *error = NULL;
273   u32 is_outbound;
274
275   clib_memset (&p, 0, sizeof (p));
276   p.lport.stop = p.rport.stop = ~0;
277   remote_range_set = local_range_set = is_outbound = 0;
278
279   if (!unformat_user (input, unformat_line_input, line_input))
280     return 0;
281
282   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
283     {
284       if (unformat (line_input, "add"))
285         is_add = 1;
286       else if (unformat (line_input, "del"))
287         is_add = 0;
288       else if (unformat (line_input, "ip6"))
289         p.is_ipv6 = 1;
290       else if (unformat (line_input, "spd %u", &p.id))
291         ;
292       else if (unformat (line_input, "inbound"))
293         is_outbound = 0;
294       else if (unformat (line_input, "outbound"))
295         is_outbound = 1;
296       else if (unformat (line_input, "priority %d", &p.priority))
297         ;
298       else if (unformat (line_input, "protocol %u", &tmp))
299         p.protocol = (u8) tmp;
300       else
301         if (unformat
302             (line_input, "action %U", unformat_ipsec_policy_action,
303              &p.policy))
304         {
305           if (p.policy == IPSEC_POLICY_ACTION_RESOLVE)
306             {
307               error = clib_error_return (0, "unsupported action: 'resolve'");
308               goto done;
309             }
310         }
311       else if (unformat (line_input, "sa %u", &p.sa_id))
312         ;
313       else if (unformat (line_input, "local-ip-range %U - %U",
314                          unformat_ip4_address, &p.laddr.start.ip4,
315                          unformat_ip4_address, &p.laddr.stop.ip4))
316         local_range_set = 1;
317       else if (unformat (line_input, "remote-ip-range %U - %U",
318                          unformat_ip4_address, &p.raddr.start.ip4,
319                          unformat_ip4_address, &p.raddr.stop.ip4))
320         remote_range_set = 1;
321       else if (unformat (line_input, "local-ip-range %U - %U",
322                          unformat_ip6_address, &p.laddr.start.ip6,
323                          unformat_ip6_address, &p.laddr.stop.ip6))
324         {
325           p.is_ipv6 = 1;
326           local_range_set = 1;
327         }
328       else if (unformat (line_input, "remote-ip-range %U - %U",
329                          unformat_ip6_address, &p.raddr.start.ip6,
330                          unformat_ip6_address, &p.raddr.stop.ip6))
331         {
332           p.is_ipv6 = 1;
333           remote_range_set = 1;
334         }
335       else if (unformat (line_input, "local-port-range %u - %u", &tmp, &tmp2))
336         {
337           p.lport.start = tmp;
338           p.lport.stop = tmp2;
339         }
340       else
341         if (unformat (line_input, "remote-port-range %u - %u", &tmp, &tmp2))
342         {
343           p.rport.start = tmp;
344           p.rport.stop = tmp2;
345         }
346       else
347         {
348           error = clib_error_return (0, "parse error: '%U'",
349                                      format_unformat_error, line_input);
350           goto done;
351         }
352     }
353
354   if (!remote_range_set)
355     {
356       if (p.is_ipv6)
357         clib_memset (&p.raddr.stop.ip6, 0xff, 16);
358       else
359         clib_memset (&p.raddr.stop.ip4, 0xff, 4);
360     }
361   if (!local_range_set)
362     {
363       if (p.is_ipv6)
364         clib_memset (&p.laddr.stop.ip6, 0xff, 16);
365       else
366         clib_memset (&p.laddr.stop.ip4, 0xff, 4);
367     }
368
369   rv = ipsec_policy_mk_type (is_outbound, p.is_ipv6, p.policy, &p.type);
370
371   if (rv)
372     {
373       error = clib_error_return (0, "unsupported policy type for:",
374                                  " outboud:%s %s action:%U",
375                                  (is_outbound ? "yes" : "no"),
376                                  (p.is_ipv6 ? "IPv4" : "IPv6"),
377                                  format_ipsec_policy_action, p.policy);
378       goto done;
379     }
380
381   rv = ipsec_add_del_policy (vm, &p, is_add, &stat_index);
382
383   if (!rv)
384     vlib_cli_output (vm, "policy-index:%d", stat_index);
385   else
386     vlib_cli_output (vm, "error:%d", rv);
387
388 done:
389   unformat_free (line_input);
390
391   return error;
392 }
393
394 /* *INDENT-OFF* */
395 VLIB_CLI_COMMAND (ipsec_policy_add_del_command, static) = {
396     .path = "ipsec policy",
397     .short_help =
398     "ipsec policy [add|del] spd <id> priority <n> ",
399     .function = ipsec_policy_add_del_command_fn,
400 };
401 /* *INDENT-ON* */
402
403 static void
404 ipsec_sa_show_all (vlib_main_t * vm, ipsec_main_t * im, u8 detail)
405 {
406   u32 sai;
407
408   /* *INDENT-OFF* */
409   pool_foreach_index (sai, im->sad, ({
410     vlib_cli_output(vm, "%U", format_ipsec_sa, sai,
411                     (detail ? IPSEC_FORMAT_DETAIL : IPSEC_FORMAT_BRIEF));
412   }));
413   /* *INDENT-ON* */
414 }
415
416 static void
417 ipsec_spd_show_all (vlib_main_t * vm, ipsec_main_t * im)
418 {
419   u32 spdi;
420
421   /* *INDENT-OFF* */
422   pool_foreach_index (spdi, im->spds, ({
423     vlib_cli_output(vm, "%U", format_ipsec_spd, spdi);
424   }));
425   /* *INDENT-ON* */
426 }
427
428 static void
429 ipsec_spd_bindings_show_all (vlib_main_t * vm, ipsec_main_t * im)
430 {
431   u32 spd_id, sw_if_index;
432   ipsec_spd_t *spd;
433
434   vlib_cli_output (vm, "SPD Bindings:");
435
436   /* *INDENT-OFF* */
437   hash_foreach(sw_if_index, spd_id, im->spd_index_by_sw_if_index, ({
438     spd = pool_elt_at_index (im->spds, spd_id);
439     vlib_cli_output (vm, "  %d -> %U", spd->id,
440                      format_vnet_sw_if_index_name, im->vnet_main,
441                      sw_if_index);
442   }));
443   /* *INDENT-ON* */
444 }
445
446 static walk_rc_t
447 ipsec_tun_protect_show_one (index_t itpi, void *ctx)
448 {
449   vlib_cli_output (ctx, "%U", format_ipsec_tun_protect_index, itpi);
450
451   return (WALK_CONTINUE);
452 }
453
454 static void
455 ipsec_tunnel_show_all (vlib_main_t * vm)
456 {
457   ipsec_tun_protect_walk (ipsec_tun_protect_show_one, vm);
458 }
459
460 static clib_error_t *
461 show_ipsec_command_fn (vlib_main_t * vm,
462                        unformat_input_t * input, vlib_cli_command_t * cmd)
463 {
464   ipsec_main_t *im = &ipsec_main;
465
466   ipsec_sa_show_all (vm, im, 0);
467   ipsec_spd_show_all (vm, im);
468   ipsec_spd_bindings_show_all (vm, im);
469   ipsec_tun_protect_walk (ipsec_tun_protect_show_one, vm);
470
471   vlib_cli_output (vm, "IPSec async mode: %s",
472                    (im->async_mode ? "on" : "off"));
473
474   return 0;
475 }
476
477 /* *INDENT-OFF* */
478 VLIB_CLI_COMMAND (show_ipsec_command, static) = {
479     .path = "show ipsec all",
480     .short_help = "show ipsec all",
481     .function = show_ipsec_command_fn,
482 };
483 /* *INDENT-ON* */
484
485 static clib_error_t *
486 show_ipsec_sa_command_fn (vlib_main_t * vm,
487                           unformat_input_t * input, vlib_cli_command_t * cmd)
488 {
489   ipsec_main_t *im = &ipsec_main;
490   u32 sai = ~0;
491   u8 detail = 0;
492
493   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
494     {
495       if (unformat (input, "%u", &sai))
496         ;
497       if (unformat (input, "detail"))
498         detail = 1;
499       else
500         break;
501     }
502
503   if (~0 == sai)
504     ipsec_sa_show_all (vm, im, detail);
505   else
506     vlib_cli_output (vm, "%U", format_ipsec_sa, sai,
507                      IPSEC_FORMAT_DETAIL | IPSEC_FORMAT_INSECURE);
508
509   return 0;
510 }
511
512 static clib_error_t *
513 clear_ipsec_sa_command_fn (vlib_main_t * vm,
514                            unformat_input_t * input, vlib_cli_command_t * cmd)
515 {
516   ipsec_main_t *im = &ipsec_main;
517   u32 sai = ~0;
518
519   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
520     {
521       if (unformat (input, "%u", &sai))
522         ;
523       else
524         break;
525     }
526
527   if (~0 == sai)
528     {
529       /* *INDENT-OFF* */
530       pool_foreach_index (sai, im->sad, ({
531         ipsec_sa_clear(sai);
532       }));
533       /* *INDENT-ON* */
534     }
535   else
536     {
537       if (pool_is_free_index (im->sad, sai))
538         return clib_error_return (0, "unknown SA index: %d", sai);
539       else
540         ipsec_sa_clear (sai);
541     }
542
543   return 0;
544 }
545
546 /* *INDENT-OFF* */
547 VLIB_CLI_COMMAND (show_ipsec_sa_command, static) = {
548     .path = "show ipsec sa",
549     .short_help = "show ipsec sa [index]",
550     .function = show_ipsec_sa_command_fn,
551 };
552
553 VLIB_CLI_COMMAND (clear_ipsec_sa_command, static) = {
554     .path = "clear ipsec sa",
555     .short_help = "clear ipsec sa [index]",
556     .function = clear_ipsec_sa_command_fn,
557 };
558 /* *INDENT-ON* */
559
560 static clib_error_t *
561 show_ipsec_spd_command_fn (vlib_main_t * vm,
562                            unformat_input_t * input, vlib_cli_command_t * cmd)
563 {
564   ipsec_main_t *im = &ipsec_main;
565   u8 show_bindings = 0;
566   u32 spdi = ~0;
567
568   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
569     {
570       if (unformat (input, "%u", &spdi))
571         ;
572       else if (unformat (input, "bindings"))
573         show_bindings = 1;
574       else
575         break;
576     }
577
578   if (show_bindings)
579     ipsec_spd_bindings_show_all (vm, im);
580   else if (~0 != spdi)
581     vlib_cli_output (vm, "%U", format_ipsec_spd, spdi);
582   else
583     ipsec_spd_show_all (vm, im);
584
585   return 0;
586 }
587
588 /* *INDENT-OFF* */
589 VLIB_CLI_COMMAND (show_ipsec_spd_command, static) = {
590     .path = "show ipsec spd",
591     .short_help = "show ipsec spd [index]",
592     .function = show_ipsec_spd_command_fn,
593 };
594 /* *INDENT-ON* */
595
596 static clib_error_t *
597 show_ipsec_tunnel_command_fn (vlib_main_t * vm,
598                               unformat_input_t * input,
599                               vlib_cli_command_t * cmd)
600 {
601   ipsec_tunnel_show_all (vm);
602
603   return 0;
604 }
605
606 /* *INDENT-OFF* */
607 VLIB_CLI_COMMAND (show_ipsec_tunnel_command, static) = {
608     .path = "show ipsec tunnel",
609     .short_help = "show ipsec tunnel",
610     .function = show_ipsec_tunnel_command_fn,
611 };
612 /* *INDENT-ON* */
613
614 static clib_error_t *
615 ipsec_show_backends_command_fn (vlib_main_t * vm,
616                                 unformat_input_t * input,
617                                 vlib_cli_command_t * cmd)
618 {
619   ipsec_main_t *im = &ipsec_main;
620   u32 verbose = 0;
621
622   (void) unformat (input, "verbose %u", &verbose);
623
624   vlib_cli_output (vm, "IPsec AH backends available:");
625   u8 *s = format (NULL, "%=25s %=25s %=10s\n", "Name", "Index", "Active");
626   ipsec_ah_backend_t *ab;
627   /* *INDENT-OFF* */
628   pool_foreach (ab, im->ah_backends, {
629     s = format (s, "%=25s %=25u %=10s\n", ab->name, ab - im->ah_backends,
630                 ab - im->ah_backends == im->ah_current_backend ? "yes" : "no");
631     if (verbose) {
632         vlib_node_t *n;
633         n = vlib_get_node (vm, ab->ah4_encrypt_node_index);
634         s = format (s, "     enc4 %s (next %d)\n", n->name, ab->ah4_encrypt_next_index);
635         n = vlib_get_node (vm, ab->ah4_decrypt_node_index);
636         s = format (s, "     dec4 %s (next %d)\n", n->name, ab->ah4_decrypt_next_index);
637         n = vlib_get_node (vm, ab->ah6_encrypt_node_index);
638         s = format (s, "     enc6 %s (next %d)\n", n->name, ab->ah6_encrypt_next_index);
639         n = vlib_get_node (vm, ab->ah6_decrypt_node_index);
640         s = format (s, "     dec6 %s (next %d)\n", n->name, ab->ah6_decrypt_next_index);
641     }
642   });
643   /* *INDENT-ON* */
644   vlib_cli_output (vm, "%v", s);
645   _vec_len (s) = 0;
646   vlib_cli_output (vm, "IPsec ESP backends available:");
647   s = format (s, "%=25s %=25s %=10s\n", "Name", "Index", "Active");
648   ipsec_esp_backend_t *eb;
649   /* *INDENT-OFF* */
650   pool_foreach (eb, im->esp_backends, {
651     s = format (s, "%=25s %=25u %=10s\n", eb->name, eb - im->esp_backends,
652                 eb - im->esp_backends == im->esp_current_backend ? "yes"
653                                                                  : "no");
654     if (verbose) {
655         vlib_node_t *n;
656         n = vlib_get_node (vm, eb->esp4_encrypt_node_index);
657         s = format (s, "     enc4 %s (next %d)\n", n->name, eb->esp4_encrypt_next_index);
658         n = vlib_get_node (vm, eb->esp4_decrypt_node_index);
659         s = format (s, "     dec4 %s (next %d)\n", n->name, eb->esp4_decrypt_next_index);
660         n = vlib_get_node (vm, eb->esp6_encrypt_node_index);
661         s = format (s, "     enc6 %s (next %d)\n", n->name, eb->esp6_encrypt_next_index);
662         n = vlib_get_node (vm, eb->esp6_decrypt_node_index);
663         s = format (s, "     dec6 %s (next %d)\n", n->name, eb->esp6_decrypt_next_index);
664     }
665   });
666   /* *INDENT-ON* */
667   vlib_cli_output (vm, "%v", s);
668
669   vec_free (s);
670   return 0;
671 }
672
673 /* *INDENT-OFF* */
674 VLIB_CLI_COMMAND (ipsec_show_backends_command, static) = {
675     .path = "show ipsec backends",
676     .short_help = "show ipsec backends",
677     .function = ipsec_show_backends_command_fn,
678 };
679 /* *INDENT-ON* */
680
681 static clib_error_t *
682 ipsec_select_backend_command_fn (vlib_main_t * vm,
683                                  unformat_input_t * input,
684                                  vlib_cli_command_t * cmd)
685 {
686   unformat_input_t _line_input, *line_input = &_line_input;
687   ipsec_main_t *im = &ipsec_main;
688   clib_error_t *error;
689   u32 backend_index;
690
691   error = ipsec_rsc_in_use (im);
692
693   if (error)
694     return error;
695
696   /* Get a line of input. */
697   if (!unformat_user (input, unformat_line_input, line_input))
698     return 0;
699
700   if (unformat (line_input, "ah"))
701     {
702       if (unformat (line_input, "%u", &backend_index))
703         {
704           if (ipsec_select_ah_backend (im, backend_index) < 0)
705             {
706               return clib_error_return (0, "Invalid AH backend index `%u'",
707                                         backend_index);
708             }
709         }
710       else
711         {
712           return clib_error_return (0, "Invalid backend index `%U'",
713                                     format_unformat_error, line_input);
714         }
715     }
716   else if (unformat (line_input, "esp"))
717     {
718       if (unformat (line_input, "%u", &backend_index))
719         {
720           if (ipsec_select_esp_backend (im, backend_index) < 0)
721             {
722               return clib_error_return (0, "Invalid ESP backend index `%u'",
723                                         backend_index);
724             }
725         }
726       else
727         {
728           return clib_error_return (0, "Invalid backend index `%U'",
729                                     format_unformat_error, line_input);
730         }
731     }
732   else
733     {
734       return clib_error_return (0, "Unknown input `%U'",
735                                 format_unformat_error, line_input);
736     }
737
738   return 0;
739 }
740
741 /* *INDENT-OFF* */
742 VLIB_CLI_COMMAND (ipsec_select_backend_command, static) = {
743     .path = "ipsec select backend",
744     .short_help = "ipsec select backend <ah|esp> <backend index>",
745     .function = ipsec_select_backend_command_fn,
746 };
747
748 /* *INDENT-ON* */
749
750 static clib_error_t *
751 clear_ipsec_counters_command_fn (vlib_main_t * vm,
752                                  unformat_input_t * input,
753                                  vlib_cli_command_t * cmd)
754 {
755   vlib_clear_combined_counters (&ipsec_spd_policy_counters);
756   vlib_clear_combined_counters (&ipsec_sa_counters);
757
758   return (NULL);
759 }
760
761 /* *INDENT-OFF* */
762 VLIB_CLI_COMMAND (clear_ipsec_counters_command, static) = {
763     .path = "clear ipsec counters",
764     .short_help = "clear ipsec counters",
765     .function = clear_ipsec_counters_command_fn,
766 };
767 /* *INDENT-ON* */
768
769 static u32
770 ipsec_tun_mk_local_sa_id (u32 ti)
771 {
772   return (0x80000000 | ti);
773 }
774
775 static u32
776 ipsec_tun_mk_remote_sa_id (u32 ti)
777 {
778   return (0xc0000000 | ti);
779 }
780
781 static clib_error_t *
782 create_ipsec_tunnel_command_fn (vlib_main_t * vm,
783                                 unformat_input_t * input,
784                                 vlib_cli_command_t * cmd)
785 {
786   unformat_input_t _line_input, *line_input = &_line_input;
787   ip46_address_t local_ip = ip46_address_initializer;
788   ip46_address_t remote_ip = ip46_address_initializer;
789   ip_address_t nh = IP_ADDRESS_V4_ALL_0S;
790   ipsec_crypto_alg_t crypto_alg = IPSEC_CRYPTO_ALG_NONE;
791   ipsec_integ_alg_t integ_alg = IPSEC_INTEG_ALG_NONE;
792   ipsec_sa_flags_t flags;
793   u32 local_spi, remote_spi, salt = 0, table_id, fib_index;
794   u32 instance = ~0;
795   int rv;
796   u32 m_args = 0;
797   u8 ipv4_set = 0;
798   u8 ipv6_set = 0;
799   u8 is_add = 1;
800   clib_error_t *error = NULL;
801   ipsec_key_t rck = { 0 };
802   ipsec_key_t lck = { 0 };
803   ipsec_key_t lik = { 0 };
804   ipsec_key_t rik = { 0 };
805
806   table_id = 0;
807   flags = IPSEC_SA_FLAG_NONE;
808
809   /* Get a line of input. */
810   if (!unformat_user (input, unformat_line_input, line_input))
811     return 0;
812
813   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
814     {
815       if (unformat
816           (line_input, "local-ip %U", unformat_ip46_address, &local_ip,
817            IP46_TYPE_ANY))
818         {
819           ip46_address_is_ip4 (&local_ip) ? (ipv4_set = 1) : (ipv6_set = 1);
820           m_args |= 1 << 0;
821         }
822       else
823         if (unformat
824             (line_input, "remote-ip %U", unformat_ip46_address, &remote_ip,
825              IP46_TYPE_ANY))
826         {
827           ip46_address_is_ip4 (&remote_ip) ? (ipv4_set = 1) : (ipv6_set = 1);
828           m_args |= 1 << 1;
829         }
830       else if (unformat (line_input, "local-spi %u", &local_spi))
831         m_args |= 1 << 2;
832       else if (unformat (line_input, "remote-spi %u", &remote_spi))
833         m_args |= 1 << 3;
834       else if (unformat (line_input, "salt 0x%x", &salt))
835         ;
836       else if (unformat (line_input, "udp-encap"))
837         flags |= IPSEC_SA_FLAG_UDP_ENCAP;
838       else if (unformat (line_input, "use-esn"))
839         flags |= IPSEC_SA_FLAG_USE_ESN;
840       else if (unformat (line_input, "use-anti-replay"))
841         flags |= IPSEC_SA_FLAG_USE_ANTI_REPLAY;
842       else if (unformat (line_input, "instance %u", &instance))
843         ;
844       else if (unformat (line_input, "tx-table %u", &table_id))
845         ;
846       else
847         if (unformat
848             (line_input, "local-crypto-key %U", unformat_ipsec_key, &lck))
849         ;
850       else
851         if (unformat
852             (line_input, "remote-crypto-key %U", unformat_ipsec_key, &rck))
853         ;
854       else if (unformat (line_input, "crypto-alg %U",
855                          unformat_ipsec_crypto_alg, &crypto_alg))
856         ;
857       else
858         if (unformat
859             (line_input, "local-integ-key %U", unformat_ipsec_key, &lik))
860         ;
861       else
862         if (unformat
863             (line_input, "remote-integ-key %U", unformat_ipsec_key, &rik))
864         ;
865       else if (unformat (line_input, "integ-alg %U",
866                          unformat_ipsec_integ_alg, &integ_alg))
867         ;
868       else if (unformat (line_input, "del"))
869         is_add = 0;
870       else if (unformat (line_input, "nh &U", unformat_ip_address, &nh))
871         ;
872       else
873         {
874           error = clib_error_return (0, "unknown input `%U'",
875                                      format_unformat_error, line_input);
876           goto done;
877         }
878     }
879
880   if (0xf != m_args)
881     {
882       error = clib_error_return (0, "mandatory argument(s) missing");
883       goto done;
884     }
885
886   if (ipv4_set && ipv6_set)
887     return clib_error_return (0, "both IPv4 and IPv6 addresses specified");
888
889   fib_index = fib_table_find (fib_ip_proto (ipv6_set), table_id);
890
891   if (~0 == fib_index)
892     {
893       rv = VNET_API_ERROR_NO_SUCH_FIB;
894       goto done;
895     }
896
897   if (is_add)
898     {
899       // remote = input, local = output
900       u32 sw_if_index;
901
902       /* create an ip-ip tunnel, then the two SA, then bind them */
903       rv =
904         ipip_add_tunnel (ipv6_set ? IPIP_TRANSPORT_IP6 : IPIP_TRANSPORT_IP4,
905                          instance, &local_ip, &remote_ip, fib_index,
906                          TUNNEL_ENCAP_DECAP_FLAG_NONE, IP_DSCP_CS0,
907                          TUNNEL_MODE_P2P, &sw_if_index);
908       rv |=
909         ipsec_sa_add_and_lock (ipsec_tun_mk_local_sa_id (sw_if_index),
910                                local_spi, IPSEC_PROTOCOL_ESP, crypto_alg,
911                                &lck, integ_alg, &lik, flags, table_id,
912                                clib_host_to_net_u32 (salt), &local_ip,
913                                &remote_ip, NULL, IPSEC_UDP_PORT_NONE,
914                                IPSEC_UDP_PORT_NONE);
915       rv |=
916         ipsec_sa_add_and_lock (ipsec_tun_mk_remote_sa_id (sw_if_index),
917                                remote_spi, IPSEC_PROTOCOL_ESP, crypto_alg,
918                                &rck, integ_alg, &rik,
919                                (flags | IPSEC_SA_FLAG_IS_INBOUND), table_id,
920                                clib_host_to_net_u32 (salt), &remote_ip,
921                                &local_ip, NULL, IPSEC_UDP_PORT_NONE,
922                                IPSEC_UDP_PORT_NONE);
923       rv |=
924         ipsec_tun_protect_update_one (sw_if_index, &nh,
925                                       ipsec_tun_mk_local_sa_id (sw_if_index),
926                                       ipsec_tun_mk_remote_sa_id
927                                       (sw_if_index));
928     }
929   else
930     rv = 0;
931
932   switch (rv)
933     {
934     case 0:
935       break;
936     case VNET_API_ERROR_INVALID_VALUE:
937       error = clib_error_return (0,
938                                  "IPSec tunnel interface already exists...");
939       goto done;
940     default:
941       error = clib_error_return (0, "ipsec_register_interface returned %d",
942                                  rv);
943       goto done;
944     }
945
946 done:
947   unformat_free (line_input);
948
949   return error;
950 }
951
952 /* *INDENT-OFF* */
953 VLIB_CLI_COMMAND (create_ipsec_tunnel_command, static) = {
954   .path = "create ipsec tunnel",
955   .short_help = "create ipsec tunnel local-ip <addr> local-spi <spi> "
956       "remote-ip <addr> remote-spi <spi> [instance <inst_num>] [udp-encap] [use-esn] [use-anti-replay] "
957       "[tx-table <table-id>]",
958   .function = create_ipsec_tunnel_command_fn,
959 };
960 /* *INDENT-ON* */
961
962 static clib_error_t *
963 ipsec_tun_protect_cmd (vlib_main_t * vm,
964                        unformat_input_t * input, vlib_cli_command_t * cmd)
965 {
966   unformat_input_t _line_input, *line_input = &_line_input;
967   u32 sw_if_index, is_del, sa_in, sa_out, *sa_ins = NULL;
968   ip_address_t peer = { };
969   vnet_main_t *vnm;
970
971   is_del = 0;
972   sw_if_index = ~0;
973   vnm = vnet_get_main ();
974
975   if (!unformat_user (input, unformat_line_input, line_input))
976     return 0;
977
978   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
979     {
980       if (unformat (line_input, "del"))
981         is_del = 1;
982       else if (unformat (line_input, "add"))
983         is_del = 0;
984       else if (unformat (line_input, "sa-in %d", &sa_in))
985         vec_add1 (sa_ins, sa_in);
986       else if (unformat (line_input, "sa-out %d", &sa_out))
987         ;
988       else if (unformat (line_input, "%U",
989                          unformat_vnet_sw_interface, vnm, &sw_if_index))
990         ;
991       else if (unformat (line_input, "%U", unformat_ip_address, &peer))
992         ;
993       else
994         return (clib_error_return (0, "unknown input '%U'",
995                                    format_unformat_error, line_input));
996     }
997
998   if (!is_del)
999     ipsec_tun_protect_update (sw_if_index, &peer, sa_out, sa_ins);
1000   else
1001     ipsec_tun_protect_del (sw_if_index, &peer);
1002
1003   unformat_free (line_input);
1004   return NULL;
1005 }
1006
1007 /**
1008  * Protect tunnel with IPSEC
1009  */
1010 /* *INDENT-OFF* */
1011 VLIB_CLI_COMMAND (ipsec_tun_protect_cmd_node, static) =
1012 {
1013   .path = "ipsec tunnel protect",
1014   .function = ipsec_tun_protect_cmd,
1015   .short_help = "ipsec tunnel protect <interface> input-sa <SA> output-sa <SA> [add|del]",
1016     // this is not MP safe
1017 };
1018 /* *INDENT-ON* */
1019
1020
1021 static clib_error_t *
1022 ipsec_tun_protect_show (vlib_main_t * vm,
1023                         unformat_input_t * input, vlib_cli_command_t * cmd)
1024 {
1025   ipsec_tun_protect_walk (ipsec_tun_protect_show_one, vm);
1026
1027   return NULL;
1028 }
1029
1030 /**
1031  * show IPSEC tunnel protection
1032  */
1033 /* *INDENT-OFF* */
1034 VLIB_CLI_COMMAND (ipsec_tun_protect_show_node, static) =
1035 {
1036   .path = "show ipsec protect",
1037   .function = ipsec_tun_protect_show,
1038   .short_help =  "show ipsec protect",
1039 };
1040 /* *INDENT-ON* */
1041
1042 static int
1043 ipsec_tun_protect4_hash_show_one (clib_bihash_kv_8_8_t * kv, void *arg)
1044 {
1045   ipsec4_tunnel_kv_t *ikv = (ipsec4_tunnel_kv_t *) kv;
1046   vlib_main_t *vm = arg;
1047
1048   vlib_cli_output (vm, " %U", format_ipsec4_tunnel_kv, ikv);
1049
1050   return (BIHASH_WALK_CONTINUE);
1051 }
1052
1053 static int
1054 ipsec_tun_protect6_hash_show_one (clib_bihash_kv_24_8_t * kv, void *arg)
1055 {
1056   ipsec6_tunnel_kv_t *ikv = (ipsec6_tunnel_kv_t *) kv;
1057   vlib_main_t *vm = arg;
1058
1059   vlib_cli_output (vm, " %U", format_ipsec6_tunnel_kv, ikv);
1060
1061   return (BIHASH_WALK_CONTINUE);
1062 }
1063
1064 static clib_error_t *
1065 ipsec_tun_protect_hash_show (vlib_main_t * vm,
1066                              unformat_input_t * input,
1067                              vlib_cli_command_t * cmd)
1068 {
1069   ipsec_main_t *im = &ipsec_main;
1070
1071   {
1072     vlib_cli_output (vm, "IPv4:");
1073
1074     clib_bihash_foreach_key_value_pair_8_8
1075       (&im->tun4_protect_by_key, ipsec_tun_protect4_hash_show_one, vm);
1076
1077     vlib_cli_output (vm, "IPv6:");
1078
1079     clib_bihash_foreach_key_value_pair_24_8
1080       (&im->tun6_protect_by_key, ipsec_tun_protect6_hash_show_one, vm);
1081   }
1082
1083   return NULL;
1084 }
1085
1086 /**
1087  * show IPSEC tunnel protection hash tables
1088  */
1089 /* *INDENT-OFF* */
1090 VLIB_CLI_COMMAND (ipsec_tun_protect_hash_show_node, static) =
1091 {
1092   .path = "show ipsec protect-hash",
1093   .function = ipsec_tun_protect_hash_show,
1094   .short_help =  "show ipsec protect-hash",
1095 };
1096 /* *INDENT-ON* */
1097
1098 clib_error_t *
1099 ipsec_cli_init (vlib_main_t * vm)
1100 {
1101   return 0;
1102 }
1103
1104 VLIB_INIT_FUNCTION (ipsec_cli_init);
1105
1106 static clib_error_t *
1107 set_async_mode_command_fn (vlib_main_t * vm, unformat_input_t * input,
1108                            vlib_cli_command_t * cmd)
1109 {
1110   unformat_input_t _line_input, *line_input = &_line_input;
1111   int async_enable = 0;
1112
1113   if (!unformat_user (input, unformat_line_input, line_input))
1114     return 0;
1115
1116   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1117     {
1118       if (unformat (line_input, "on"))
1119         async_enable = 1;
1120       else if (unformat (line_input, "off"))
1121         async_enable = 0;
1122       else
1123         return (clib_error_return (0, "unknown input '%U'",
1124                                    format_unformat_error, line_input));
1125     }
1126
1127   vnet_crypto_request_async_mode (async_enable);
1128   ipsec_set_async_mode (async_enable);
1129
1130   unformat_free (line_input);
1131   return (NULL);
1132 }
1133
1134 /* *INDENT-OFF* */
1135 VLIB_CLI_COMMAND (set_async_mode_command, static) = {
1136     .path = "set ipsec async mode",
1137     .short_help = "set ipsec async mode on|off",
1138     .function = set_async_mode_command_fn,
1139 };
1140 /* *INDENT-ON* */
1141
1142 /*
1143  * fd.io coding-style-patch-verification: ON
1144  *
1145  * Local Variables:
1146  * eval: (c-set-style "gnu")
1147  * End:
1148  */