vrrp: add plugin providing vrrp support
[vpp.git] / src / plugins / vrrp / vrrp_cli.c
1 /*
2  * vrrp_cli.c - vrrp plugin debug CLI commands
3  *
4  * Copyright 2019-2020 Rubicon Communications, LLC (Netgate)
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  *
8  */
9
10 #include <vnet/vnet.h>
11 #include <vnet/plugin/plugin.h>
12 #include <vrrp/vrrp.h>
13
14 #include <vlibapi/api.h>
15 #include <vlibmemory/api.h>
16 #include <vpp/app/version.h>
17
18
19 static clib_error_t *
20 vrrp_vr_add_del_command_fn (vlib_main_t * vm,
21                             unformat_input_t * input,
22                             vlib_cli_command_t * cmd, u8 is_add)
23 {
24   vrrp_main_t *vmp = &vrrp_main;
25   vrrp_vr_config_t vr_conf;
26   u32 sw_if_index, vr_id, priority, interval;
27   ip46_address_t addr, *addrs;
28   u8 n_addrs4, n_addrs6;
29   clib_error_t *ret = 0;
30   int rv;
31
32   clib_memset (&vr_conf, 0, sizeof (vr_conf));
33
34   /* RFC 5798 - preempt enabled by default */
35   vr_conf.flags = VRRP_VR_PREEMPT;
36
37   addrs = 0;
38   n_addrs4 = n_addrs6 = 0;
39
40   /* defaults */
41   sw_if_index = ~0;
42   vr_id = 0;
43   priority = 100;
44   interval = 100;
45
46   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
47     {
48       clib_memset (&addr, 0, sizeof (addr));
49
50       if (unformat (input, "%U", unformat_vnet_sw_interface, vmp->vnet_main,
51                     &sw_if_index))
52         ;
53       else if (unformat (input, "vr_id %u", &vr_id))
54         ;
55       else if (unformat (input, "ipv6"))
56         vr_conf.flags |= VRRP_VR_IPV6;
57       else if (unformat (input, "priority %u", &priority))
58         ;
59       else if (unformat (input, "interval %u", &interval))
60         ;
61       else if (unformat (input, "no_preempt"))
62         vr_conf.flags &= ~VRRP_VR_PREEMPT;
63       else if (unformat (input, "accept_mode"))
64         vr_conf.flags |= VRRP_VR_ACCEPT;
65       else if (unformat (input, "unicast"))
66         vr_conf.flags |= VRRP_VR_UNICAST;
67       else if (unformat (input, "%U", unformat_ip4_address, &addr.ip4))
68         {
69           n_addrs4++;
70           vec_add1 (addrs, addr);
71         }
72       else if (unformat (input, "%U", unformat_ip6_address, &addr.ip6))
73         {
74           n_addrs6++;
75           vec_add1 (addrs, addr);
76         }
77       else
78         break;
79     }
80
81   if (sw_if_index == ~0)
82     ret = clib_error_return (0, "Please specify an interface...");
83   else if (!vr_id || vr_id > 0xff)
84     ret = clib_error_return (0, "VR ID must be between 1 and 255...");
85
86   if (is_add)
87     {
88       if (!priority || priority > 0xff)
89         ret = clib_error_return (0, "priority must be between 1 and 255...");
90       else if (interval > 0xffff)
91         ret = clib_error_return (0, "interval must be <= 65535...");
92       else if (n_addrs4 && (n_addrs6 || vr_conf.flags & VRRP_VR_IPV6))
93         ret = clib_error_return (0, "Mismatched address families");
94     }
95
96   if (ret)                      /* data validation failed */
97     goto done;
98
99   vr_conf.sw_if_index = sw_if_index;
100   vr_conf.vr_id = (u8) vr_id;
101   vr_conf.priority = (u8) priority;
102   vr_conf.adv_interval = (u16) interval;
103   vr_conf.vr_addrs = addrs;
104
105   rv = vrrp_vr_add_del (is_add, &vr_conf);
106
107   switch (rv)
108     {
109     case 0:
110       break;
111
112       /* adding */
113     case VNET_API_ERROR_ENTRY_ALREADY_EXISTS:
114       ret = clib_error_return (0, "Failed to add VR that already exists");
115       goto done;
116       break;
117
118     case VNET_API_ERROR_INVALID_SRC_ADDRESS:
119       ret = clib_error_return (0, "Failed to add VR with no IP addresses");
120       goto done;
121       break;
122
123     case VNET_API_ERROR_ADDRESS_NOT_FOUND_FOR_INTERFACE:
124       ret = clib_error_return (0, "Failed to add VR with priority 255 - "
125                                "VR IP addresses not configured on interface");
126       goto done;
127       break;
128
129       /* deleting */
130     case VNET_API_ERROR_NO_SUCH_ENTRY:
131       ret = clib_error_return (0, "Failed to delete VR which does not exist");
132       goto done;
133       break;
134
135     default:
136       ret = clib_error_return (0, "vrrp_vr_add_del returned %d", rv);
137       goto done;
138       break;
139     }
140
141 done:
142   vec_free (addrs);
143
144   return ret;
145 }
146
147 static clib_error_t *
148 vrrp_vr_add_command_fn (vlib_main_t * vm, unformat_input_t * input,
149                         vlib_cli_command_t * cmd)
150 {
151   return vrrp_vr_add_del_command_fn (vm, input, cmd, 1 /* is_add */ );
152 }
153
154 /* *INDENT-OFF* */
155 VLIB_CLI_COMMAND (vrrp_vr_add_command, static) =
156 {
157   .path = "vrrp vr add",
158   .short_help =
159   "vrrp vr add <interface> [vr_id <n>] [ipv6] [priority <value>] [interval <value>] [no_preempt] [accept_mode] [unicast] [<ip_addr> ...]",
160   .function = vrrp_vr_add_command_fn,
161 };
162 /* *INDENT-ON* */
163
164 static clib_error_t *
165 vrrp_vr_del_command_fn (vlib_main_t * vm, unformat_input_t * input,
166                         vlib_cli_command_t * cmd)
167 {
168   return vrrp_vr_add_del_command_fn (vm, input, cmd, 0 /* is_add */ );
169 }
170
171 /* *INDENT-OFF* */
172 VLIB_CLI_COMMAND (vrrp_vr_del_command, static) =
173 {
174   .path = "vrrp vr del",
175   .short_help = "vrrp vr del <interface> [vr_id <n>] [ipv6]",
176   .function = vrrp_vr_del_command_fn,
177 };
178 /* *INDENT-ON* */
179
180 static clib_error_t *
181 vrrp_show_vr_command_fn (vlib_main_t * vm,
182                          unformat_input_t * input, vlib_cli_command_t * cmd)
183 {
184   vrrp_main_t *vmp = &vrrp_main;
185   vrrp_vr_t *vr;
186   u32 sw_if_index = ~0;
187
188   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
189     {
190       if (unformat (input, "%U", unformat_vnet_sw_interface, vmp->vnet_main,
191                     &sw_if_index))
192         ;
193       else if (unformat (input, "sw_if_index %u", &sw_if_index))
194         ;
195       else
196         break;
197     }
198
199   pool_foreach (vr, vmp->vrs, (
200                                 {
201
202                                 if (sw_if_index && (sw_if_index != ~0) &&
203                                     (sw_if_index != vr->config.sw_if_index))
204                                 continue;
205                                 vlib_cli_output (vm, "%U", format_vrrp_vr,
206                                                  vr);}
207                 ));
208
209   return 0;
210 }
211
212 /* *INDENT-OFF* */
213 VLIB_CLI_COMMAND (vrrp_show_vr_command, static) =
214 {
215   .path = "show vrrp vr",
216   .short_help =
217   "show vrrp vr [(<intf_name>|sw_if_index <n>)]",
218   .function = vrrp_show_vr_command_fn,
219 };
220 /* *INDENT-ON* */
221
222 static clib_error_t *
223 vrrp_proto_start_stop_command_fn (vlib_main_t * vm,
224                                   unformat_input_t * input,
225                                   vlib_cli_command_t * cmd)
226 {
227   vrrp_main_t *vmp = &vrrp_main;
228   vrrp_vr_key_t vr_key;
229   u32 sw_if_index;
230   u32 vr_id;
231   u8 is_ipv6, is_start, is_stop;
232   int rv;
233
234   clib_memset (&vr_key, 0, sizeof (vr_key));
235
236   /* defaults */
237   sw_if_index = ~0;
238   vr_id = 0;
239   is_ipv6 = is_start = is_stop = 0;
240
241   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
242     {
243       if (unformat (input, "%U", unformat_vnet_sw_interface, vmp->vnet_main,
244                     &sw_if_index))
245         ;
246       else if (unformat (input, "vr_id %u", &vr_id))
247         ;
248       else if (unformat (input, "ipv6"))
249         is_ipv6 = 1;
250       else if (unformat (input, "start"))
251         is_start = 1;
252       else if (unformat (input, "stop"))
253         is_stop = 1;
254       else
255         return clib_error_return (0, "unknown input `%U'",
256                                   format_unformat_error, input);
257     }
258
259   if (is_start == is_stop)
260     return clib_error_return (0, "One of start or stop must be specified");
261   else if (sw_if_index == ~0)
262     return clib_error_return (0, "Please specify an interface...");
263   else if (!vr_id)
264     return clib_error_return (0, "Invalid VR ID...");
265
266   vr_key.sw_if_index = sw_if_index;
267   vr_key.vr_id = vr_id;
268   vr_key.is_ipv6 = (is_ipv6 != 0);
269
270   rv = vrrp_vr_start_stop (is_start, &vr_key);
271
272   switch (rv)
273     {
274     case 0:
275       break;
276     case VNET_API_ERROR_INIT_FAILED:
277       return clib_error_return (0, "Cannot start unicast VR without peers");
278       break;
279     default:
280       return clib_error_return (0, "vrrp_vr_start_stop returned %d", rv);
281       break;
282     }
283
284   return 0;
285 }
286
287 static clib_error_t *
288 vrrp_peers_command_fn (vlib_main_t * vm, unformat_input_t * input,
289                        vlib_cli_command_t * cmd)
290 {
291   vrrp_main_t *vmp = &vrrp_main;
292   vrrp_vr_key_t vr_key;
293   u32 sw_if_index;
294   u32 vr_id;
295   u8 is_ipv6;
296   int rv;
297   ip46_address_t addr, *addrs;
298   u8 n_addrs4, n_addrs6;
299   clib_error_t *ret = 0;
300
301   clib_memset (&vr_key, 0, sizeof (vr_key));
302
303   /* defaults */
304   addrs = 0;
305   n_addrs4 = n_addrs6 = 0;
306   sw_if_index = ~0;
307   vr_id = 0;
308   is_ipv6 = 0;
309
310   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
311     {
312       if (unformat (input, "%U", unformat_vnet_sw_interface, vmp->vnet_main,
313                     &sw_if_index))
314         ;
315       else if (unformat (input, "vr_id %u", &vr_id))
316         ;
317       else if (unformat (input, "ipv6"))
318         is_ipv6 = 1;
319       else if (unformat (input, "%U", unformat_ip4_address, &addr.ip4))
320         {
321           n_addrs4++;
322           vec_add1 (addrs, addr);
323         }
324       else if (unformat (input, "%U", unformat_ip6_address, &addr.ip6))
325         {
326           n_addrs6++;
327           vec_add1 (addrs, addr);
328         }
329       else
330         {
331           ret = clib_error_return (0, "unknown input `%U'",
332                                    format_unformat_error, input);
333           goto done;
334         }
335     }
336
337   if (sw_if_index == ~0)
338     ret = clib_error_return (0, "Please specify an interface...");
339   else if (!vr_id)
340     ret = clib_error_return (0, "Invalid VR ID...");
341   else if (n_addrs4 && (n_addrs6 || is_ipv6))
342     ret = clib_error_return (0, "Mismatched address families");
343
344   if (ret)                      /* data validation failed */
345     goto done;
346
347   vr_key.sw_if_index = sw_if_index;
348   vr_key.vr_id = vr_id;
349   vr_key.is_ipv6 = (is_ipv6 != 0);
350
351   rv = vrrp_vr_set_peers (&vr_key, addrs);
352
353   switch (rv)
354     {
355     case 0:
356       break;
357     case VNET_API_ERROR_INVALID_ARGUMENT:
358       ret = clib_error_return (0, "Peers can only be set on a unicast VR");
359       break;
360     case VNET_API_ERROR_RSRC_IN_USE:
361       ret = clib_error_return (0, "Cannot set peers on a running VR");
362       break;
363     case VNET_API_ERROR_INVALID_DST_ADDRESS:
364       ret = clib_error_return (0, "No peer addresses provided");
365       break;
366     default:
367       ret = clib_error_return (0, "vrrp_vr_set_peers returned %d", rv);
368       break;
369     }
370
371 done:
372   vec_free (addrs);
373
374   return ret;
375 }
376
377 /* *INDENT-OFF* */
378 VLIB_CLI_COMMAND (vrrp_proto_start_stop_command, static) =
379 {
380   .path = "vrrp proto",
381   .short_help =
382   "vrrp proto (start|stop) (<intf_name>|sw_if_index <n>) vr_id <n> [ipv6]",
383   .function = vrrp_proto_start_stop_command_fn,
384 };
385 /* *INDENT-ON* */
386
387 /* *INDENT-OFF* */
388 VLIB_CLI_COMMAND (vrrp_peers_command, static) =
389 {
390   .path = "vrrp peers",
391   .short_help =
392   "vrrp peers (<intf_name>|sw_if_index <n>) vr_id <n> [ipv6] <peer1_addr> [<peer2_addr> ...]",
393   .function = vrrp_peers_command_fn,
394 };
395 /* *INDENT-ON* */
396
397 static clib_error_t *
398 vrrp_vr_track_if_command_fn (vlib_main_t * vm,
399                              unformat_input_t * input,
400                              vlib_cli_command_t * cmd)
401 {
402   vnet_main_t *vnm = vnet_get_main ();
403   vrrp_main_t *vmp = &vrrp_main;
404   u32 sw_if_index, track_if_index, vr_id, priority;
405   u8 is_ipv6 = 0;
406   clib_error_t *ret = 0;
407   vrrp_vr_tracking_if_t *track_intfs = 0, *track_intf;
408   vrrp_vr_t *vr;
409   u8 is_add, is_del;
410   int rv;
411
412   /* defaults */
413   sw_if_index = ~0;
414   vr_id = 0;
415   is_add = is_del = 0;
416
417   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
418     {
419       if (unformat (input, "%U", unformat_vnet_sw_interface, vmp->vnet_main,
420                     &sw_if_index))
421         ;
422       else if (unformat (input, "add"))
423         is_add = 1;
424       else if (unformat (input, "del"))
425         is_del = 1;
426       else if (unformat (input, "vr_id %u", &vr_id))
427         ;
428       else if (unformat (input, "ipv6"))
429         is_ipv6 = 1;
430       else if (unformat (input, "track-index %u priority %u", &track_if_index,
431                          &priority))
432         {
433           vec_add2 (track_intfs, track_intf, 1);;
434           track_intf->sw_if_index = track_if_index;
435           track_intf->priority = priority;
436         }
437       else
438         break;
439     }
440
441   if (sw_if_index == ~0)
442     ret = clib_error_return (0, "Please specify an interface");
443   else if (!vr_id || vr_id > 0xff)
444     ret = clib_error_return (0, "VR ID must be between 1 and 255");
445   else if (is_add == is_del)
446     ret = clib_error_return (0, "One of add,delete must be specified");
447
448   if (ret)
449     goto done;
450
451   vr = vrrp_vr_lookup (sw_if_index, vr_id, is_ipv6);
452   if (!vr)
453     {
454       ret = clib_error_return (0, "VR not found");
455       goto done;
456     }
457
458   vec_foreach (track_intf, track_intfs)
459   {
460     if (!vnet_sw_interface_is_valid (vnm, track_intf->sw_if_index))
461       {
462         ret = clib_error_return (0, "tracked intf sw_if_index %u invalid",
463                                  track_intf->sw_if_index);
464         goto done;
465       }
466     if (!track_intf->priority)
467       {
468         ret = clib_error_return (0, "tracked intf priority must be > 0");
469         goto done;
470       }
471     if (track_intf->priority >= vr->config.priority)
472       {
473         ret = clib_error_return (0, "tracked intf priority must be less "
474                                  "than VR priority (%u)",
475                                  vr->config.priority);
476         goto done;
477       }
478   }
479
480   rv = vrrp_vr_tracking_ifs_add_del (vr, track_intfs, is_add);
481   if (rv)
482     ret = clib_error_return (0, "vrrp_vr_tracking_ifs_add_del returned %d",
483                              rv);
484
485 done:
486   vec_free (track_intfs);
487
488   return ret;
489 }
490
491 /* *INDENT-OFF* */
492 VLIB_CLI_COMMAND (vrrp_vr_track_if_command, static) =
493 {
494   .path = "vrrp vr track-if",
495   .short_help =
496   "vrrp vr track-if (add|del) (<intf_name>|sw_if_index <n>) vr_id <n> [ipv6] track-index <n> priority <n> [ track-index <n> priority <n> ...]",
497   .function = vrrp_vr_track_if_command_fn,
498 };
499 /* *INDENT-ON* */
500
501 /*
502  * fd.io coding-style-patch-verification: ON
503  *
504  * Local Variables:
505  * eval: (c-set-style "gnu")
506  * End:
507  */