vrrp: add stats support and update API
[vpp.git] / src / plugins / vrrp / vrrp_api.c
1 /*
2  * vrrp.c - vpp vrrp plug-in
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 /* define message IDs */
19 #include <vnet/format_fns.h>
20 #include <vrrp/vrrp.api_enum.h>
21 #include <vrrp/vrrp.api_types.h>
22
23 #define REPLY_MSG_ID_BASE vrrp_main.msg_id_base
24 #include <vlibapi/api_helper_macros.h>
25
26 /* API message handlers */
27 static void
28 vl_api_vrrp_vr_update_t_handler (vl_api_vrrp_vr_update_t *mp)
29 {
30   vl_api_vrrp_vr_update_reply_t *rmp;
31   vrrp_vr_config_t vr_conf;
32   u32 api_flags;
33   u32 vrrp_index = INDEX_INVALID;
34   ip46_address_t *addrs = 0;
35   int rv;
36
37   VALIDATE_SW_IF_INDEX (mp);
38
39   api_flags = htonl (mp->flags);
40
41   clib_memset (&vr_conf, 0, sizeof (vr_conf));
42
43   vr_conf.sw_if_index = ntohl (mp->sw_if_index);
44   vr_conf.vr_id = mp->vr_id;
45   vr_conf.priority = mp->priority;
46   vr_conf.adv_interval = ntohs (mp->interval);
47
48   if (api_flags & VRRP_API_VR_PREEMPT)
49     vr_conf.flags |= VRRP_VR_PREEMPT;
50
51   if (api_flags & VRRP_API_VR_ACCEPT)
52     vr_conf.flags |= VRRP_VR_ACCEPT;
53
54   if (api_flags & VRRP_API_VR_UNICAST)
55     vr_conf.flags |= VRRP_VR_UNICAST;
56
57   if (api_flags & VRRP_API_VR_IPV6)
58     vr_conf.flags |= VRRP_VR_IPV6;
59
60   int i;
61   for (i = 0; i < mp->n_addrs; i++)
62     {
63       ip46_address_t *addr;
64       void *src, *dst;
65       int len;
66
67       vec_add2 (addrs, addr, 1);
68
69       if (ntohl (mp->addrs[i].af) == ADDRESS_IP4)
70         {
71           src = &mp->addrs[i].un.ip4;
72           dst = &addr->ip4;
73           len = sizeof (addr->ip4);
74         }
75       else
76         {
77           src = &mp->addrs[i].un.ip6;
78           dst = &addr->ip6;
79           len = sizeof (addr->ip6);
80         }
81
82       clib_memcpy (dst, src, len);
83     }
84
85   vr_conf.vr_addrs = addrs;
86
87   if (vr_conf.priority == 0)
88     {
89       clib_warning ("VR priority must be > 0");
90       rv = VNET_API_ERROR_INVALID_VALUE;
91     }
92   else if (vr_conf.adv_interval == 0)
93     {
94       clib_warning ("VR advertisement interval must be > 0");
95       rv = VNET_API_ERROR_INVALID_VALUE;
96     }
97   else if (vr_conf.vr_id == 0)
98     {
99       clib_warning ("VR ID must be > 0");
100       rv = VNET_API_ERROR_INVALID_VALUE;
101     }
102   else
103     {
104       vrrp_index = ntohl (mp->vrrp_index);
105       rv = vrrp_vr_update (&vrrp_index, &vr_conf);
106     }
107
108   vec_free (addrs);
109
110   BAD_SW_IF_INDEX_LABEL;
111   // clang-format off
112   REPLY_MACRO2 (VL_API_VRRP_VR_UPDATE_REPLY,
113   ({
114     rmp->vrrp_index = htonl (vrrp_index);
115   }));
116   // clang-format on
117 }
118
119 static void
120 vl_api_vrrp_vr_del_t_handler (vl_api_vrrp_vr_del_t *mp)
121 {
122   vl_api_vrrp_vr_del_reply_t *rmp;
123   int rv;
124
125   rv = vrrp_vr_del (ntohl (mp->vrrp_index));
126
127   REPLY_MACRO (VL_API_VRRP_VR_DEL_REPLY);
128 }
129
130 static void
131 vl_api_vrrp_vr_add_del_t_handler (vl_api_vrrp_vr_add_del_t * mp)
132 {
133   vl_api_vrrp_vr_add_del_reply_t *rmp;
134   vrrp_vr_config_t vr_conf;
135   u32 api_flags;
136   ip46_address_t *addrs = 0;
137   int rv;
138
139   VALIDATE_SW_IF_INDEX (mp);
140
141   api_flags = htonl (mp->flags);
142
143   clib_memset (&vr_conf, 0, sizeof (vr_conf));
144
145   vr_conf.sw_if_index = ntohl (mp->sw_if_index);
146   vr_conf.vr_id = mp->vr_id;
147   vr_conf.priority = mp->priority;
148   vr_conf.adv_interval = ntohs (mp->interval);
149
150   if (api_flags & VRRP_API_VR_PREEMPT)
151     vr_conf.flags |= VRRP_VR_PREEMPT;
152
153   if (api_flags & VRRP_API_VR_ACCEPT)
154     vr_conf.flags |= VRRP_VR_ACCEPT;
155
156   if (api_flags & VRRP_API_VR_UNICAST)
157     vr_conf.flags |= VRRP_VR_UNICAST;
158
159   if (api_flags & VRRP_API_VR_IPV6)
160     vr_conf.flags |= VRRP_VR_IPV6;
161
162   if (mp->is_add)
163     {
164       int i;
165
166       for (i = 0; i < mp->n_addrs; i++)
167         {
168           ip46_address_t *addr;
169           void *src, *dst;
170           int len;
171
172           vec_add2 (addrs, addr, 1);
173
174           if (ntohl (mp->addrs[i].af) == ADDRESS_IP4)
175             {
176               src = &mp->addrs[i].un.ip4;
177               dst = &addr->ip4;
178               len = sizeof (addr->ip4);
179             }
180           else
181             {
182               src = &mp->addrs[i].un.ip6;
183               dst = &addr->ip6;
184               len = sizeof (addr->ip6);
185             }
186
187           clib_memcpy (dst, src, len);
188         }
189
190       vr_conf.vr_addrs = addrs;
191     }
192
193   if (vr_conf.priority == 0)
194     {
195       clib_warning ("VR priority must be > 0");
196       rv = VNET_API_ERROR_INVALID_VALUE;
197     }
198   else if (vr_conf.adv_interval == 0)
199     {
200       clib_warning ("VR advertisement interval must be > 0");
201       rv = VNET_API_ERROR_INVALID_VALUE;
202     }
203   else if (vr_conf.vr_id == 0)
204     {
205       clib_warning ("VR ID must be > 0");
206       rv = VNET_API_ERROR_INVALID_VALUE;
207     }
208   else
209     rv = vrrp_vr_add_del (mp->is_add, &vr_conf, NULL);
210
211   vec_free (addrs);
212
213   BAD_SW_IF_INDEX_LABEL;
214   REPLY_MACRO (VL_API_VRRP_VR_ADD_DEL_REPLY);
215 }
216
217 static vl_api_vrrp_vr_state_t
218 vrrp_vr_state_encode (vrrp_vr_state_t vr_state)
219 {
220   if (vr_state == VRRP_VR_STATE_BACKUP)
221     return VRRP_API_VR_STATE_BACKUP;
222   if (vr_state == VRRP_VR_STATE_MASTER)
223     return VRRP_API_VR_STATE_MASTER;
224   if (vr_state == VRRP_VR_STATE_INTF_DOWN)
225     return VRRP_API_VR_STATE_INTF_DOWN;
226
227   return VRRP_API_VR_STATE_INIT;
228 }
229
230 static void
231 send_vrrp_vr_details (vrrp_vr_t * vr, vl_api_registration_t * reg,
232                       u32 context)
233 {
234   vrrp_main_t *vmp = &vrrp_main;
235   vl_api_vrrp_vr_details_t *mp;
236   int n_addrs, msg_size;
237   ip46_address_t *addr;
238   vl_api_address_t *api_addr;
239   u32 api_flags = 0;
240
241   n_addrs = vec_len (vr->config.vr_addrs);
242   msg_size = sizeof (*mp) + n_addrs * sizeof (*api_addr);
243   mp = vl_msg_api_alloc (msg_size);
244   if (!mp)
245     return;
246   clib_memset (mp, 0, msg_size);
247   mp->_vl_msg_id = htons (VL_API_VRRP_VR_DETAILS + vmp->msg_id_base);
248   mp->context = context;
249
250   /* config */
251   mp->config.sw_if_index = htonl (vr->config.sw_if_index);
252   mp->config.vr_id = vr->config.vr_id;
253   mp->config.priority = vr->config.priority;
254   mp->config.interval = htons (vr->config.adv_interval);
255
256   if (vr->config.flags & VRRP_VR_PREEMPT)
257     api_flags |= VRRP_API_VR_PREEMPT;
258   if (vr->config.flags & VRRP_VR_ACCEPT)
259     api_flags |= VRRP_API_VR_ACCEPT;
260   if (vrrp_vr_is_unicast (vr))
261     api_flags |= VRRP_API_VR_UNICAST;
262   if (vrrp_vr_is_ipv6 (vr))
263     api_flags |= VRRP_API_VR_IPV6;
264
265   mp->config.flags = htonl (api_flags);
266
267   /* runtime */
268   mp->runtime.state = htonl (vrrp_vr_state_encode (vr->runtime.state));
269
270   mp->runtime.master_adv_int = htons (vr->runtime.master_adv_int);
271   mp->runtime.skew = htons (vr->runtime.skew);
272   mp->runtime.master_down_int = htons (vr->runtime.master_down_int);
273   clib_memcpy (&mp->runtime.mac, &vr->runtime.mac, sizeof (vr->runtime.mac));
274
275   mp->runtime.tracking.interfaces_dec = htonl (vr->tracking.interfaces_dec);
276   mp->runtime.tracking.priority = vrrp_vr_priority (vr);
277
278   /* addrs */
279   mp->n_addrs = vec_len (vr->config.vr_addrs);
280   api_addr = mp->addrs;
281   vec_foreach (addr, vr->config.vr_addrs)
282   {
283     void *src, *dst;
284     size_t len;
285
286     if (vrrp_vr_is_ipv6 (vr))
287       {
288         api_addr->af = ADDRESS_IP6;
289         dst = &api_addr->un.ip6;
290         src = &addr->ip6;
291         len = sizeof (addr->ip6);
292       }
293     else
294       {
295         api_addr->af = ADDRESS_IP4;
296         dst = &api_addr->un.ip4;
297         src = &addr->ip4;
298         len = sizeof (addr->ip4);
299       }
300     clib_memcpy (dst, src, len);
301     api_addr++;
302   }
303
304   vl_api_send_msg (reg, (u8 *) mp);
305 }
306
307 static void
308 vl_api_vrrp_vr_dump_t_handler (vl_api_vrrp_vr_dump_t * mp)
309 {
310   vrrp_main_t *vmp = &vrrp_main;
311   vl_api_registration_t *reg;
312   vrrp_vr_t *vr;
313   u32 sw_if_index;
314
315   reg = vl_api_client_index_to_registration (mp->client_index);
316   if (!reg)
317     return;
318
319   sw_if_index = htonl (mp->sw_if_index);
320
321   /* *INDENT-OFF* */
322   pool_foreach (vr, vmp->vrs)  {
323
324     if (sw_if_index && (sw_if_index != ~0) &&
325         (sw_if_index != vr->config.sw_if_index))
326       continue;
327
328     send_vrrp_vr_details (vr, reg, mp->context);
329   }
330   /* *INDENT-ON* */
331 }
332
333 static void
334 vl_api_vrrp_vr_start_stop_t_handler (vl_api_vrrp_vr_start_stop_t * mp)
335 {
336   vl_api_vrrp_vr_start_stop_reply_t *rmp;
337   vrrp_vr_key_t vr_key;
338   int rv;
339
340   clib_memset (&vr_key, 0, sizeof (vr_key));
341
342   vr_key.sw_if_index = ntohl (mp->sw_if_index);
343   vr_key.vr_id = mp->vr_id;
344   vr_key.is_ipv6 = (mp->is_ipv6 != 0);
345
346   rv = vrrp_vr_start_stop ((mp->is_start != 0), &vr_key);
347
348   REPLY_MACRO (VL_API_VRRP_VR_START_STOP_REPLY);
349 }
350
351 static void
352 vl_api_vrrp_vr_set_peers_t_handler (vl_api_vrrp_vr_set_peers_t * mp)
353 {
354   vl_api_vrrp_vr_set_peers_reply_t *rmp;
355   vrrp_vr_key_t vr_key;
356   ip46_address_t *peer_addrs = 0;
357   int i;
358   int rv;
359
360   clib_memset (&vr_key, 0, sizeof (vr_key));
361
362   vr_key.sw_if_index = ntohl (mp->sw_if_index);
363   vr_key.vr_id = mp->vr_id;
364   vr_key.is_ipv6 = (mp->is_ipv6 != 0);
365
366   for (i = 0; i < mp->n_addrs; i++)
367     {
368       ip46_address_t *peer;
369
370       vec_add2 (peer_addrs, peer, 1);
371
372       if (mp->is_ipv6)
373         clib_memcpy (&peer->ip6, mp->addrs[i].un.ip6, 16);
374       else
375         clib_memcpy (&peer->ip4, mp->addrs[i].un.ip4, 4);
376     }
377
378   rv = vrrp_vr_set_peers (&vr_key, peer_addrs);
379
380   vec_free (peer_addrs);
381   REPLY_MACRO (VL_API_VRRP_VR_SET_PEERS_REPLY);
382 }
383
384 static void
385 send_vrrp_vr_peer_details (vrrp_vr_t * vr, vl_api_registration_t * reg,
386                            u32 context)
387 {
388   vrrp_main_t *vmp = &vrrp_main;
389   vl_api_vrrp_vr_peer_details_t *mp;
390   int n_addrs, msg_size;
391   ip46_address_t *addr;
392   vl_api_address_t *api_addr;
393
394   n_addrs = vec_len (vr->config.peer_addrs);
395   msg_size = sizeof (*mp) + n_addrs * sizeof (*api_addr);
396   mp = vl_msg_api_alloc (msg_size);
397   if (!mp)
398     return;
399   clib_memset (mp, 0, msg_size);
400   mp->_vl_msg_id = htons (VL_API_VRRP_VR_PEER_DETAILS + vmp->msg_id_base);
401   mp->context = context;
402
403   mp->sw_if_index = htonl (vr->config.sw_if_index);
404   mp->vr_id = vr->config.vr_id;
405   mp->is_ipv6 = vrrp_vr_is_ipv6 (vr);
406
407   /* addrs */
408   mp->n_peer_addrs = n_addrs;
409   api_addr = mp->peer_addrs;
410   vec_foreach (addr, vr->config.peer_addrs)
411   {
412     void *src, *dst;
413     size_t len;
414
415     if (vrrp_vr_is_ipv6 (vr))
416       {
417         api_addr->af = ADDRESS_IP6;
418         dst = &api_addr->un.ip6;
419         src = &addr->ip6;
420         len = sizeof (addr->ip6);
421       }
422     else
423       {
424         api_addr->af = ADDRESS_IP4;
425         dst = &api_addr->un.ip4;
426         src = &addr->ip4;
427         len = sizeof (addr->ip4);
428       }
429     clib_memcpy (dst, src, len);
430     api_addr++;
431   }
432
433   vl_api_send_msg (reg, (u8 *) mp);
434 }
435
436 static void
437 vl_api_vrrp_vr_peer_dump_t_handler (vl_api_vrrp_vr_peer_dump_t * mp)
438 {
439   vrrp_main_t *vmp = &vrrp_main;
440   vl_api_registration_t *reg;
441   vrrp_vr_t *vr;
442   vrrp_vr_key_t vr_key;
443
444   reg = vl_api_client_index_to_registration (mp->client_index);
445   if (!reg)
446     return;
447
448   vr_key.sw_if_index = ntohl (mp->sw_if_index);
449
450   if (vr_key.sw_if_index && (vr_key.sw_if_index != ~0))
451     {
452       uword *p;
453       u32 vr_index = ~0;
454
455       vr_key.vr_id = mp->vr_id;
456       vr_key.is_ipv6 = mp->is_ipv6;
457
458       p = mhash_get (&vmp->vr_index_by_key, &vr_key);
459       if (!p)
460         return;
461
462       vr_index = p[0];
463       vr = pool_elt_at_index (vmp->vrs, vr_index);
464       send_vrrp_vr_peer_details (vr, reg, mp->context);
465
466       return;
467     }
468
469   /* *INDENT-OFF* */
470   pool_foreach (vr, vmp->vrs)  {
471
472     if (!vec_len (vr->config.peer_addrs))
473       continue;
474
475     send_vrrp_vr_details (vr, reg, mp->context);
476
477   }
478   /* *INDENT-ON* */
479 }
480
481 static void
482   vl_api_vrrp_vr_track_if_add_del_t_handler
483   (vl_api_vrrp_vr_track_if_add_del_t * mp)
484 {
485   vl_api_vrrp_vr_track_if_add_del_reply_t *rmp;
486   vrrp_vr_t *vr;
487   vrrp_vr_tracking_if_t *track_if, *track_ifs = 0;
488   int rv = 0, i;
489
490   /* lookup VR and return error if it does not exist */
491   vr =
492     vrrp_vr_lookup (ntohl (mp->sw_if_index), mp->vr_id, (mp->is_ipv6 != 0));
493   if (!vr)
494     {
495       rv = VNET_API_ERROR_INVALID_VALUE;
496       goto done;
497     }
498
499   for (i = 0; i < mp->n_ifs; i++)
500     {
501       vl_api_vrrp_vr_track_if_t *api_track_if = &mp->ifs[i];
502
503       vec_add2 (track_ifs, track_if, 1);
504       track_if->sw_if_index = ntohl (api_track_if->sw_if_index);
505       track_if->priority = api_track_if->priority;
506     }
507
508   rv = vrrp_vr_tracking_ifs_add_del (vr, track_ifs, mp->is_add != 0);
509
510 done:
511   vec_free (track_ifs);
512   REPLY_MACRO (VL_API_VRRP_VR_TRACK_IF_ADD_DEL_REPLY);
513 }
514
515 static void
516 send_vrrp_vr_track_if_details (vrrp_vr_t * vr, vl_api_registration_t * reg,
517                                u32 context)
518 {
519   vrrp_main_t *vmp = &vrrp_main;
520   vl_api_vrrp_vr_track_if_details_t *mp;
521   int n_ifs, msg_size;
522   vl_api_vrrp_vr_track_if_t *api_track_if;
523   vrrp_vr_tracking_if_t *track_if;
524
525   if (!vr)
526     return;
527
528   n_ifs = vec_len (vr->tracking.interfaces);
529   msg_size = sizeof (*mp) + n_ifs * sizeof (*api_track_if);
530   mp = vl_msg_api_alloc (msg_size);
531   if (!mp)
532     return;
533   clib_memset (mp, 0, msg_size);
534   mp->_vl_msg_id = htons (VL_API_VRRP_VR_TRACK_IF_DETAILS + vmp->msg_id_base);
535   mp->context = context;
536
537   mp->sw_if_index = htonl (vr->config.sw_if_index);
538   mp->vr_id = vr->config.vr_id;
539   mp->is_ipv6 = vrrp_vr_is_ipv6 (vr);
540
541   /* tracked interfaces */
542   mp->n_ifs = n_ifs;
543   api_track_if = mp->ifs;
544   vec_foreach (track_if, vr->tracking.interfaces)
545   {
546     api_track_if->sw_if_index = htonl (track_if->sw_if_index);
547     api_track_if->priority = track_if->priority;
548     api_track_if += 1;
549   }
550
551   vl_api_send_msg (reg, (u8 *) mp);
552 }
553
554 static void
555 vl_api_vrrp_vr_track_if_dump_t_handler (vl_api_vrrp_vr_track_if_dump_t * mp)
556 {
557   vrrp_main_t *vmp = &vrrp_main;
558   vl_api_registration_t *reg;
559   vrrp_vr_t *vr;
560
561   reg = vl_api_client_index_to_registration (mp->client_index);
562   if (!reg)
563     return;
564
565   if (!mp->dump_all)
566     {
567       vr = vrrp_vr_lookup (ntohl (mp->sw_if_index), mp->vr_id, mp->is_ipv6);
568       send_vrrp_vr_track_if_details (vr, reg, mp->context);
569
570       return;
571     }
572
573   /* *INDENT-OFF* */
574   pool_foreach (vr, vmp->vrs)  {
575
576     if (!vec_len (vr->tracking.interfaces))
577       continue;
578
579     send_vrrp_vr_track_if_details (vr, reg, mp->context);
580
581   }
582   /* *INDENT-ON* */
583 }
584
585 static void
586 send_vrrp_vr_event (vpe_client_registration_t * reg,
587                     vl_api_registration_t * vl_reg,
588                     vrrp_vr_t * vr, vrrp_vr_state_t new_state)
589 {
590   vrrp_main_t *vmp = &vrrp_main;
591   vl_api_vrrp_vr_event_t *mp;
592
593   mp = vl_msg_api_alloc (sizeof (*mp));
594
595   clib_memset (mp, 0, sizeof (*mp));
596   mp->_vl_msg_id = ntohs (VL_API_VRRP_VR_EVENT + vmp->msg_id_base);
597   mp->client_index = reg->client_index;
598   mp->pid = reg->client_pid;
599   mp->vr.sw_if_index = ntohl (vr->config.sw_if_index);
600   mp->vr.vr_id = vr->config.vr_id;
601   mp->vr.is_ipv6 = ((vr->config.flags & VRRP_VR_IPV6) != 0);
602
603   mp->old_state = htonl (vrrp_vr_state_encode (vr->runtime.state));
604   mp->new_state = htonl (vrrp_vr_state_encode (new_state));
605
606   vl_api_send_msg (vl_reg, (u8 *) mp);
607 }
608
609 void
610 vrrp_vr_event (vrrp_vr_t * vr, vrrp_vr_state_t new_state)
611 {
612   vpe_api_main_t *vam = &vpe_api_main;
613   vpe_client_registration_t *reg;
614   vl_api_registration_t *vl_reg;
615
616   /* *INDENT-OFF* */
617   pool_foreach (reg, vam->vrrp_vr_events_registrations)
618    {
619     vl_reg = vl_api_client_index_to_registration (reg->client_index);
620     if (vl_reg)
621       send_vrrp_vr_event (reg, vl_reg, vr, new_state);
622   }
623   /* *INDENT-ON* */
624 }
625
626 pub_sub_handler (vrrp_vr_events, VRRP_VR_EVENTS);
627
628 /* Set up the API message handling tables */
629 #include <vrrp/vrrp.api.c>
630 clib_error_t *
631 vrrp_plugin_api_hookup (vlib_main_t * vm)
632 {
633   vrrp_main_t *vmp = &vrrp_main;
634
635   /* Ask for a correctly-sized block of API message decode slots */
636   vmp->msg_id_base = setup_message_id_table ();
637
638   return 0;
639 }
640
641 /* *INDENT-ON* */
642
643 /*
644  * fd.io coding-style-patch-verification: ON
645  *
646  * Local Variables:
647  * eval: (c-set-style "gnu")
648  * End:
649  */