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