vrrp: backup processes priority 255 advertisement
[vpp.git] / src / plugins / vrrp / vrrp.c
1 /*
2  * vrrp.c - vrrp plugin action functions
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 <vnet/mfib/mfib_entry.h>
13 #include <vnet/mfib/mfib_table.h>
14 #include <vnet/adj/adj.h>
15 #include <vnet/adj/adj_mcast.h>
16 #include <vnet/fib/fib_table.h>
17 #include <vnet/ip/igmp_packet.h>
18 #include <vnet/ip/ip6_link.h>
19
20 #include <vrrp/vrrp.h>
21 #include <vrrp/vrrp_packet.h>
22
23 #include <vpp/app/version.h>
24
25 vrrp_main_t vrrp_main;
26
27 static const mac_address_t ipv4_vmac = {
28   .bytes = {0x00, 0x00, 0x5e, 0x00, 0x01, 0x00}
29 };
30
31 static const mac_address_t ipv6_vmac = {
32   .bytes = {0x00, 0x00, 0x5e, 0x00, 0x02, 0x00}
33 };
34
35 typedef struct
36 {
37   vrrp_vr_key_t key;
38   u32 count;
39 } vrrp_hwif_vr_count_t;
40
41 typedef enum
42 {
43   VRRP_IF_UPDATE_IP,
44   VRRP_IF_UPDATE_HW_LINK,
45   VRRP_IF_UPDATE_SW_ADMIN,
46 } vrrp_intf_update_type_t;
47
48 typedef struct
49 {
50   vrrp_intf_update_type_t type;
51   u32 sw_if_index;
52   u32 hw_if_index;
53   int intf_up;
54 } vrrp_intf_update_t;
55
56 static int vrrp_intf_is_up (u32 sw_if_index, u8 is_ipv6,
57                             vrrp_intf_update_t * pending);
58
59 static walk_rc_t
60 vrrp_hwif_master_count_walk (vnet_main_t * vnm, u32 sw_if_index, void *arg)
61 {
62   vrrp_hwif_vr_count_t *vr_count = arg;
63   vrrp_vr_t *vr;
64
65   vr = vrrp_vr_lookup (sw_if_index, vr_count->key.vr_id,
66                        vr_count->key.is_ipv6);
67
68   if (vr && (vr->runtime.state == VRRP_VR_STATE_MASTER))
69     vr_count->count++;
70
71   return WALK_CONTINUE;
72 }
73
74 /*
75  * Get a count of VRs in master state on a given hardware interface with
76  * the provided VR ID and AF.
77  */
78 static u32
79 vrrp_vr_hwif_master_vrs_by_vrid (u32 hw_if_index, u8 vr_id, u8 is_ipv6)
80 {
81   vnet_main_t *vnm = vnet_get_main ();
82   vrrp_hwif_vr_count_t vr_count;
83
84   clib_memset (&vr_count, 0, sizeof (vr_count));
85
86   vr_count.key.vr_id = vr_id;
87   vr_count.key.is_ipv6 = is_ipv6;
88
89   vnet_hw_interface_walk_sw (vnm, hw_if_index,
90                              vrrp_hwif_master_count_walk, &vr_count);
91
92   return vr_count.count;
93 }
94
95 /*
96  * Add or delete the VR virtual MAC address on the hardware interface
97  * when a VR enters or leaves the master state.
98  *
99  * Multiple subinterfaces may host the same VR ID. We should only add or
100  * delete the virtual MAC if this is the first VR being enabled on the
101  * hardware interface or the last one being disabled, respectively.
102  */
103 void
104 vrrp_vr_transition_vmac (vrrp_vr_t * vr, vrrp_vr_state_t new_state)
105 {
106   vnet_main_t *vnm = vnet_get_main ();
107   clib_error_t *error = 0;
108   vnet_hw_interface_t *hw;
109   u8 enable = (new_state == VRRP_VR_STATE_MASTER);
110   u32 n_master_vrs;
111
112   hw = vnet_get_sup_hw_interface (vnm, vr->config.sw_if_index);
113   n_master_vrs =
114     vrrp_vr_hwif_master_vrs_by_vrid (hw->hw_if_index, vr->config.vr_id,
115                                      vrrp_vr_is_ipv6 (vr));
116
117   /* enable only if current master vrs is 0, disable only if 0 or 1 */
118   if ((enable && !n_master_vrs) || (!enable && (n_master_vrs < 2)))
119     {
120       clib_warning ("%s virtual MAC address %U on hardware interface %u",
121                     (enable) ? "Adding" : "Deleting",
122                     format_ethernet_address, vr->runtime.mac.bytes,
123                     hw->hw_if_index);
124
125       error = vnet_hw_interface_add_del_mac_address
126         (vnm, hw->hw_if_index, vr->runtime.mac.bytes, enable);
127     }
128
129   if (error)
130     clib_error_report (error);
131 }
132
133 /*
134  * Manage VR interface data on transition to/from master:
135  *  - enable or disable ARP/ND input feature if appropriate
136  *  - update count of VRs in master state
137  */
138 static void
139 vrrp_vr_transition_intf (vrrp_vr_t * vr, vrrp_vr_state_t new_state)
140 {
141   vrrp_intf_t *intf;
142   const char *arc_name = 0, *node_name = 0;
143   const char *mc_arc_name = 0, *mc_node_name = 0;
144   u8 is_ipv6 = vrrp_vr_is_ipv6 (vr);
145   u32 *vr_index;
146   int n_master_accept = 0;
147
148   /* only need to do something if entering or leaving master state */
149   if ((vr->runtime.state != VRRP_VR_STATE_MASTER) &&
150       (new_state != VRRP_VR_STATE_MASTER))
151     return;
152
153   if (is_ipv6)
154     {
155       arc_name = "ip6-local";
156       node_name = "vrrp6-nd-input";
157       mc_arc_name = "ip6-multicast";
158       mc_node_name = "vrrp6-accept-owner-input";
159     }
160   else
161     {
162       arc_name = "arp";
163       node_name = "vrrp4-arp-input";
164       mc_arc_name = "ip4-multicast";
165       mc_node_name = "vrrp4-accept-owner-input";
166     }
167
168   intf = vrrp_intf_get (vr->config.sw_if_index);
169
170   if (new_state == VRRP_VR_STATE_MASTER)
171     {
172       intf->n_master_vrs[is_ipv6]++;
173       if (intf->n_master_vrs[is_ipv6] == 1)
174         vnet_feature_enable_disable (arc_name, node_name,
175                                      vr->config.sw_if_index, 1, NULL, 0);
176     }
177   else
178     {
179       if (intf->n_master_vrs[is_ipv6] == 1)
180         vnet_feature_enable_disable (arc_name, node_name,
181                                      vr->config.sw_if_index, 0, NULL, 0);
182       /* If the count were already 0, leave it at 0 */
183       if (intf->n_master_vrs[is_ipv6])
184         intf->n_master_vrs[is_ipv6]--;
185     }
186
187   /* accept mode enabled, count the other master VRs w/ accept mode on intf */
188   if (vrrp_vr_accept_mode_enabled (vr))
189     {
190       vec_foreach (vr_index, intf->vr_indices[is_ipv6])
191       {
192         vrrp_vr_t *intf_vr = vrrp_vr_lookup_index (*vr_index);
193
194         if (intf_vr == vr)
195           continue;
196
197         if (vrrp_vr_accept_mode_enabled (intf_vr) &&
198             (intf_vr->runtime.state == VRRP_VR_STATE_MASTER))
199           n_master_accept++;
200       }
201
202       /* If no others, enable or disable the feature based on new state */
203       if (!n_master_accept)
204         vnet_feature_enable_disable (mc_arc_name, mc_node_name,
205                                      vr->config.sw_if_index,
206                                      (new_state == VRRP_VR_STATE_MASTER),
207                                      NULL, 0);
208     }
209
210 }
211
212 /* If accept mode enabled, add/remove VR addresses from interface */
213 static void
214 vrrp_vr_transition_addrs (vrrp_vr_t * vr, vrrp_vr_state_t new_state)
215 {
216   vlib_main_t *vm = vlib_get_main ();
217   u8 is_del;
218   ip46_address_t *vr_addr;
219
220   if (!vrrp_vr_accept_mode_enabled (vr))
221     return;
222
223   /* owner always has VR addresses configured, should never remove them */
224   if (vrrp_vr_is_owner (vr))
225     return;
226
227   if (vrrp_vr_is_unicast (vr))
228     return;
229
230   /* only need to do something if entering or leaving master state */
231   if ((vr->runtime.state != VRRP_VR_STATE_MASTER) &&
232       (new_state != VRRP_VR_STATE_MASTER))
233     return;
234
235   is_del = (new_state != VRRP_VR_STATE_MASTER);
236
237   clib_warning ("%s VR addresses on sw_if_index %u",
238                 (is_del) ? "Deleting" : "Adding", vr->config.sw_if_index);
239
240   vec_foreach (vr_addr, vr->config.vr_addrs)
241   {
242     ip_interface_address_t *ia = NULL;
243
244     /* We need to know the address length to use, find it from another
245      * address on the interface. Or use a default (/24, /64).
246      */
247     if (!vrrp_vr_is_ipv6 (vr))
248       {
249         ip4_main_t *im = &ip4_main;
250         ip4_address_t *intf4;
251
252         intf4 =
253           ip4_interface_address_matching_destination
254           (im, &vr_addr->ip4, vr->config.sw_if_index, &ia);
255
256         ip4_add_del_interface_address (vm, vr->config.sw_if_index,
257                                        &vr_addr->ip4,
258                                        (intf4 ? ia->address_length : 24),
259                                        is_del);
260       }
261     else
262       {
263         ip6_main_t *im = &ip6_main;
264         ip6_address_t *intf6;
265
266         intf6 =
267           ip6_interface_address_matching_destination
268           (im, &vr_addr->ip6, vr->config.sw_if_index, &ia);
269
270         ip6_add_del_interface_address (vm, vr->config.sw_if_index,
271                                        &vr_addr->ip6,
272                                        (intf6 ? ia->address_length : 64),
273                                        is_del);
274       }
275   }
276 }
277
278 void
279 vrrp_vr_transition (vrrp_vr_t * vr, vrrp_vr_state_t new_state, void *data)
280 {
281
282   clib_warning ("VR %U transitioning to %U", format_vrrp_vr_key, vr,
283                 format_vrrp_vr_state, new_state);
284
285   /* Don't do anything if transitioning to the state VR is already in.
286    * This should never happen, just covering our bases.
287    */
288   if (new_state == vr->runtime.state)
289     return;
290
291   if (new_state == VRRP_VR_STATE_MASTER)
292     {
293       /* RFC 5798 sec 6.4.1 (105) - startup event for VR with priority 255
294        *          sec 6.4.2 (365) - master down timer fires on backup VR
295        */
296
297       vrrp_vr_multicast_group_join (vr);
298       vrrp_adv_send (vr, 0);
299       vrrp_garp_or_na_send (vr);
300
301       vrrp_vr_timer_set (vr, VRRP_VR_TIMER_ADV);
302     }
303   else if (new_state == VRRP_VR_STATE_BACKUP)
304     {
305       /* RFC 5798 sec 6.4.1 (150) - startup event for VR with priority < 255
306        *          sec 6.4.3 (735) - master preempted by higher priority VR
307        */
308
309       vrrp_vr_multicast_group_join (vr);
310
311       if (vr->runtime.state == VRRP_VR_STATE_MASTER)
312         {
313           vrrp_header_t *pkt = data;
314           vr->runtime.master_adv_int = vrrp_adv_int_from_packet (pkt);
315
316         }
317       else                      /* INIT, INTF_DOWN */
318         vr->runtime.master_adv_int = vr->config.adv_interval;
319
320       vrrp_vr_skew_compute (vr);
321       vrrp_vr_master_down_compute (vr);
322       vrrp_vr_timer_set (vr, VRRP_VR_TIMER_MASTER_DOWN);
323
324     }
325   else if (new_state == VRRP_VR_STATE_INIT)
326     {
327       /* RFC 5798 sec 6.4.2 (345) - shutdown event for backup VR
328        *          sec 6.4.3 (655) - shutdown event for master VR
329        */
330
331       vrrp_vr_timer_cancel (vr);
332       if (vr->runtime.state == VRRP_VR_STATE_MASTER)
333         vrrp_adv_send (vr, 1);
334     }
335   else if (new_state == VRRP_VR_STATE_INTF_DOWN)
336     /* State is not specified by RFC. This is to avoid attempting to
337      * send packets on an interface that's down and to avoid having a
338      * VR believe it is already the master when an interface is brought up
339      */
340     vrrp_vr_timer_cancel (vr);
341
342   /* add/delete virtual IP addrs if accept_mode is true */
343   vrrp_vr_transition_addrs (vr, new_state);
344
345   /* enable/disable input features if necessary */
346   vrrp_vr_transition_intf (vr, new_state);
347
348   /* add/delete virtual MAC address on NIC if necessary */
349   vrrp_vr_transition_vmac (vr, new_state);
350
351   vr->runtime.state = new_state;
352 }
353
354 #define VRRP4_MCAST_ADDR_AS_U8 { 224, 0, 0, 18 }
355 #define VRRP6_MCAST_ADDR_AS_U8 \
356 { 0xff, 0x2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x12 }
357
358 static const mfib_prefix_t all_vrrp4_routers = {
359   .fp_proto = FIB_PROTOCOL_IP4,
360   .fp_len = 32,
361   .fp_grp_addr = {
362                   .ip4 = {
363                           .as_u8 = VRRP4_MCAST_ADDR_AS_U8,
364                           },
365                   },
366 };
367
368 static const mfib_prefix_t all_vrrp6_routers = {
369   .fp_proto = FIB_PROTOCOL_IP6,
370   .fp_len = 128,
371   .fp_grp_addr = {
372                   .ip6 = {
373                           .as_u8 = VRRP6_MCAST_ADDR_AS_U8,
374                           },
375                   },
376 };
377
378 static int
379 vrrp_intf_enable_disable_mcast (u8 enable, u32 sw_if_index, u8 is_ipv6)
380 {
381   vrrp_main_t *vrm = &vrrp_main;
382   vrrp_intf_t *intf;
383   u32 fib_index;
384   const mfib_prefix_t *vrrp_prefix;
385   fib_protocol_t proto;
386   vnet_link_t link_type;
387   fib_route_path_t for_us = {
388     .frp_sw_if_index = 0xffffffff,
389     .frp_weight = 1,
390     .frp_flags = FIB_ROUTE_PATH_LOCAL,
391     .frp_mitf_flags = MFIB_ITF_FLAG_FORWARD,
392   };
393   fib_route_path_t via_itf = {
394     .frp_sw_if_index = sw_if_index,
395     .frp_weight = 1,
396     .frp_mitf_flags = MFIB_ITF_FLAG_ACCEPT,
397   };
398
399   intf = vrrp_intf_get (sw_if_index);
400
401   if (is_ipv6)
402     {
403       proto = FIB_PROTOCOL_IP6;
404       link_type = VNET_LINK_IP6;
405       vrrp_prefix = &all_vrrp6_routers;
406     }
407   else
408     {
409       proto = FIB_PROTOCOL_IP4;
410       link_type = VNET_LINK_IP4;
411       vrrp_prefix = &all_vrrp4_routers;
412     }
413
414   for_us.frp_proto = fib_proto_to_dpo (proto);
415   via_itf.frp_proto = fib_proto_to_dpo (proto);
416   fib_index = mfib_table_get_index_for_sw_if_index (proto, sw_if_index);
417
418   if (enable)
419     {
420       if (pool_elts (vrm->vrs) == 1)
421         mfib_table_entry_path_update (fib_index, vrrp_prefix, MFIB_SOURCE_API,
422                                       &for_us);
423
424       mfib_table_entry_path_update (fib_index, vrrp_prefix, MFIB_SOURCE_API,
425                                     &via_itf);
426       intf->mcast_adj_index[! !is_ipv6] =
427         adj_mcast_add_or_lock (proto, link_type, sw_if_index);
428     }
429   else
430     {
431       if (pool_elts (vrm->vrs) == 0)
432         mfib_table_entry_path_remove (fib_index, vrrp_prefix, MFIB_SOURCE_API,
433                                       &for_us);
434
435       mfib_table_entry_path_remove (fib_index, vrrp_prefix, MFIB_SOURCE_API,
436                                     &via_itf);
437     }
438
439   return 0;
440 }
441
442 static int
443 vrrp_intf_vr_add_del (u8 is_add, u32 sw_if_index, u32 vr_index, u8 is_ipv6)
444 {
445   vrrp_intf_t *vr_intf;
446
447   vr_intf = vrrp_intf_get (sw_if_index);
448   if (!vr_intf)
449     return -1;
450
451   if (is_add)
452     {
453       if (!vec_len (vr_intf->vr_indices[is_ipv6]))
454         vrrp_intf_enable_disable_mcast (1, sw_if_index, is_ipv6);
455
456       vec_add1 (vr_intf->vr_indices[is_ipv6], vr_index);
457     }
458   else
459     {
460       u32 per_intf_index =
461         vec_search (vr_intf->vr_indices[is_ipv6], vr_index);
462
463       if (per_intf_index != ~0)
464         vec_del1 (vr_intf->vr_indices[is_ipv6], per_intf_index);
465
466       /* no more VRs on this interface, disable multicast */
467       if (!vec_len (vr_intf->vr_indices[is_ipv6]))
468         vrrp_intf_enable_disable_mcast (0, sw_if_index, is_ipv6);
469     }
470
471   return 0;
472 }
473
474 /* RFC 5798 section 8.3.2 says to take care not to configure more than
475  * one VRRP router as the "IPvX address owner" of a VRID. Make sure that
476  * all of the addresses configured for this VR are configured on the
477  * interface.
478  */
479 static int
480 vrrp_vr_valid_addrs_owner (vrrp_vr_config_t * vr_conf)
481 {
482   ip46_address_t *addr;
483   u8 is_ipv6 = (vr_conf->flags & VRRP_VR_IPV6) != 0;
484
485   vec_foreach (addr, vr_conf->vr_addrs)
486   {
487     if (!ip_interface_has_address (vr_conf->sw_if_index, addr, !is_ipv6))
488       return VNET_API_ERROR_ADDRESS_NOT_FOUND_FOR_INTERFACE;
489   }
490
491   return 0;
492 }
493
494 static int
495 vrrp_vr_valid_addrs_unused (vrrp_vr_config_t * vr_conf)
496 {
497   ip46_address_t *vr_addr;
498   u8 is_ipv6 = (vr_conf->flags & VRRP_VR_IPV6) != 0;
499
500   vec_foreach (vr_addr, vr_conf->vr_addrs)
501   {
502     u32 vr_index;
503     void *addr;
504
505     addr = (is_ipv6) ? (void *) &vr_addr->ip6 : (void *) &vr_addr->ip4;
506     vr_index = vrrp_vr_lookup_address (vr_conf->sw_if_index, is_ipv6, addr);
507     if (vr_index != ~0)
508       return VNET_API_ERROR_ADDRESS_IN_USE;
509   }
510
511   return 0;
512 }
513
514 static int
515 vrrp_vr_valid_addrs (vrrp_vr_config_t * vr_conf)
516 {
517   int ret = 0;
518
519   /* If the VR owns the addresses, make sure they are configured */
520   if (vr_conf->priority == 255 &&
521       (ret = vrrp_vr_valid_addrs_owner (vr_conf)) < 0)
522     return ret;
523
524   /* make sure no other VR has already configured any of the VR addresses */
525   ret = vrrp_vr_valid_addrs_unused (vr_conf);
526
527   return ret;
528 }
529
530 int
531 vrrp_vr_addr_add_del (vrrp_vr_t * vr, u8 is_add, ip46_address_t * vr_addr)
532 {
533   vrrp_main_t *vmp = &vrrp_main;
534   u32 vr_index;
535   vrrp4_arp_key_t key4;
536   vrrp6_nd_key_t key6;
537   ip46_address_t *addr;
538
539   if (!vr || !vr_addr)
540     return VNET_API_ERROR_INVALID_ARGUMENT;
541
542   vr_index = vr - vmp->vrs;
543
544   if (vrrp_vr_is_ipv6 (vr))
545     {
546       key6.sw_if_index = vr->config.sw_if_index;
547       key6.addr = vr_addr->ip6;
548       if (is_add)
549         {
550           hash_set_mem_alloc (&vmp->vrrp6_nd_lookup, &key6, vr_index);
551           vec_add1 (vr->config.vr_addrs, vr_addr[0]);
552         }
553       else
554         {
555           hash_unset_mem_free (&vmp->vrrp6_nd_lookup, &key6);
556           vec_foreach (addr, vr->config.vr_addrs)
557           {
558             if (!ip46_address_cmp (addr, vr_addr))
559               {
560                 vec_del1 (vr->config.vr_addrs, vr->config.vr_addrs - addr);
561                 break;
562               }
563           }
564         }
565     }
566   else
567     {
568       key4.sw_if_index = vr->config.sw_if_index;
569       key4.addr = vr_addr->ip4;
570       if (is_add)
571         {
572           hash_set (vmp->vrrp4_arp_lookup, key4.as_u64, vr_index);
573           vec_add1 (vr->config.vr_addrs, vr_addr[0]);
574         }
575       else
576         {
577           hash_unset (vmp->vrrp4_arp_lookup, key4.as_u64);
578           vec_foreach (addr, vr->config.vr_addrs)
579           {
580             if (!ip46_address_cmp (addr, vr_addr))
581               {
582                 vec_del1 (vr->config.vr_addrs, vr->config.vr_addrs - addr);
583                 break;
584               }
585           }
586         }
587     }
588
589   return 0;
590 }
591
592 static void
593 vrrp_vr_addrs_add_del (vrrp_vr_t * vr, u8 is_add, ip46_address_t * vr_addrs)
594 {
595   ip46_address_t *vr_addr;
596
597   vec_foreach (vr_addr, vr_addrs)
598   {
599     vrrp_vr_addr_add_del (vr, is_add, vr_addr);
600   }
601 }
602
603 /* Action function shared between message handler and debug CLI */
604 int
605 vrrp_vr_add_del (u8 is_add, vrrp_vr_config_t * vr_conf)
606 {
607   vrrp_main_t *vrm = &vrrp_main;
608   vnet_main_t *vnm = vnet_get_main ();
609   vrrp_vr_key_t key;
610   uword *p;
611   u32 vr_index;
612   vrrp_vr_t *vr = 0;
613   int ret;
614
615   if (vr_conf->sw_if_index == ~0 ||
616       !vnet_sw_interface_is_valid (vnm, vr_conf->sw_if_index))
617     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
618
619   clib_memset (&key, 0, sizeof (key));
620
621   key.sw_if_index = vr_conf->sw_if_index;
622   key.vr_id = vr_conf->vr_id;
623   key.is_ipv6 = ((vr_conf->flags & VRRP_VR_IPV6) != 0);
624
625   p = mhash_get (&vrm->vr_index_by_key, &key);
626
627   if (is_add)
628     {
629       /* does a VR matching this key already exist ? */
630       if (p)
631         {
632           clib_warning ("VR %u for IPv%d already exists on sw_if_index %u",
633                         key.vr_id, (key.is_ipv6) ? 6 : 4, key.sw_if_index);
634           return VNET_API_ERROR_ENTRY_ALREADY_EXISTS;
635         }
636
637       /* were IPvX addresses included ? */
638       if (!vec_len (vr_conf->vr_addrs))
639         {
640           clib_warning ("Conf of VR %u for IPv%d on sw_if_index %u "
641                         " does not contain IP addresses",
642                         key.vr_id, (key.is_ipv6) ? 6 : 4, key.sw_if_index);
643           return VNET_API_ERROR_INVALID_SRC_ADDRESS;
644         }
645
646       /* Make sure the addresses are ok to use */
647       if ((ret = vrrp_vr_valid_addrs (vr_conf)) < 0)
648         return ret;
649
650       pool_get_zero (vrm->vrs, vr);
651       vr_index = vr - vrm->vrs;
652
653       clib_memcpy (&vr->config, vr_conf, sizeof (vrrp_vr_config_t));
654
655       vr->config.vr_addrs = 0;  /* allocate our own memory */
656       vrrp_vr_addrs_add_del (vr, is_add, vr_conf->vr_addrs);
657
658       vr->runtime.state = VRRP_VR_STATE_INIT;
659       vr->runtime.timer_index = ~0;
660
661       /* set virtual MAC based on IP version and VR ID */
662       vr->runtime.mac = (key.is_ipv6) ? ipv6_vmac : ipv4_vmac;
663       vr->runtime.mac.bytes[5] = vr_conf->vr_id;
664
665       mhash_set (&vrm->vr_index_by_key, &key, vr_index, 0);
666     }
667   else
668     {
669       if (!p)
670         {
671           clib_warning ("No VR %u for IPv%d exists on sw_if_index %u",
672                         key.vr_id, (key.is_ipv6) ? 6 : 4, key.sw_if_index);
673           return VNET_API_ERROR_NO_SUCH_ENTRY;
674         }
675
676       vr_index = p[0];
677       vr = pool_elt_at_index (vrm->vrs, vr_index);
678
679       vrrp_vr_tracking_ifs_add_del (vr, vr->tracking.interfaces, is_add);
680       vrrp_vr_addrs_add_del (vr, is_add, vr->config.vr_addrs);
681       mhash_unset (&vrm->vr_index_by_key, &key, 0);
682       vec_free (vr->config.vr_addrs);
683       vec_free (vr->tracking.interfaces);
684       pool_put (vrm->vrs, vr);
685     }
686
687   vrrp_intf_vr_add_del (is_add, vr_conf->sw_if_index, vr_index, key.is_ipv6);
688
689   return 0;
690 }
691
692 int
693 vrrp_vr_start_stop (u8 is_start, vrrp_vr_key_t * vr_key)
694 {
695   vrrp_main_t *vmp = &vrrp_main;
696   uword *p;
697   vrrp_vr_t *vr = 0;
698
699   p = mhash_get (&vmp->vr_index_by_key, vr_key);
700   if (!p)
701     return VNET_API_ERROR_NO_SUCH_ENTRY;
702
703   vr = pool_elt_at_index (vmp->vrs, p[0]);
704
705   /* return success if already in the desired state */
706   switch (vr->runtime.state)
707     {
708     case VRRP_VR_STATE_INIT:
709       if (!is_start)
710         {
711           clib_warning ("Attempting to stop already stopped VR (%U)",
712                         format_vrrp_vr_key, vr);
713           return 0;
714         }
715       break;
716     default:
717       if (is_start)
718         {
719           clib_warning ("Attempting to start already started VR (%U)",
720                         format_vrrp_vr_key, vr);
721           return 0;
722         }
723       break;
724     }
725
726   if (is_start)
727     {
728       if (vrrp_vr_is_unicast (vr) && vec_len (vr->config.peer_addrs) == 0)
729         {
730           clib_warning ("Cannot start unicast VR without peers");
731           return VNET_API_ERROR_INIT_FAILED;
732         }
733
734       vmp->n_vrs_started++;
735
736       if (!vrrp_intf_is_up (vr->config.sw_if_index, vrrp_vr_is_ipv6 (vr),
737                             NULL))
738         {
739           clib_warning ("VRRP VR started on down interface (%U)",
740                         format_vrrp_vr_key, vr);
741           vrrp_vr_transition (vr, VRRP_VR_STATE_INTF_DOWN, NULL);
742         }
743       else if (vrrp_vr_is_owner (vr))
744         vrrp_vr_transition (vr, VRRP_VR_STATE_MASTER, NULL);
745       else
746         vrrp_vr_transition (vr, VRRP_VR_STATE_BACKUP, NULL);
747     }
748   else
749     {
750       vmp->n_vrs_started--;
751
752       vrrp_vr_transition (vr, VRRP_VR_STATE_INIT, NULL);
753     }
754
755   clib_warning ("%d VRs configured, %d VRs running",
756                 pool_elts (vmp->vrs), vmp->n_vrs_started);
757
758   return 0;
759 }
760
761 static int
762 vrrp_vr_set_peers_validate (vrrp_vr_t * vr, ip46_address_t * peers)
763 {
764   if (!vrrp_vr_is_unicast (vr))
765     {
766       clib_warning ("Peers can only be set on a unicast VR");
767       return VNET_API_ERROR_INVALID_ARGUMENT;
768     }
769
770   if (vr->runtime.state != VRRP_VR_STATE_INIT)
771     {
772       clib_warning ("Cannot set peers on a running VR");
773       return VNET_API_ERROR_RSRC_IN_USE;
774     }
775
776   if (vec_len (peers) == 0)
777     {
778       clib_warning ("No peer addresses provided");
779       return VNET_API_ERROR_INVALID_DST_ADDRESS;
780     }
781
782   return 0;
783 }
784
785 int
786 vrrp_vr_set_peers (vrrp_vr_key_t * vr_key, ip46_address_t * peers)
787 {
788   vrrp_main_t *vmp = &vrrp_main;
789   uword *p;
790   vrrp_vr_t *vr = 0;
791   int ret = 0;
792
793   p = mhash_get (&vmp->vr_index_by_key, vr_key);
794   if (!p)
795     return VNET_API_ERROR_NO_SUCH_ENTRY;
796
797   vr = pool_elt_at_index (vmp->vrs, p[0]);
798
799   ret = vrrp_vr_set_peers_validate (vr, peers);
800   if (ret < 0)
801     return ret;
802
803   if (vr->config.peer_addrs)
804     vec_free (vr->config.peer_addrs);
805
806   vr->config.peer_addrs = vec_dup (peers);
807
808   return 0;
809 }
810
811 /* Manage reference on the interface to the VRs which track that interface */
812 static void
813 vrrp_intf_tracking_vr_add_del (u32 sw_if_index, vrrp_vr_t * vr, u8 is_add)
814 {
815   vrrp_intf_t *intf;
816   u32 vr_index;
817   u8 is_ipv6 = vrrp_vr_is_ipv6 (vr);
818   int i;
819
820   intf = vrrp_intf_get (sw_if_index);
821   vr_index = vrrp_vr_index (vr);
822
823   /* Try to find the VR index in the list of tracking VRs */
824   vec_foreach_index (i, intf->tracking_vrs[is_ipv6])
825   {
826     if (vec_elt (intf->tracking_vrs[is_ipv6], i) != vr_index)
827       continue;
828
829     /* Current index matches VR index */
830     if (!is_add)
831       vec_delete (intf->tracking_vrs[is_ipv6], 1, i);
832
833     /* If deleting, the job is done. If adding, it's already here */
834     return;
835   }
836
837   /* vr index was not found. */
838   if (is_add)
839     vec_add1 (intf->tracking_vrs[is_ipv6], vr_index);
840 }
841
842 /* Check if sw intf admin state is up or in the process of coming up */
843 static int
844 vrrp_intf_sw_admin_up (u32 sw_if_index, vrrp_intf_update_t * pending)
845 {
846   vnet_main_t *vnm = vnet_get_main ();
847   int admin_up;
848
849   if (pending && (pending->type == VRRP_IF_UPDATE_SW_ADMIN))
850     admin_up = pending->intf_up;
851   else
852     admin_up = vnet_sw_interface_is_admin_up (vnm, sw_if_index);
853
854   return admin_up;
855 }
856
857 /* Check if hw intf link state is up or int the process of coming up */
858 static int
859 vrrp_intf_hw_link_up (u32 sw_if_index, vrrp_intf_update_t * pending)
860 {
861   vnet_main_t *vnm = vnet_get_main ();
862   vnet_sw_interface_t *sup_sw;
863   int link_up;
864
865   sup_sw = vnet_get_sup_sw_interface (vnm, sw_if_index);
866
867   if (pending && (pending->type == VRRP_IF_UPDATE_HW_LINK) &&
868       (pending->hw_if_index == sup_sw->hw_if_index))
869     link_up = pending->intf_up;
870   else
871     link_up = vnet_hw_interface_is_link_up (vnm, sup_sw->hw_if_index);
872
873   return link_up;
874 }
875
876 /* Check if interface has ability to send IP packets. */
877 static int
878 vrrp_intf_ip_up (u32 sw_if_index, u8 is_ipv6, vrrp_intf_update_t * pending)
879 {
880   int ip_up;
881
882   if (pending && pending->type == VRRP_IF_UPDATE_IP)
883     ip_up = pending->intf_up;
884   else
885     /* Either a unicast address has to be explicitly assigned, or
886      * for IPv6 only, a link local assigned and multicast/ND enabled
887      */
888     ip_up =
889       ((ip_interface_get_first_ip (sw_if_index, !is_ipv6) != 0) ||
890        (is_ipv6 && ip6_link_is_enabled (sw_if_index)));
891
892   return ip_up;
893 }
894
895 static int
896 vrrp_intf_is_up (u32 sw_if_index, u8 is_ipv6, vrrp_intf_update_t * pending)
897 {
898   int admin_up, link_up, ip_up;
899
900   admin_up = vrrp_intf_sw_admin_up (sw_if_index, pending);
901   link_up = vrrp_intf_hw_link_up (sw_if_index, pending);
902   ip_up = vrrp_intf_ip_up (sw_if_index, is_ipv6, pending);
903
904   return (admin_up && link_up && ip_up);
905 }
906
907 /* Examine the state of interfaces tracked by a VR and compute the priority
908  * adjustment that should be applied to the VR. If this is being called
909  * by the hw_link_up_down callback, the pending new flags on the sup hw
910  * interface have not been updated yet, so accept those as an optional
911  * argument.
912  */
913 void
914 vrrp_vr_tracking_ifs_compute (vrrp_vr_t * vr, vrrp_intf_update_t * pending)
915 {
916   vrrp_vr_tracking_if_t *intf;
917   u32 total_priority = 0;
918
919   vec_foreach (intf, vr->tracking.interfaces)
920   {
921     if (vrrp_intf_is_up (intf->sw_if_index, vrrp_vr_is_ipv6 (vr), pending))
922       continue;
923
924     total_priority += intf->priority;
925   }
926
927   if (total_priority != vr->tracking.interfaces_dec)
928     {
929       clib_warning ("VR %U interface track adjustment change from %u to %u",
930                     format_vrrp_vr_key, vr, vr->tracking.interfaces_dec,
931                     total_priority);
932       vr->tracking.interfaces_dec = total_priority;
933     }
934 }
935
936 /* Manage tracked interfaces on a VR */
937 int
938 vrrp_vr_tracking_if_add_del (vrrp_vr_t * vr, u32 sw_if_index, u8 prio,
939                              u8 is_add)
940 {
941   vnet_main_t *vnm = vnet_get_main ();
942   vrrp_vr_tracking_if_t *track_intf;
943
944   /* VR can't track non-existent interface */
945   if (!vnet_sw_interface_is_valid (vnm, sw_if_index))
946     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
947
948   /* VR can't track it's own interface */
949   if (sw_if_index == vr->config.sw_if_index)
950     return VNET_API_ERROR_INVALID_SW_IF_INDEX_2;
951
952   /* update intf vector of tracking VRs */
953   vrrp_intf_tracking_vr_add_del (sw_if_index, vr, is_add);
954
955   /* update VR vector of tracked interfaces */
956   vec_foreach (track_intf, vr->tracking.interfaces)
957   {
958     if (track_intf->sw_if_index != sw_if_index)
959       continue;
960
961     /* found it */
962     if (!is_add)
963       vec_delete
964         (vr->tracking.interfaces, 1, track_intf - vr->tracking.interfaces);
965
966     return 0;
967   }
968
969   if (is_add)
970     {
971       vec_add2 (vr->tracking.interfaces, track_intf, 1);
972
973       track_intf->sw_if_index = sw_if_index;
974       track_intf->priority = prio;
975     }
976
977   return 0;
978 }
979
980 int
981 vrrp_vr_tracking_ifs_add_del (vrrp_vr_t * vr,
982                               vrrp_vr_tracking_if_t * track_ifs, u8 is_add)
983 {
984   vrrp_vr_tracking_if_t *track_if, *ifs_copy;
985   int rv = 0;
986
987   /* if deleting & track_ifs points to the VR list of tracked intfs, the
988    * vector could be modified as we iterate it. make a copy instead */
989   ifs_copy = vec_dup (track_ifs);
990
991   /* add each tracked interface in the vector */
992   vec_foreach (track_if, ifs_copy)
993   {
994     rv = vrrp_vr_tracking_if_add_del (vr, track_if->sw_if_index,
995                                       track_if->priority, (is_add != 0));
996
997     /* if operation failed, undo the previous changes */
998     if (rv)
999       {
1000         vrrp_vr_tracking_if_t *rb_if;
1001
1002         for (rb_if = track_if - 1; rb_if >= track_ifs; rb_if -= 1)
1003           vrrp_vr_tracking_if_add_del (vr, rb_if->sw_if_index,
1004                                        rb_if->priority, !(is_add != 0));
1005         break;
1006       }
1007   }
1008
1009   vec_free (ifs_copy);
1010
1011   vrrp_vr_tracking_ifs_compute (vr, 0);
1012
1013   return rv;
1014 }
1015
1016 /* Compute priority to advertise on all VRs which track the given interface
1017  * and address family. The flags on an HW interface are not updated until
1018  * after link up/down callbacks are invoked, so if this function is called
1019  * by the link up/down callback, the flags about to be set will be passed
1020  * via the 'pending' argument. Otherwise, pending will be NULL.
1021  */
1022 static void
1023 vrrp_intf_tracking_vrs_compute (u32 sw_if_index,
1024                                 vrrp_intf_update_t * pending, u8 is_ipv6)
1025 {
1026   u32 *vr_index;
1027   vrrp_vr_t *vr;
1028   vrrp_intf_t *intf = vrrp_intf_get (sw_if_index);
1029
1030   vec_foreach (vr_index, intf->tracking_vrs[is_ipv6])
1031   {
1032     vr = vrrp_vr_lookup_index (*vr_index);
1033     if (vr)
1034       vrrp_vr_tracking_ifs_compute (vr, pending);
1035   }
1036 }
1037
1038 /* Interface being brought up/down is a quasi-{startup/shutdown} event.
1039  * Execute an appropriate state transition for all VRs on the interface.
1040  * This function may be invoked by:
1041  *    sw interface admin up/down event
1042  *    hw interface link up/down event
1043  */
1044 clib_error_t *
1045 vrrp_sw_interface_up_down (vrrp_intf_update_t * pending)
1046 {
1047   vrrp_intf_t *intf;
1048   int i;
1049   u32 *vr_index;
1050   vrrp_vr_t *vr;
1051
1052   intf = vrrp_intf_get (pending->sw_if_index);
1053   if (!intf)
1054     return 0;
1055
1056   /* adjust state of VR's configured on this interface */
1057   for (i = 0; i < 2; i++)
1058     {
1059       int is_up;
1060
1061       if (!intf->vr_indices[i])
1062         continue;
1063
1064       is_up = vrrp_intf_is_up (pending->sw_if_index, i, pending);
1065
1066       vec_foreach (vr_index, intf->vr_indices[i])
1067       {
1068         vrrp_vr_state_t vr_state;
1069
1070         vr = vrrp_vr_lookup_index (*vr_index);
1071         if (!vr)
1072           continue;
1073
1074         if (vr->runtime.state == VRRP_VR_STATE_INIT)
1075           continue;             /* VR not started yet, no transition */
1076
1077         if (!is_up)
1078           vr_state = VRRP_VR_STATE_INTF_DOWN;
1079         else
1080           {
1081             if (vr->runtime.state != VRRP_VR_STATE_INTF_DOWN)
1082               continue;         /* shouldn't happen */
1083
1084             vr_state = (vrrp_vr_is_owner (vr)) ?
1085               VRRP_VR_STATE_MASTER : VRRP_VR_STATE_BACKUP;
1086           }
1087
1088         vrrp_vr_transition (vr, vr_state, NULL);
1089       }
1090     }
1091
1092   /* compute adjustments on any VR's tracking this interface */
1093   vrrp_intf_tracking_vrs_compute (pending->sw_if_index, pending,
1094                                   0 /* is_ipv6 */ );
1095   vrrp_intf_tracking_vrs_compute (pending->sw_if_index, pending,
1096                                   1 /* is_ipv6 */ );
1097
1098   return 0;
1099 }
1100
1101 /* Process change in admin status on an interface */
1102 clib_error_t *
1103 vrrp_sw_interface_admin_up_down (vnet_main_t * vnm, u32 sw_if_index,
1104                                  u32 flags)
1105 {
1106   vrrp_intf_update_t pending = {
1107     .type = VRRP_IF_UPDATE_SW_ADMIN,
1108     .sw_if_index = sw_if_index,
1109     .intf_up = ((flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) != 0),
1110   };
1111
1112   return vrrp_sw_interface_up_down (&pending);
1113 }
1114
1115 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (vrrp_sw_interface_admin_up_down);
1116
1117 static walk_rc_t
1118 vrrp_hw_interface_link_up_down_walk (vnet_main_t * vnm,
1119                                      u32 sw_if_index, void *arg)
1120 {
1121   vrrp_intf_update_t *pending = arg;
1122
1123   pending->sw_if_index = sw_if_index;
1124   vrrp_sw_interface_up_down (pending);
1125
1126   return WALK_CONTINUE;
1127 }
1128
1129 static clib_error_t *
1130 vrrp_hw_interface_link_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
1131 {
1132   vrrp_intf_update_t pending = {
1133     .type = VRRP_IF_UPDATE_HW_LINK,
1134     .hw_if_index = hw_if_index,
1135     .intf_up = ((flags & VNET_HW_INTERFACE_FLAG_LINK_UP) != 0),
1136   };
1137
1138   /* walk the sw interface and sub interfaces to adjust interface tracking */
1139   vnet_hw_interface_walk_sw (vnm, hw_if_index,
1140                              vrrp_hw_interface_link_up_down_walk, &pending);
1141
1142   return 0;
1143 }
1144
1145 VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (vrrp_hw_interface_link_up_down);
1146
1147 static void
1148 vrrp_ip4_add_del_interface_addr (ip4_main_t * im,
1149                                  uword opaque,
1150                                  u32 sw_if_index,
1151                                  ip4_address_t * address,
1152                                  u32 address_length,
1153                                  u32 if_address_index, u32 is_del)
1154 {
1155   vrrp_intf_tracking_vrs_compute (sw_if_index, NULL, 0 /* is_ipv6 */ );
1156 }
1157
1158 static ip6_link_delegate_id_t vrrp_ip6_delegate_id;
1159
1160 static u8 *
1161 format_vrrp_ip6_link (u8 * s, va_list * args)
1162 {
1163   index_t indi = va_arg (*args, index_t);
1164   u32 indent = va_arg (*args, u32);
1165   vrrp_intf_t *intf;
1166   u32 *vr_index;
1167
1168   intf = vrrp_intf_get ((u32) indi);
1169
1170   s = format (s, "%UVRRP VRs monitoring this link:\n",
1171               format_white_space, indent);
1172
1173   vec_foreach (vr_index, intf->tracking_vrs[1])
1174   {
1175     vrrp_vr_t *vr = vrrp_vr_lookup_index (*vr_index);
1176
1177     s = format (s, "%U%U\n", format_white_space, indent + 2,
1178                 format_vrrp_vr_key, vr);
1179   }
1180
1181   return s;
1182 }
1183
1184 static void
1185 vrrp_intf_ip6_enable_disable (u32 sw_if_index, int enable)
1186 {
1187   vrrp_intf_update_t pending = {
1188     .type = VRRP_IF_UPDATE_IP,
1189     .sw_if_index = sw_if_index,
1190     .intf_up = enable,
1191   };
1192
1193   vrrp_intf_tracking_vrs_compute (sw_if_index, &pending, 1 /* is_ipv6 */ );
1194 }
1195
1196 static void
1197 vrrp_intf_ip6_enable (u32 sw_if_index)
1198 {
1199   vrrp_intf_ip6_enable_disable (sw_if_index, 1 /* enable */ );
1200   ip6_link_delegate_update (sw_if_index, vrrp_ip6_delegate_id, sw_if_index);
1201 }
1202
1203 static void
1204 vrrp_intf_ip6_disable (index_t indi)
1205 {
1206   vrrp_intf_ip6_enable_disable (indi, 0 /* enable */ );
1207 }
1208
1209 const static ip6_link_delegate_vft_t vrrp_ip6_delegate_vft = {
1210   .ildv_enable = vrrp_intf_ip6_enable,
1211   .ildv_disable = vrrp_intf_ip6_disable,
1212   .ildv_format = format_vrrp_ip6_link,
1213 };
1214
1215 static clib_error_t *
1216 vrrp_init (vlib_main_t * vm)
1217 {
1218   vrrp_main_t *vmp = &vrrp_main;
1219   clib_error_t *error = 0;
1220   ip4_main_t *im4 = &ip4_main;
1221   ip4_add_del_interface_address_callback_t cb4;
1222   vlib_node_t *intf_output_node;
1223
1224   clib_memset (vmp, 0, sizeof (*vmp));
1225
1226   if ((error = vlib_call_init_function (vm, ip4_lookup_init)) ||
1227       (error = vlib_call_init_function (vm, ip6_lookup_init)))
1228     return error;
1229
1230   vmp->vlib_main = vm;
1231   vmp->vnet_main = vnet_get_main ();
1232
1233   intf_output_node = vlib_get_node_by_name (vm, (u8 *) "interface-output");
1234   vmp->intf_output_node_idx = intf_output_node->index;
1235
1236   error = vrrp_plugin_api_hookup (vm);
1237
1238   if (error)
1239     return error;
1240
1241   mhash_init (&vmp->vr_index_by_key, sizeof (u32), sizeof (vrrp_vr_key_t));
1242   vmp->vrrp4_arp_lookup = hash_create (0, sizeof (uword));
1243   vmp->vrrp6_nd_lookup = hash_create_mem (0, sizeof (vrrp6_nd_key_t),
1244                                           sizeof (uword));
1245
1246   cb4.function = vrrp_ip4_add_del_interface_addr;
1247   cb4.function_opaque = 0;
1248   vec_add1 (im4->add_del_interface_address_callbacks, cb4);
1249
1250   vrrp_ip6_delegate_id = ip6_link_delegate_register (&vrrp_ip6_delegate_vft);
1251
1252   return error;
1253 }
1254
1255 VLIB_INIT_FUNCTION (vrrp_init);
1256
1257
1258 /* *INDENT-OFF* */
1259 VLIB_PLUGIN_REGISTER () =
1260 {
1261   .version = VPP_BUILD_VER,
1262   .description = "VRRP v3 (RFC 5798)",
1263 };
1264 /* *INDENT-ON* */
1265
1266 /*
1267  * fd.io coding-style-patch-verification: ON
1268  *
1269  * Local Variables:
1270  * eval: (c-set-style "gnu")
1271  * End:
1272  */